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