fmsystem-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Fmsystem-commits] [17159] API: upgrade HTMLPurifier from upstream


From: sigurdne
Subject: [Fmsystem-commits] [17159] API: upgrade HTMLPurifier from upstream
Date: Tue, 17 Oct 2017 10:10:28 -0400 (EDT)

Revision: 17159
          http://svn.sv.gnu.org/viewvc/?view=rev&root=fmsystem&revision=17159
Author:   sigurdne
Date:     2017-10-17 10:10:28 -0400 (Tue, 17 Oct 2017)
Log Message:
-----------
API: upgrade HTMLPurifier from upstream

Modified Paths:
--------------
    trunk/helpdesk/setup/phpgw_no.lang
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Arborize.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrCollections.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS/Color.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS/URI.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/HTML/ID.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/URI/Host.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/ImgRequired.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/CSSDefinition.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ChildDef/List.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ChildDef/Table.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Config.php
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema.ser
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/DefinitionCache/Serializer.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/DefinitionCache.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Encoder.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/EntityParser.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Generator.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModuleManager.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/Linkify.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/RemoveEmpty.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/SafeObject.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/DOMLex.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/DirectLex.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/PH5P.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Printer/ConfigForm.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Strategy/MakeWellFormed.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Token.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URI.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URIScheme/data.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.includes.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.safe-includes.php

Added Paths:
-----------
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/TargetNoopener.php
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/TargetNoreferrer.php
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt
    
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModule/TargetNoopener.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModule/TargetNoreferrer.php
    trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URIScheme/tel.php

Modified: trunk/helpdesk/setup/phpgw_no.lang
===================================================================
--- trunk/helpdesk/setup/phpgw_no.lang  2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/helpdesk/setup/phpgw_no.lang  2017-10-17 14:10:28 UTC (rev 17159)
@@ -117,7 +117,7 @@
 priority changed       helpdesk        no      Prioritet er endret
 percent        helpdesk        no      prosent
 project        helpdesk        no      Prosjekt
-reported by    helpdesk        no      Rapportert av
+reported by    helpdesk        no      Innmelder
 entry date     helpdesk        no      Registrert dato
 entry_date     helpdesk        no      Registrert dato
 send   helpdesk        no      Send

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Arborize.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Arborize.php   2017-10-17 
12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Arborize.php   2017-10-17 
14:10:28 UTC (rev 17159)
@@ -19,8 +19,8 @@
             if ($token instanceof HTMLPurifier_Token_End) {
                 $token->start = null; // [MUT]
                 $r = array_pop($stack);
-                assert($r->name === $token->name);
-                assert(empty($token->attr));
+                //assert($r->name === $token->name);
+                //assert(empty($token->attr));
                 $r->endCol = $token->col;
                 $r->endLine = $token->line;
                 $r->endArmor = $token->armor;
@@ -32,7 +32,7 @@
                 $stack[] = $node;
             }
         }
-        assert(count($stack) == 1);
+        //assert(count($stack) == 1);
         return $stack[0];
     }
 

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrCollections.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrCollections.php    
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrCollections.php    
2017-10-17 14:10:28 UTC (rev 17159)
@@ -22,6 +22,11 @@
      */
     public function __construct($attr_types, $modules)
     {
+        $this->doConstruct($attr_types, $modules);
+    }
+
+    public function doConstruct($attr_types, $modules)
+    {
         // load extensions from the modules
         foreach ($modules as $module) {
             foreach ($module->attr_collections as $coll_i => $coll) {

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS/Color.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS/Color.php  
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS/Color.php  
2017-10-17 14:10:28 UTC (rev 17159)
@@ -7,6 +7,16 @@
 {
 
     /**
+     * @type HTMLPurifier_AttrDef_CSS_AlphaValue
+     */
+    protected $alpha;
+
+    public function __construct()
+    {
+        $this->alpha = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+    }
+
+    /**
      * @param string $color
      * @param HTMLPurifier_Config $config
      * @param HTMLPurifier_Context $context
@@ -29,59 +39,104 @@
             return $colors[$lower];
         }
 
-        if (strpos($color, 'rgb(') !== false) {
-            // rgb literal handling
+        if (preg_match('#(rgb|rgba|hsl|hsla)\(#', $color, $matches) === 1) {
             $length = strlen($color);
             if (strpos($color, ')') !== $length - 1) {
                 return false;
             }
-            $triad = substr($color, 4, $length - 4 - 1);
-            $parts = explode(',', $triad);
-            if (count($parts) !== 3) {
+
+            // get used function : rgb, rgba, hsl or hsla
+            $function = $matches[1];
+
+            $parameters_size = 3;
+            $alpha_channel = false;
+            if (substr($function, -1) === 'a') {
+                $parameters_size = 4;
+                $alpha_channel = true;
+            }
+
+            /*
+             * Allowed types for values :
+             * parameter_position => [type => max_value]
+             */
+            $allowed_types = array(
+                1 => array('percentage' => 100, 'integer' => 255),
+                2 => array('percentage' => 100, 'integer' => 255),
+                3 => array('percentage' => 100, 'integer' => 255),
+            );
+            $allow_different_types = false;
+
+            if (strpos($function, 'hsl') !== false) {
+                $allowed_types = array(
+                    1 => array('integer' => 360),
+                    2 => array('percentage' => 100),
+                    3 => array('percentage' => 100),
+                );
+                $allow_different_types = true;
+            }
+
+            $values = trim(str_replace($function, '', $color), ' ()');
+
+            $parts = explode(',', $values);
+            if (count($parts) !== $parameters_size) {
                 return false;
             }
-            $type = false; // to ensure that they're all the same type
+
+            $type = false;
             $new_parts = array();
+            $i = 0;
+
             foreach ($parts as $part) {
+                $i++;
                 $part = trim($part);
+
                 if ($part === '') {
                     return false;
                 }
-                $length = strlen($part);
-                if ($part[$length - 1] === '%') {
-                    // handle percents
-                    if (!$type) {
-                        $type = 'percentage';
-                    } elseif ($type !== 'percentage') {
+
+                // different check for alpha channel
+                if ($alpha_channel === true && $i === count($parts)) {
+                    $result = $this->alpha->validate($part, $config, $context);
+
+                    if ($result === false) {
                         return false;
                     }
-                    $num = (float)substr($part, 0, $length - 1);
-                    if ($num < 0) {
-                        $num = 0;
+
+                    $new_parts[] = (string)$result;
+                    continue;
                     }
-                    if ($num > 100) {
-                        $num = 100;
-                    }
-                    $new_parts[] = "$num%";
+
+                if (substr($part, -1) === '%') {
+                    $current_type = 'percentage';
                 } else {
-                    // handle integers
-                    if (!$type) {
-                        $type = 'integer';
-                    } elseif ($type !== 'integer') {
+                    $current_type = 'integer';
+                }
+
+                if (!array_key_exists($current_type, $allowed_types[$i])) {
                         return false;
                     }
-                    $num = (int)$part;
-                    if ($num < 0) {
-                        $num = 0;
+
+                if (!$type) {
+                    $type = $current_type;
                     }
-                    if ($num > 255) {
-                        $num = 255;
+
+                if ($allow_different_types === false && $type != 
$current_type) {
+                    return false;
                     }
-                    $new_parts[] = (string)$num;
+
+                $max_value = $allowed_types[$i][$current_type];
+
+                if ($current_type == 'integer') {
+                    // Return value between range 0 -> $max_value
+                    $new_parts[] = (int)max(min($part, $max_value), 0);
+                } elseif ($current_type == 'percentage') {
+                    $new_parts[] = (float)max(min(rtrim($part, '%'), 
$max_value), 0) . '%';
                 }
             }
-            $new_triad = implode(',', $new_parts);
-            $color = "rgb($new_triad)";
+
+            $new_values = implode(',', $new_parts);
+
+            $color = $function . '(' . $new_values . ')';
         } else {
             // hexadecimal handling
             if ($color[0] === '#') {
@@ -100,6 +155,7 @@
         }
         return $color;
     }
+
 }
 
 // vim: et sw=4 sts=4

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS/URI.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS/URI.php    
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS/URI.php    
2017-10-17 14:10:28 UTC (rev 17159)
@@ -33,6 +33,9 @@
             return false;
         }
         $uri_string = substr($uri_string, 4);
+        if (strlen($uri_string) == 0) {
+            return false;
+        }
         $new_length = strlen($uri_string) - 1;
         if ($uri_string[$new_length] != ')') {
             return false;

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS.php        
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/CSS.php        
2017-10-17 14:10:28 UTC (rev 17159)
@@ -25,15 +25,42 @@
         $css = $this->parseCDATA($css);
 
         $definition = $config->getCSSDefinition();
+        $allow_duplicates = $config->get("CSS.AllowDuplicates");
 
-        // we're going to break the spec and explode by semicolons.
-        // This is because semicolon rarely appears in escaped form
-        // Doing this is generally flaky but fast
-        // IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
-        // for details
 
-        $declarations = explode(';', $css);
+        // According to the CSS2.1 spec, the places where a
+        // non-delimiting semicolon can appear are in strings
+        // escape sequences.   So here is some dumb hack to
+        // handle quotes.
+        $len = strlen($css);
+        $accum = "";
+        $declarations = array();
+        $quoted = false;
+        for ($i = 0; $i < $len; $i++) {
+            $c = strcspn($css, ";'\"", $i);
+            $accum .= substr($css, $i, $c);
+            $i += $c;
+            if ($i == $len) break;
+            $d = $css[$i];
+            if ($quoted) {
+                $accum .= $d;
+                if ($d == $quoted) {
+                    $quoted = false;
+                }
+            } else {
+                if ($d == ";") {
+                    $declarations[] = $accum;
+                    $accum = "";
+                } else {
+                    $accum .= $d;
+                    $quoted = $d;
+                }
+            }
+        }
+        if ($accum != "") $declarations[] = $accum;
+
         $propvalues = array();
+        $new_declarations = '';
 
         /**
          * Name of the current CSS property being validated.
@@ -83,8 +110,12 @@
             if ($result === false) {
                 continue;
             }
+            if ($allow_duplicates) {
+                $new_declarations .= "$property:$result;";
+            } else {
             $propvalues[$property] = $result;
         }
+        }
 
         $context->destroy('CurrentCSSProperty');
 
@@ -92,7 +123,6 @@
         // slightly inefficient, but it's the only way of getting rid of
         // duplicates. Perhaps config to optimize it, but not now.
 
-        $new_declarations = '';
         foreach ($propvalues as $prop => $value) {
             $new_declarations .= "$prop:$value;";
         }

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/HTML/ID.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/HTML/ID.php    
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/HTML/ID.php    
2017-10-17 14:10:28 UTC (rev 17159)
@@ -72,8 +72,13 @@
 
         // we purposely avoid using regex, hopefully this is faster
 
+        if ($config->get('Attr.ID.HTML5') === true) {
+            if (preg_match('/[\t\n\x0b\x0c ]/', $id)) {
+                return false;
+            }
+        } else {
         if (ctype_alpha($id)) {
-            $result = true;
+                // OK
         } else {
             if (!ctype_alpha(@$id[0])) {
                 return false;
@@ -83,7 +88,10 @@
                 $id,
                 'A..Za..z0..9:-._'
               );
-            $result = ($trim === '');
+                if ($trim !== '') {
+                    return false;
+                }
+            }
         }
 
         $regexp = $config->get('Attr.IDBlacklistRegexp');
@@ -91,7 +99,7 @@
             return false;
         }
 
-        if (!$this->selector && $result) {
+        if (!$this->selector) {
             $id_accumulator->add($id);
         }
 
@@ -98,7 +106,7 @@
         // if no change was made to the ID, return the result
         // else, return the new id if stripping whitespace made it
         //     valid, or return false.
-        return $result ? $id : false;
+        return $id;
     }
 }
 

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/URI/Host.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/URI/Host.php   
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef/URI/Host.php   
2017-10-17 14:10:28 UTC (rev 17159)
@@ -76,24 +76,33 @@
         // fairly well supported.
         $underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
 
+        // Based off of RFC 1738, but amended so that
+        // as per RFC 3696, the top label need only not be all numeric.
         // The productions describing this are:
         $a   = '[a-z]';     // alpha
         $an  = '[a-z0-9]';  // alphanum
         $and = "[a-z0-9-$underscore]"; // alphanum | "-"
         // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
-        $domainlabel   = "$an($and*$an)?";
-        // toplabel    = alpha | alpha *( alphanum | "-" ) alphanum
-        $toplabel      = "$a($and*$an)?";
+        $domainlabel = "$an(?:$and*$an)?";
+        // AMENDED as per RFC 3696
+        // toplabel    = alphanum | alphanum *( alphanum | "-" ) alphanum
+        //      side condition: not all numeric
+        $toplabel = "$an(?:$and*$an)?";
         // hostname    = *( domainlabel "." ) toplabel [ "." ]
-        if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
+        if (preg_match("/^(?:$domainlabel\.)*($toplabel)\.?$/i", $string, 
$matches)) {
+            if (!ctype_digit($matches[1])) {
             return $string;
         }
+        }
 
+        // PHP 5.3 and later support this functionality natively
+        if (function_exists('idn_to_ascii')) {
+            $string = idn_to_ascii($string);
+
         // If we have Net_IDNA2 support, we can support IRIs by
         // punycoding them. (This is the most portable thing to do,
         // since otherwise we have to assume browsers support
-
-        if ($config->get('Core.EnableIDNA')) {
+        } elseif ($config->get('Core.EnableIDNA')) {
             $idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => 
false, 'strict' => true));
             // we need to encode each period separately
             $parts = explode('.', $string);
@@ -114,13 +123,14 @@
                     }
                 }
                 $string = implode('.', $new_parts);
-                if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) 
{
-                    return $string;
-                }
             } catch (Exception $e) {
                 // XXX error reporting
             }
         }
+        // Try again
+        if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
+            return $string;
+        }
         return false;
     }
 }

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef.php    2017-10-17 
12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrDef.php    2017-10-17 
14:10:28 UTC (rev 17159)
@@ -86,7 +86,13 @@
      */
     protected function mungeRgb($string)
     {
-        return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 
'rgb(\1,\2,\3)', $string);
+        $p = '\s*(\d+(\.\d+)?([%]?))\s*';
+
+        if (preg_match('/(rgba|hsla)\(/', $string)) {
+            return 
preg_replace('/(rgba|hsla)\('.$p.','.$p.','.$p.','.$p.'\)/', 
'\1(\2,\5,\8,\11)', $string);
+        }
+
+        return preg_replace('/(rgb|hsl)\('.$p.','.$p.','.$p.'\)/', 
'\1(\2,\5,\8)', $string);
     }
 
     /**

Modified: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/ImgRequired.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/ImgRequired.php  
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/ImgRequired.php  
2017-10-17 14:10:28 UTC (rev 17159)
@@ -32,8 +32,7 @@
             if ($src) {
                 $alt = $config->get('Attr.DefaultImageAlt');
                 if ($alt === null) {
-                    // truncate if the alt is too long
-                    $attr['alt'] = substr(basename($attr['src']), 0, 40);
+                    $attr['alt'] = basename($attr['src']);
                 } else {
                     $attr['alt'] = $alt;
                 }

Added: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/TargetNoopener.php
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/TargetNoopener.php   
                            (rev 0)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/TargetNoopener.php   
    2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,37 @@
+<?php
+
+// must be called POST validation
+
+/**
+ * Adds rel="noopener" to any links which target a different window
+ * than the current one.  This is used to prevent malicious websites
+ * from silently replacing the original window, which could be used
+ * to do phishing.
+ * This transform is controlled by %HTML.TargetNoopener.
+ */
+class HTMLPurifier_AttrTransform_TargetNoopener extends 
HTMLPurifier_AttrTransform
+{
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (isset($attr['rel'])) {
+            $rels = explode(' ', $attr['rel']);
+        } else {
+            $rels = array();
+        }
+        if (isset($attr['target']) && !in_array('noopener', $rels)) {
+            $rels[] = 'noopener';
+        }
+        if (!empty($rels) || isset($attr['rel'])) {
+            $attr['rel'] = implode(' ', $rels);
+        }
+
+        return $attr;
+    }
+}
+

Added: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/TargetNoreferrer.php
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/TargetNoreferrer.php 
                            (rev 0)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/AttrTransform/TargetNoreferrer.php 
    2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,37 @@
+<?php
+
+// must be called POST validation
+
+/**
+ * Adds rel="noreferrer" to any links which target a different window
+ * than the current one.  This is used to prevent malicious websites
+ * from silently replacing the original window, which could be used
+ * to do phishing.
+ * This transform is controlled by %HTML.TargetNoreferrer.
+ */
+class HTMLPurifier_AttrTransform_TargetNoreferrer extends 
HTMLPurifier_AttrTransform
+{
+    /**
+     * @param array $attr
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return array
+     */
+    public function transform($attr, $config, $context)
+    {
+        if (isset($attr['rel'])) {
+            $rels = explode(' ', $attr['rel']);
+        } else {
+            $rels = array();
+        }
+        if (isset($attr['target']) && !in_array('noreferrer', $rels)) {
+            $rels[] = 'noreferrer';
+        }
+        if (!empty($rels) || isset($attr['rel'])) {
+            $attr['rel'] = implode(' ', $rels);
+        }
+
+        return $attr;
+    }
+}
+

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/CSSDefinition.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/CSSDefinition.php      
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/CSSDefinition.php      
2017-10-17 14:10:28 UTC (rev 17159)
@@ -225,6 +225,10 @@
         );
         $max = $config->get('CSS.MaxImgLength');
 
+        $this->info['min-width'] =
+        $this->info['max-width'] =
+        $this->info['min-height'] =
+        $this->info['max-height'] =
         $this->info['width'] =
         $this->info['height'] =
             $max === null ?
@@ -370,6 +374,19 @@
         );
         $this->info['page-break-inside'] = new 
HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
 
+        $border_radius = new HTMLPurifier_AttrDef_CSS_Composite(
+            array(
+                new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow 
negative
+                new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
+            ));
+
+        $this->info['border-top-left-radius'] =
+        $this->info['border-top-right-radius'] =
+        $this->info['border-bottom-right-radius'] =
+        $this->info['border-bottom-left-radius'] = new 
HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2);
+        // TODO: support SLASH syntax
+        $this->info['border-radius'] = new 
HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
+
     }
 
     /**

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ChildDef/List.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ChildDef/List.php      
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ChildDef/List.php      
2017-10-17 14:10:28 UTC (rev 17159)
@@ -38,6 +38,12 @@
             return false;
         }
 
+        // if li is not allowed, delete parent node
+        if (!isset($config->getHTMLDefinition()->info['li'])) {
+            trigger_error("Cannot allow ul/ol without allowing li", 
E_USER_WARNING);
+            return false;
+        }
+
         // the new set of children
         $result = array();
 
@@ -44,7 +50,7 @@
         // a little sanity check to make sure it's not ALL whitespace
         $all_whitespace = true;
 
-        $current_li = false;
+        $current_li = null;
 
         foreach ($children as $node) {
             if (!empty($node->is_whitespace)) {
@@ -65,7 +71,7 @@
                 // to handle non-list elements; non-list elements should
                 // not be appended to an existing li; only li created
                 // for non-list. This distinction is not currently made.
-                if ($current_li === false) {
+                if ($current_li === null) {
                     $current_li = new HTMLPurifier_Node_Element('li');
                     $result[] = $current_li;
                 }

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ChildDef/Table.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ChildDef/Table.php     
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ChildDef/Table.php     
2017-10-17 14:10:28 UTC (rev 17159)
@@ -203,7 +203,7 @@
                     $current_tr_tbody->children[] = $node;
                     break;
                 case '#PCDATA':
-                    assert($node->is_whitespace);
+                    //assert($node->is_whitespace);
                     if ($current_tr_tbody === null) {
                         $ret[] = $node;
                 } else {

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Config.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Config.php     2017-10-17 
12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Config.php     2017-10-17 
14:10:28 UTC (rev 17159)
@@ -21,7 +21,7 @@
      * HTML Purifier's version
      * @type string
      */
-    public $version = '4.7.0';
+    public $version = '4.9.3';
 
     /**
      * Whether or not to automatically finalize
@@ -333,7 +333,7 @@
         }
 
         // Raw type might be negative when using the fully optimized form
-        // of stdclass, which indicates allow_null == true
+        // of stdClass, which indicates allow_null == true
         $rtype = is_int($def) ? $def : $def->type;
         if ($rtype < 0) {
             $type = -$rtype;

Added: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt
                          (rev 0)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt
  2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,10 @@
+Attr.ID.HTML5
+TYPE: bool/null
+DEFAULT: null
+VERSION: 4.8.0
+--DESCRIPTION--
+In HTML5, restrictions on the format of the id attribute have been 
significantly
+relaxed, such that any string is valid so long as it contains no spaces and
+is at least one character.  In lieu of a general HTML5 compatibility flag,
+set this configuration directive to true to use the relaxed rules.
+--# vim: et sw=4 sts=4

Added: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt
                            (rev 0)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt
    2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,11 @@
+CSS.AllowDuplicates
+TYPE: bool
+DEFAULT: false
+VERSION: 4.8.0
+--DESCRIPTION--
+<p>
+  By default, HTML Purifier removes duplicate CSS properties,
+  like <code>color:red; color:blue</code>.  If this is set to
+  true, duplicate properties are allowed.
+</p>
+--# vim: et sw=4 sts=4

Modified: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
    2017-10-17 12:07:58 UTC (rev 17158)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
    2017-10-17 14:10:28 UTC (rev 17159)
@@ -1,5 +1,5 @@
 Cache.SerializerPermissions
-TYPE: int
+TYPE: int/null
 VERSION: 4.3.0
 DEFAULT: 0755
 --DESCRIPTION--
@@ -8,4 +8,9 @@
     Directory permissions of the files and directories created inside
     the DefinitionCache/Serializer or other custom serializer path.
 </p>
+<p>
+    In HTML Purifier 4.8.0, this also supports <code>NULL</code>,
+    which means that no chmod'ing or directory creation shall
+    occur.
+</p>
 --# vim: et sw=4 sts=4

Added: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt
                          (rev 0)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt
  2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,16 @@
+Core.AggressivelyRemoveScript
+TYPE: bool
+VERSION: 4.9.0
+DEFAULT: true
+--DESCRIPTION--
+<p>
+    This directive enables aggressive pre-filter removal of
+    script tags.  This is not necessary for security,
+    but it can help work around a bug in libxml where embedded
+    HTML elements inside script sections cause the parser to
+    choke.  To revert to pre-4.9.0 behavior, set this to false.
+    This directive has no effect if %Core.Trusted is true,
+    %Core.RemoveScriptContents is false, or %Core.HiddenElements
+    does not contain script.
+</p>
+--# vim: et sw=4 sts=4

Added: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt
                               (rev 0)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt
       2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,36 @@
+Core.LegacyEntityDecoder
+TYPE: bool
+VERSION: 4.9.0
+DEFAULT: false
+--DESCRIPTION--
+<p>
+    Prior to HTML Purifier 4.9.0, entities were decoded by performing
+    a global search replace for all entities whose decoded versions
+    did not have special meanings under HTML, and replaced them with
+    their decoded versions.  We would match all entities, even if they did
+    not have a trailing semicolon, but only if there weren't any trailing
+    alphanumeric characters.
+</p>
+<table>
+<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
+<tr><td>&amp;yen;</td><td>&yen;</td><td>&yen;</td></tr>
+<tr><td>&amp;yen</td><td>&yen;</td><td>&yen;</td></tr>
+<tr><td>&amp;yena</td><td>&amp;yena</td><td>&amp;yena</td></tr>
+<tr><td>&amp;yen=</td><td>&yen;=</td><td>&yen;=</td></tr>
+</table>
+<p>
+    In HTML Purifier 4.9.0, we changed the behavior of entity parsing
+    to match entities that had missing trailing semicolons in less
+    cases, to more closely match HTML5 parsing behavior:
+</p>
+<table>
+<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
+<tr><td>&amp;yen;</td><td>&yen;</td><td>&yen;</td></tr>
+<tr><td>&amp;yen</td><td>&yen;</td><td>&yen;</td></tr>
+<tr><td>&amp;yena</td><td>&yen;a</td><td>&amp;yena</td></tr>
+<tr><td>&amp;yen=</td><td>&yen;=</td><td>&amp;yen=</td></tr>
+</table>
+<p>
+    This flag reverts back to pre-HTML Purifier 4.9.0 behavior.
+</p>
+--# vim: et sw=4 sts=4

Added: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt
                            (rev 0)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt
    2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,10 @@
+--# vim: et sw=4 sts=4
+HTML.TargetNoopener
+TYPE: bool
+VERSION: 4.8.0
+DEFAULT: TRUE
+--DESCRIPTION--
+If enabled, noopener rel attributes are added to links which have
+a target attribute associated with them.  This prevents malicious
+destinations from overwriting the original window.
+--# vim: et sw=4 sts=4

Added: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt
                          (rev 0)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt
  2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,9 @@
+HTML.TargetNoreferrer
+TYPE: bool
+VERSION: 4.8.0
+DEFAULT: TRUE
+--DESCRIPTION--
+If enabled, noreferrer rel attributes are added to links which have
+a target attribute associated with them.  This prevents malicious
+destinations from overwriting the original window.
+--# vim: et sw=4 sts=4

Modified: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
     2017-10-17 12:07:58 UTC (rev 17158)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
     2017-10-17 14:10:28 UTC (rev 17159)
@@ -8,6 +8,7 @@
   'ftp' => true,
   'nntp' => true,
   'news' => true,
+  'tel' => true,
 )
 --DESCRIPTION--
 Whitelist that defines the schemes that a URI is allowed to have.  This

Modified: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
      2017-10-17 12:07:58 UTC (rev 17158)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
      2017-10-17 14:10:28 UTC (rev 17159)
@@ -1,5 +1,5 @@
 URI.DefaultScheme
-TYPE: string
+TYPE: string/null
 DEFAULT: 'http'
 --DESCRIPTION--
 
@@ -7,4 +7,9 @@
     Defines through what scheme the output will be served, in order to
     select the proper object validator when no scheme information is present.
 </p>
+
+<p>
+    Starting with HTML Purifier 4.9.0, the default scheme can be null, in
+    which case we reject all URIs which do not have explicit schemes.
+</p>
 --# vim: et sw=4 sts=4

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema.ser
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema.ser        
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema/schema.ser        
2017-10-17 14:10:28 UTC (rev 17159)
@@ -1 +1 @@
-O:25:"HTMLPurifier_ConfigSchema":3:{s:8:"defaults";a:119:{s:19:"Attr.AllowedClasses";N;s:24:"Attr.AllowedFrameTargets";a:0:{}s:15:"Attr.AllowedRel";a:0:{}s:15:"Attr.AllowedRev";a:0:{}s:18:"Attr.ClassUseCDATA";N;s:20:"Attr.DefaultImageAlt";N;s:24:"Attr.DefaultInvalidImage";s:0:"";s:27:"Attr.DefaultInvalidImageAlt";s:13:"Invalid
 
image";s:19:"Attr.DefaultTextDir";s:3:"ltr";s:13:"Attr.EnableID";b:0;s:21:"Attr.ForbiddenClasses";a:0:{}s:16:"Attr.IDBlacklist";a:0:{}s:22:"Attr.IDBlacklistRegexp";N;s:13:"Attr.IDPrefix";s:0:"";s:18:"Attr.IDPrefixLocal";s:0:"";s:24:"AutoFormat.AutoParagraph";b:0;s:17:"AutoFormat.Custom";a:0:{}s:25:"AutoFormat.DisplayLinkURI";b:0;s:18:"AutoFormat.Linkify";b:0;s:33:"AutoFormat.PurifierLinkify.DocURL";s:3:"#%s";s:26:"AutoFormat.PurifierLinkify";b:0;s:32:"AutoFormat.RemoveEmpty.Predicate";a:4:{s:8:"colgroup";a:0:{}s:2:"th";a:0:{}s:2:"td";a:0:{}s:6:"iframe";a:1:{i:0;s:3:"src";}}s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";a:2:{s:2:"td";b:1;s:2:"th";b:1;}s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";b:0;s:22:"AutoFormat.RemoveEmpty";b:0;s:39:"AutoFormat.RemoveSpansWithoutAttributes";b:0;s:18:"CSS.AllowImportant";b:0;s:15:"CSS.AllowTricky";b:0;s:16:"CSS.AllowedFonts";N;s:21:"CSS.AllowedProperties";N;s:17:"CSS.DefinitionRev";i:1;s:23:"CSS.ForbiddenProperties";a:0:{}s:16:"CSS.MaxImgLength";s:6:"1200px";s:15:"CSS.Proprietary";b:0;s:11:"CSS.Trusted";b:0;s:20:"Cache.DefinitionImpl";s:10:"Serializer";s:20:"Cache.SerializerPath";N;s:27:"Cache.SerializerPermissions";i:493;s:22:"Core.AggressivelyFixLt";b:1;s:28:"Core.AllowHostnameUnderscore";b:0;s:18:"Core.CollectErrors";b:0;s:18:"Core.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:30:"Core.ConvertDocumentToFragment";b:1;s:36:"Core.DirectLexLineNumberSyncInterval";i:0;s:20:"Core.DisableExcludes";b:0;s:15:"Core.EnableIDNA";b:0;s:13:"Core.Encoding";s:5:"utf-8";s:26:"Core.EscapeInvalidChildren";b:0;s:22:"Core.EscapeInvalidTags";b:0;s:29:"Core.EscapeNonASCIICharacters";b:0;s:19:"Core.HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:13:"Core.Language";s:2:"en";s:14:"Core.LexerImpl";N;s:24:"Core.MaintainLineNumbers";N;s:22:"Core.NormalizeNewlines";b:1;s:21:"Core.RemoveInvalidImg";b:1;s:33:"Core.RemoveProcessingInstructions";b:0;s:25:"Core.RemoveScriptContents";N;s:13:"Filter.Custom";a:0:{}s:34:"Filter.ExtractStyleBlocks.Escaping";b:1;s:31:"Filter.ExtractStyleBlocks.Scope";N;s:34:"Filter.ExtractStyleBlocks.TidyImpl";N;s:25:"Filter.ExtractStyleBlocks";b:0;s:14:"Filter.YouTube";b:0;s:12:"HTML.Allowed";N;s:22:"HTML.AllowedAttributes";N;s:20:"HTML.AllowedComments";a:0:{}s:26:"HTML.AllowedCommentsRegexp";N;s:20:"HTML.AllowedElements";N;s:19:"HTML.AllowedModules";N;s:23:"HTML.Attr.Name.UseCDATA";b:0;s:17:"HTML.BlockWrapper";s:1:"p";s:16:"HTML.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:18:"HTML.CustomDoctype";N;s:17:"HTML.DefinitionID";N;s:18:"HTML.DefinitionRev";i:1;s:12:"HTML.Doctype";N;s:25:"HTML.FlashAllowFullScreen";b:0;s:24:"HTML.ForbiddenAttributes";a:0:{}s:22:"HTML.ForbiddenElements";a:0:{}s:17:"HTML.MaxImgLength";i:1200;s:13:"HTML.Nofollow";b:0;s:11:"HTML.Parent";s:3:"div";s:16:"HTML.Proprietary";b:0;s:14:"HTML.SafeEmbed";b:0;s:15:"HTML.SafeIframe";b:0;s:15:"HTML.SafeObject";b:0;s:18:"HTML.SafeScripting";a:0:{}s:11:"HTML.Strict";b:0;s:16:"HTML.TargetBlank";b:0;s:12:"HTML.TidyAdd";a:0:{}s:14:"HTML.TidyLevel";s:6:"medium";s:15:"HTML.TidyRemove";a:0:{}s:12:"HTML.Trusted";b:0;s:10:"HTML.XHTML";b:1;s:28:"Output.CommentScriptContents";b:1;s:19:"Output.FixInnerHTML";b:1;s:18:"Output.FlashCompat";b:0;s:14:"Output.Newline";N;s:15:"Output.SortAttr";b:0;s:17:"Output.TidyFormat";b:0;s:17:"Test.ForceNoIconv";b:0;s:18:"URI.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:8:"URI.Base";N;s:17:"URI.DefaultScheme";s:4:"http";s:16:"URI.DefinitionID";N;s:17:"URI.DefinitionRev";i:1;s:11:"URI.Disable";b:0;s:19:"URI.DisableExternal";b:0;s:28:"URI.DisableExternalResources";b:0;s:20:"URI.DisableResources";b:0;s:8:"URI.Host";N;s:17:"URI.HostBlacklist";a:0:{}s:16:"URI.MakeAbsolute";b:0;s:9:"URI.Munge";N;s:18:"URI.MungeResources";b:0;s:18:"URI.MungeSecretKey";N;s:26:"URI.OverrideAllowedSchemes";b:1;s:20:"URI.SafeIframeRegexp";N;}s:12:"defaultPlist";O:25:"HTMLPurifier_PropertyList":3:{s:7:"*data";a:119:{s:19:"Attr.AllowedClasses";N;s:24:"Attr.AllowedFrameTargets";a:0:{}s:15:"Attr.AllowedRel";a:0:{}s:15:"Attr.AllowedRev";a:0:{}s:18:"Attr.ClassUseCDATA";N;s:20:"Attr.DefaultImageAlt";N;s:24:"Attr.DefaultInvalidImage";s:0:"";s:27:"Attr.DefaultInvalidImageAlt";s:13:"Invalid
 
image";s:19:"Attr.DefaultTextDir";s:3:"ltr";s:13:"Attr.EnableID";b:0;s:21:"Attr.ForbiddenClasses";a:0:{}s:16:"Attr.IDBlacklist";a:0:{}s:22:"Attr.IDBlacklistRegexp";N;s:13:"Attr.IDPrefix";s:0:"";s:18:"Attr.IDPrefixLocal";s:0:"";s:24:"AutoFormat.AutoParagraph";b:0;s:17:"AutoFormat.Custom";a:0:{}s:25:"AutoFormat.DisplayLinkURI";b:0;s:18:"AutoFormat.Linkify";b:0;s:33:"AutoFormat.PurifierLinkify.DocURL";s:3:"#%s";s:26:"AutoFormat.PurifierLinkify";b:0;s:32:"AutoFormat.RemoveEmpty.Predicate";a:4:{s:8:"colgroup";a:0:{}s:2:"th";a:0:{}s:2:"td";a:0:{}s:6:"iframe";a:1:{i:0;s:3:"src";}}s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";a:2:{s:2:"td";b:1;s:2:"th";b:1;}s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";b:0;s:22:"AutoFormat.RemoveEmpty";b:0;s:39:"AutoFormat.RemoveSpansWithoutAttributes";b:0;s:18:"CSS.AllowImportant";b:0;s:15:"CSS.AllowTricky";b:0;s:16:"CSS.AllowedFonts";N;s:21:"CSS.AllowedProperties";N;s:17:"CSS.DefinitionRev";i:1;s:23:"CSS.ForbiddenProperties";a:0:{}s:16:"CSS.MaxImgLength";s:6:"1200px";s:15:"CSS.Proprietary";b:0;s:11:"CSS.Trusted";b:0;s:20:"Cache.DefinitionImpl";s:10:"Serializer";s:20:"Cache.SerializerPath";N;s:27:"Cache.SerializerPermissions";i:493;s:22:"Core.AggressivelyFixLt";b:1;s:28:"Core.AllowHostnameUnderscore";b:0;s:18:"Core.CollectErrors";b:0;s:18:"Core.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:30:"Core.ConvertDocumentToFragment";b:1;s:36:"Core.DirectLexLineNumberSyncInterval";i:0;s:20:"Core.DisableExcludes";b:0;s:15:"Core.EnableIDNA";b:0;s:13:"Core.Encoding";s:5:"utf-8";s:26:"Core.EscapeInvalidChildren";b:0;s:22:"Core.EscapeInvalidTags";b:0;s:29:"Core.EscapeNonASCIICharacters";b:0;s:19:"Core.HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:13:"Core.Language";s:2:"en";s:14:"Core.LexerImpl";N;s:24:"Core.MaintainLineNumbers";N;s:22:"Core.NormalizeNewlines";b:1;s:21:"Core.RemoveInvalidImg";b:1;s:33:"Core.RemoveProcessingInstructions";b:0;s:25:"Core.RemoveScriptContents";N;s:13:"Filter.Custom";a:0:{}s:34:"Filter.ExtractStyleBlocks.Escaping";b:1;s:31:"Filter.ExtractStyleBlocks.Scope";N;s:34:"Filter.ExtractStyleBlocks.TidyImpl";N;s:25:"Filter.ExtractStyleBlocks";b:0;s:14:"Filter.YouTube";b:0;s:12:"HTML.Allowed";N;s:22:"HTML.AllowedAttributes";N;s:20:"HTML.AllowedComments";a:0:{}s:26:"HTML.AllowedCommentsRegexp";N;s:20:"HTML.AllowedElements";N;s:19:"HTML.AllowedModules";N;s:23:"HTML.Attr.Name.UseCDATA";b:0;s:17:"HTML.BlockWrapper";s:1:"p";s:16:"HTML.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:18:"HTML.CustomDoctype";N;s:17:"HTML.DefinitionID";N;s:18:"HTML.DefinitionRev";i:1;s:12:"HTML.Doctype";N;s:25:"HTML.FlashAllowFullScreen";b:0;s:24:"HTML.ForbiddenAttributes";a:0:{}s:22:"HTML.ForbiddenElements";a:0:{}s:17:"HTML.MaxImgLength";i:1200;s:13:"HTML.Nofollow";b:0;s:11:"HTML.Parent";s:3:"div";s:16:"HTML.Proprietary";b:0;s:14:"HTML.SafeEmbed";b:0;s:15:"HTML.SafeIframe";b:0;s:15:"HTML.SafeObject";b:0;s:18:"HTML.SafeScripting";a:0:{}s:11:"HTML.Strict";b:0;s:16:"HTML.TargetBlank";b:0;s:12:"HTML.TidyAdd";a:0:{}s:14:"HTML.TidyLevel";s:6:"medium";s:15:"HTML.TidyRemove";a:0:{}s:12:"HTML.Trusted";b:0;s:10:"HTML.XHTML";b:1;s:28:"Output.CommentScriptContents";b:1;s:19:"Output.FixInnerHTML";b:1;s:18:"Output.FlashCompat";b:0;s:14:"Output.Newline";N;s:15:"Output.SortAttr";b:0;s:17:"Output.TidyFormat";b:0;s:17:"Test.ForceNoIconv";b:0;s:18:"URI.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:8:"URI.Base";N;s:17:"URI.DefaultScheme";s:4:"http";s:16:"URI.DefinitionID";N;s:17:"URI.DefinitionRev";i:1;s:11:"URI.Disable";b:0;s:19:"URI.DisableExternal";b:0;s:28:"URI.DisableExternalResources";b:0;s:20:"URI.DisableResources";b:0;s:8:"URI.Host";N;s:17:"URI.HostBlacklist";a:0:{}s:16:"URI.MakeAbsolute";b:0;s:9:"URI.Munge";N;s:18:"URI.MungeResources";b:0;s:18:"URI.MungeSecretKey";N;s:26:"URI.OverrideAllowedSchemes";b:1;s:20:"URI.SafeIframeRegexp";N;}s:9:"*parent";N;s:8:"*cache";N;}s:4:"info";a:132:{s:19:"Attr.AllowedClasses";i:-8;s:24:"Attr.AllowedFrameTargets";i:8;s:15:"Attr.AllowedRel";i:8;s:15:"Attr.AllowedRev";i:8;s:18:"Attr.ClassUseCDATA";i:-7;s:20:"Attr.DefaultImageAlt";i:-1;s:24:"Attr.DefaultInvalidImage";i:1;s:27:"Attr.DefaultInvalidImageAlt";i:1;s:19:"Attr.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:13:"Attr.EnableID";i:7;s:17:"HTML.EnableAttrID";O:8:"stdClass":2:{s:3:"key";s:13:"Attr.EnableID";s:7:"isAlias";b:1;}s:21:"Attr.ForbiddenClasses";i:8;s:16:"Attr.IDBlacklist";i:9;s:22:"Attr.IDBlacklistRegexp";i:-1;s:13:"Attr.IDPrefix";i:1;s:18:"Attr.IDPrefixLocal";i:1;s:24:"AutoFormat.AutoParagraph";i:7;s:17:"AutoFormat.Custom";i:9;s:25:"AutoFormat.DisplayLinkURI";i:7;s:18:"AutoFormat.Linkify";i:7;s:33:"AutoFormat.PurifierLinkify.DocURL";i:1;s:37:"AutoFormatParam.PurifierLinkifyDocURL";O:8:"stdClass":2:{s:3:"key";s:33:"AutoFormat.PurifierLinkify.DocURL";s:7:"isAlias";b:1;}s:26:"AutoFormat.PurifierLinkify";i:7;s:32:"AutoFormat.RemoveEmpty.Predicate";i:10;s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";i:8;s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";i:7;s:22:"AutoFormat.RemoveEmpty";i:7;s:39:"AutoFormat.RemoveSpansWithoutAttributes";i:7;s:18:"CSS.AllowImportant";i:7;s:15:"CSS.AllowTricky";i:7;s:16:"CSS.AllowedFonts";i:-8;s:21:"CSS.AllowedProperties";i:-8;s:17:"CSS.DefinitionRev";i:5;s:23:"CSS.ForbiddenProperties";i:8;s:16:"CSS.MaxImgLength";i:-1;s:15:"CSS.Proprietary";i:7;s:11:"CSS.Trusted";i:7;s:20:"Cache.DefinitionImpl";i:-1;s:20:"Core.DefinitionCache";O:8:"stdClass":2:{s:3:"key";s:20:"Cache.DefinitionImpl";s:7:"isAlias";b:1;}s:20:"Cache.SerializerPath";i:-1;s:27:"Cache.SerializerPermissions";i:5;s:22:"Core.AggressivelyFixLt";i:7;s:28:"Core.AllowHostnameUnderscore";i:7;s:18:"Core.CollectErrors";i:7;s:18:"Core.ColorKeywords";i:10;s:30:"Core.ConvertDocumentToFragment";i:7;s:24:"Core.AcceptFullDocuments";O:8:"stdClass":2:{s:3:"key";s:30:"Core.ConvertDocumentToFragment";s:7:"isAlias";b:1;}s:36:"Core.DirectLexLineNumberSyncInterval";i:5;s:20:"Core.DisableExcludes";i:7;s:15:"Core.EnableIDNA";i:7;s:13:"Core.Encoding";i:2;s:26:"Core.EscapeInvalidChildren";i:7;s:22:"Core.EscapeInvalidTags";i:7;s:29:"Core.EscapeNonASCIICharacters";i:7;s:19:"Core.HiddenElements";i:8;s:13:"Core.Language";i:1;s:14:"Core.LexerImpl";i:-11;s:24:"Core.MaintainLineNumbers";i:-7;s:22:"Core.NormalizeNewlines";i:7;s:21:"Core.RemoveInvalidImg";i:7;s:33:"Core.RemoveProcessingInstructions";i:7;s:25:"Core.RemoveScriptContents";i:-7;s:13:"Filter.Custom";i:9;s:34:"Filter.ExtractStyleBlocks.Escaping";i:7;s:33:"Filter.ExtractStyleBlocksEscaping";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.Escaping";s:7:"isAlias";b:1;}s:38:"FilterParam.ExtractStyleBlocksEscaping";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.Escaping";s:7:"isAlias";b:1;}s:31:"Filter.ExtractStyleBlocks.Scope";i:-1;s:30:"Filter.ExtractStyleBlocksScope";O:8:"stdClass":2:{s:3:"key";s:31:"Filter.ExtractStyleBlocks.Scope";s:7:"isAlias";b:1;}s:35:"FilterParam.ExtractStyleBlocksScope";O:8:"stdClass":2:{s:3:"key";s:31:"Filter.ExtractStyleBlocks.Scope";s:7:"isAlias";b:1;}s:34:"Filter.ExtractStyleBlocks.TidyImpl";i:-11;s:38:"FilterParam.ExtractStyleBlocksTidyImpl";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.TidyImpl";s:7:"isAlias";b:1;}s:25:"Filter.ExtractStyleBlocks";i:7;s:14:"Filter.YouTube";i:7;s:12:"HTML.Allowed";i:-4;s:22:"HTML.AllowedAttributes";i:-8;s:20:"HTML.AllowedComments";i:8;s:26:"HTML.AllowedCommentsRegexp";i:-1;s:20:"HTML.AllowedElements";i:-8;s:19:"HTML.AllowedModules";i:-8;s:23:"HTML.Attr.Name.UseCDATA";i:7;s:17:"HTML.BlockWrapper";i:1;s:16:"HTML.CoreModules";i:8;s:18:"HTML.CustomDoctype";i:-1;s:17:"HTML.DefinitionID";i:-1;s:18:"HTML.DefinitionRev";i:5;s:12:"HTML.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:25:"HTML.FlashAllowFullScreen";i:7;s:24:"HTML.ForbiddenAttributes";i:8;s:22:"HTML.ForbiddenElements";i:8;s:17:"HTML.MaxImgLength";i:-5;s:13:"HTML.Nofollow";i:7;s:11:"HTML.Parent";i:1;s:16:"HTML.Proprietary";i:7;s:14:"HTML.SafeEmbed";i:7;s:15:"HTML.SafeIframe";i:7;s:15:"HTML.SafeObject";i:7;s:18:"HTML.SafeScripting";i:8;s:11:"HTML.Strict";i:7;s:16:"HTML.TargetBlank";i:7;s:12:"HTML.TidyAdd";i:8;s:14:"HTML.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:15:"HTML.TidyRemove";i:8;s:12:"HTML.Trusted";i:7;s:10:"HTML.XHTML";i:7;s:10:"Core.XHTML";O:8:"stdClass":2:{s:3:"key";s:10:"HTML.XHTML";s:7:"isAlias";b:1;}s:28:"Output.CommentScriptContents";i:7;s:26:"Core.CommentScriptContents";O:8:"stdClass":2:{s:3:"key";s:28:"Output.CommentScriptContents";s:7:"isAlias";b:1;}s:19:"Output.FixInnerHTML";i:7;s:18:"Output.FlashCompat";i:7;s:14:"Output.Newline";i:-1;s:15:"Output.SortAttr";i:7;s:17:"Output.TidyFormat";i:7;s:15:"Core.TidyFormat";O:8:"stdClass":2:{s:3:"key";s:17:"Output.TidyFormat";s:7:"isAlias";b:1;}s:17:"Test.ForceNoIconv";i:7;s:18:"URI.AllowedSchemes";i:8;s:8:"URI.Base";i:-1;s:17:"URI.DefaultScheme";i:1;s:16:"URI.DefinitionID";i:-1;s:17:"URI.DefinitionRev";i:5;s:11:"URI.Disable";i:7;s:15:"Attr.DisableURI";O:8:"stdClass":2:{s:3:"key";s:11:"URI.Disable";s:7:"isAlias";b:1;}s:19:"URI.DisableExternal";i:7;s:28:"URI.DisableExternalResources";i:7;s:20:"URI.DisableResources";i:7;s:8:"URI.Host";i:-1;s:17:"URI.HostBlacklist";i:9;s:16:"URI.MakeAbsolute";i:7;s:9:"URI.Munge";i:-1;s:18:"URI.MungeResources";i:7;s:18:"URI.MungeSecretKey";i:-1;s:26:"URI.OverrideAllowedSchemes";i:7;s:20:"URI.SafeIframeRegexp";i:-1;}}
\ No newline at end of file
+O:25:"HTMLPurifier_ConfigSchema":3:{s:8:"defaults";a:125:{s:19:"Attr.AllowedClasses";N;s:24:"Attr.AllowedFrameTargets";a:0:{}s:15:"Attr.AllowedRel";a:0:{}s:15:"Attr.AllowedRev";a:0:{}s:18:"Attr.ClassUseCDATA";N;s:20:"Attr.DefaultImageAlt";N;s:24:"Attr.DefaultInvalidImage";s:0:"";s:27:"Attr.DefaultInvalidImageAlt";s:13:"Invalid
 
image";s:19:"Attr.DefaultTextDir";s:3:"ltr";s:13:"Attr.EnableID";b:0;s:21:"Attr.ForbiddenClasses";a:0:{}s:13:"Attr.ID.HTML5";N;s:16:"Attr.IDBlacklist";a:0:{}s:22:"Attr.IDBlacklistRegexp";N;s:13:"Attr.IDPrefix";s:0:"";s:18:"Attr.IDPrefixLocal";s:0:"";s:24:"AutoFormat.AutoParagraph";b:0;s:17:"AutoFormat.Custom";a:0:{}s:25:"AutoFormat.DisplayLinkURI";b:0;s:18:"AutoFormat.Linkify";b:0;s:33:"AutoFormat.PurifierLinkify.DocURL";s:3:"#%s";s:26:"AutoFormat.PurifierLinkify";b:0;s:32:"AutoFormat.RemoveEmpty.Predicate";a:4:{s:8:"colgroup";a:0:{}s:2:"th";a:0:{}s:2:"td";a:0:{}s:6:"iframe";a:1:{i:0;s:3:"src";}}s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";a:2:{s:2:"td";b:1;s:2:"th";b:1;}s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";b:0;s:22:"AutoFormat.RemoveEmpty";b:0;s:39:"AutoFormat.RemoveSpansWithoutAttributes";b:0;s:19:"CSS.AllowDuplicates";b:0;s:18:"CSS.AllowImportant";b:0;s:15:"CSS.AllowTricky";b:0;s:16:"CSS.AllowedFonts";N;s:21:"CSS.AllowedProperties";N;s:17:"CSS.DefinitionRev";i:1;s:23:"CSS.ForbiddenProperties";a:0:{}s:16:"CSS.MaxImgLength";s:6:"1200px";s:15:"CSS.Proprietary";b:0;s:11:"CSS.Trusted";b:0;s:20:"Cache.DefinitionImpl";s:10:"Serializer";s:20:"Cache.SerializerPath";N;s:27:"Cache.SerializerPermissions";i:493;s:22:"Core.AggressivelyFixLt";b:1;s:29:"Core.AggressivelyRemoveScript";b:1;s:28:"Core.AllowHostnameUnderscore";b:0;s:18:"Core.CollectErrors";b:0;s:18:"Core.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:30:"Core.ConvertDocumentToFragment";b:1;s:36:"Core.DirectLexLineNumberSyncInterval";i:0;s:20:"Core.DisableExcludes";b:0;s:15:"Core.EnableIDNA";b:0;s:13:"Core.Encoding";s:5:"utf-8";s:26:"Core.EscapeInvalidChildren";b:0;s:22:"Core.EscapeInvalidTags";b:0;s:29:"Core.EscapeNonASCIICharacters";b:0;s:19:"Core.HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:13:"Core.Language";s:2:"en";s:24:"Core.LegacyEntityDecoder";b:0;s:14:"Core.LexerImpl";N;s:24:"Core.MaintainLineNumbers";N;s:22:"Core.NormalizeNewlines";b:1;s:21:"Core.RemoveInvalidImg";b:1;s:33:"Core.RemoveProcessingInstructions";b:0;s:25:"Core.RemoveScriptContents";N;s:13:"Filter.Custom";a:0:{}s:34:"Filter.ExtractStyleBlocks.Escaping";b:1;s:31:"Filter.ExtractStyleBlocks.Scope";N;s:34:"Filter.ExtractStyleBlocks.TidyImpl";N;s:25:"Filter.ExtractStyleBlocks";b:0;s:14:"Filter.YouTube";b:0;s:12:"HTML.Allowed";N;s:22:"HTML.AllowedAttributes";N;s:20:"HTML.AllowedComments";a:0:{}s:26:"HTML.AllowedCommentsRegexp";N;s:20:"HTML.AllowedElements";N;s:19:"HTML.AllowedModules";N;s:23:"HTML.Attr.Name.UseCDATA";b:0;s:17:"HTML.BlockWrapper";s:1:"p";s:16:"HTML.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:18:"HTML.CustomDoctype";N;s:17:"HTML.DefinitionID";N;s:18:"HTML.DefinitionRev";i:1;s:12:"HTML.Doctype";N;s:25:"HTML.FlashAllowFullScreen";b:0;s:24:"HTML.ForbiddenAttributes";a:0:{}s:22:"HTML.ForbiddenElements";a:0:{}s:17:"HTML.MaxImgLength";i:1200;s:13:"HTML.Nofollow";b:0;s:11:"HTML.Parent";s:3:"div";s:16:"HTML.Proprietary";b:0;s:14:"HTML.SafeEmbed";b:0;s:15:"HTML.SafeIframe";b:0;s:15:"HTML.SafeObject";b:0;s:18:"HTML.SafeScripting";a:0:{}s:11:"HTML.Strict";b:0;s:16:"HTML.TargetBlank";b:0;s:19:"HTML.TargetNoopener";b:1;s:21:"HTML.TargetNoreferrer";b:1;s:12:"HTML.TidyAdd";a:0:{}s:14:"HTML.TidyLevel";s:6:"medium";s:15:"HTML.TidyRemove";a:0:{}s:12:"HTML.Trusted";b:0;s:10:"HTML.XHTML";b:1;s:28:"Output.CommentScriptContents";b:1;s:19:"Output.FixInnerHTML";b:1;s:18:"Output.FlashCompat";b:0;s:14:"Output.Newline";N;s:15:"Output.SortAttr";b:0;s:17:"Output.TidyFormat";b:0;s:17:"Test.ForceNoIconv";b:0;s:18:"URI.AllowedSchemes";a:7:{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:3:"tel";b:1;}s:8:"URI.Base";N;s:17:"URI.DefaultScheme";s:4:"http";s:16:"URI.DefinitionID";N;s:17:"URI.DefinitionRev";i:1;s:11:"URI.Disable";b:0;s:19:"URI.DisableExternal";b:0;s:28:"URI.DisableExternalResources";b:0;s:20:"URI.DisableResources";b:0;s:8:"URI.Host";N;s:17:"URI.HostBlacklist";a:0:{}s:16:"URI.MakeAbsolute";b:0;s:9:"URI.Munge";N;s:18:"URI.MungeResources";b:0;s:18:"URI.MungeSecretKey";N;s:26:"URI.OverrideAllowedSchemes";b:1;s:20:"URI.SafeIframeRegexp";N;}s:12:"defaultPlist";O:25:"HTMLPurifier_PropertyList":3:{s:7:"*data";a:125:{s:19:"Attr.AllowedClasses";N;s:24:"Attr.AllowedFrameTargets";a:0:{}s:15:"Attr.AllowedRel";a:0:{}s:15:"Attr.AllowedRev";a:0:{}s:18:"Attr.ClassUseCDATA";N;s:20:"Attr.DefaultImageAlt";N;s:24:"Attr.DefaultInvalidImage";s:0:"";s:27:"Attr.DefaultInvalidImageAlt";s:13:"Invalid
 
image";s:19:"Attr.DefaultTextDir";s:3:"ltr";s:13:"Attr.EnableID";b:0;s:21:"Attr.ForbiddenClasses";a:0:{}s:13:"Attr.ID.HTML5";N;s:16:"Attr.IDBlacklist";a:0:{}s:22:"Attr.IDBlacklistRegexp";N;s:13:"Attr.IDPrefix";s:0:"";s:18:"Attr.IDPrefixLocal";s:0:"";s:24:"AutoFormat.AutoParagraph";b:0;s:17:"AutoFormat.Custom";a:0:{}s:25:"AutoFormat.DisplayLinkURI";b:0;s:18:"AutoFormat.Linkify";b:0;s:33:"AutoFormat.PurifierLinkify.DocURL";s:3:"#%s";s:26:"AutoFormat.PurifierLinkify";b:0;s:32:"AutoFormat.RemoveEmpty.Predicate";a:4:{s:8:"colgroup";a:0:{}s:2:"th";a:0:{}s:2:"td";a:0:{}s:6:"iframe";a:1:{i:0;s:3:"src";}}s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";a:2:{s:2:"td";b:1;s:2:"th";b:1;}s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";b:0;s:22:"AutoFormat.RemoveEmpty";b:0;s:39:"AutoFormat.RemoveSpansWithoutAttributes";b:0;s:19:"CSS.AllowDuplicates";b:0;s:18:"CSS.AllowImportant";b:0;s:15:"CSS.AllowTricky";b:0;s:16:"CSS.AllowedFonts";N;s:21:"CSS.AllowedProperties";N;s:17:"CSS.DefinitionRev";i:1;s:23:"CSS.ForbiddenProperties";a:0:{}s:16:"CSS.MaxImgLength";s:6:"1200px";s:15:"CSS.Proprietary";b:0;s:11:"CSS.Trusted";b:0;s:20:"Cache.DefinitionImpl";s:10:"Serializer";s:20:"Cache.SerializerPath";N;s:27:"Cache.SerializerPermissions";i:493;s:22:"Core.AggressivelyFixLt";b:1;s:29:"Core.AggressivelyRemoveScript";b:1;s:28:"Core.AllowHostnameUnderscore";b:0;s:18:"Core.CollectErrors";b:0;s:18:"Core.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:30:"Core.ConvertDocumentToFragment";b:1;s:36:"Core.DirectLexLineNumberSyncInterval";i:0;s:20:"Core.DisableExcludes";b:0;s:15:"Core.EnableIDNA";b:0;s:13:"Core.Encoding";s:5:"utf-8";s:26:"Core.EscapeInvalidChildren";b:0;s:22:"Core.EscapeInvalidTags";b:0;s:29:"Core.EscapeNonASCIICharacters";b:0;s:19:"Core.HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:13:"Core.Language";s:2:"en";s:24:"Core.LegacyEntityDecoder";b:0;s:14:"Core.LexerImpl";N;s:24:"Core.MaintainLineNumbers";N;s:22:"Core.NormalizeNewlines";b:1;s:21:"Core.RemoveInvalidImg";b:1;s:33:"Core.RemoveProcessingInstructions";b:0;s:25:"Core.RemoveScriptContents";N;s:13:"Filter.Custom";a:0:{}s:34:"Filter.ExtractStyleBlocks.Escaping";b:1;s:31:"Filter.ExtractStyleBlocks.Scope";N;s:34:"Filter.ExtractStyleBlocks.TidyImpl";N;s:25:"Filter.ExtractStyleBlocks";b:0;s:14:"Filter.YouTube";b:0;s:12:"HTML.Allowed";N;s:22:"HTML.AllowedAttributes";N;s:20:"HTML.AllowedComments";a:0:{}s:26:"HTML.AllowedCommentsRegexp";N;s:20:"HTML.AllowedElements";N;s:19:"HTML.AllowedModules";N;s:23:"HTML.Attr.Name.UseCDATA";b:0;s:17:"HTML.BlockWrapper";s:1:"p";s:16:"HTML.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:18:"HTML.CustomDoctype";N;s:17:"HTML.DefinitionID";N;s:18:"HTML.DefinitionRev";i:1;s:12:"HTML.Doctype";N;s:25:"HTML.FlashAllowFullScreen";b:0;s:24:"HTML.ForbiddenAttributes";a:0:{}s:22:"HTML.ForbiddenElements";a:0:{}s:17:"HTML.MaxImgLength";i:1200;s:13:"HTML.Nofollow";b:0;s:11:"HTML.Parent";s:3:"div";s:16:"HTML.Proprietary";b:0;s:14:"HTML.SafeEmbed";b:0;s:15:"HTML.SafeIframe";b:0;s:15:"HTML.SafeObject";b:0;s:18:"HTML.SafeScripting";a:0:{}s:11:"HTML.Strict";b:0;s:16:"HTML.TargetBlank";b:0;s:19:"HTML.TargetNoopener";b:1;s:21:"HTML.TargetNoreferrer";b:1;s:12:"HTML.TidyAdd";a:0:{}s:14:"HTML.TidyLevel";s:6:"medium";s:15:"HTML.TidyRemove";a:0:{}s:12:"HTML.Trusted";b:0;s:10:"HTML.XHTML";b:1;s:28:"Output.CommentScriptContents";b:1;s:19:"Output.FixInnerHTML";b:1;s:18:"Output.FlashCompat";b:0;s:14:"Output.Newline";N;s:15:"Output.SortAttr";b:0;s:17:"Output.TidyFormat";b:0;s:17:"Test.ForceNoIconv";b:0;s:18:"URI.AllowedSchemes";a:7:{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:3:"tel";b:1;}s:8:"URI.Base";N;s:17:"URI.DefaultScheme";s:4:"http";s:16:"URI.DefinitionID";N;s:17:"URI.DefinitionRev";i:1;s:11:"URI.Disable";b:0;s:19:"URI.DisableExternal";b:0;s:28:"URI.DisableExternalResources";b:0;s:20:"URI.DisableResources";b:0;s:8:"URI.Host";N;s:17:"URI.HostBlacklist";a:0:{}s:16:"URI.MakeAbsolute";b:0;s:9:"URI.Munge";N;s:18:"URI.MungeResources";b:0;s:18:"URI.MungeSecretKey";N;s:26:"URI.OverrideAllowedSchemes";b:1;s:20:"URI.SafeIframeRegexp";N;}s:9:"*parent";N;s:8:"*cache";N;}s:4:"info";a:138:{s:19:"Attr.AllowedClasses";i:-8;s:24:"Attr.AllowedFrameTargets";i:8;s:15:"Attr.AllowedRel";i:8;s:15:"Attr.AllowedRev";i:8;s:18:"Attr.ClassUseCDATA";i:-7;s:20:"Attr.DefaultImageAlt";i:-1;s:24:"Attr.DefaultInvalidImage";i:1;s:27:"Attr.DefaultInvalidImageAlt";i:1;s:19:"Attr.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:13:"Attr.EnableID";i:7;s:17:"HTML.EnableAttrID";O:8:"stdClass":2:{s:3:"key";s:13:"Attr.EnableID";s:7:"isAlias";b:1;}s:21:"Attr.ForbiddenClasses";i:8;s:13:"Attr.ID.HTML5";i:-7;s:16:"Attr.IDBlacklist";i:9;s:22:"Attr.IDBlacklistRegexp";i:-1;s:13:"Attr.IDPrefix";i:1;s:18:"Attr.IDPrefixLocal";i:1;s:24:"AutoFormat.AutoParagraph";i:7;s:17:"AutoFormat.Custom";i:9;s:25:"AutoFormat.DisplayLinkURI";i:7;s:18:"AutoFormat.Linkify";i:7;s:33:"AutoFormat.PurifierLinkify.DocURL";i:1;s:37:"AutoFormatParam.PurifierLinkifyDocURL";O:8:"stdClass":2:{s:3:"key";s:33:"AutoFormat.PurifierLinkify.DocURL";s:7:"isAlias";b:1;}s:26:"AutoFormat.PurifierLinkify";i:7;s:32:"AutoFormat.RemoveEmpty.Predicate";i:10;s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";i:8;s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";i:7;s:22:"AutoFormat.RemoveEmpty";i:7;s:39:"AutoFormat.RemoveSpansWithoutAttributes";i:7;s:19:"CSS.AllowDuplicates";i:7;s:18:"CSS.AllowImportant";i:7;s:15:"CSS.AllowTricky";i:7;s:16:"CSS.AllowedFonts";i:-8;s:21:"CSS.AllowedProperties";i:-8;s:17:"CSS.DefinitionRev";i:5;s:23:"CSS.ForbiddenProperties";i:8;s:16:"CSS.MaxImgLength";i:-1;s:15:"CSS.Proprietary";i:7;s:11:"CSS.Trusted";i:7;s:20:"Cache.DefinitionImpl";i:-1;s:20:"Core.DefinitionCache";O:8:"stdClass":2:{s:3:"key";s:20:"Cache.DefinitionImpl";s:7:"isAlias";b:1;}s:20:"Cache.SerializerPath";i:-1;s:27:"Cache.SerializerPermissions";i:-5;s:22:"Core.AggressivelyFixLt";i:7;s:29:"Core.AggressivelyRemoveScript";i:7;s:28:"Core.AllowHostnameUnderscore";i:7;s:18:"Core.CollectErrors";i:7;s:18:"Core.ColorKeywords";i:10;s:30:"Core.ConvertDocumentToFragment";i:7;s:24:"Core.AcceptFullDocuments";O:8:"stdClass":2:{s:3:"key";s:30:"Core.ConvertDocumentToFragment";s:7:"isAlias";b:1;}s:36:"Core.DirectLexLineNumberSyncInterval";i:5;s:20:"Core.DisableExcludes";i:7;s:15:"Core.EnableIDNA";i:7;s:13:"Core.Encoding";i:2;s:26:"Core.EscapeInvalidChildren";i:7;s:22:"Core.EscapeInvalidTags";i:7;s:29:"Core.EscapeNonASCIICharacters";i:7;s:19:"Core.HiddenElements";i:8;s:13:"Core.Language";i:1;s:24:"Core.LegacyEntityDecoder";i:7;s:14:"Core.LexerImpl";i:-11;s:24:"Core.MaintainLineNumbers";i:-7;s:22:"Core.NormalizeNewlines";i:7;s:21:"Core.RemoveInvalidImg";i:7;s:33:"Core.RemoveProcessingInstructions";i:7;s:25:"Core.RemoveScriptContents";i:-7;s:13:"Filter.Custom";i:9;s:34:"Filter.ExtractStyleBlocks.Escaping";i:7;s:33:"Filter.ExtractStyleBlocksEscaping";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.Escaping";s:7:"isAlias";b:1;}s:38:"FilterParam.ExtractStyleBlocksEscaping";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.Escaping";s:7:"isAlias";b:1;}s:31:"Filter.ExtractStyleBlocks.Scope";i:-1;s:30:"Filter.ExtractStyleBlocksScope";O:8:"stdClass":2:{s:3:"key";s:31:"Filter.ExtractStyleBlocks.Scope";s:7:"isAlias";b:1;}s:35:"FilterParam.ExtractStyleBlocksScope";O:8:"stdClass":2:{s:3:"key";s:31:"Filter.ExtractStyleBlocks.Scope";s:7:"isAlias";b:1;}s:34:"Filter.ExtractStyleBlocks.TidyImpl";i:-11;s:38:"FilterParam.ExtractStyleBlocksTidyImpl";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.TidyImpl";s:7:"isAlias";b:1;}s:25:"Filter.ExtractStyleBlocks";i:7;s:14:"Filter.YouTube";i:7;s:12:"HTML.Allowed";i:-4;s:22:"HTML.AllowedAttributes";i:-8;s:20:"HTML.AllowedComments";i:8;s:26:"HTML.AllowedCommentsRegexp";i:-1;s:20:"HTML.AllowedElements";i:-8;s:19:"HTML.AllowedModules";i:-8;s:23:"HTML.Attr.Name.UseCDATA";i:7;s:17:"HTML.BlockWrapper";i:1;s:16:"HTML.CoreModules";i:8;s:18:"HTML.CustomDoctype";i:-1;s:17:"HTML.DefinitionID";i:-1;s:18:"HTML.DefinitionRev";i:5;s:12:"HTML.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:25:"HTML.FlashAllowFullScreen";i:7;s:24:"HTML.ForbiddenAttributes";i:8;s:22:"HTML.ForbiddenElements";i:8;s:17:"HTML.MaxImgLength";i:-5;s:13:"HTML.Nofollow";i:7;s:11:"HTML.Parent";i:1;s:16:"HTML.Proprietary";i:7;s:14:"HTML.SafeEmbed";i:7;s:15:"HTML.SafeIframe";i:7;s:15:"HTML.SafeObject";i:7;s:18:"HTML.SafeScripting";i:8;s:11:"HTML.Strict";i:7;s:16:"HTML.TargetBlank";i:7;s:19:"HTML.TargetNoopener";i:7;s:21:"HTML.TargetNoreferrer";i:7;s:12:"HTML.TidyAdd";i:8;s:14:"HTML.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:15:"HTML.TidyRemove";i:8;s:12:"HTML.Trusted";i:7;s:10:"HTML.XHTML";i:7;s:10:"Core.XHTML";O:8:"stdClass":2:{s:3:"key";s:10:"HTML.XHTML";s:7:"isAlias";b:1;}s:28:"Output.CommentScriptContents";i:7;s:26:"Core.CommentScriptContents";O:8:"stdClass":2:{s:3:"key";s:28:"Output.CommentScriptContents";s:7:"isAlias";b:1;}s:19:"Output.FixInnerHTML";i:7;s:18:"Output.FlashCompat";i:7;s:14:"Output.Newline";i:-1;s:15:"Output.SortAttr";i:7;s:17:"Output.TidyFormat";i:7;s:15:"Core.TidyFormat";O:8:"stdClass":2:{s:3:"key";s:17:"Output.TidyFormat";s:7:"isAlias";b:1;}s:17:"Test.ForceNoIconv";i:7;s:18:"URI.AllowedSchemes";i:8;s:8:"URI.Base";i:-1;s:17:"URI.DefaultScheme";i:-1;s:16:"URI.DefinitionID";i:-1;s:17:"URI.DefinitionRev";i:5;s:11:"URI.Disable";i:7;s:15:"Attr.DisableURI";O:8:"stdClass":2:{s:3:"key";s:11:"URI.Disable";s:7:"isAlias";b:1;}s:19:"URI.DisableExternal";i:7;s:28:"URI.DisableExternalResources";i:7;s:20:"URI.DisableResources";i:7;s:8:"URI.Host";i:-1;s:17:"URI.HostBlacklist";i:9;s:16:"URI.MakeAbsolute";i:7;s:9:"URI.Munge";i:-1;s:18:"URI.MungeResources";i:7;s:18:"URI.MungeSecretKey";i:-1;s:26:"URI.OverrideAllowedSchemes";i:7;s:20:"URI.SafeIframeRegexp";i:-1;}}
\ No newline at end of file

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema.php       
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/ConfigSchema.php       
2017-10-17 14:10:28 UTC (rev 17159)
@@ -24,11 +24,11 @@
      *
      *  array(
      *      'Namespace' => array(
-     *          'Directive' => new stdclass(),
+     *          'Directive' => new stdClass(),
      *      )
      *  )
      *
-     * The stdclass may have the following properties:
+     * The stdClass may have the following properties:
      *
      *  - If isAlias isn't set:
      *      - type: Integer type of directive, see HTMLPurifier_VarParser for 
definitions
@@ -39,8 +39,8 @@
      *      - 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
+     * 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.
      *
@@ -105,7 +105,7 @@
      */
     public function add($key, $default, $type, $allow_null)
     {
-        $obj = new stdclass();
+        $obj = new stdClass();
         $obj->type = is_int($type) ? $type : 
HTMLPurifier_VarParser::$types[$type];
         if ($allow_null) {
             $obj->allow_null = true;
@@ -152,7 +152,7 @@
      */
     public function addAlias($key, $new_key)
     {
-        $obj = new stdclass;
+        $obj = new stdClass;
         $obj->key = $new_key;
         $obj->isAlias = true;
         $this->info[$key] = $obj;
@@ -159,7 +159,7 @@
     }
 
     /**
-     * Replaces any stdclass that only has the type property with type integer.
+     * Replaces any stdClass that only has the type property with type integer.
      */
     public function postProcess()
     {

Modified: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/DefinitionCache/Serializer.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/DefinitionCache/Serializer.php 
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/DefinitionCache/Serializer.php 
2017-10-17 14:10:28 UTC (rev 17159)
@@ -97,6 +97,12 @@
         }
         $dir = $this->generateDirectoryPath($config);
         $dh  = opendir($dir);
+        // Apparently, on some versions of PHP, readdir will return
+        // an empty string if you pass an invalid argument to readdir.
+        // So you need this test.  See #49.
+        if (false === $dh) {
+            return false;
+        }
         while (false !== ($filename = readdir($dh))) {
             if (empty($filename)) {
                 continue;
@@ -106,6 +112,8 @@
             }
             unlink($dir . '/' . $filename);
         }
+        closedir($dh);
+        return true;
     }
 
     /**
@@ -119,6 +127,10 @@
         }
         $dir = $this->generateDirectoryPath($config);
         $dh  = opendir($dir);
+        // See #49 (and above).
+        if (false === $dh) {
+            return false;
+        }
         while (false !== ($filename = readdir($dh))) {
             if (empty($filename)) {
                 continue;
@@ -131,6 +143,8 @@
                 unlink($dir . '/' . $filename);
             }
         }
+        closedir($dh);
+        return true;
     }
 
     /**
@@ -186,11 +200,9 @@
         if ($result !== false) {
             // set permissions of the new file (no execute)
             $chmod = $config->get('Cache.SerializerPermissions');
-            if (!$chmod) {
-                $chmod = 0644; // invalid config or simpletest
+            if ($chmod !== null) {
+                chmod($file, $chmod & 0666);
             }
-            $chmod = $chmod & 0666;
-            chmod($file, $chmod);
         }
         return $result;
     }
@@ -204,8 +216,10 @@
     {
         $directory = $this->generateDirectoryPath($config);
         $chmod = $config->get('Cache.SerializerPermissions');
-        if (!$chmod) {
-            $chmod = 0755; // invalid config or simpletest
+        if ($chmod === null) {
+            // TODO: This races
+            if (is_dir($directory)) return true;
+            return mkdir($directory);
         }
         if (!is_dir($directory)) {
             $base = $this->generateBaseDirectoryPath($config);
@@ -219,15 +233,16 @@
             } elseif (!$this->_testPermissions($base, $chmod)) {
                 return false;
             }
-            mkdir($directory, $chmod);
-            if (!$this->_testPermissions($directory, $chmod)) {
+            if (!mkdir($directory, $chmod)) {
                 trigger_error(
-                    'Base directory ' . $base . ' does not exist,
-                    please create or change using %Cache.SerializerPath',
+                    'Could not create directory ' . $directory . '',
                     E_USER_WARNING
                 );
                 return false;
             }
+            if (!$this->_testPermissions($directory, $chmod)) {
+                return false;
+            }
         } elseif (!$this->_testPermissions($directory, $chmod)) {
             return false;
         }
@@ -256,7 +271,7 @@
             );
             return false;
         }
-        if (function_exists('posix_getuid')) {
+        if (function_exists('posix_getuid') && $chmod !== null) {
             // POSIX system, we can give more specific advice
             if (fileowner($dir) === posix_getuid()) {
                 // we can chmod it ourselves

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/DefinitionCache.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/DefinitionCache.php    
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/DefinitionCache.php    
2017-10-17 14:10:28 UTC (rev 17159)
@@ -118,7 +118,7 @@
 
     /**
      * Clears all expired (older version or revision) objects from cache
-     * @note Be carefuly implementing this method as flush. Flush must
+     * @note Be careful implementing this method as flush. Flush must
      *       not interfere with other Definition types, and cleanup()
      *       should not be repeatedly called by userland code.
      * @param HTMLPurifier_Config $config

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Encoder.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Encoder.php    2017-10-17 
12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Encoder.php    2017-10-17 
14:10:28 UTC (rev 17159)
@@ -101,6 +101,14 @@
      * It will parse according to UTF-8 and return a valid UTF8 string, with
      * non-SGML codepoints excluded.
      *
+     * Specifically, it will permit:
+     * 
\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}
+     * Source: https://www.w3.org/TR/REC-xml/#NT-Char
+     * Arguably this function should be modernized to the HTML5 set
+     * of allowed characters:
+     * https://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream
+     * which simultaneously expand and restrict the set of allowed characters.
+     *
      * @param string $str The string to clean
      * @param bool $force_php
      * @return string
@@ -122,7 +130,6 @@
      *       function that needs to be able to understand UTF-8 characters.
      *       As of right now, only smart lossless character encoding converters
      *       would need that, and I'm probably not going to implement them.
-     *       Once again, PHP 6 should solve all our problems.
      */
     public static function cleanUTF8($str, $force_php = false)
     {
@@ -129,8 +136,6 @@
         // UTF-8 validity is checked since PHP 4.3.5
         // This is an optimization: if the string is already valid UTF-8, no
         // need to do PHP stuff. 99% of the time, this will be the case.
-        // 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
@@ -255,6 +260,7 @@
                                 // 7F-9F is not strictly prohibited by XML,
                                 // but it is non-SGML, and thus we don't allow 
it
                                 (0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) ||
+                                (0xE000 <= $mUcs4 && 0xFFFD >= $mUcs4) ||
                                 (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
                             )
                         ) {

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/EntityParser.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/EntityParser.php       
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/EntityParser.php       
2017-10-17 14:10:28 UTC (rev 17159)
@@ -17,6 +17,138 @@
     protected $_entity_lookup;
 
     /**
+     * Callback regex string for entities in text.
+     * @type string
+     */
+    protected $_textEntitiesRegex;
+
+    /**
+     * Callback regex string for entities in attributes.
+     * @type string
+     */
+    protected $_attrEntitiesRegex;
+
+    /**
+     * Tests if the beginning of a string is a semi-optional regex
+     */
+    protected $_semiOptionalPrefixRegex;
+
+    public function __construct() {
+        // From
+        // 
http://stackoverflow.com/questions/15532252/why-is-reg-being-rendered-as-without-the-bounding-semicolon
+        $semi_optional = 
"quot|QUOT|lt|LT|gt|GT|amp|AMP|AElig|Aacute|Acirc|Agrave|Aring|Atilde|Auml|COPY|Ccedil|ETH|Eacute|Ecirc|Egrave|Euml|Iacute|Icirc|Igrave|Iuml|Ntilde|Oacute|Ocirc|Ograve|Oslash|Otilde|Ouml|REG|THORN|Uacute|Ucirc|Ugrave|Uuml|Yacute|aacute|acirc|acute|aelig|agrave|aring|atilde|auml|brvbar|ccedil|cedil|cent|copy|curren|deg|divide|eacute|ecirc|egrave|eth|euml|frac12|frac14|frac34|iacute|icirc|iexcl|igrave|iquest|iuml|laquo|macr|micro|middot|nbsp|not|ntilde|oacute|ocirc|ograve|ordf|ordm|oslash|otilde|ouml|para|plusmn|pound|raquo|reg|sect|shy|sup1|sup2|sup3|szlig|thorn|times|uacute|ucirc|ugrave|uml|uuml|yacute|yen|yuml";
+
+        // NB: three empty captures to put the fourth match in the right
+        // place
+        $this->_semiOptionalPrefixRegex = "/&()()()($semi_optional)/";
+
+        $this->_textEntitiesRegex =
+            '/&(?:'.
+            // hex
+            '[#]x([a-fA-F0-9]+);?|'.
+            // dec
+            '[#]0*(\d+);?|'.
+            // string (mandatory semicolon)
+            // NB: order matters: match semicolon preferentially
+            '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
+            // string (optional semicolon)
+            "($semi_optional)".
+            ')/';
+
+        $this->_attrEntitiesRegex =
+            '/&(?:'.
+            // hex
+            '[#]x([a-fA-F0-9]+);?|'.
+            // dec
+            '[#]0*(\d+);?|'.
+            // string (mandatory semicolon)
+            // NB: order matters: match semicolon preferentially
+            '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
+            // string (optional semicolon)
+            // don't match if trailing is equals or alphanumeric (URL
+            // like)
+            "($semi_optional)(?![=;A-Za-z0-9])".
+            ')/';
+
+    }
+
+    /**
+     * Substitute entities with the parsed equivalents.  Use this on
+     * textual data in an HTML document (as opposed to attributes.)
+     *
+     * @param string $string String to have entities parsed.
+     * @return string Parsed string.
+     */
+    public function substituteTextEntities($string)
+    {
+        return preg_replace_callback(
+            $this->_textEntitiesRegex,
+            array($this, 'entityCallback'),
+            $string
+        );
+    }
+
+    /**
+     * Substitute entities with the parsed equivalents.  Use this on
+     * attribute contents in documents.
+     *
+     * @param string $string String to have entities parsed.
+     * @return string Parsed string.
+     */
+    public function substituteAttrEntities($string)
+    {
+        return preg_replace_callback(
+            $this->_attrEntitiesRegex,
+            array($this, 'entityCallback'),
+            $string
+        );
+    }
+
+    /**
+     * Callback function for substituteNonSpecialEntities() that does the work.
+     *
+     * @param array $matches  PCRE matches array, with 0 the entire match, and
+     *                  either index 1, 2 or 3 set with a hex value, dec value,
+     *                  or string (respectively).
+     * @return string Replacement string.
+     */
+
+    protected function entityCallback($matches)
+    {
+        $entity = $matches[0];
+        $hex_part = @$matches[1];
+        $dec_part = @$matches[2];
+        $named_part = empty($matches[3]) ? @$matches[4] : $matches[3];
+        if ($hex_part !== NULL && $hex_part !== "") {
+            return HTMLPurifier_Encoder::unichr(hexdec($hex_part));
+        } elseif ($dec_part !== NULL && $dec_part !== "") {
+            return HTMLPurifier_Encoder::unichr((int) $dec_part);
+        } else {
+            if (!$this->_entity_lookup) {
+                $this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
+            }
+            if (isset($this->_entity_lookup->table[$named_part])) {
+                return $this->_entity_lookup->table[$named_part];
+            } else {
+                // exact match didn't match anything, so test if
+                // any of the semicolon optional match the prefix.
+                // Test that this is an EXACT match is important to
+                // prevent infinite loop
+                if (!empty($matches[3])) {
+                    return preg_replace_callback(
+                        $this->_semiOptionalPrefixRegex,
+                        array($this, 'entityCallback'),
+                        $entity
+                    );
+                }
+                return $entity;
+            }
+        }
+    }
+
+    // LEGACY CODE BELOW
+
+    /**
      * Callback regex string for parsing entities.
      * @type string
      */
@@ -144,7 +276,7 @@
                 $entity;
         } else {
             return isset($this->_special_ent2dec[$matches[3]]) ?
-                $this->_special_ent2dec[$matches[3]] :
+                $this->_special_dec2str[$this->_special_ent2dec[$matches[3]]] :
                 $entity;
         }
     }

Modified: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php  
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php  
2017-10-17 14:10:28 UTC (rev 17159)
@@ -95,7 +95,10 @@
         if ($tidy !== null) {
             $this->_tidy = $tidy;
         }
-        $html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', 
array($this, 'styleCallback'), $html);
+        // NB: this must be NON-greedy because if we have
+        // <style>foo</style>  <style>bar</style>
+        // we must not grab foo</style>  <style>bar
+        $html = preg_replace_callback('#<style(?:\s.*)?>(.*)<\/style>#isU', 
array($this, 'styleCallback'), $html);
         $style_blocks = $this->_styleMatches;
         $this->_styleMatches = array(); // reset
         $context->register('StyleBlocks', $style_blocks); // $context must not 
be reused

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Generator.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Generator.php  2017-10-17 
12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Generator.php  2017-10-17 
14:10:28 UTC (rev 17159)
@@ -146,7 +146,7 @@
             $attr = $this->generateAttributes($token->attr, $token->name);
             if ($this->_flashCompat) {
                 if ($token->name == "object") {
-                    $flash = new stdclass();
+                    $flash = new stdClass();
                     $flash->attr = $token->attr;
                     $flash->param = array();
                     $this->_flashStack[] = $flash;

Added: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModule/TargetNoopener.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModule/TargetNoopener.php  
                        (rev 0)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModule/TargetNoopener.php  
2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * Module adds the target-based noopener attribute transformation to a tags.  
It
+ * is enabled by HTML.TargetNoopener
+ */
+class HTMLPurifier_HTMLModule_TargetNoopener extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'TargetNoopener';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config) {
+        $a = $this->addBlankElement('a');
+        $a->attr_transform_post[] = new 
HTMLPurifier_AttrTransform_TargetNoopener();
+    }
+}

Added: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModule/TargetNoreferrer.php
===================================================================
--- 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModule/TargetNoreferrer.php    
                            (rev 0)
+++ 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModule/TargetNoreferrer.php    
    2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * Module adds the target-based noreferrer attribute transformation to a tags. 
 It
+ * is enabled by HTML.TargetNoreferrer
+ */
+class HTMLPurifier_HTMLModule_TargetNoreferrer extends HTMLPurifier_HTMLModule
+{
+    /**
+     * @type string
+     */
+    public $name = 'TargetNoreferrer';
+
+    /**
+     * @param HTMLPurifier_Config $config
+     */
+    public function setup($config) {
+        $a = $this->addBlankElement('a');
+        $a->attr_transform_post[] = new 
HTMLPurifier_AttrTransform_TargetNoreferrer();
+    }
+}

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModuleManager.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModuleManager.php  
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/HTMLModuleManager.php  
2017-10-17 14:10:28 UTC (rev 17159)
@@ -271,6 +271,14 @@
         if ($config->get('HTML.TargetBlank')) {
             $modules[] = 'TargetBlank';
         }
+        // NB: HTML.TargetNoreferrer and HTML.TargetNoopener must be AFTER 
HTML.TargetBlank
+        // so that its post-attr-transform gets run afterwards.
+        if ($config->get('HTML.TargetNoreferrer')) {
+            $modules[] = 'TargetNoreferrer';
+        }
+        if ($config->get('HTML.TargetNoopener')) {
+            $modules[] = 'TargetNoopener';
+        }
 
         // merge in custom modules
         $modules = array_merge($modules, $this->userModules);

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/Linkify.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/Linkify.php   
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/Linkify.php   
2017-10-17 14:10:28 UTC (rev 17159)
@@ -31,9 +31,14 @@
             return;
         }
 
-        // there is/are URL(s). Let's split the string:
-        // Note: this regex is extremely permissive
-        $bits = preg_split('#((?:https?|ftp)://[^\s\'",<>()]+)#Su', 
$token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
+        // there is/are URL(s). Let's split the string.
+        // We use this regex:
+        // https://gist.github.com/gruber/249502
+        // but with @cscott's backtracking fix and also
+        // the Unicode characters un-Unicodified.
+        $bits = preg_split(
+            
'/\\b((?:[a-z][\\w\\-]+:(?:\\/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}\\/)(?:[^\\s()<>]|\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\))+(?:\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'".,<>?\x{00ab}\x{00bb}\x{201c}\x{201d}\x{2018}\x{2019}]))/iu',
+            $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
 
 
         $token = array();

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/RemoveEmpty.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/RemoveEmpty.php       
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/RemoveEmpty.php       
2017-10-17 14:10:28 UTC (rev 17159)
@@ -46,6 +46,12 @@
         $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
         $this->removeNbspExceptions = 
$config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
         $this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
+        foreach ($this->exclude as $key => $attrs) {
+            if (!is_array($attrs)) {
+                // HACK, see HTMLPurifier/Printer/ConfigForm.php
+                $this->exclude[$key] = explode(';', $attrs);
+            }
+        }
         $this->attrValidator = new HTMLPurifier_AttrValidator();
     }
 

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/SafeObject.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/SafeObject.php        
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Injector/SafeObject.php        
2017-10-17 14:10:28 UTC (rev 17159)
@@ -36,6 +36,7 @@
     );
 
     /**
+     * These are all lower-case keys.
      * @type array
      */
     protected $allowedParam = array(
@@ -43,7 +44,7 @@
         'movie' => true,
         'flashvars' => true,
         'src' => true,
-        'allowFullScreen' => true, // if omitted, assume to be 'false'
+        'allowfullscreen' => true, // if omitted, assume to be 'false'
     );
 
     /**
@@ -93,9 +94,11 @@
                     $token->attr['name'] === $this->addParam[$n]) {
                     // keep token, and add to param stack
                     $this->paramStack[$i][$n] = true;
-                } elseif (isset($this->allowedParam[$n])) {
+                } elseif (isset($this->allowedParam[strtolower($n)])) {
                     // keep token, don't do anything to it
                     // (could possibly check for duplicates here)
+                    // Note: In principle, parameters should be case sensitive.
+                    // But it seems they are not really; so accept any case.
                 } else {
                     $token = false;
                 }

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/DOMLex.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/DOMLex.php       
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/DOMLex.php       
2017-10-17 14:10:28 UTC (rev 17159)
@@ -72,12 +72,20 @@
         $doc->loadHTML($html);
         restore_error_handler();
 
+        $body = $doc->getElementsByTagName('html')->item(0)-> // <html>
+                      getElementsByTagName('body')->item(0);  // <body>
+
+        $div = $body->getElementsByTagName('div')->item(0); // <div>
         $tokens = array();
-        $this->tokenizeDOM(
-            $doc->getElementsByTagName('html')->item(0)-> // <html>
-            getElementsByTagName('body')->item(0), //   <body>
-            $tokens
-        );
+        $this->tokenizeDOM($div, $tokens, $config);
+        // If the div has a sibling, that means we tripped across
+        // a premature </div> tag.  So remove the div we parsed,
+        // and then tokenize the rest of body.  We can't tokenize
+        // the sibling directly as we'll lose the tags in that case.
+        if ($div->nextSibling) {
+            $body->removeChild($div);
+            $this->tokenizeDOM($body, $tokens, $config);
+        }
         return $tokens;
     }
 
@@ -88,7 +96,7 @@
      * @param HTMLPurifier_Token[] $tokens   Array-list of already tokenized 
tokens.
      * @return HTMLPurifier_Token of node appended to previously passed tokens.
      */
-    protected function tokenizeDOM($node, &$tokens)
+    protected function tokenizeDOM($node, &$tokens, $config)
     {
         $level = 0;
         $nodes = array($level => new HTMLPurifier_Queue(array($node)));
@@ -97,7 +105,7 @@
             while (!$nodes[$level]->isEmpty()) {
                 $node = $nodes[$level]->shift(); // FIFO
                 $collect = $level > 0 ? true : false;
-                $needEndingTag = $this->createStartNode($node, $tokens, 
$collect);
+                $needEndingTag = $this->createStartNode($node, $tokens, 
$collect, $config);
                 if ($needEndingTag) {
                     $closingNodes[$level][] = $node;
                 }
@@ -127,7 +135,7 @@
      * @return bool if the token needs an endtoken
      * @todo data and tagName properties don't seem to exist in DOMNode?
      */
-    protected function createStartNode($node, &$tokens, $collect)
+    protected function createStartNode($node, &$tokens, $collect, $config)
     {
         // intercept non element nodes. WE MUST catch all of them,
         // but we're not getting the character reference nodes because
@@ -151,7 +159,7 @@
                     }
                 }
             }
-            $tokens[] = $this->factory->createText($this->parseData($data));
+            $tokens[] = $this->factory->createText($this->parseText($data, 
$config));
             return false;
         } elseif ($node->nodeType === XML_COMMENT_NODE) {
             // this is code is only invoked for comments in script/style in 
versions
@@ -252,7 +260,7 @@
      * @param HTMLPurifier_Context $context
      * @return string
      */
-    protected function wrapHTML($html, $config, $context)
+    protected function wrapHTML($html, $config, $context, $use_div = true)
     {
         $def = $config->getDefinition('HTML');
         $ret = '';
@@ -271,7 +279,11 @@
         $ret .= '<html><head>';
         $ret .= '<meta http-equiv="Content-Type" content="text/html; 
charset=utf-8" />';
         // No protection if $html contains a stray </div>!
-        $ret .= '</head><body>' . $html . '</body></html>';
+        $ret .= '</head><body>';
+        if ($use_div) $ret .= '<div>';
+        $ret .= $html;
+        if ($use_div) $ret .= '</div>';
+        $ret .= '</body></html>';
         return $ret;
     }
 }

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/DirectLex.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/DirectLex.php    
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/DirectLex.php    
2017-10-17 14:10:28 UTC (rev 17159)
@@ -129,12 +129,12 @@
                 // We are not inside tag and there still is another tag to 
parse
                 $token = new
                     HTMLPurifier_Token_Text(
-                        $this->parseData(
+                    $this->parseText(
                             substr(
                             $html,
                             $cursor,
                             $position_next_lt - $cursor
-                            )
+                        ), $config
                         )
                     );
                 if ($maintain_line_numbers) {
@@ -154,11 +154,11 @@
                 // Create Text of rest of string
                 $token = new
                     HTMLPurifier_Token_Text(
-                        $this->parseData(
+                    $this->parseText(
                             substr(
                             $html,
                             $cursor
-                            )
+                        ), $config
                         )
                     );
                 if ($maintain_line_numbers) {
@@ -324,8 +324,8 @@
                 $token = new
                     HTMLPurifier_Token_Text(
                         '<' .
-                        $this->parseData(
-                            substr($html, $cursor)
+                    $this->parseText(
+                        substr($html, $cursor), $config
                         )
                     );
                 if ($maintain_line_numbers) {
@@ -429,7 +429,7 @@
             if ($value === false) {
                 $value = '';
             }
-            return array($key => $this->parseData($value));
+            return array($key => $this->parseAttr($value, $config));
         }
 
         // setup loop environment
@@ -518,7 +518,7 @@
                 if ($value === false) {
                     $value = '';
                 }
-                $array[$key] = $this->parseData($value);
+                $array[$key] = $this->parseAttr($value, $config);
                 $cursor++;
             } else {
                 // boolattr

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/PH5P.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/PH5P.php 2017-10-17 
12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer/PH5P.php 2017-10-17 
14:10:28 UTC (rev 17159)
@@ -21,7 +21,7 @@
     public function tokenizeHTML($html, $config, $context)
     {
         $new_html = $this->normalize($html, $config, $context);
-        $new_html = $this->wrapHTML($new_html, $config, $context);
+        $new_html = $this->wrapHTML($new_html, $config, $context, false /* no 
div */);
         try {
             $parser = new HTML5($new_html);
             $doc = $parser->save();
@@ -36,7 +36,7 @@
             $doc->getElementsByTagName('html')->item(0)-> // <html>
                 getElementsByTagName('body')->item(0) //   <body>
             ,
-            $tokens
+            $tokens, $config
         );
         return $tokens;
     }
@@ -1515,6 +1515,7 @@
                 // Consume the maximum number of characters possible, with the
                 // consumed characters case-sensitively matching one of the
                 // identifiers in the first column of the entities table.
+
                 $e_name = $this->characters('0-9A-Za-z;', $this->char + 1);
                 $len = strlen($e_name);
 
@@ -1547,7 +1548,7 @@
 
         // Return a character token for the character corresponding to the
         // entity name (as given by the second column of the entities table).
-        return html_entity_decode('&' . $entity . ';', ENT_QUOTES, 'UTF-8');
+        return html_entity_decode('&' . rtrim($entity, ';') . ';', ENT_QUOTES, 
'UTF-8');
     }
 
     private function emitToken($token)

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer.php      2017-10-17 
12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Lexer.php      2017-10-17 
14:10:28 UTC (rev 17159)
@@ -96,7 +96,7 @@
                     break;
                 }
 
-                    if (class_exists('DOMDocument') &&
+                    if (class_exists('DOMDocument', false) &&
                     method_exists('DOMDocument', 'loadHTML') &&
                     !extension_loaded('domxml')
                 ) {
@@ -169,6 +169,14 @@
                     '&#x27;' => "'"
             );
 
+    public function parseText($string, $config) {
+        return $this->parseData($string, false, $config);
+    }
+
+    public function parseAttr($string, $config) {
+        return $this->parseData($string, true, $config);
+    }
+
     /**
      * Parses special entities into the proper characters.
      *
@@ -175,15 +183,10 @@
      * This string will translate escaped versions of the special characters
      * into the correct ones.
      *
-     * @warning
-     * You should be able to treat the output of this function as
-     * completely parsed, but that's only because all other entities should
-     * have been handled previously in substituteNonSpecialEntities()
-     *
      * @param string $string String character data to be parsed.
      * @return string Parsed character data.
      */
-    public function parseData($string)
+    public function parseData($string, $is_attr, $config)
     {
         // following functions require at least one character
         if ($string === '') {
@@ -209,7 +212,15 @@
         }
 
         // hmm... now we have some uncommon entities. Use the callback.
+        if ($config->get('Core.LegacyEntityDecoder')) {
         $string = $this->_entity_parser->substituteSpecialEntities($string);
+        } else {
+            if ($is_attr) {
+                $string = 
$this->_entity_parser->substituteAttrEntities($string);
+            } else {
+                $string = 
$this->_entity_parser->substituteTextEntities($string);
+            }
+        }
         return $string;
     }
 
@@ -323,7 +334,9 @@
         }
 
         // expand entities that aren't the big five
+        if ($config->get('Core.LegacyEntityDecoder')) {
         $html = $this->_entity_parser->substituteNonSpecialEntities($html);
+        }
 
         // clean into wellformed UTF-8 string for an SGML context: this has
         // to be done after entity expansion because the entities sometimes
@@ -335,6 +348,13 @@
             $html = preg_replace('#<\?.+?\?>#s', '', $html);
         }
 
+        $hidden_elements = $config->get('Core.HiddenElements');
+        if ($config->get('Core.AggressivelyRemoveScript') &&
+            !($config->get('HTML.Trusted') || 
!$config->get('Core.RemoveScriptContents')
+            || empty($hidden_elements["script"]))) {
+            $html = preg_replace('#<script[^>]*>.*?</script>#i', '', $html);
+        }
+
         return $html;
     }
 
@@ -345,13 +365,18 @@
     public function extractBody($html)
     {
         $matches = array();
-        $result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches);
+        $result = preg_match('|(.*?)<body[^>]*>(.*)</body>|is', $html, 
$matches);
         if ($result) {
-            return $matches[1];
-        } else {
-            return $html;
+            // Make sure it's not in a comment
+            $comment_start = strrpos($matches[1], '<!--');
+            $comment_end   = strrpos($matches[1], '-->');
+            if ($comment_start === false ||
+                ($comment_end !== false && $comment_end > $comment_start)) {
+                return $matches[2];
         }
     }
+        return $html;
+    }
 }
 
 // vim: et sw=4 sts=4

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Printer/ConfigForm.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Printer/ConfigForm.php 
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Printer/ConfigForm.php 
2017-10-17 14:10:28 UTC (rev 17159)
@@ -327,6 +327,10 @@
                 case HTMLPurifier_VarParser::HASH:
                     $nvalue = '';
                     foreach ($value as $i => $v) {
+                        if (is_array($v)) {
+                            // HACK
+                            $v = implode(";", $v);
+                        }
                         $nvalue .= "$i:$v" . PHP_EOL;
                     }
                     $value = $nvalue;

Modified: 
trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Strategy/MakeWellFormed.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Strategy/MakeWellFormed.php    
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Strategy/MakeWellFormed.php    
2017-10-17 14:10:28 UTC (rev 17159)
@@ -165,7 +165,7 @@
                         if (empty($zipper->front)) break;
                         $token = $zipper->prev($token);
                         // indicate that other injectors should not process 
this token,
-                        // but we need to reprocess it
+                        // but we need to reprocess it.  See Note [Injector 
skips]
                         unset($token->skip[$i]);
                         $token->rewind = $i;
                         if ($token instanceof HTMLPurifier_Token_Start) {
@@ -210,6 +210,7 @@
                 if ($token instanceof HTMLPurifier_Token_Text) {
                     foreach ($this->injectors as $i => $injector) {
                         if (isset($token->skip[$i])) {
+                            // See Note [Injector skips]
                             continue;
                         }
                         if ($token->rewind !== null && $token->rewind !== $i) {
@@ -367,6 +368,7 @@
             if ($ok) {
                 foreach ($this->injectors as $i => $injector) {
                     if (isset($token->skip[$i])) {
+                        // See Note [Injector skips]
                         continue;
                     }
                     if ($token->rewind !== null && $token->rewind !== $i) {
@@ -422,6 +424,7 @@
                 $token->start = $current_parent;
                 foreach ($this->injectors as $i => $injector) {
                     if (isset($token->skip[$i])) {
+                        // See Note [Injector skips]
                         continue;
                     }
                     if ($token->rewind !== null && $token->rewind !== $i) {
@@ -534,12 +537,17 @@
      */
     protected function processToken($token, $injector = -1)
     {
+        // Zend OpCache miscompiles $token = array($token), so
+        // avoid this pattern.  See: 
https://github.com/ezyang/htmlpurifier/issues/108
+
         // normalize forms of token
         if (is_object($token)) {
-            $token = array(1, $token);
+            $tmp = $token;
+            $token = array(1, $tmp);
         }
         if (is_int($token)) {
-            $token = array($token);
+            $tmp = $token;
+            $token = array($tmp);
         }
         if ($token === false) {
             $token = array(1);
@@ -561,7 +569,12 @@
         list($old, $r) = $this->zipper->splice($this->token, $delete, $token);
 
         if ($injector > -1) {
-            // determine appropriate skips
+            // See Note [Injector skips]
+            // Determine appropriate skips.  Here's what the code does:
+            //  *If* we deleted one or more tokens, copy the skips
+            //  of those tokens into the skips of the new tokens (in $token).
+            //  Also, mark the newly inserted tokens as having come from
+            //  $injector.
             $oldskip = isset($old[0]) ? $old[0]->skip : array();
             foreach ($token as $object) {
                 $object->skip = $oldskip;
@@ -597,4 +610,50 @@
     }
 }
 
+// Note [Injector skips]
+// ~~~~~~~~~~~~~~~~~~~~~
+// When I originally designed this class, the idea behind the 'skip'
+// property of HTMLPurifier_Token was to help avoid infinite loops
+// in injector processing.  For example, suppose you wrote an injector
+// that bolded swear words.  Naively, you might write it so that
+// whenever you saw ****, you replaced it with <strong>****</strong>.
+//
+// When this happens, we will reprocess all of the tokens with the
+// other injectors.  Now there is an opportunity for infinite loop:
+// if we rerun the swear-word injector on these tokens, we might
+// see **** and then reprocess again to get
+// <strong><strong>****</strong></strong> ad infinitum.
+//
+// Thus, the idea of a skip is that once we process a token with
+// an injector, we mark all of those tokens as having "come from"
+// the injector, and we never run the injector again on these
+// tokens.
+//
+// There were two more complications, however:
+//
+//  - With HTMLPurifier_Injector_RemoveEmpty, we noticed that if
+//    you had <b><i></i></b>, after you removed the <i></i>, you
+//    really would like this injector to go back and reprocess
+//    the <b> tag, discovering that it is now empty and can be
+//    removed.  So we reintroduced the possibility of infinite looping
+//    by adding a "rewind" function, which let you go back to an
+//    earlier point in the token stream and reprocess it with injectors.
+//    Needless to say, we need to UN-skip the token so it gets
+//    reprocessed.
+//
+//  - Suppose that you successfuly process a token, replace it with
+//    one with your skip mark, but now another injector wants to
+//    process the skipped token with another token.  Should you continue
+//    to skip that new token, or reprocess it?  If you reprocess,
+//    you can end up with an infinite loop where one injector converts
+//    <a> to <b>, and then another injector converts it back.  So
+//    we inherit the skips, but for some reason, I thought that we
+//    should inherit the skip from the first token of the token
+//    that we deleted.  Why?  Well, it seems to work OK.
+//
+// If I were to redesign this functionality, I would absolutely not
+// go about doing it this way: the semantics are just not very well
+// defined, and in any case you probably wanted to operate on trees,
+// not token streams.
+
 // vim: et sw=4 sts=4

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Token.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Token.php      2017-10-17 
12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/Token.php      2017-10-17 
14:10:28 UTC (rev 17159)
@@ -26,7 +26,7 @@
     public $armor = array();
 
     /**
-     * Used during MakeWellFormed.
+     * Used during MakeWellFormed.  See Note [Injector skips]
      * @type
      */
     public $skip;

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URI.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URI.php        2017-10-17 
12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URI.php        2017-10-17 
14:10:28 UTC (rev 17159)
@@ -85,11 +85,13 @@
             $def = $config->getDefinition('URI');
             $scheme_obj = $def->getDefaultScheme($config, $context);
             if (!$scheme_obj) {
+                if ($def->defaultScheme !== null) {
                 // something funky happened to the default scheme object
                 trigger_error(
                     'Default scheme object "' . $def->defaultScheme . '" was 
not readable',
                     E_USER_WARNING
                 );
+                } // suppress error if it's null
                 return false;
             }
         }

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URIScheme/data.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URIScheme/data.php     
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URIScheme/data.php     
2017-10-17 14:10:28 UTC (rev 17159)
@@ -79,9 +79,18 @@
         } else {
             $raw_data = $data;
         }
+        if ( strlen($raw_data) < 12 ) {
+            // error; exif_imagetype throws exception with small files,
+            // and this likely indicates a corrupt URI/failed parse anyway
+            return false;
+        }
         // XXX probably want to refactor this into a general mechanism
         // for filtering arbitrary content types
+        if (function_exists('sys_get_temp_dir')) {
+            $file = tempnam(sys_get_temp_dir(), "");
+        } else {
         $file = tempnam("/tmp", "");
+        }
         file_put_contents($file, $raw_data);
         if (function_exists('exif_imagetype')) {
             $image_code = exif_imagetype($file);

Added: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URIScheme/tel.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URIScheme/tel.php              
                (rev 0)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier/URIScheme/tel.php      
2017-10-17 14:10:28 UTC (rev 17159)
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * Validates tel (for phone numbers).
+ *
+ * The relevant specifications for this protocol are RFC 3966 and RFC 5341,
+ * but this class takes a much simpler approach: we normalize phone
+ * numbers so that they only include (possibly) a leading plus,
+ * and then any number of digits and x'es.
+ */
+
+class HTMLPurifier_URIScheme_tel extends HTMLPurifier_URIScheme
+{
+    /**
+     * @type bool
+     */
+    public $browsable = false;
+
+    /**
+     * @type bool
+     */
+    public $may_omit_host = true;
+
+    /**
+     * @param HTMLPurifier_URI $uri
+     * @param HTMLPurifier_Config $config
+     * @param HTMLPurifier_Context $context
+     * @return bool
+     */
+    public function doValidate(&$uri, $config, $context)
+    {
+        $uri->userinfo = null;
+        $uri->host     = null;
+        $uri->port     = null;
+
+        // Delete all non-numeric characters, non-x characters
+        // from phone number, EXCEPT for a leading plus sign.
+        $uri->path = preg_replace('/(?!^\+)[^\dx]/', '',
+                     // Normalize e(x)tension to lower-case
+                     str_replace('X', 'x', $uri->path));
+
+        return true;
+    }
+}
+
+// vim: et sw=4 sts=4

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.includes.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.includes.php   2017-10-17 
12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.includes.php   2017-10-17 
14:10:28 UTC (rev 17159)
@@ -7,7 +7,7 @@
  * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
  * FILE, changes will be overwritten the next time the script is run.
  *
- * @version 4.7.0
+ * @version 4.9.3
  *
  * @warning
  *      You must *not* include any other HTML Purifier files before this file,
@@ -137,6 +137,8 @@
 require 'HTMLPurifier/AttrTransform/SafeParam.php';
 require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
 require 'HTMLPurifier/AttrTransform/TargetBlank.php';
+require 'HTMLPurifier/AttrTransform/TargetNoopener.php';
+require 'HTMLPurifier/AttrTransform/TargetNoreferrer.php';
 require 'HTMLPurifier/AttrTransform/Textarea.php';
 require 'HTMLPurifier/ChildDef/Chameleon.php';
 require 'HTMLPurifier/ChildDef/Custom.php';
@@ -175,6 +177,8 @@
 require 'HTMLPurifier/HTMLModule/Tables.php';
 require 'HTMLPurifier/HTMLModule/Target.php';
 require 'HTMLPurifier/HTMLModule/TargetBlank.php';
+require 'HTMLPurifier/HTMLModule/TargetNoopener.php';
+require 'HTMLPurifier/HTMLModule/TargetNoreferrer.php';
 require 'HTMLPurifier/HTMLModule/Text.php';
 require 'HTMLPurifier/HTMLModule/Tidy.php';
 require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
@@ -225,5 +229,6 @@
 require 'HTMLPurifier/URIScheme/mailto.php';
 require 'HTMLPurifier/URIScheme/news.php';
 require 'HTMLPurifier/URIScheme/nntp.php';
+require 'HTMLPurifier/URIScheme/tel.php';
 require 'HTMLPurifier/VarParser/Flexible.php';
 require 'HTMLPurifier/VarParser/Native.php';

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.php    2017-10-17 12:07:58 UTC 
(rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.php    2017-10-17 14:10:28 UTC 
(rev 17159)
@@ -19,7 +19,7 @@
  */
 
 /*
-    HTML Purifier 4.7.0 - Standards Compliant HTML Filtering
+    HTML Purifier 4.9.3 - Standards Compliant HTML Filtering
     Copyright (C) 2006-2008 Edward Z. Yang
 
     This library is free software; you can redistribute it and/or
@@ -58,12 +58,12 @@
      * Version of HTML Purifier.
      * @type string
      */
-    public $version = '4.7.0';
+    public $version = '4.9.3';
 
     /**
      * Constant with version of HTML Purifier.
      */
-    const VERSION = '4.7.0';
+    const VERSION = '4.9.3';
 
     /**
      * Global configuration object.
@@ -104,7 +104,7 @@
     /**
      * Initializes the purifier.
      *
-     * @param HTMLPurifier_Config $config Optional HTMLPurifier_Config object
+     * @param HTMLPurifier_Config|mixed $config Optional HTMLPurifier_Config 
object
      *                for all instances of the purifier, if omitted, a default
      *                configuration is supplied (which can be overridden on a
      *                per-use basis).

Modified: trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.safe-includes.php
===================================================================
--- trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.safe-includes.php      
2017-10-17 12:07:58 UTC (rev 17158)
+++ trunk/phpgwapi/inc/htmlpurifier/HTMLPurifier.safe-includes.php      
2017-10-17 14:10:28 UTC (rev 17159)
@@ -131,6 +131,8 @@
 require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
 require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
 require_once $__dir . '/HTMLPurifier/AttrTransform/TargetBlank.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoopener.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoreferrer.php';
 require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
 require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
 require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
@@ -169,6 +171,8 @@
 require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
 require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
 require_once $__dir . '/HTMLPurifier/HTMLModule/TargetBlank.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoopener.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoreferrer.php';
 require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
 require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
 require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
@@ -219,5 +223,6 @@
 require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
 require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
 require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
+require_once $__dir . '/HTMLPurifier/URIScheme/tel.php';
 require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
 require_once $__dir . '/HTMLPurifier/VarParser/Native.php';




reply via email to

[Prev in Thread] Current Thread [Next in Thread]