purifier upgrade to 4.0.0

This commit is contained in:
Klaus Leithoff 2009-11-27 10:06:15 +00:00
parent 9000c22f49
commit e185b84018
72 changed files with 653 additions and 383 deletions

View File

@ -231,12 +231,12 @@ HTML Purifier uses iconv to support other character encodings, as such,
any encoding that iconv supports <http://www.gnu.org/software/libiconv/> any encoding that iconv supports <http://www.gnu.org/software/libiconv/>
HTML Purifier supports with this code: HTML Purifier supports with this code:
$config->set('Core', 'Encoding', /* put your encoding here */); $config->set('Core.Encoding', /* put your encoding here */);
An example usage for Latin-1 websites (the most common encoding for English An example usage for Latin-1 websites (the most common encoding for English
websites): websites):
$config->set('Core', 'Encoding', 'ISO-8859-1'); $config->set('Core.Encoding', 'ISO-8859-1');
Note that HTML Purifier's support for non-Unicode encodings is crippled by the Note that HTML Purifier's support for non-Unicode encodings is crippled by the
fact that any character not supported by that encoding will be silently fact that any character not supported by that encoding will be silently
@ -251,7 +251,7 @@ reason, I do not include the solution in this document).
For those of you using HTML 4.01 Transitional, you can disable For those of you using HTML 4.01 Transitional, you can disable
XHTML output like this: XHTML output like this:
$config->set('HTML', 'Doctype', 'HTML 4.01 Transitional'); $config->set('HTML.Doctype', 'HTML 4.01 Transitional');
Other supported doctypes include: Other supported doctypes include:
@ -277,14 +277,14 @@ are, respectively, %HTML.Allowed, %URI.MakeAbsolute and %URI.Base, and
%AutoFormat.AutoParagraph. The %Namespace.Directive naming convention %AutoFormat.AutoParagraph. The %Namespace.Directive naming convention
translates to: translates to:
$config->set('Namespace', 'Directive', $value); $config->set('Namespace.Directive', $value);
E.g. E.g.
$config->set('HTML', 'Allowed', 'p,b,a[href],i'); $config->set('HTML.Allowed', 'p,b,a[href],i');
$config->set('URI', 'Base', 'http://www.example.com'); $config->set('URI.Base', 'http://www.example.com');
$config->set('URI', 'MakeAbsolute', true); $config->set('URI.MakeAbsolute', true);
$config->set('AutoFormat', 'AutoParagraph', true); $config->set('AutoFormat.AutoParagraph', true);
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
@ -318,11 +318,11 @@ If you are unable or unwilling to give write permissions to the cache
directory, you can either disable the cache (and suffer a performance directory, you can either disable the cache (and suffer a performance
hit): hit):
$config->set('Core', 'DefinitionCache', null); $config->set('Core.DefinitionCache', null);
Or move the cache directory somewhere else (no trailing slash): Or move the cache directory somewhere else (no trailing slash):
$config->set('Cache', 'SerializerPath', '/home/user/absolute/path'); $config->set('Cache.SerializerPath', '/home/user/absolute/path');
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
@ -363,8 +363,8 @@ If your website is in a different encoding or doctype, use this code:
require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php'; require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';
$config = HTMLPurifier_Config::createDefault(); $config = HTMLPurifier_Config::createDefault();
$config->set('Core', 'Encoding', 'ISO-8859-1'); // replace with your encoding $config->set('Core.Encoding', 'ISO-8859-1'); // replace with your encoding
$config->set('HTML', 'Doctype', 'HTML 4.01 Transitional'); // replace with your doctype $config->set('HTML.Doctype', 'HTML 4.01 Transitional'); // replace with your doctype
$purifier = new HTMLPurifier($config); $purifier = new HTMLPurifier($config);
$clean_html = $purifier->purify($dirty_html); $clean_html = $purifier->purify($dirty_html);

View File

@ -9,6 +9,56 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
. Internal change . Internal change
========================== ==========================
4.0.0, released 2009-07-07
# APIs for ConfigSchema subsystem have substantially changed. See
docs/dev-config-bcbreaks.txt for details; in essence, anything that
had both namespace and directive now have a single unified key.
# Some configuration directives were renamed, specifically:
%AutoFormatParam.PurifierLinkifyDocURL -> %AutoFormat.PurifierLinkify.DocURL
%FilterParam.ExtractStyleBlocksEscaping -> %Filter.ExtractStyleBlocks.Escaping
%FilterParam.ExtractStyleBlocksScope -> %Filter.ExtractStyleBlocks.Scope
%FilterParam.ExtractStyleBlocksTidyImpl -> %Filter.ExtractStyleBlocks.TidyImpl
As usual, the old directive names will still work, but will throw E_NOTICE
errors.
# The allowed values for class have been relaxed to allow all of CDATA for
doctypes that are not XHTML 1.1 or XHTML 2.0. For old behavior, set
%Attr.ClassUseCDATA to false.
# Instead of appending the content model to an old content model, a blank
element will replace the old content model. You can use #SUPER to get
the old content model.
! More robust support for name="" and id=""
! HTMLPurifier_Config::inherit($config) allows you to inherit one
configuration, and have changes to that configuration be propagated
to all of its children.
! Implement %HTML.Attr.Name.UseCDATA, which relaxes validation rules on
the name attribute when set. Use with care. Thanks Ian Cook for
sponsoring.
! Implement %AutoFormat.RemoveEmpty.RemoveNbsp, which removes empty
tags that contain non-breaking spaces as well other whitespace. You
can also modify which tags should have &nbsp; maintained with
%AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.
! Implement %Attr.AllowedClasses, which allows administrators to restrict
classes users can use to a specified finite set of classes, and
%Attr.ForbiddenClasses, which is the logical inverse.
! You can now maintain your own configuration schema directories by
creating a config-schema.php file or passing an extra argument. Check
docs/dev-config-schema.html for more details.
! Added HTMLPurifier_Config->serialize() method, which lets you save away
your configuration in a compact serial file, which you can unserialize
and use directly without having to go through the overhead of setup.
- Fix bug where URIDefinition would not get cleared if it's directives got
changed.
- Fix fatal error in HTMLPurifier_Encoder on certain platforms (probably NetBSD 5.0)
- Fix bug in Linkify autoformatter involving <a><span>http://foo</span></a>
- Make %URI.Munge not apply to links that have the same host as your host.
- Prevent stray </body> tag from truncating output, if a second </body>
is present.
. Created script maintenance/rename-config.php for renaming a configuration
directive while maintaining its alias. This script does not change source code.
. Implement namespace locking for definition construction, to prevent
bugs where a directive is used for definition construction but is not
used to construct the cache hash.
3.3.0, released 2009-02-16 3.3.0, released 2009-02-16
! Implement CSS property 'overflow' when %CSS.AllowTricky is true. ! Implement CSS property 'overflow' when %CSS.AllowTricky is true.
! Implement generic property list classess ! Implement generic property list classess

View File

@ -7,7 +7,7 @@
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
* FILE, changes will be overwritten the next time the script is run. * FILE, changes will be overwritten the next time the script is run.
* *
* @version 3.3.0 * @version 4.0.0
* *
* @warning * @warning
* You must *not* include any other HTML Purifier files before this file, * You must *not* include any other HTML Purifier files before this file,
@ -98,6 +98,8 @@ require 'HTMLPurifier/AttrDef/CSS/Percentage.php';
require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php'; require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require 'HTMLPurifier/AttrDef/CSS/URI.php'; require 'HTMLPurifier/AttrDef/CSS/URI.php';
require 'HTMLPurifier/AttrDef/HTML/Bool.php'; require 'HTMLPurifier/AttrDef/HTML/Bool.php';
require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require 'HTMLPurifier/AttrDef/HTML/Class.php';
require 'HTMLPurifier/AttrDef/HTML/Color.php'; require 'HTMLPurifier/AttrDef/HTML/Color.php';
require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php'; require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php';
require 'HTMLPurifier/AttrDef/HTML/ID.php'; require 'HTMLPurifier/AttrDef/HTML/ID.php';
@ -105,7 +107,6 @@ require 'HTMLPurifier/AttrDef/HTML/Pixels.php';
require 'HTMLPurifier/AttrDef/HTML/Length.php'; require 'HTMLPurifier/AttrDef/HTML/Length.php';
require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php'; require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php';
require 'HTMLPurifier/AttrDef/HTML/MultiLength.php'; require 'HTMLPurifier/AttrDef/HTML/MultiLength.php';
require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require 'HTMLPurifier/AttrDef/URI/Email.php'; require 'HTMLPurifier/AttrDef/URI/Email.php';
require 'HTMLPurifier/AttrDef/URI/Host.php'; require 'HTMLPurifier/AttrDef/URI/Host.php';
require 'HTMLPurifier/AttrDef/URI/IPv4.php'; require 'HTMLPurifier/AttrDef/URI/IPv4.php';
@ -123,6 +124,7 @@ require 'HTMLPurifier/AttrTransform/Input.php';
require 'HTMLPurifier/AttrTransform/Lang.php'; require 'HTMLPurifier/AttrTransform/Lang.php';
require 'HTMLPurifier/AttrTransform/Length.php'; require 'HTMLPurifier/AttrTransform/Length.php';
require 'HTMLPurifier/AttrTransform/Name.php'; require 'HTMLPurifier/AttrTransform/Name.php';
require 'HTMLPurifier/AttrTransform/NameSync.php';
require 'HTMLPurifier/AttrTransform/SafeEmbed.php'; require 'HTMLPurifier/AttrTransform/SafeEmbed.php';
require 'HTMLPurifier/AttrTransform/SafeObject.php'; require 'HTMLPurifier/AttrTransform/SafeObject.php';
require 'HTMLPurifier/AttrTransform/SafeParam.php'; require 'HTMLPurifier/AttrTransform/SafeParam.php';

View File

@ -17,11 +17,11 @@ function kses($string, $allowed_html, $allowed_protocols = null) {
$allowed_attributes["$element.$attribute"] = true; $allowed_attributes["$element.$attribute"] = true;
} }
} }
$config->set('HTML', 'AllowedElements', $allowed_elements); $config->set('HTML.AllowedElements', $allowed_elements);
$config->set('HTML', 'AllowedAttributes', $allowed_attributes); $config->set('HTML.AllowedAttributes', $allowed_attributes);
$allowed_schemes = array(); $allowed_schemes = array();
if ($allowed_protocols !== null) { if ($allowed_protocols !== null) {
$config->set('URI', 'AllowedSchemes', $allowed_protocols); $config->set('URI.AllowedSchemes', $allowed_protocols);
} }
$purifier = new HTMLPurifier($config); $purifier = new HTMLPurifier($config);
return $purifier->purify($string); return $purifier->purify($string);

View File

@ -19,7 +19,7 @@
*/ */
/* /*
HTML Purifier 3.3.0 - Standards Compliant HTML Filtering HTML Purifier 4.0.0 - Standards Compliant HTML Filtering
Copyright (C) 2006-2008 Edward Z. Yang Copyright (C) 2006-2008 Edward Z. Yang
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
@ -55,10 +55,10 @@ class HTMLPurifier
{ {
/** Version of HTML Purifier */ /** Version of HTML Purifier */
public $version = '3.3.0'; public $version = '4.0.0';
/** Constant with version of HTML Purifier */ /** Constant with version of HTML Purifier */
const VERSION = '3.3.0'; const VERSION = '4.0.0';
/** Global configuration object */ /** Global configuration object */
public $config; public $config;
@ -128,7 +128,7 @@ class HTMLPurifier
$context->register('Generator', $this->generator); $context->register('Generator', $this->generator);
// set up global context variables // set up global context variables
if ($config->get('Core', 'CollectErrors')) { if ($config->get('Core.CollectErrors')) {
// may get moved out if other facilities use it // may get moved out if other facilities use it
$language_factory = HTMLPurifier_LanguageFactory::instance(); $language_factory = HTMLPurifier_LanguageFactory::instance();
$language = $language_factory->create($config, $context); $language = $language_factory->create($config, $context);
@ -152,6 +152,7 @@ class HTMLPurifier
$filters = array(); $filters = array();
foreach ($filter_flags as $filter => $flag) { foreach ($filter_flags as $filter => $flag) {
if (!$flag) continue; if (!$flag) continue;
if (strpos($filter, '.') !== false) continue;
$class = "HTMLPurifier_Filter_$filter"; $class = "HTMLPurifier_Filter_$filter";
$filters[] = new $class; $filters[] = new $class;
} }

View File

@ -92,6 +92,8 @@ require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Percentage.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php'; require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php'; require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php'; require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Class.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Color.php'; require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Color.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/FrameTarget.php'; require_once $__dir . '/HTMLPurifier/AttrDef/HTML/FrameTarget.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ID.php'; require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ID.php';
@ -99,7 +101,6 @@ require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Pixels.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Length.php'; require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Length.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/LinkTypes.php'; require_once $__dir . '/HTMLPurifier/AttrDef/HTML/LinkTypes.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/MultiLength.php'; require_once $__dir . '/HTMLPurifier/AttrDef/HTML/MultiLength.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email.php'; require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Host.php'; require_once $__dir . '/HTMLPurifier/AttrDef/URI/Host.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv4.php'; require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv4.php';
@ -117,6 +118,7 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/Input.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/NameSync.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';

View File

@ -9,7 +9,7 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
public function validate($color, $config, $context) { public function validate($color, $config, $context) {
static $colors = null; static $colors = null;
if ($colors === null) $colors = $config->get('Core', 'ColorKeywords'); if ($colors === null) $colors = $config->get('Core.ColorKeywords');
$color = trim($color); $color = trim($color);
if ($color === '') return false; if ($color === '') return false;

View File

@ -0,0 +1,34 @@
<?php
/**
* Implements special behavior for class attribute (normally NMTOKENS)
*/
class HTMLPurifier_AttrDef_HTML_Class extends HTMLPurifier_AttrDef_HTML_Nmtokens
{
protected function split($string, $config, $context) {
// really, this twiddle should be lazy loaded
$name = $config->getDefinition('HTML')->doctype->name;
if ($name == "XHTML 1.1" || $name == "XHTML 2.0") {
return parent::split($string, $config, $context);
} else {
return preg_split('/\s+/', $string);
}
}
protected function filter($tokens, $config, $context) {
$allowed = $config->get('Attr.AllowedClasses');
$forbidden = $config->get('Attr.ForbiddenClasses');
$ret = array();
foreach ($tokens as $token) {
if (
($allowed === null || isset($allowed[$token])) &&
!isset($forbidden[$token]) &&
// We need this O(n) check because of PHP's array
// implementation that casts -0 to 0.
!in_array($token, $ret, true)
) {
$ret[] = $token;
}
}
return $ret;
}
}

View File

@ -9,7 +9,7 @@ class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef
public function validate($string, $config, $context) { public function validate($string, $config, $context) {
static $colors = null; static $colors = null;
if ($colors === null) $colors = $config->get('Core', 'ColorKeywords'); if ($colors === null) $colors = $config->get('Core.ColorKeywords');
$string = trim($string); $string = trim($string);

View File

@ -12,7 +12,7 @@ class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum
public function __construct() {} public function __construct() {}
public function validate($string, $config, $context) { public function validate($string, $config, $context) {
if ($this->valid_values === false) $this->valid_values = $config->get('Attr', 'AllowedFrameTargets'); if ($this->valid_values === false) $this->valid_values = $config->get('Attr.AllowedFrameTargets');
return parent::validate($string, $config, $context); return parent::validate($string, $config, $context);
} }

View File

@ -17,18 +17,18 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
public function validate($id, $config, $context) { public function validate($id, $config, $context) {
if (!$config->get('Attr', 'EnableID')) return false; if (!$config->get('Attr.EnableID')) return false;
$id = trim($id); // trim it first $id = trim($id); // trim it first
if ($id === '') return false; if ($id === '') return false;
$prefix = $config->get('Attr', 'IDPrefix'); $prefix = $config->get('Attr.IDPrefix');
if ($prefix !== '') { if ($prefix !== '') {
$prefix .= $config->get('Attr', 'IDPrefixLocal'); $prefix .= $config->get('Attr.IDPrefixLocal');
// prevent re-appending the prefix // prevent re-appending the prefix
if (strpos($id, $prefix) !== 0) $id = $prefix . $id; if (strpos($id, $prefix) !== 0) $id = $prefix . $id;
} elseif ($config->get('Attr', 'IDPrefixLocal') !== '') { } elseif ($config->get('Attr.IDPrefixLocal') !== '') {
trigger_error('%Attr.IDPrefixLocal cannot be used unless '. trigger_error('%Attr.IDPrefixLocal cannot be used unless '.
'%Attr.IDPrefix is set', E_USER_WARNING); '%Attr.IDPrefix is set', E_USER_WARNING);
} }
@ -51,7 +51,7 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
$result = ($trim === ''); $result = ($trim === '');
} }
$regexp = $config->get('Attr', 'IDBlacklistRegexp'); $regexp = $config->get('Attr.IDBlacklistRegexp');
if ($regexp && preg_match($regexp, $id)) { if ($regexp && preg_match($regexp, $id)) {
return false; return false;
} }

View File

@ -27,7 +27,7 @@ class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef
public function validate($string, $config, $context) { public function validate($string, $config, $context) {
$allowed = $config->get('Attr', $this->name); $allowed = $config->get('Attr.' . $this->name);
if (empty($allowed)) return false; if (empty($allowed)) return false;
$string = $this->parseCDATA($string); $string = $this->parseCDATA($string);

View File

@ -2,10 +2,6 @@
/** /**
* Validates contents based on NMTOKENS attribute type. * Validates contents based on NMTOKENS attribute type.
* @note The only current use for this is the class attribute in HTML
* @note Could have some functionality factored out into Nmtoken class
* @warning We cannot assume this class will be used only for 'class'
* attributes. Not sure how to hook in magic behavior, then.
*/ */
class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef
{ {
@ -17,6 +13,17 @@ class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef
// early abort: '' and '0' (strings that convert to false) are invalid // early abort: '' and '0' (strings that convert to false) are invalid
if (!$string) return false; if (!$string) return false;
$tokens = $this->split($string, $config, $context);
$tokens = $this->filter($tokens, $config, $context);
if (empty($tokens)) return false;
return implode(' ', $tokens);
}
/**
* Splits a space separated list of tokens into its constituent parts.
*/
protected function split($string, $config, $context) {
// OPTIMIZABLE! // OPTIMIZABLE!
// do the preg_match, capture all subpatterns for reformulation // do the preg_match, capture all subpatterns for reformulation
@ -24,23 +31,20 @@ class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef
// escaping because I don't know how to do that with regexps // escaping because I don't know how to do that with regexps
// and plus it would complicate optimization efforts (you never // and plus it would complicate optimization efforts (you never
// see that anyway). // see that anyway).
$matches = array();
$pattern = '/(?:(?<=\s)|\A)'. // look behind for space or string start $pattern = '/(?:(?<=\s)|\A)'. // look behind for space or string start
'((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'. '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'.
'(?:(?=\s)|\z)/'; // look ahead for space or string end '(?:(?=\s)|\z)/'; // look ahead for space or string end
preg_match_all($pattern, $string, $matches); preg_match_all($pattern, $string, $matches);
return $matches[1];
}
if (empty($matches[1])) return false; /**
* Template method for removing certain tokens based on arbitrary criteria.
// reconstruct string * @note If we wanted to be really functional, we'd do an array_filter
$new_string = ''; * with a callback. But... we're not.
foreach ($matches[1] as $token) { */
$new_string .= $token . ' '; protected function filter($tokens, $config, $context) {
} return $tokens;
$new_string = rtrim($new_string);
return $new_string;
} }
} }

View File

@ -25,7 +25,7 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
public function validate($uri, $config, $context) { public function validate($uri, $config, $context) {
if ($config->get('URI', 'Disable')) return false; if ($config->get('URI.Disable')) return false;
$uri = $this->parseCDATA($uri); $uri = $this->parseCDATA($uri);

View File

@ -10,7 +10,7 @@ class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
public function transform($attr, $config, $context) { public function transform($attr, $config, $context) {
if (isset($attr['dir'])) return $attr; if (isset($attr['dir'])) return $attr;
$attr['dir'] = $config->get('Attr', 'DefaultTextDir'); $attr['dir'] = $config->get('Attr.DefaultTextDir');
return $attr; return $attr;
} }

View File

@ -15,21 +15,21 @@ class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
$src = true; $src = true;
if (!isset($attr['src'])) { if (!isset($attr['src'])) {
if ($config->get('Core', 'RemoveInvalidImg')) return $attr; if ($config->get('Core.RemoveInvalidImg')) return $attr;
$attr['src'] = $config->get('Attr', 'DefaultInvalidImage'); $attr['src'] = $config->get('Attr.DefaultInvalidImage');
$src = false; $src = false;
} }
if (!isset($attr['alt'])) { if (!isset($attr['alt'])) {
if ($src) { if ($src) {
$alt = $config->get('Attr', 'DefaultImageAlt'); $alt = $config->get('Attr.DefaultImageAlt');
if ($alt === null) { if ($alt === null) {
$attr['alt'] = basename($attr['src']); $attr['alt'] = basename($attr['src']);
} else { } else {
$attr['alt'] = $alt; $attr['alt'] = $alt;
} }
} else { } else {
$attr['alt'] = $config->get('Attr', 'DefaultInvalidImageAlt'); $attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt');
} }
} }

View File

@ -7,6 +7,8 @@ class HTMLPurifier_AttrTransform_Name extends HTMLPurifier_AttrTransform
{ {
public function transform($attr, $config, $context) { public function transform($attr, $config, $context) {
// Abort early if we're using relaxed definition of name
if ($config->get('HTML.Attr.Name.UseCDATA')) return $attr;
if (!isset($attr['name'])) return $attr; if (!isset($attr['name'])) return $attr;
$id = $this->confiscateAttr($attr, 'name'); $id = $this->confiscateAttr($attr, 'name');
if ( isset($attr['id'])) return $attr; if ( isset($attr['id'])) return $attr;

View File

@ -0,0 +1,27 @@
<?php
/**
* Post-transform that performs validation to the name attribute; if
* it is present with an equivalent id attribute, it is passed through;
* otherwise validation is performed.
*/
class HTMLPurifier_AttrTransform_NameSync extends HTMLPurifier_AttrTransform
{
public function __construct() {
$this->idDef = new HTMLPurifier_AttrDef_HTML_ID();
}
public function transform($attr, $config, $context) {
if (!isset($attr['name'])) return $attr;
$name = $attr['name'];
if (isset($attr['id']) && $attr['id'] === $name) return $attr;
$result = $this->idDef->validate($name, $config, $context);
if ($result === false) unset($attr['name']);
else $attr['name'] = $result;
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -36,6 +36,9 @@ class HTMLPurifier_AttrTypes
$this->info['Charsets'] = new HTMLPurifier_AttrDef_Text(); $this->info['Charsets'] = new HTMLPurifier_AttrDef_Text();
$this->info['Character'] = new HTMLPurifier_AttrDef_Text(); $this->info['Character'] = new HTMLPurifier_AttrDef_Text();
// "proprietary" types
$this->info['Class'] = new HTMLPurifier_AttrDef_HTML_Class();
// number is really a positive integer (one or more digits) // number is really a positive integer (one or more digits)
// FIXME: ^^ not always, see start and value of list items // FIXME: ^^ not always, see start and value of list items
$this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true); $this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true);

View File

@ -154,7 +154,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
new HTMLPurifier_AttrDef_CSS_Percentage(true), new HTMLPurifier_AttrDef_CSS_Percentage(true),
new HTMLPurifier_AttrDef_Enum(array('auto')) new HTMLPurifier_AttrDef_Enum(array('auto'))
)); ));
$max = $config->get('CSS', 'MaxImgLength'); $max = $config->get('CSS.MaxImgLength');
$this->info['width'] = $this->info['width'] =
$this->info['height'] = $this->info['height'] =
@ -211,15 +211,15 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
// partial support // partial support
$this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap')); $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap'));
if ($config->get('CSS', 'Proprietary')) { if ($config->get('CSS.Proprietary')) {
$this->doSetupProprietary($config); $this->doSetupProprietary($config);
} }
if ($config->get('CSS', 'AllowTricky')) { if ($config->get('CSS.AllowTricky')) {
$this->doSetupTricky($config); $this->doSetupTricky($config);
} }
$allow_important = $config->get('CSS', 'AllowImportant'); $allow_important = $config->get('CSS.AllowImportant');
// wrap all attr-defs with decorator that handles !important // wrap all attr-defs with decorator that handles !important
foreach ($this->info as $k => $v) { foreach ($this->info as $k => $v) {
$this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important); $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
@ -272,7 +272,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
// setup allowed elements // setup allowed elements
$support = "(for information on implementing this, see the ". $support = "(for information on implementing this, see the ".
"support forums) "; "support forums) ";
$allowed_attributes = $config->get('CSS', 'AllowedProperties'); $allowed_attributes = $config->get('CSS.AllowedProperties');
if ($allowed_attributes !== null) { if ($allowed_attributes !== null) {
foreach ($this->info as $name => $d) { foreach ($this->info as $name => $d) {
if(!isset($allowed_attributes[$name])) unset($this->info[$name]); if(!isset($allowed_attributes[$name])) unset($this->info[$name]);

View File

@ -59,7 +59,7 @@ class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
$all_whitespace = true; $all_whitespace = true;
// some configuration // some configuration
$escape_invalid_children = $config->get('Core', 'EscapeInvalidChildren'); $escape_invalid_children = $config->get('Core.EscapeInvalidChildren');
// generator // generator
$gen = new HTMLPurifier_Generator($config, $context); $gen = new HTMLPurifier_Generator($config, $context);

View File

@ -20,7 +20,7 @@ class HTMLPurifier_Config
/** /**
* HTML Purifier's version * HTML Purifier's version
*/ */
public $version = '3.3.0'; public $version = '4.0.0';
/** /**
* Bool indicator whether or not to automatically finalize * Bool indicator whether or not to automatically finalize
@ -68,12 +68,30 @@ class HTMLPurifier_Config
*/ */
protected $plist; protected $plist;
/**
* Whether or not a set is taking place due to an
* alias lookup.
*/
private $aliasMode;
/**
* Set to false if you do not want line and file numbers in errors
* (useful when unit testing)
*/
public $chatty = true;
/**
* Current lock; only gets to this namespace are allowed.
*/
private $lock;
/** /**
* @param $definition HTMLPurifier_ConfigSchema that defines what directives * @param $definition HTMLPurifier_ConfigSchema that defines what directives
* are allowed. * are allowed.
*/ */
public function __construct($definition) { public function __construct($definition, $parent = null) {
$this->plist = new HTMLPurifier_PropertyList($definition->defaultPlist); $parent = $parent ? $parent : $definition->defaultPlist;
$this->plist = new HTMLPurifier_PropertyList($parent);
$this->def = $definition; // keep a copy around for checking $this->def = $definition; // keep a copy around for checking
$this->parser = new HTMLPurifier_VarParser_Flexible(); $this->parser = new HTMLPurifier_VarParser_Flexible();
} }
@ -102,6 +120,16 @@ class HTMLPurifier_Config
return $ret; return $ret;
} }
/**
* Creates a new config object that inherits from a previous one.
* @param HTMLPurifier_Config $config Configuration object to inherit
* from.
* @return HTMLPurifier_Config object with $config as its parent.
*/
public static function inherit(HTMLPurifier_Config $config) {
return new HTMLPurifier_Config($config->def, $config->plist);
}
/** /**
* Convenience constructor that creates a default configuration object. * Convenience constructor that creates a default configuration object.
* @return Default HTMLPurifier_Config object. * @return Default HTMLPurifier_Config object.
@ -114,24 +142,34 @@ class HTMLPurifier_Config
/** /**
* Retreives a value from the configuration. * Retreives a value from the configuration.
* @param $namespace String namespace
* @param $key String key * @param $key String key
*/ */
public function get($namespace, $key) { public function get($key, $a = null) {
if (!$this->finalized) $this->autoFinalize ? $this->finalize() : $this->plist->squash(true); if ($a !== null) {
if (!isset($this->def->info[$namespace][$key])) { $this->triggerError("Using deprecated API: use \$config->get('$key.$a') instead", E_USER_WARNING);
$key = "$key.$a";
}
if (!$this->finalized) $this->autoFinalize();
if (!isset($this->def->info[$key])) {
// can't add % due to SimpleTest bug // can't add % due to SimpleTest bug
trigger_error('Cannot retrieve value of undefined directive ' . htmlspecialchars("$namespace.$key"), $this->triggerError('Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
E_USER_WARNING); E_USER_WARNING);
return; return;
} }
if (isset($this->def->info[$namespace][$key]->isAlias)) { if (isset($this->def->info[$key]->isAlias)) {
$d = $this->def->info[$namespace][$key]; $d = $this->def->info[$key];
trigger_error('Cannot get value from aliased directive, use real name ' . $d->namespace . '.' . $d->name, $this->triggerError('Cannot get value from aliased directive, use real name ' . $d->key,
E_USER_ERROR); E_USER_ERROR);
return; return;
} }
return $this->plist->get("$namespace.$key"); if ($this->lock) {
list($ns) = explode('.', $key);
if ($ns !== $this->lock) {
$this->triggerError('Cannot get value of namespace ' . $ns . ' when lock for ' . $this->lock . ' is active, this probably indicates a Definition setup method is accessing directives that are not within its namespace', E_USER_ERROR);
return;
}
}
return $this->plist->get($key);
} }
/** /**
@ -139,13 +177,13 @@ class HTMLPurifier_Config
* @param $namespace String namespace * @param $namespace String namespace
*/ */
public function getBatch($namespace) { public function getBatch($namespace) {
if (!$this->finalized) $this->autoFinalize ? $this->finalize() : $this->plist->squash(true); if (!$this->finalized) $this->autoFinalize();
if (!isset($this->def->info[$namespace])) { $full = $this->getAll();
trigger_error('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace), if (!isset($full[$namespace])) {
$this->triggerError('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace),
E_USER_WARNING); E_USER_WARNING);
return; return;
} }
$full = $this->getAll();
return $full[$namespace]; return $full[$namespace];
} }
@ -178,9 +216,10 @@ class HTMLPurifier_Config
/** /**
* Retrieves all directives, organized by namespace * Retrieves all directives, organized by namespace
* @warning This is a pretty inefficient function, avoid if you can
*/ */
public function getAll() { public function getAll() {
if (!$this->finalized) $this->autoFinalize ? $this->finalize() : $this->plist->squash(true); if (!$this->finalized) $this->autoFinalize();
$ret = array(); $ret = array();
foreach ($this->plist->squash() as $name => $value) { foreach ($this->plist->squash() as $name => $value) {
list($ns, $key) = explode('.', $name, 2); list($ns, $key) = explode('.', $name, 2);
@ -191,29 +230,37 @@ class HTMLPurifier_Config
/** /**
* Sets a value to configuration. * Sets a value to configuration.
* @param $namespace String namespace
* @param $key String key * @param $key String key
* @param $value Mixed value * @param $value Mixed value
*/ */
public function set($namespace, $key, $value, $from_alias = false) { public function set($key, $value, $a = null) {
if (strpos($key, '.') === false) {
$namespace = $key;
$directive = $value;
$value = $a;
$key = "$key.$directive";
$this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE);
} else {
list($namespace) = explode('.', $key);
}
if ($this->isFinalized('Cannot set directive after finalization')) return; if ($this->isFinalized('Cannot set directive after finalization')) return;
if (!isset($this->def->info[$namespace][$key])) { if (!isset($this->def->info[$key])) {
trigger_error('Cannot set undefined directive ' . htmlspecialchars("$namespace.$key") . ' to value', $this->triggerError('Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
E_USER_WARNING); E_USER_WARNING);
return; return;
} }
$def = $this->def->info[$namespace][$key]; $def = $this->def->info[$key];
if (isset($def->isAlias)) { if (isset($def->isAlias)) {
if ($from_alias) { if ($this->aliasMode) {
trigger_error('Double-aliases not allowed, please fix '. $this->triggerError('Double-aliases not allowed, please fix '.
'ConfigSchema bug with' . "$namespace.$key", E_USER_ERROR); 'ConfigSchema bug with' . $key, E_USER_ERROR);
return; return;
} }
$this->set($new_ns = $def->namespace, $this->aliasMode = true;
$new_dir = $def->name, $this->set($def->key, $value);
$value, true); $this->aliasMode = false;
trigger_error("$namespace.$key is an alias, preferred directive name is $new_ns.$new_dir", E_USER_NOTICE); $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE);
return; return;
} }
@ -231,7 +278,7 @@ class HTMLPurifier_Config
try { try {
$value = $this->parser->parse($value, $type, $allow_null); $value = $this->parser->parse($value, $type, $allow_null);
} catch (HTMLPurifier_VarParserException $e) { } catch (HTMLPurifier_VarParserException $e) {
trigger_error('Value for ' . "$namespace.$key" . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING); $this->triggerError('Value for ' . $key . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING);
return; return;
} }
if (is_string($value) && is_object($def)) { if (is_string($value) && is_object($def)) {
@ -241,17 +288,17 @@ class HTMLPurifier_Config
} }
// check to see if the value is allowed // check to see if the value is allowed
if (isset($def->allowed) && !isset($def->allowed[$value])) { if (isset($def->allowed) && !isset($def->allowed[$value])) {
trigger_error('Value not supported, valid values are: ' . $this->triggerError('Value not supported, valid values are: ' .
$this->_listify($def->allowed), E_USER_WARNING); $this->_listify($def->allowed), E_USER_WARNING);
return; return;
} }
} }
$this->plist->set("$namespace.$key", $value); $this->plist->set($key, $value);
// reset definitions if the directives they depend on changed // reset definitions if the directives they depend on changed
// this is a very costly process, so it's discouraged // this is a very costly process, so it's discouraged
// with finalization // with finalization
if ($namespace == 'HTML' || $namespace == 'CSS') { if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') {
$this->definitions[$namespace] = null; $this->definitions[$namespace] = null;
} }
@ -291,9 +338,13 @@ class HTMLPurifier_Config
* @param $raw Whether or not definition should be returned raw * @param $raw Whether or not definition should be returned raw
*/ */
public function getDefinition($type, $raw = false) { public function getDefinition($type, $raw = false) {
if (!$this->finalized) $this->autoFinalize ? $this->finalize() : $this->plist->squash(true); if (!$this->finalized) $this->autoFinalize();
// temporarily suspend locks, so we can handle recursive definition calls
$lock = $this->lock;
$this->lock = null;
$factory = HTMLPurifier_DefinitionCacheFactory::instance(); $factory = HTMLPurifier_DefinitionCacheFactory::instance();
$cache = $factory->create($type, $this); $cache = $factory->create($type, $this);
$this->lock = $lock;
if (!$raw) { if (!$raw) {
// see if we can quickly supply a definition // see if we can quickly supply a definition
if (!empty($this->definitions[$type])) { if (!empty($this->definitions[$type])) {
@ -328,14 +379,16 @@ class HTMLPurifier_Config
} }
// quick abort if raw // quick abort if raw
if ($raw) { if ($raw) {
if (is_null($this->get($type, 'DefinitionID'))) { if (is_null($this->get($type . '.DefinitionID'))) {
// fatally error out if definition ID not set // fatally error out if definition ID not set
throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID"); throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID");
} }
return $this->definitions[$type]; return $this->definitions[$type];
} }
// set it up // set it up
$this->lock = $type;
$this->definitions[$type]->setup($this); $this->definitions[$type]->setup($this);
$this->lock = null;
// save in cache // save in cache
$cache->set($this->definitions[$type], $this); $cache->set($this->definitions[$type], $this);
return $this->definitions[$type]; return $this->definitions[$type];
@ -351,14 +404,12 @@ class HTMLPurifier_Config
foreach ($config_array as $key => $value) { foreach ($config_array as $key => $value) {
$key = str_replace('_', '.', $key); $key = str_replace('_', '.', $key);
if (strpos($key, '.') !== false) { if (strpos($key, '.') !== false) {
// condensed form $this->set($key, $value);
list($namespace, $directive) = explode('.', $key);
$this->set($namespace, $directive, $value);
} else { } else {
$namespace = $key; $namespace = $key;
$namespace_values = $value; $namespace_values = $value;
foreach ($namespace_values as $directive => $value) { foreach ($namespace_values as $directive => $value) {
$this->set($namespace, $directive, $value); $this->set($namespace .'.'. $directive, $value);
} }
} }
} }
@ -394,16 +445,15 @@ class HTMLPurifier_Config
} }
} }
$ret = array(); $ret = array();
foreach ($schema->info as $ns => $keypairs) { foreach ($schema->info as $key => $def) {
foreach ($keypairs as $directive => $def) { list($ns, $directive) = explode('.', $key, 2);
if ($allowed !== true) { if ($allowed !== true) {
if (isset($blacklisted_directives["$ns.$directive"])) continue; if (isset($blacklisted_directives["$ns.$directive"])) continue;
if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue; if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue;
}
if (isset($def->isAlias)) continue;
if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue;
$ret[] = array($ns, $directive);
} }
if (isset($def->isAlias)) continue;
if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue;
$ret[] = array($ns, $directive);
} }
return $ret; return $ret;
} }
@ -472,7 +522,7 @@ class HTMLPurifier_Config
*/ */
public function isFinalized($error = false) { public function isFinalized($error = false) {
if ($this->finalized && $error) { if ($this->finalized && $error) {
trigger_error($error, E_USER_ERROR); $this->triggerError($error, E_USER_ERROR);
} }
return $this->finalized; return $this->finalized;
} }
@ -482,7 +532,11 @@ class HTMLPurifier_Config
* already finalized * already finalized
*/ */
public function autoFinalize() { public function autoFinalize() {
if (!$this->finalized && $this->autoFinalize) $this->finalize(); if ($this->autoFinalize) {
$this->finalize();
} else {
$this->plist->squash(true);
}
} }
/** /**
@ -490,6 +544,35 @@ class HTMLPurifier_Config
*/ */
public function finalize() { public function finalize() {
$this->finalized = true; $this->finalized = true;
unset($this->parser);
}
/**
* Produces a nicely formatted error message by supplying the
* stack frame information from two levels up and OUTSIDE of
* HTMLPurifier_Config.
*/
protected function triggerError($msg, $no) {
// determine previous stack frame
$backtrace = debug_backtrace();
if ($this->chatty && isset($backtrace[1])) {
$frame = $backtrace[1];
$extra = " on line {$frame['line']} in file {$frame['file']}";
} else {
$extra = '';
}
trigger_error($msg . $extra, $no);
}
/**
* Returns a serialized form of the configuration object that can
* be reconstituted.
*/
public function serialize() {
$this->getDefinition('HTML');
$this->getDefinition('CSS');
$this->getDefinition('URI');
return serialize($this);
} }
} }

View File

@ -87,24 +87,13 @@ class HTMLPurifier_ConfigSchema {
* HTMLPurifier_DirectiveDef::$type for allowed values * HTMLPurifier_DirectiveDef::$type for allowed values
* @param $allow_null Whether or not to allow null values * @param $allow_null Whether or not to allow null values
*/ */
public function add($namespace, $name, $default, $type, $allow_null) { public function add($key, $default, $type, $allow_null) {
$obj = new stdclass(); $obj = new stdclass();
$obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type]; $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
if ($allow_null) $obj->allow_null = true; if ($allow_null) $obj->allow_null = true;
$this->info[$namespace][$name] = $obj; $this->info[$key] = $obj;
$this->defaults[$namespace][$name] = $default; $this->defaults[$key] = $default;
$this->defaultPlist->set("$namespace.$name", $default); $this->defaultPlist->set($key, $default);
}
/**
* Defines a namespace for directives to be put into.
* @warning This is slightly different from the corresponding static
* method.
* @param $namespace Namespace's name
*/
public function addNamespace($namespace) {
$this->info[$namespace] = array();
$this->defaults[$namespace] = array();
} }
/** /**
@ -116,12 +105,12 @@ class HTMLPurifier_ConfigSchema {
* @param $name Name of Directive * @param $name Name of Directive
* @param $aliases Hash of aliased values to the real alias * @param $aliases Hash of aliased values to the real alias
*/ */
public function addValueAliases($namespace, $name, $aliases) { public function addValueAliases($key, $aliases) {
if (!isset($this->info[$namespace][$name]->aliases)) { if (!isset($this->info[$key]->aliases)) {
$this->info[$namespace][$name]->aliases = array(); $this->info[$key]->aliases = array();
} }
foreach ($aliases as $alias => $real) { foreach ($aliases as $alias => $real) {
$this->info[$namespace][$name]->aliases[$alias] = $real; $this->info[$key]->aliases[$alias] = $real;
} }
} }
@ -133,8 +122,8 @@ class HTMLPurifier_ConfigSchema {
* @param $name Name of directive * @param $name Name of directive
* @param $allowed Lookup array of allowed values * @param $allowed Lookup array of allowed values
*/ */
public function addAllowedValues($namespace, $name, $allowed) { public function addAllowedValues($key, $allowed) {
$this->info[$namespace][$name]->allowed = $allowed; $this->info[$key]->allowed = $allowed;
} }
/** /**
@ -144,88 +133,26 @@ class HTMLPurifier_ConfigSchema {
* @param $new_namespace * @param $new_namespace
* @param $new_name Directive that the alias will be to * @param $new_name Directive that the alias will be to
*/ */
public function addAlias($namespace, $name, $new_namespace, $new_name) { public function addAlias($key, $new_key) {
$obj = new stdclass; $obj = new stdclass;
$obj->namespace = $new_namespace; $obj->key = $new_key;
$obj->name = $new_name;
$obj->isAlias = true; $obj->isAlias = true;
$this->info[$namespace][$name] = $obj; $this->info[$key] = $obj;
} }
/** /**
* Replaces any stdclass that only has the type property with type integer. * Replaces any stdclass that only has the type property with type integer.
*/ */
public function postProcess() { public function postProcess() {
foreach ($this->info as $namespace => $info) { foreach ($this->info as $key => $v) {
foreach ($info as $directive => $v) { if (count((array) $v) == 1) {
if (count((array) $v) == 1) { $this->info[$key] = $v->type;
$this->info[$namespace][$directive] = $v->type; } elseif (count((array) $v) == 2 && isset($v->allow_null)) {
} elseif (count((array) $v) == 2 && isset($v->allow_null)) { $this->info[$key] = -$v->type;
$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__);
$type_values = explode('/', $type, 2);
$type = $type_values[0];
$modifier = isset($type_values[1]) ? $type_values[1] : false;
$allow_null = ($modifier === 'null');
$def = HTMLPurifier_ConfigSchema::instance();
$def->add($namespace, $name, $default, $type, $allow_null);
}
/** @see HTMLPurifier_ConfigSchema->addNamespace() */
public static function defineNamespace($namespace, $description) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def = HTMLPurifier_ConfigSchema::instance();
$def->addNamespace($namespace);
}
/** @see HTMLPurifier_ConfigSchema->addValueAliases() */
public static function defineValueAliases($namespace, $name, $aliases) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def = HTMLPurifier_ConfigSchema::instance();
$def->addValueAliases($namespace, $name, $aliases);
}
/** @see HTMLPurifier_ConfigSchema->addAllowedValues() */
public static function defineAllowedValues($namespace, $name, $allowed_values) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$allowed = array();
foreach ($allowed_values as $value) {
$allowed[$value] = true;
}
$def = HTMLPurifier_ConfigSchema::instance();
$def->addAllowedValues($namespace, $name, $allowed);
}
/** @see HTMLPurifier_ConfigSchema->addAlias() */
public static function defineAlias($namespace, $name, $new_namespace, $new_name) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def = HTMLPurifier_ConfigSchema::instance();
$def->addAlias($namespace, $name, $new_namespace, $new_name);
}
/** @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);
$parser = new HTMLPurifier_VarParser();
return $parser->parse($a, $b, $c);
}
/**
* Throws an E_USER_NOTICE stating that a method is deprecated.
*/
private static function deprecated($method) {
trigger_error("Static HTMLPurifier_ConfigSchema::$method deprecated, use add*() method instead", E_USER_NOTICE);
}
} }
// vim: et sw=4 sts=4 // vim: et sw=4 sts=4

View File

@ -9,36 +9,28 @@ class HTMLPurifier_ConfigSchema_Builder_ConfigSchema
public function build($interchange) { public function build($interchange) {
$schema = new HTMLPurifier_ConfigSchema(); $schema = new HTMLPurifier_ConfigSchema();
foreach ($interchange->namespaces as $n) {
$schema->addNamespace($n->namespace);
}
foreach ($interchange->directives as $d) { foreach ($interchange->directives as $d) {
$schema->add( $schema->add(
$d->id->namespace, $d->id->key,
$d->id->directive,
$d->default, $d->default,
$d->type, $d->type,
$d->typeAllowsNull $d->typeAllowsNull
); );
if ($d->allowed !== null) { if ($d->allowed !== null) {
$schema->addAllowedValues( $schema->addAllowedValues(
$d->id->namespace, $d->id->key,
$d->id->directive,
$d->allowed $d->allowed
); );
} }
foreach ($d->aliases as $alias) { foreach ($d->aliases as $alias) {
$schema->addAlias( $schema->addAlias(
$alias->namespace, $alias->key,
$alias->directive, $d->id->key
$d->id->namespace,
$d->id->directive
); );
} }
if ($d->valueAliases !== null) { if ($d->valueAliases !== null) {
$schema->addValueAliases( $schema->addValueAliases(
$d->id->namespace, $d->id->key,
$d->id->directive,
$d->valueAliases $d->valueAliases
); );
} }

View File

@ -8,6 +8,7 @@ class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
{ {
protected $interchange; protected $interchange;
private $namespace;
protected function writeHTMLDiv($html) { protected function writeHTMLDiv($html) {
$this->startElement('div'); $this->startElement('div');
@ -34,36 +35,33 @@ class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
$this->startElement('configdoc'); $this->startElement('configdoc');
$this->writeElement('title', $interchange->name); $this->writeElement('title', $interchange->name);
foreach ($interchange->namespaces as $namespace) { foreach ($interchange->directives as $directive) {
$this->buildNamespace($namespace); $this->buildDirective($directive);
} }
if ($this->namespace) $this->endElement(); // namespace
$this->endElement(); // configdoc $this->endElement(); // configdoc
$this->flush(); $this->flush();
} }
public function buildNamespace($namespace) { public function buildDirective($directive) {
$this->startElement('namespace');
$this->writeAttribute('id', $namespace->namespace);
$this->writeElement('name', $namespace->namespace); // Kludge, although I suppose having a notion of a "root namespace"
$this->startElement('description'); // certainly makes things look nicer when documentation is built.
$this->writeHTMLDiv($namespace->description); // Depends on things being sorted.
$this->endElement(); // description if (!$this->namespace || $this->namespace !== $directive->id->getRootNamespace()) {
if ($this->namespace) $this->endElement(); // namespace
foreach ($this->interchange->directives as $directive) { $this->namespace = $directive->id->getRootNamespace();
if ($directive->id->namespace !== $namespace->namespace) continue; $this->startElement('namespace');
$this->buildDirective($directive); $this->writeAttribute('id', $this->namespace);
$this->writeElement('name', $this->namespace);
} }
$this->endElement(); // namespace
}
public function buildDirective($directive) {
$this->startElement('directive'); $this->startElement('directive');
$this->writeAttribute('id', $directive->id->toString()); $this->writeAttribute('id', $directive->id->toString());
$this->writeElement('name', $directive->id->directive); $this->writeElement('name', $directive->id->getDirective());
$this->startElement('aliases'); $this->startElement('aliases');
foreach ($directive->aliases as $alias) $this->writeElement('alias', $alias->toString()); foreach ($directive->aliases as $alias) $this->writeElement('alias', $alias->toString());

View File

@ -13,26 +13,11 @@ class HTMLPurifier_ConfigSchema_Interchange
*/ */
public $name; public $name;
/**
* Array of Namespace ID => array(namespace info)
*/
public $namespaces = array();
/** /**
* Array of Directive ID => array(directive info) * Array of Directive ID => array(directive info)
*/ */
public $directives = array(); public $directives = array();
/**
* Adds a namespace array to $namespaces
*/
public function addNamespace($namespace) {
if (isset($this->namespaces[$i = $namespace->namespace])) {
throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine namespace '$i'");
}
$this->namespaces[$i] = $namespace;
}
/** /**
* Adds a directive array to $directives * Adds a directive array to $directives
*/ */

View File

@ -6,11 +6,10 @@
class HTMLPurifier_ConfigSchema_Interchange_Id class HTMLPurifier_ConfigSchema_Interchange_Id
{ {
public $namespace, $directive; public $key;
public function __construct($namespace, $directive) { public function __construct($key) {
$this->namespace = $namespace; $this->key = $key;
$this->directive = $directive;
} }
/** /**
@ -18,12 +17,19 @@ class HTMLPurifier_ConfigSchema_Interchange_Id
* cause problems for PHP 5.0 support. * cause problems for PHP 5.0 support.
*/ */
public function toString() { public function toString() {
return $this->namespace . '.' . $this->directive; return $this->key;
}
public function getRootNamespace() {
return substr($this->key, 0, strpos($this->key, "."));
}
public function getDirective() {
return substr($this->key, strpos($this->key, ".") + 1);
} }
public static function make($id) { public static function make($id) {
list($namespace, $directive) = explode('.', $id); return new HTMLPurifier_ConfigSchema_Interchange_Id($id);
return new HTMLPurifier_ConfigSchema_Interchange_Id($namespace, $directive);
} }
} }

View File

@ -13,13 +13,17 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder
} }
public static function buildFromDirectory($dir = null) { public static function buildFromDirectory($dir = null) {
$parser = new HTMLPurifier_StringHashParser();
$builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder(); $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
$interchange = new HTMLPurifier_ConfigSchema_Interchange(); $interchange = new HTMLPurifier_ConfigSchema_Interchange();
return $builder->buildDir($interchange, $dir);
}
if (!$dir) $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema/'; public function buildDir($interchange, $dir = null) {
$info = parse_ini_file($dir . 'info.ini'); if (!$dir) $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema';
$interchange->name = $info['name']; if (file_exists($dir . '/info.ini')) {
$info = parse_ini_file($dir . '/info.ini');
$interchange->name = $info['name'];
}
$files = array(); $files = array();
$dh = opendir($dir); $dh = opendir($dir);
@ -33,15 +37,20 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder
sort($files); sort($files);
foreach ($files as $file) { foreach ($files as $file) {
$builder->build( $this->buildFile($interchange, $dir . '/' . $file);
$interchange,
new HTMLPurifier_StringHash( $parser->parseFile($dir . $file) )
);
} }
return $interchange; return $interchange;
} }
public function buildFile($interchange, $file) {
$parser = new HTMLPurifier_StringHashParser();
$this->build(
$interchange,
new HTMLPurifier_StringHash( $parser->parseFile($file) )
);
}
/** /**
* Builds an interchange object based on a hash. * Builds an interchange object based on a hash.
* @param $interchange HTMLPurifier_ConfigSchema_Interchange object to build * @param $interchange HTMLPurifier_ConfigSchema_Interchange object to build
@ -55,22 +64,17 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder
throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID'); throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID');
} }
if (strpos($hash['ID'], '.') === false) { if (strpos($hash['ID'], '.') === false) {
$this->buildNamespace($interchange, $hash); if (count($hash) == 2 && isset($hash['DESCRIPTION'])) {
$hash->offsetGet('DESCRIPTION'); // prevent complaining
} else {
throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace');
}
} else { } else {
$this->buildDirective($interchange, $hash); $this->buildDirective($interchange, $hash);
} }
$this->_findUnused($hash); $this->_findUnused($hash);
} }
public function buildNamespace($interchange, $hash) {
$namespace = new HTMLPurifier_ConfigSchema_Interchange_Namespace();
$namespace->namespace = $hash->offsetGet('ID');
if (isset($hash['DESCRIPTION'])) {
$namespace->description = $hash->offsetGet('DESCRIPTION');
}
$interchange->addNamespace($namespace);
}
public function buildDirective($interchange, $hash) { public function buildDirective($interchange, $hash) {
$directive = new HTMLPurifier_ConfigSchema_Interchange_Directive(); $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();

View File

@ -39,10 +39,6 @@ class HTMLPurifier_ConfigSchema_Validator
$this->aliases = array(); $this->aliases = array();
// PHP is a bit lax with integer <=> string conversions in // PHP is a bit lax with integer <=> string conversions in
// arrays, so we don't use the identical !== comparison // arrays, so we don't use the identical !== comparison
foreach ($interchange->namespaces as $i => $namespace) {
if ($i != $namespace->namespace) $this->error(false, "Integrity violation: key '$i' does not match internal id '{$namespace->namespace}'");
$this->validateNamespace($namespace);
}
foreach ($interchange->directives as $i => $directive) { foreach ($interchange->directives as $i => $directive) {
$id = $directive->id->toString(); $id = $directive->id->toString();
if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'"); if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
@ -51,20 +47,6 @@ class HTMLPurifier_ConfigSchema_Validator
return true; return true;
} }
/**
* Validates a HTMLPurifier_ConfigSchema_Interchange_Namespace object.
*/
public function validateNamespace($n) {
$this->context[] = "namespace '{$n->namespace}'";
$this->with($n, 'namespace')
->assertNotEmpty()
->assertAlnum(); // implicit assertIsString handled by InterchangeBuilder
$this->with($n, 'description')
->assertNotEmpty()
->assertIsString(); // handled by InterchangeBuilder
array_pop($this->context);
}
/** /**
* Validates a HTMLPurifier_ConfigSchema_Interchange_Id object. * Validates a HTMLPurifier_ConfigSchema_Interchange_Id object.
*/ */
@ -75,12 +57,11 @@ class HTMLPurifier_ConfigSchema_Validator
// handled by InterchangeBuilder // handled by InterchangeBuilder
$this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id'); $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
} }
if (!isset($this->interchange->namespaces[$id->namespace])) { // keys are now unconstrained (we might want to narrow down to A-Za-z0-9.)
$this->error('namespace', 'does not exist'); // assumes that the namespace was validated already // we probably should check that it has at least one namespace
} $this->with($id, 'key')
$this->with($id, 'directive')
->assertNotEmpty() ->assertNotEmpty()
->assertAlnum(); // implicit assertIsString handled by InterchangeBuilder ->assertIsString(); // implicit assertIsString handled by InterchangeBuilder
array_pop($this->context); array_pop($this->context);
} }

View File

@ -0,0 +1,8 @@
Attr.AllowedClasses
TYPE: lookup/null
VERSION: 4.0.0
DEFAULT: null
--DESCRIPTION--
List of allowed class values in the class attribute. By default, this is null,
which means all classes are allowed.
--# vim: et sw=4 sts=4

View File

@ -0,0 +1,19 @@
Attr.ClassUseCDATA
TYPE: bool/null
DEFAULT: null
VERSION: 4.0.0
--DESCRIPTION--
If null, class will auto-detect the doctype and, if matching XHTML 1.1 or
XHTML 2.0, will use the restrictive NMTOKENS specification of class. Otherwise,
it will use a relaxed CDATA definition. If true, the relaxed CDATA definition
is forced; if false, the NMTOKENS definition is forced. To get behavior
of HTML Purifier prior to 4.0.0, set this directive to false.
Some rational behind the auto-detection:
in previous versions of HTML Purifier, it was assumed that the form of
class was NMTOKENS, as specified by the XHTML Modularization (representing
XHTML 1.1 and XHTML 2.0). The DTDs for HTML 4.01 and XHTML 1.0, however
specify class as CDATA. HTML 5 effectively defines it as CDATA, but
with the additional constraint that each name should be unique (this is not
explicitly outlined in previous specifications).
--# vim: et sw=4 sts=4

View File

@ -0,0 +1,8 @@
Attr.ForbiddenClasses
TYPE: lookup
VERSION: 4.0.0
DEFAULT: array()
--DESCRIPTION--
List of forbidden class values in the class attribute. By default, this is
empty, which means that no classes are forbidden. See also %Attr.AllowedClasses.
--# vim: et sw=4 sts=4

View File

@ -0,0 +1,12 @@
AutoFormat.PurifierLinkify.DocURL
TYPE: string
VERSION: 2.0.1
DEFAULT: '#%s'
ALIASES: AutoFormatParam.PurifierLinkifyDocURL
--DESCRIPTION--
<p>
Location of configuration documentation to link to, let %s substitute
into the configuration's namespace and directive names sans the percent
sign.
</p>
--# vim: et sw=4 sts=4

View File

@ -0,0 +1,11 @@
AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions
TYPE: lookup
VERSION: 4.0.0
DEFAULT: array('td' => true, 'th' => true)
--DESCRIPTION--
<p>
When %AutoFormat.RemoveEmpty and %AutoFormat.RemoveEmpty.RemoveNbsp
are enabled, this directive defines what HTML elements should not be
removede if they have only a non-breaking space in them.
</p>
--# vim: et sw=4 sts=4

View File

@ -0,0 +1,15 @@
AutoFormat.RemoveEmpty.RemoveNbsp
TYPE: bool
VERSION: 4.0.0
DEFAULT: false
--DESCRIPTION--
<p>
When enabled, HTML Purifier will treat any elements that contain only
non-breaking spaces as well as regular whitespace as empty, and remove
them when %AutoForamt.RemoveEmpty is enabled.
</p>
<p>
See %AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions for a list of elements
that don't have this behavior applied to them.
</p>
--# vim: et sw=4 sts=4

View File

@ -31,7 +31,8 @@ DEFAULT: false
</p> </p>
<p> <p>
Elements that contain only whitespace will be treated as empty. Non-breaking Elements that contain only whitespace will be treated as empty. Non-breaking
spaces, however, do not count as whitespace. spaces, however, do not count as whitespace. See
%AutoFormat.RemoveEmpty.RemoveNbsp for alternate behavior.
</p> </p>
<p> <p>
This algorithm is not perfect; you may still notice some empty tags, This algorithm is not perfect; you may still notice some empty tags,
@ -39,7 +40,7 @@ DEFAULT: false
because they were not permitted in that context, or tags that, after because they were not permitted in that context, or tags that, after
being auto-closed by another tag, where empty. This is for safety reasons being auto-closed by another tag, where empty. This is for safety reasons
to prevent clever code from breaking validation. The general rule of thumb: to prevent clever code from breaking validation. The general rule of thumb:
if a tag looked empty on the way end, it will get removed; if HTML Purifier if a tag looked empty on the way in, it will get removed; if HTML Purifier
made it empty, it will stay. made it empty, it will stay.
</p> </p>
--# vim: et sw=4 sts=4 --# vim: et sw=4 sts=4

View File

@ -0,0 +1,14 @@
Filter.ExtractStyleBlocks.Escaping
TYPE: bool
VERSION: 3.0.0
DEFAULT: true
ALIASES: Filter.ExtractStyleBlocksEscaping, FilterParam.ExtractStyleBlocksEscaping
--DESCRIPTION--
<p>
Whether or not to escape the dangerous characters &lt;, &gt; and &amp;
as \3C, \3E and \26, respectively. This is can be safely set to false
if the contents of StyleBlocks will be placed in an external stylesheet,
where there is no risk of it being interpreted as HTML.
</p>
--# vim: et sw=4 sts=4

View File

@ -0,0 +1,29 @@
Filter.ExtractStyleBlocks.Scope
TYPE: string/null
VERSION: 3.0.0
DEFAULT: NULL
ALIASES: Filter.ExtractStyleBlocksScope, FilterParam.ExtractStyleBlocksScope
--DESCRIPTION--
<p>
If you would like users to be able to define external stylesheets, but
only allow them to specify CSS declarations for a specific node and
prevent them from fiddling with other elements, use this directive.
It accepts any valid CSS selector, and will prepend this to any
CSS declaration extracted from the document. For example, if this
directive is set to <code>#user-content</code> and a user uses the
selector <code>a:hover</code>, the final selector will be
<code>#user-content a:hover</code>.
</p>
<p>
The comma shorthand may be used; consider the above example, with
<code>#user-content, #user-content2</code>, the final selector will
be <code>#user-content a:hover, #user-content2 a:hover</code>.
</p>
<p>
<strong>Warning:</strong> It is possible for users to bypass this measure
using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML
Purifier, and I am working to get it fixed. Until then, HTML Purifier
performs a basic check to prevent this.
</p>
--# vim: et sw=4 sts=4

View File

@ -0,0 +1,16 @@
Filter.ExtractStyleBlocks.TidyImpl
TYPE: mixed/null
VERSION: 3.1.0
DEFAULT: NULL
ALIASES: FilterParam.ExtractStyleBlocksTidyImpl
--DESCRIPTION--
<p>
If left NULL, HTML Purifier will attempt to instantiate a <code>csstidy</code>
class to use for internal cleaning. This will usually be good enough.
</p>
<p>
However, for trusted user input, you can set this to <code>false</code> to
disable cleaning. In addition, you can supply your own concrete implementation
of Tidy's interface to use, although I don't know why you'd want to do that.
</p>
--# vim: et sw=4 sts=4

View File

@ -0,0 +1,11 @@
HTML.Attr.Name.UseCDATA
TYPE: bool
DEFAULT: false
VERSION: 4.0.0
--DESCRIPTION--
The W3C specification DTD defines the name attribute to be CDATA, not ID, due
to limitations of DTD. In certain documents, this relaxed behavior is desired,
whether it is to specify duplicate names, or to specify names that would be
illegal IDs (for example, names that begin with a digit.) Set this configuration
directive to true to use the relaxed parsing rules.
--# vim: et sw=4 sts=4

View File

@ -28,7 +28,7 @@ abstract class HTMLPurifier_DefinitionCache
public function generateKey($config) { public function generateKey($config) {
return $config->version . ',' . // possibly replace with function calls return $config->version . ',' . // possibly replace with function calls
$config->getBatchSerial($this->type) . ',' . $config->getBatchSerial($this->type) . ',' .
$config->get($this->type, 'DefinitionRev'); $config->get($this->type . '.DefinitionRev');
} }
/** /**
@ -46,7 +46,7 @@ abstract class HTMLPurifier_DefinitionCache
// versions match, ids match, check revision number // versions match, ids match, check revision number
if ( if (
$hash == $config->getBatchSerial($this->type) && $hash == $config->getBatchSerial($this->type) &&
$revision < $config->get($this->type, 'DefinitionRev') $revision < $config->get($this->type . '.DefinitionRev')
) return true; ) return true;
return false; return false;
} }

View File

@ -88,7 +88,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends
* @todo Make protected * @todo Make protected
*/ */
public function generateBaseDirectoryPath($config) { public function generateBaseDirectoryPath($config) {
$base = $config->get('Cache', 'SerializerPath'); $base = $config->get('Cache.SerializerPath');
$base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base; $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
return $base; return $base;
} }

View File

@ -46,7 +46,7 @@ class HTMLPurifier_DefinitionCacheFactory
* @param $config Instance of HTMLPurifier_Config * @param $config Instance of HTMLPurifier_Config
*/ */
public function create($type, $config) { public function create($type, $config) {
$method = $config->get('Cache', 'DefinitionImpl'); $method = $config->get('Cache.DefinitionImpl');
if ($method === null) { if ($method === null) {
return new HTMLPurifier_DefinitionCache_Null($type); return new HTMLPurifier_DefinitionCache_Null($type);
} }

View File

@ -80,17 +80,17 @@ class HTMLPurifier_DoctypeRegistry
*/ */
public function getDoctypeFromConfig($config) { public function getDoctypeFromConfig($config) {
// recommended test // recommended test
$doctype = $config->get('HTML', 'Doctype'); $doctype = $config->get('HTML.Doctype');
if (!empty($doctype)) return $doctype; if (!empty($doctype)) return $doctype;
$doctype = $config->get('HTML', 'CustomDoctype'); $doctype = $config->get('HTML.CustomDoctype');
if (!empty($doctype)) return $doctype; if (!empty($doctype)) return $doctype;
// backwards-compatibility // backwards-compatibility
if ($config->get('HTML', 'XHTML')) { if ($config->get('HTML.XHTML')) {
$doctype = 'XHTML 1.0'; $doctype = 'XHTML 1.0';
} else { } else {
$doctype = 'HTML 4.01'; $doctype = 'HTML 4.01';
} }
if ($config->get('HTML', 'Strict')) { if ($config->get('HTML.Strict')) {
$doctype .= ' Strict'; $doctype .= ' Strict';
} else { } else {
$doctype .= ' Transitional'; $doctype .= ' Transitional';

View File

@ -142,7 +142,8 @@ class HTMLPurifier_ElementDef
$this->_mergeAssocArray($this->excludes, $def->excludes); $this->_mergeAssocArray($this->excludes, $def->excludes);
if(!empty($def->content_model)) { if(!empty($def->content_model)) {
$this->content_model .= ' | ' . $def->content_model; $this->content_model =
str_replace("#SUPER", $this->content_model, $def->content_model);
$this->child = false; $this->child = false;
} }
if(!empty($def->content_model_type)) { if(!empty($def->content_model_type)) {

View File

@ -17,7 +17,7 @@ class HTMLPurifier_Encoder
/** /**
* Error-handler that mutes errors, alternative to shut-up operator. * Error-handler that mutes errors, alternative to shut-up operator.
*/ */
private static function muteErrorHandler() {} public static function muteErrorHandler() {}
/** /**
* Cleans a UTF-8 string for well-formedness and SGML validity * Cleans a UTF-8 string for well-formedness and SGML validity
@ -264,12 +264,12 @@ class HTMLPurifier_Encoder
* Converts a string to UTF-8 based on configuration. * Converts a string to UTF-8 based on configuration.
*/ */
public static function convertToUTF8($str, $config, $context) { public static function convertToUTF8($str, $config, $context) {
$encoding = $config->get('Core', 'Encoding'); $encoding = $config->get('Core.Encoding');
if ($encoding === 'utf-8') return $str; if ($encoding === 'utf-8') return $str;
static $iconv = null; static $iconv = null;
if ($iconv === null) $iconv = function_exists('iconv'); if ($iconv === null) $iconv = function_exists('iconv');
set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler')); set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
if ($iconv && !$config->get('Test', 'ForceNoIconv')) { if ($iconv && !$config->get('Test.ForceNoIconv')) {
$str = iconv($encoding, 'utf-8//IGNORE', $str); $str = iconv($encoding, 'utf-8//IGNORE', $str);
if ($str === false) { if ($str === false) {
// $encoding is not a valid encoding // $encoding is not a valid encoding
@ -297,15 +297,15 @@ class HTMLPurifier_Encoder
* characters being omitted. * characters being omitted.
*/ */
public static function convertFromUTF8($str, $config, $context) { public static function convertFromUTF8($str, $config, $context) {
$encoding = $config->get('Core', 'Encoding'); $encoding = $config->get('Core.Encoding');
if ($encoding === 'utf-8') return $str; if ($encoding === 'utf-8') return $str;
static $iconv = null; static $iconv = null;
if ($iconv === null) $iconv = function_exists('iconv'); if ($iconv === null) $iconv = function_exists('iconv');
if ($escape = $config->get('Core', 'EscapeNonASCIICharacters')) { if ($escape = $config->get('Core.EscapeNonASCIICharacters')) {
$str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str); $str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str);
} }
set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler')); set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
if ($iconv && !$config->get('Test', 'ForceNoIconv')) { if ($iconv && !$config->get('Test.ForceNoIconv')) {
// Undo our previous fix in convertToUTF8, otherwise iconv will barf // Undo our previous fix in convertToUTF8, otherwise iconv will barf
$ascii_fix = HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding); $ascii_fix = HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding);
if (!$escape && !empty($ascii_fix)) { if (!$escape && !empty($ascii_fix)) {

View File

@ -38,7 +38,7 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
* @todo Extend to indicate non-text/css style blocks * @todo Extend to indicate non-text/css style blocks
*/ */
public function preFilter($html, $config, $context) { public function preFilter($html, $config, $context) {
$tidy = $config->get('FilterParam', 'ExtractStyleBlocksTidyImpl'); $tidy = $config->get('Filter.ExtractStyleBlocks.TidyImpl');
if ($tidy !== null) $this->_tidy = $tidy; if ($tidy !== null) $this->_tidy = $tidy;
$html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html); $html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html);
$style_blocks = $this->_styleMatches; $style_blocks = $this->_styleMatches;
@ -62,7 +62,7 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
*/ */
public function cleanCSS($css, $config, $context) { public function cleanCSS($css, $config, $context) {
// prepare scope // prepare scope
$scope = $config->get('FilterParam', 'ExtractStyleBlocksScope'); $scope = $config->get('Filter.ExtractStyleBlocks.Scope');
if ($scope !== null) { if ($scope !== null) {
$scopes = array_map('trim', explode(',', $scope)); $scopes = array_map('trim', explode(',', $scope));
} else { } else {
@ -120,7 +120,7 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
$css = $this->_tidy->print->plain(); $css = $this->_tidy->print->plain();
// we are going to escape any special characters <>& to ensure // we are going to escape any special characters <>& to ensure
// that no funny business occurs (i.e. </style> in a font-family prop). // that no funny business occurs (i.e. </style> in a font-family prop).
if ($config->get('FilterParam', 'ExtractStyleBlocksEscaping')) { if ($config->get('Filter.ExtractStyleBlocks.Escaping')) {
$css = str_replace( $css = str_replace(
array('<', '>', '&'), array('<', '>', '&'),
array('\3C ', '\3E ', '\26 '), array('\3C ', '\3E ', '\26 '),

View File

@ -42,8 +42,8 @@ class HTMLPurifier_Generator
*/ */
public function __construct($config, $context) { public function __construct($config, $context) {
$this->config = $config; $this->config = $config;
$this->_scriptFix = $config->get('Output', 'CommentScriptContents'); $this->_scriptFix = $config->get('Output.CommentScriptContents');
$this->_sortAttr = $config->get('Output', 'SortAttr'); $this->_sortAttr = $config->get('Output.SortAttr');
$this->_def = $config->getHTMLDefinition(); $this->_def = $config->getHTMLDefinition();
$this->_xhtml = $this->_def->doctype->xml; $this->_xhtml = $this->_def->doctype->xml;
} }
@ -72,7 +72,7 @@ class HTMLPurifier_Generator
} }
// Tidy cleanup // Tidy cleanup
if (extension_loaded('tidy') && $this->config->get('Output', 'TidyFormat')) { if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) {
$tidy = new Tidy; $tidy = new Tidy;
$tidy->parseString($html, array( $tidy->parseString($html, array(
'indent'=> true, 'indent'=> true,
@ -86,7 +86,7 @@ class HTMLPurifier_Generator
} }
// Normalize newlines to system defined value // Normalize newlines to system defined value
$nl = $this->config->get('Output', 'Newline'); $nl = $this->config->get('Output.Newline');
if ($nl === null) $nl = PHP_EOL; if ($nl === null) $nl = PHP_EOL;
if ($nl !== "\n") $html = str_replace("\n", $nl, $html); if ($nl !== "\n") $html = str_replace("\n", $nl, $html);
return $html; return $html;

View File

@ -114,7 +114,7 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
* @note See HTMLPurifier_HTMLModule::addElement for detailed * @note See HTMLPurifier_HTMLModule::addElement for detailed
* parameter and return value descriptions. * parameter and return value descriptions.
*/ */
public function addElement($element_name, $type, $contents, $attr_collections, $attributes) { public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) {
$module = $this->getAnonymousModule(); $module = $this->getAnonymousModule();
// assume that if the user is calling this, the element // assume that if the user is calling this, the element
// is safe. This may not be a good idea // is safe. This may not be a good idea
@ -219,7 +219,7 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
*/ */
protected function setupConfigStuff($config) { protected function setupConfigStuff($config) {
$block_wrapper = $config->get('HTML', 'BlockWrapper'); $block_wrapper = $config->get('HTML.BlockWrapper');
if (isset($this->info_content_sets['Block'][$block_wrapper])) { if (isset($this->info_content_sets['Block'][$block_wrapper])) {
$this->info_block_wrapper = $block_wrapper; $this->info_block_wrapper = $block_wrapper;
} else { } else {
@ -227,7 +227,7 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
E_USER_ERROR); E_USER_ERROR);
} }
$parent = $config->get('HTML', 'Parent'); $parent = $config->get('HTML.Parent');
$def = $this->manager->getElement($parent, true); $def = $this->manager->getElement($parent, true);
if ($def) { if ($def) {
$this->info_parent = $parent; $this->info_parent = $parent;
@ -244,11 +244,11 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
// setup allowed elements ----------------------------------------- // setup allowed elements -----------------------------------------
$allowed_elements = $config->get('HTML', 'AllowedElements'); $allowed_elements = $config->get('HTML.AllowedElements');
$allowed_attributes = $config->get('HTML', 'AllowedAttributes'); // retrieve early $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early
if (!is_array($allowed_elements) && !is_array($allowed_attributes)) { if (!is_array($allowed_elements) && !is_array($allowed_attributes)) {
$allowed = $config->get('HTML', 'Allowed'); $allowed = $config->get('HTML.Allowed');
if (is_string($allowed)) { if (is_string($allowed)) {
list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed); list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed);
} }
@ -334,8 +334,8 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
// setup forbidden elements --------------------------------------- // setup forbidden elements ---------------------------------------
$forbidden_elements = $config->get('HTML', 'ForbiddenElements'); $forbidden_elements = $config->get('HTML.ForbiddenElements');
$forbidden_attributes = $config->get('HTML', 'ForbiddenAttributes'); $forbidden_attributes = $config->get('HTML.ForbiddenAttributes');
foreach ($this->info as $tag => $info) { foreach ($this->info as $tag => $info) {
if (isset($forbidden_elements[$tag])) { if (isset($forbidden_elements[$tag])) {

View File

@ -8,7 +8,7 @@ class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule
'Core' => array( 'Core' => array(
0 => array('Style'), 0 => array('Style'),
// 'xml:space' => false, // 'xml:space' => false,
'class' => 'NMTOKENS', 'class' => 'Class',
'id' => 'ID', 'id' => 'ID',
'title' => 'CDATA', 'title' => 'CDATA',
), ),
@ -20,6 +20,7 @@ class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule
0 => array('Core', 'I18N') 0 => array('Core', 'I18N')
) )
); );
} }
// vim: et sw=4 sts=4 // vim: et sw=4 sts=4

View File

@ -11,7 +11,7 @@ class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule
public $name = 'Image'; public $name = 'Image';
public function setup($config) { public function setup($config) {
$max = $config->get('HTML', 'MaxImgLength'); $max = $config->get('HTML.MaxImgLength');
$img = $this->addElement( $img = $this->addElement(
'img', 'Inline', 'Empty', 'Common', 'img', 'Inline', 'Empty', 'Common',
array( array(
@ -24,7 +24,7 @@ class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule
'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded 'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded
) )
); );
if ($max === null || $config->get('HTML', 'Trusted')) { if ($max === null || $config->get('HTML.Trusted')) {
$img->attr['height'] = $img->attr['height'] =
$img->attr['width'] = 'Length'; $img->attr['width'] = 'Length';
} }

View File

@ -9,7 +9,10 @@ class HTMLPurifier_HTMLModule_Name extends HTMLPurifier_HTMLModule
$elements = array('a', 'applet', 'form', 'frame', 'iframe', 'img', 'map'); $elements = array('a', 'applet', 'form', 'frame', 'iframe', 'img', 'map');
foreach ($elements as $name) { foreach ($elements as $name) {
$element = $this->addBlankElement($name); $element = $this->addBlankElement($name);
$element->attr['name'] = 'ID'; $element->attr['name'] = 'CDATA';
if (!$config->get('HTML.Attr.Name.UseCDATA')) {
$element->attr_transform_post['NameSync'] = new HTMLPurifier_AttrTransform_NameSync();
}
} }
} }

View File

@ -10,7 +10,7 @@ class HTMLPurifier_HTMLModule_SafeEmbed extends HTMLPurifier_HTMLModule
public function setup($config) { public function setup($config) {
$max = $config->get('HTML', 'MaxImgLength'); $max = $config->get('HTML.MaxImgLength');
$embed = $this->addElement( $embed = $this->addElement(
'embed', 'Inline', 'Empty', 'Common', 'embed', 'Inline', 'Empty', 'Common',
array( array(

View File

@ -16,7 +16,7 @@ class HTMLPurifier_HTMLModule_SafeObject extends HTMLPurifier_HTMLModule
// These definitions are not intrinsically safe: the attribute transforms // These definitions are not intrinsically safe: the attribute transforms
// are a vital part of ensuring safety. // are a vital part of ensuring safety.
$max = $config->get('HTML', 'MaxImgLength'); $max = $config->get('HTML.MaxImgLength');
$object = $this->addElement( $object = $this->addElement(
'object', 'object',
'Inline', 'Inline',

View File

@ -42,12 +42,12 @@ class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule
$this->makeFixesForLevel($fixes); $this->makeFixesForLevel($fixes);
// figure out which fixes to use // figure out which fixes to use
$level = $config->get('HTML', 'TidyLevel'); $level = $config->get('HTML.TidyLevel');
$fixes_lookup = $this->getFixesForLevel($level); $fixes_lookup = $this->getFixesForLevel($level);
// get custom fix declarations: these need namespace processing // get custom fix declarations: these need namespace processing
$add_fixes = $config->get('HTML', 'TidyAdd'); $add_fixes = $config->get('HTML.TidyAdd');
$remove_fixes = $config->get('HTML', 'TidyRemove'); $remove_fixes = $config->get('HTML.TidyRemove');
foreach ($fixes as $name => $fix) { foreach ($fixes as $name => $fix) {
// needs to be refactored a little to implement globbing // needs to be refactored a little to implement globbing

View File

@ -199,15 +199,15 @@ class HTMLPurifier_HTMLModuleManager
*/ */
public function setup($config) { public function setup($config) {
$this->trusted = $config->get('HTML', 'Trusted'); $this->trusted = $config->get('HTML.Trusted');
// generate // generate
$this->doctype = $this->doctypes->make($config); $this->doctype = $this->doctypes->make($config);
$modules = $this->doctype->modules; $modules = $this->doctype->modules;
// take out the default modules that aren't allowed // take out the default modules that aren't allowed
$lookup = $config->get('HTML', 'AllowedModules'); $lookup = $config->get('HTML.AllowedModules');
$special_cases = $config->get('HTML', 'CoreModules'); $special_cases = $config->get('HTML.CoreModules');
if (is_array($lookup)) { if (is_array($lookup)) {
foreach ($modules as $k => $m) { foreach ($modules as $k => $m) {
@ -218,15 +218,15 @@ class HTMLPurifier_HTMLModuleManager
// add proprietary module (this gets special treatment because // add proprietary module (this gets special treatment because
// it is completely removed from doctypes, etc.) // it is completely removed from doctypes, etc.)
if ($config->get('HTML', 'Proprietary')) { if ($config->get('HTML.Proprietary')) {
$modules[] = 'Proprietary'; $modules[] = 'Proprietary';
} }
// add SafeObject/Safeembed modules // add SafeObject/Safeembed modules
if ($config->get('HTML', 'SafeObject')) { if ($config->get('HTML.SafeObject')) {
$modules[] = 'SafeObject'; $modules[] = 'SafeObject';
} }
if ($config->get('HTML', 'SafeEmbed')) { if ($config->get('HTML.SafeEmbed')) {
$modules[] = 'SafeEmbed'; $modules[] = 'SafeEmbed';
} }

View File

@ -23,7 +23,7 @@ class HTMLPurifier_IDAccumulator
*/ */
public static function build($config, $context) { public static function build($config, $context) {
$id_accumulator = new HTMLPurifier_IDAccumulator(); $id_accumulator = new HTMLPurifier_IDAccumulator();
$id_accumulator->load($config->get('Attr', 'IDBlacklist')); $id_accumulator->load($config->get('Attr.IDBlacklist'));
return $id_accumulator; return $id_accumulator;
} }

View File

@ -137,6 +137,12 @@ abstract class HTMLPurifier_Injector
if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) { if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) {
return false; return false;
} }
// check for exclusion
for ($i = count($this->currentNesting) - 2; $i >= 0; $i--) {
$node = $this->currentNesting[$i];
$def = $this->htmlDefinition->info[$node->name];
if (isset($def->excludes[$name])) return false;
}
return true; return true;
} }

View File

@ -12,7 +12,7 @@ class HTMLPurifier_Injector_PurifierLinkify extends HTMLPurifier_Injector
public $needed = array('a' => array('href')); public $needed = array('a' => array('href'));
public function prepare($config, $context) { public function prepare($config, $context) {
$this->docURL = $config->get('AutoFormatParam', 'PurifierLinkifyDocURL'); $this->docURL = $config->get('AutoFormat.PurifierLinkify.DocURL');
return parent::prepare($config, $context); return parent::prepare($config, $context);
} }

View File

@ -3,12 +3,14 @@
class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
{ {
private $context, $config; private $context, $config, $attrValidator, $removeNbsp, $removeNbspExceptions;
public function prepare($config, $context) { public function prepare($config, $context) {
parent::prepare($config, $context); parent::prepare($config, $context);
$this->config = $config; $this->config = $config;
$this->context = $context; $this->context = $context;
$this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
$this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
$this->attrValidator = new HTMLPurifier_AttrValidator(); $this->attrValidator = new HTMLPurifier_AttrValidator();
} }
@ -17,7 +19,14 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
$next = false; $next = false;
for ($i = $this->inputIndex + 1, $c = count($this->inputTokens); $i < $c; $i++) { for ($i = $this->inputIndex + 1, $c = count($this->inputTokens); $i < $c; $i++) {
$next = $this->inputTokens[$i]; $next = $this->inputTokens[$i];
if ($next instanceof HTMLPurifier_Token_Text && $next->is_whitespace) continue; if ($next instanceof HTMLPurifier_Token_Text) {
if ($next->is_whitespace) continue;
if ($this->removeNbsp && !isset($this->removeNbspExceptions[$token->name])) {
$plain = str_replace("\xC2\xA0", "", $next->data);
$isWsOrNbsp = $plain === '' || ctype_space($plain);
if ($isWsOrNbsp) continue;
}
}
break; break;
} }
if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) { if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {

View File

@ -85,7 +85,7 @@ class HTMLPurifier_LanguageFactory
// validate language code // validate language code
if ($code === false) { if ($code === false) {
$code = $this->validator->validate( $code = $this->validator->validate(
$config->get('Core', 'Language'), $config, $context $config->get('Core.Language'), $config, $context
); );
} else { } else {
$code = $this->validator->validate($code, $config, $context); $code = $this->validator->validate($code, $config, $context);

View File

@ -73,12 +73,12 @@ class HTMLPurifier_Lexer
HTMLPurifier_Lexer::create() is deprecated, please instead HTMLPurifier_Lexer::create() is deprecated, please instead
use %Core.LexerImpl", E_USER_WARNING); use %Core.LexerImpl", E_USER_WARNING);
} else { } else {
$lexer = $config->get('Core', 'LexerImpl'); $lexer = $config->get('Core.LexerImpl');
} }
$needs_tracking = $needs_tracking =
$config->get('Core', 'MaintainLineNumbers') || $config->get('Core.MaintainLineNumbers') ||
$config->get('Core', 'CollectErrors'); $config->get('Core.CollectErrors');
$inst = null; $inst = null;
if (is_object($lexer)) { if (is_object($lexer)) {
@ -255,7 +255,7 @@ class HTMLPurifier_Lexer
$html = str_replace("\r\n", "\n", $html); $html = str_replace("\r\n", "\n", $html);
$html = str_replace("\r", "\n", $html); $html = str_replace("\r", "\n", $html);
if ($config->get('HTML', 'Trusted')) { if ($config->get('HTML.Trusted')) {
// escape convoluted CDATA // escape convoluted CDATA
$html = $this->escapeCommentedCDATA($html); $html = $this->escapeCommentedCDATA($html);
} }
@ -264,7 +264,7 @@ class HTMLPurifier_Lexer
$html = $this->escapeCDATA($html); $html = $this->escapeCDATA($html);
// extract body from document if applicable // extract body from document if applicable
if ($config->get('Core', 'ConvertDocumentToFragment')) { if ($config->get('Core.ConvertDocumentToFragment')) {
$html = $this->extractBody($html); $html = $this->extractBody($html);
} }
@ -285,7 +285,7 @@ class HTMLPurifier_Lexer
*/ */
public function extractBody($html) { public function extractBody($html) {
$matches = array(); $matches = array();
$result = preg_match('!<body[^>]*>(.+?)</body>!is', $html, $matches); $result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches);
if ($result) { if ($result) {
return $matches[1]; return $matches[1];
} else { } else {

View File

@ -41,7 +41,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
// attempt to armor stray angled brackets that cannot possibly // attempt to armor stray angled brackets that cannot possibly
// form tags and thus are probably being used as emoticons // form tags and thus are probably being used as emoticons
if ($config->get('Core', 'AggressivelyFixLt')) { if ($config->get('Core.AggressivelyFixLt')) {
$char = '[^a-z!\/]'; $char = '[^a-z!\/]';
$comment = "/<!--(.*?)(-->|\z)/is"; $comment = "/<!--(.*?)(-->|\z)/is";
$html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html); $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html);

View File

@ -33,7 +33,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
// special normalization for script tags without any armor // special normalization for script tags without any armor
// our "armor" heurstic is a < sign any number of whitespaces after // our "armor" heurstic is a < sign any number of whitespaces after
// the first script tag // the first script tag
if ($config->get('HTML', 'Trusted')) { if ($config->get('HTML.Trusted')) {
$html = preg_replace_callback('#(<script[^>]*>)(\s*[^<].+?)(</script>)#si', $html = preg_replace_callback('#(<script[^>]*>)(\s*[^<].+?)(</script>)#si',
array($this, 'scriptCallback'), $html); array($this, 'scriptCallback'), $html);
} }
@ -45,12 +45,12 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
$array = array(); // result array $array = array(); // result array
// This is also treated to mean maintain *column* numbers too // This is also treated to mean maintain *column* numbers too
$maintain_line_numbers = $config->get('Core', 'MaintainLineNumbers'); $maintain_line_numbers = $config->get('Core.MaintainLineNumbers');
if ($maintain_line_numbers === null) { if ($maintain_line_numbers === null) {
// automatically determine line numbering by checking // automatically determine line numbering by checking
// if error collection is on // if error collection is on
$maintain_line_numbers = $config->get('Core', 'CollectErrors'); $maintain_line_numbers = $config->get('Core.CollectErrors');
} }
if ($maintain_line_numbers) { if ($maintain_line_numbers) {
@ -67,10 +67,10 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
$nl = "\n"; $nl = "\n";
// how often to manually recalculate. This will ALWAYS be right, // how often to manually recalculate. This will ALWAYS be right,
// but it's pretty wasteful. Set to 0 to turn off // but it's pretty wasteful. Set to 0 to turn off
$synchronize_interval = $config->get('Core', 'DirectLexLineNumberSyncInterval'); $synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval');
$e = false; $e = false;
if ($config->get('Core', 'CollectErrors')) { if ($config->get('Core.CollectErrors')) {
$e =& $context->get('ErrorCollector'); $e =& $context->get('ErrorCollector');
} }
@ -345,7 +345,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
if ($string == '') return array(); // no attributes if ($string == '') return array(); // no attributes
$e = false; $e = false;
if ($config->get('Core', 'CollectErrors')) { if ($config->get('Core.CollectErrors')) {
$e =& $context->get('ErrorCollector'); $e =& $context->get('ErrorCollector');
} }

View File

@ -91,7 +91,7 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
$all = array(); $all = array();
foreach ($allowed as $key) { foreach ($allowed as $key) {
list($ns, $directive) = $key; list($ns, $directive) = $key;
$all[$ns][$directive] = $config->get($ns, $directive); $all[$ns][$directive] = $config->get($ns .'.'. $directive);
} }
$ret = ''; $ret = '';
@ -159,7 +159,7 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
$ret .= $this->end('th'); $ret .= $this->end('th');
$ret .= $this->start('td'); $ret .= $this->start('td');
$def = $this->config->def->info[$ns][$directive]; $def = $this->config->def->info["$ns.$directive"];
if (is_int($def)) { if (is_int($def)) {
$allow_null = $def < 0; $allow_null = $def < 0;
$type = abs($def); $type = abs($def);
@ -248,7 +248,7 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
$this->prepareGenerator($gen_config); $this->prepareGenerator($gen_config);
// this should probably be split up a little // this should probably be split up a little
$ret = ''; $ret = '';
$def = $config->def->info[$ns][$directive]; $def = $config->def->info["$ns.$directive"];
if (is_int($def)) { if (is_int($def)) {
$type = abs($def); $type = abs($def);
} else { } else {

View File

@ -42,7 +42,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
// local variables // local variables
$generator = new HTMLPurifier_Generator($config, $context); $generator = new HTMLPurifier_Generator($config, $context);
$escape_invalid_tags = $config->get('Core', 'EscapeInvalidTags'); $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
$e = $context->get('ErrorCollector', true); $e = $context->get('ErrorCollector', true);
$t = false; // token index $t = false; // token index
$i = false; // injector index $i = false; // injector index
@ -72,6 +72,8 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$custom_injectors = $injectors['Custom']; $custom_injectors = $injectors['Custom'];
unset($injectors['Custom']); // special case unset($injectors['Custom']); // special case
foreach ($injectors as $injector => $b) { foreach ($injectors as $injector => $b) {
// XXX: Fix with a legitimate lookup table of enabled filters
if (strpos($injector, '.') !== false) continue;
$injector = "HTMLPurifier_Injector_$injector"; $injector = "HTMLPurifier_Injector_$injector";
if (!$b) continue; if (!$b) continue;
$this->injectors[] = new $injector; $this->injectors[] = new $injector;

View File

@ -16,14 +16,14 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
$generator = new HTMLPurifier_Generator($config, $context); $generator = new HTMLPurifier_Generator($config, $context);
$result = array(); $result = array();
$escape_invalid_tags = $config->get('Core', 'EscapeInvalidTags'); $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
$remove_invalid_img = $config->get('Core', 'RemoveInvalidImg'); $remove_invalid_img = $config->get('Core.RemoveInvalidImg');
// currently only used to determine if comments should be kept // currently only used to determine if comments should be kept
$trusted = $config->get('HTML', 'Trusted'); $trusted = $config->get('HTML.Trusted');
$remove_script_contents = $config->get('Core', 'RemoveScriptContents'); $remove_script_contents = $config->get('Core.RemoveScriptContents');
$hidden_elements = $config->get('Core', 'HiddenElements'); $hidden_elements = $config->get('Core.HiddenElements');
// remove script contents compatibility // remove script contents compatibility
if ($remove_script_contents === true) { if ($remove_script_contents === true) {
@ -44,7 +44,7 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
$context->register('CurrentToken', $token); $context->register('CurrentToken', $token);
$e = false; $e = false;
if ($config->get('Core', 'CollectErrors')) { if ($config->get('Core.CollectErrors')) {
$e =& $context->get('ErrorCollector'); $e =& $context->get('ErrorCollector');
} }

View File

@ -52,7 +52,7 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
protected function setupFilters($config) { protected function setupFilters($config) {
foreach ($this->registeredFilters as $name => $filter) { foreach ($this->registeredFilters as $name => $filter) {
$conf = $config->get('URI', $name); $conf = $config->get('URI.' . $name);
if ($conf !== false && $conf !== null) { if ($conf !== false && $conf !== null) {
$this->addFilter($filter, $config); $this->addFilter($filter, $config);
} }
@ -61,15 +61,15 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
} }
protected function setupMemberVariables($config) { protected function setupMemberVariables($config) {
$this->host = $config->get('URI', 'Host'); $this->host = $config->get('URI.Host');
$base_uri = $config->get('URI', 'Base'); $base_uri = $config->get('URI.Base');
if (!is_null($base_uri)) { if (!is_null($base_uri)) {
$parser = new HTMLPurifier_URIParser(); $parser = new HTMLPurifier_URIParser();
$this->base = $parser->parse($base_uri); $this->base = $parser->parse($base_uri);
$this->defaultScheme = $this->base->scheme; $this->defaultScheme = $this->base->scheme;
if (is_null($this->host)) $this->host = $this->base->host; if (is_null($this->host)) $this->host = $this->base->host;
} }
if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI', 'DefaultScheme'); if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme');
} }
public function filter(&$uri, $config, $context) { public function filter(&$uri, $config, $context) {

View File

@ -5,7 +5,7 @@ class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter
public $name = 'HostBlacklist'; public $name = 'HostBlacklist';
protected $blacklist = array(); protected $blacklist = array();
public function prepare($config) { public function prepare($config) {
$this->blacklist = $config->get('URI', 'HostBlacklist'); $this->blacklist = $config->get('URI.HostBlacklist');
return true; return true;
} }
public function filter(&$uri, $config, $context) { public function filter(&$uri, $config, $context) {

View File

@ -9,10 +9,10 @@ class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter
protected $replace = array(); protected $replace = array();
public function prepare($config) { public function prepare($config) {
$this->target = $config->get('URI', $this->name); $this->target = $config->get('URI.' . $this->name);
$this->parser = new HTMLPurifier_URIParser(); $this->parser = new HTMLPurifier_URIParser();
$this->doEmbed = $config->get('URI', 'MungeResources'); $this->doEmbed = $config->get('URI.MungeResources');
$this->secretKey = $config->get('URI', 'MungeSecretKey'); $this->secretKey = $config->get('URI.MungeSecretKey');
return true; return true;
} }
public function filter(&$uri, $config, $context) { public function filter(&$uri, $config, $context) {
@ -23,6 +23,10 @@ class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter
if (is_null($uri->host) || empty($scheme_obj->browsable)) { if (is_null($uri->host) || empty($scheme_obj->browsable)) {
return true; return true;
} }
// don't redirect if target host is our host
if ($uri->host === $config->getDefinition('URI')->host) {
return true;
}
$this->makeReplace($uri, $config, $context); $this->makeReplace($uri, $config, $context);
$this->replace = array_map('rawurlencode', $this->replace); $this->replace = array_map('rawurlencode', $this->replace);

View File

@ -36,21 +36,20 @@ class HTMLPurifier_URISchemeRegistry
*/ */
public function getScheme($scheme, $config, $context) { public function getScheme($scheme, $config, $context) {
if (!$config) $config = HTMLPurifier_Config::createDefault(); if (!$config) $config = HTMLPurifier_Config::createDefault();
$null = null; // for the sake of passing by reference
// important, otherwise attacker could include arbitrary file // important, otherwise attacker could include arbitrary file
$allowed_schemes = $config->get('URI', 'AllowedSchemes'); $allowed_schemes = $config->get('URI.AllowedSchemes');
if (!$config->get('URI', 'OverrideAllowedSchemes') && if (!$config->get('URI.OverrideAllowedSchemes') &&
!isset($allowed_schemes[$scheme]) !isset($allowed_schemes[$scheme])
) { ) {
return $null; return;
} }
if (isset($this->schemes[$scheme])) return $this->schemes[$scheme]; if (isset($this->schemes[$scheme])) return $this->schemes[$scheme];
if (!isset($allowed_schemes[$scheme])) return $null; if (!isset($allowed_schemes[$scheme])) return;
$class = 'HTMLPurifier_URIScheme_' . $scheme; $class = 'HTMLPurifier_URIScheme_' . $scheme;
if (!class_exists($class)) return $null; if (!class_exists($class)) return;
$this->schemes[$scheme] = new $class(); $this->schemes[$scheme] = new $class();
return $this->schemes[$scheme]; return $this->schemes[$scheme];
} }