1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-08-07 22:56:32 +02:00

Release 1.5.0, merged in r688-867.

- LanguageFactory::instance() declared static
- HTMLModuleManagerTest pass by reference bug fixed, merge back into trunk scheduled

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/branches/strict@869 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang
2007-03-24 01:04:06 +00:00
parent cec7a1c087
commit dd2fd06591
130 changed files with 4324 additions and 1385 deletions

View File

@@ -0,0 +1,100 @@
<?php
require_once 'HTMLPurifier/AttrTypes.php';
require_once 'HTMLPurifier/AttrDef/Lang.php';
/**
* Defines common attribute collections that modules reference
*/
class HTMLPurifier_AttrCollections
{
/**
* Associative array of attribute collections, indexed by name
* @note Technically, the composition of these is more complicated,
* but we bypass it using our own excludes property
*/
var $info = array();
/**
* Performs all expansions on internal data for use by other inclusions
* It also collects all attribute collection extensions from
* modules
* @param $attr_types HTMLPurifier_AttrTypes instance
* @param $modules Hash array of HTMLPurifier_HTMLModule members
*/
function HTMLPurifier_AttrCollections($attr_types, $modules) {
$info =& $this->info;
// load extensions from the modules
foreach ($modules as $module) {
foreach ($module->attr_collections as $coll_i => $coll) {
foreach ($coll as $attr_i => $attr) {
if ($attr_i === 0 && isset($info[$coll_i][$attr_i])) {
// merge in includes
$info[$coll_i][$attr_i] = array_merge(
$info[$coll_i][$attr_i], $attr);
continue;
}
$info[$coll_i][$attr_i] = $attr;
}
}
}
// perform internal expansions and inclusions
foreach ($info as $name => $attr) {
// merge attribute collections that include others
$this->performInclusions($info[$name]);
// replace string identifiers with actual attribute objects
$this->expandIdentifiers($info[$name], $attr_types);
}
}
/**
* Takes a reference to an attribute associative array and performs
* all inclusions specified by the zero index.
* @param &$attr Reference to attribute array
*/
function performInclusions(&$attr) {
if (!isset($attr[0])) return;
$merge = $attr[0];
// loop through all the inclusions
for ($i = 0; isset($merge[$i]); $i++) {
// foreach attribute of the inclusion, copy it over
foreach ($this->info[$merge[$i]] as $key => $value) {
if (isset($attr[$key])) continue; // also catches more inclusions
$attr[$key] = $value;
}
if (isset($info[$merge[$i]][0])) {
// recursion
$merge = array_merge($merge, isset($info[$merge[$i]][0]));
}
}
unset($attr[0]);
}
/**
* Expands all string identifiers in an attribute array by replacing
* them with the appropriate values inside HTMLPurifier_AttrTypes
* @param &$attr Reference to attribute array
* @param $attr_types HTMLPurifier_AttrTypes instance
*/
function expandIdentifiers(&$attr, $attr_types) {
foreach ($attr as $def_i => $def) {
if ($def_i === 0) continue;
if (!is_string($def)) continue;
if ($def === false) {
unset($attr[$def_i]);
continue;
}
if (isset($attr_types->info[$def])) {
$attr[$def_i] = $attr_types->info[$def];
} else {
trigger_error('Attempted to reference undefined attribute type', E_USER_ERROR);
unset($attr[$def_i]);
}
}
}
}
?>

View File

@@ -7,7 +7,7 @@ require_once 'HTMLPurifier/CSSDefinition.php';
* Validates shorthand CSS property background.
* @warning Does not support url tokens that have internal spaces.
*/
class HTMLPurifier_AttrDef_Background extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef
{
/**
@@ -16,7 +16,7 @@ class HTMLPurifier_AttrDef_Background extends HTMLPurifier_AttrDef
*/
var $info;
function HTMLPurifier_AttrDef_Background($config) {
function HTMLPurifier_AttrDef_CSS_Background($config) {
$def = $config->getCSSDefinition();
$this->info['background-color'] = $def->info['background-color'];
$this->info['background-image'] = $def->info['background-image'];

View File

@@ -1,8 +1,8 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/CSSLength.php';
require_once 'HTMLPurifier/AttrDef/Percentage.php';
require_once 'HTMLPurifier/AttrDef/CSS/Length.php';
require_once 'HTMLPurifier/AttrDef/CSS/Percentage.php';
/* W3C says:
[ // adjective and number must be in correct order, even if
@@ -45,15 +45,15 @@ require_once 'HTMLPurifier/AttrDef/Percentage.php';
/**
* Validates the value of background-position.
*/
class HTMLPurifier_AttrDef_BackgroundPosition extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef
{
var $length;
var $percentage;
function HTMLPurifier_AttrDef_BackgroundPosition() {
$this->length = new HTMLPurifier_AttrDef_CSSLength();
$this->percentage = new HTMLPurifier_AttrDef_Percentage();
function HTMLPurifier_AttrDef_CSS_BackgroundPosition() {
$this->length = new HTMLPurifier_AttrDef_CSS_Length();
$this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage();
}
function validate($string, $config, &$context) {

View File

@@ -5,7 +5,7 @@ require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates the border property as defined by CSS.
*/
class HTMLPurifier_AttrDef_Border extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef
{
/**
@@ -13,7 +13,7 @@ class HTMLPurifier_AttrDef_Border extends HTMLPurifier_AttrDef
*/
var $info = array();
function HTMLPurifier_AttrDef_Border($config) {
function HTMLPurifier_AttrDef_CSS_Border($config) {
$def = $config->getCSSDefinition();
$this->info['border-width'] = $def->info['border-width'];
$this->info['border-style'] = $def->info['border-style'];

View File

@@ -5,7 +5,7 @@ require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates Color as defined by CSS.
*/
class HTMLPurifier_AttrDef_Color extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
{
/**

View File

@@ -9,7 +9,7 @@
* especially useful for CSS values, which often are a choice between
* an enumerated set of predefined values or a flexible data type.
*/
class HTMLPurifier_AttrDef_Composite extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef
{
/**
@@ -21,7 +21,7 @@ class HTMLPurifier_AttrDef_Composite extends HTMLPurifier_AttrDef
/**
* @param $defs List of HTMLPurifier_AttrDef objects
*/
function HTMLPurifier_AttrDef_Composite($defs) {
function HTMLPurifier_AttrDef_CSS_Composite($defs) {
$this->defs = $defs;
}

View File

@@ -5,7 +5,7 @@ require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates shorthand CSS property font.
*/
class HTMLPurifier_AttrDef_Font extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef
{
/**
@@ -30,7 +30,7 @@ class HTMLPurifier_AttrDef_Font extends HTMLPurifier_AttrDef
'status-bar' => true
);
function HTMLPurifier_AttrDef_Font($config) {
function HTMLPurifier_AttrDef_CSS_Font($config) {
$def = $config->getCSSDefinition();
$this->info['font-style'] = $def->info['font-style'];
$this->info['font-variant'] = $def->info['font-variant'];

View File

@@ -7,7 +7,7 @@ require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates a font family list according to CSS spec
*/
class HTMLPurifier_AttrDef_FontFamily extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
{
/**

View File

@@ -1,13 +1,12 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Number.php';
require_once 'HTMLPurifier/AttrDef/CSS/Number.php';
/**
* Represents a Length as defined by CSS.
* @warning Be sure not to confuse this with HTMLPurifier_AttrDef_Length!
*/
class HTMLPurifier_AttrDef_CSSLength extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef
{
/**
@@ -26,8 +25,8 @@ class HTMLPurifier_AttrDef_CSSLength extends HTMLPurifier_AttrDef
* @param $non_negative Bool indication whether or not negative values are
* allowed.
*/
function HTMLPurifier_AttrDef_CSSLength($non_negative = false) {
$this->number_def = new HTMLPurifier_AttrDef_Number($non_negative);
function HTMLPurifier_AttrDef_CSS_Length($non_negative = false) {
$this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
}
function validate($length, $config, &$context) {

View File

@@ -6,16 +6,16 @@ require_once 'HTMLPurifier/AttrDef.php';
* Validates shorthand CSS property list-style.
* @warning Does not support url tokens that have internal spaces.
*/
class HTMLPurifier_AttrDef_ListStyle extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef
{
/**
* Local copy of component validators.
* @note See HTMLPurifier_AttrDef_Font::$info for a similar impl.
* @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl.
*/
var $info;
function HTMLPurifier_AttrDef_ListStyle($config) {
function HTMLPurifier_AttrDef_CSS_ListStyle($config) {
$def = $config->getCSSDefinition();
$this->info['list-style-type'] = $def->info['list-style-type'];
$this->info['list-style-position'] = $def->info['list-style-position'];

View File

@@ -13,7 +13,7 @@ require_once 'HTMLPurifier/AttrDef.php';
* can only be used alone: it will never manifest as part of a multi
* shorthand declaration. Thus, this class does not allow inherit.
*/
class HTMLPurifier_AttrDef_Multiple extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef
{
/**
@@ -30,7 +30,7 @@ class HTMLPurifier_AttrDef_Multiple extends HTMLPurifier_AttrDef
* @param $single HTMLPurifier_AttrDef to multiply
* @param $max Max number of values allowed (usually four)
*/
function HTMLPurifier_AttrDef_Multiple($single, $max = 4) {
function HTMLPurifier_AttrDef_CSS_Multiple($single, $max = 4) {
$this->single = $single;
$this->max = $max;
}

View File

@@ -3,7 +3,7 @@
/**
* Validates a number as defined by the CSS spec.
*/
class HTMLPurifier_AttrDef_Number extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef
{
/**
@@ -14,7 +14,7 @@ class HTMLPurifier_AttrDef_Number extends HTMLPurifier_AttrDef
/**
* @param $non_negative Bool indicating whether negatives are forbidden
*/
function HTMLPurifier_AttrDef_Number($non_negative = false) {
function HTMLPurifier_AttrDef_CSS_Number($non_negative = false) {
$this->non_negative = $non_negative;
}

View File

@@ -1,24 +1,24 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Number.php';
require_once 'HTMLPurifier/AttrDef/CSS/Number.php';
/**
* Validates a Percentage as defined by the CSS spec.
*/
class HTMLPurifier_AttrDef_Percentage extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef
{
/**
* Instance of HTMLPurifier_AttrDef_Number to defer number validation
* Instance of HTMLPurifier_AttrDef_CSS_Number to defer number validation
*/
var $number_def;
/**
* @param Bool indicating whether to forbid negative values
*/
function HTMLPurifier_AttrDef_Percentage($non_negative = false) {
$this->number_def = new HTMLPurifier_AttrDef_Number($non_negative);
function HTMLPurifier_AttrDef_CSS_Percentage($non_negative = false) {
$this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
}
function validate($string, $config, &$context) {

View File

@@ -7,7 +7,7 @@ require_once 'HTMLPurifier/AttrDef.php';
* @note This class could be generalized into a version that acts sort of
* like Enum except you can compound the allowed values.
*/
class HTMLPurifier_AttrDef_TextDecoration extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef
{
/**

View File

@@ -4,17 +4,17 @@ require_once 'HTMLPurifier/AttrDef/URI.php';
/**
* Validates a URI in CSS syntax, which uses url('http://example.com')
* @note While theoretically speaking we a URI in a CSS document could
* @note While theoretically speaking a URI in a CSS document could
* be non-embedded, as of CSS2 there is no such usage so we're
* generalizing it. This may need to be changed in the future.
* @warning Since HTMLPurifier_AttrDef_CSS blindly uses semicolons as
* the separator, you cannot put a literal semicolon in
* in the URI. Try percent encoding it, in that case.
*/
class HTMLPurifier_AttrDef_CSSURI extends HTMLPurifier_AttrDef_URI
class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
{
function HTMLPurifier_AttrDef_CSSURI() {
function HTMLPurifier_AttrDef_CSS_URI() {
$this->HTMLPurifier_AttrDef_URI(true); // always embedded
}

View File

@@ -25,8 +25,8 @@ class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef
* @param $case_sensitive Bool indicating whether or not case sensitive
*/
function HTMLPurifier_AttrDef_Enum(
$valid_values = array(), $case_sensitive = false) {
$valid_values = array(), $case_sensitive = false
) {
$this->valid_values = array_flip($valid_values);
$this->case_sensitive = $case_sensitive;
}

View File

@@ -3,6 +3,22 @@
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/IDAccumulator.php';
HTMLPurifier_ConfigSchema::define(
'Attr', 'EnableID', false, 'bool',
'Allows the ID attribute in HTML. This is disabled by default '.
'due to the fact that without proper configuration user input can '.
'easily break the validation of a webpage by specifying an ID that is '.
'already on the surrounding HTML. If you don\'t mind throwing caution to '.
'the wind, enable this directive, but I strongly recommend you also '.
'consider blacklisting IDs you use (%Attr.IDBlacklist) or prefixing all '.
'user supplied IDs (%Attr.IDPrefix). This directive has been available '.
'since 1.2.0, and when set to true reverts to the behavior of pre-1.2.0 '.
'versions.'
);
HTMLPurifier_ConfigSchema::defineAlias(
'HTML', 'EnableAttrID', 'Attr', 'EnableID'
);
HTMLPurifier_ConfigSchema::define(
'Attr', 'IDPrefix', '', 'string',
'String to prefix to IDs. If you have no idea what IDs your pages '.
@@ -36,11 +52,16 @@ HTMLPurifier_ConfigSchema::define(
* blacklist. If you're hacking around, make sure you use load()!
*/
class HTMLPurifier_AttrDef_ID extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
{
// ref functionality disabled, since we also have to verify
// whether or not the ID it refers to exists
function validate($id, $config, &$context) {
if (!$config->get('Attr', 'EnableID')) return false;
$id = trim($id); // trim it first
if ($id === '') return false;
@@ -55,8 +76,10 @@ class HTMLPurifier_AttrDef_ID extends HTMLPurifier_AttrDef
'%Attr.IDPrefix is set', E_USER_WARNING);
}
$id_accumulator =& $context->get('IDAccumulator');
if (isset($id_accumulator->ids[$id])) return false;
//if (!$this->ref) {
$id_accumulator =& $context->get('IDAccumulator');
if (isset($id_accumulator->ids[$id])) return false;
//}
// we purposely avoid using regex, hopefully this is faster
@@ -71,7 +94,7 @@ class HTMLPurifier_AttrDef_ID extends HTMLPurifier_AttrDef
$result = ($trim === '');
}
if ($result) $id_accumulator->add($id);
if (/*!$this->ref && */$result) $id_accumulator->add($id);
// if no change was made to the ID, return the result
// else, return the new id if stripping whitespace made it

View File

@@ -1,18 +1,16 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Pixels.php';
require_once 'HTMLPurifier/AttrDef/HTML/Pixels.php';
/**
* Validates the HTML type length (not to be confused with CSS's length).
*
* This accepts integer pixels or percentages as lengths for certain
* HTML attributes. Don't use this for CSS: that's
* HTMLPurifier_AttrDef_CSSLength which requires prefixes and allows a lot
* more different types.
* HTML attributes.
*/
class HTMLPurifier_AttrDef_Length extends HTMLPurifier_AttrDef_Pixels
class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels
{
function validate($string, $config, &$context) {

View File

@@ -1,7 +1,7 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Length.php';
require_once 'HTMLPurifier/AttrDef/HTML/Length.php';
/**
* Validates a MultiLength as defined by the HTML spec.
@@ -9,7 +9,7 @@ require_once 'HTMLPurifier/AttrDef/Length.php';
* A multilength is either a integer (pixel count), a percentage, or
* a relative number.
*/
class HTMLPurifier_AttrDef_MultiLength extends HTMLPurifier_AttrDef_Length
class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length
{
function validate($string, $config, &$context) {
@@ -27,12 +27,14 @@ class HTMLPurifier_AttrDef_MultiLength extends HTMLPurifier_AttrDef_Length
$int = substr($string, 0, $length - 1);
if ($int == '') return '*';
if (!is_numeric($int)) return false;
$int = (int) $int;
if ($int < 0) return '0*';
if ($int < 0) return false;
if ($int == 0) return '0';
if ($int == 1) return '*';
return ((string) $int) . '*';
}

View File

@@ -4,9 +4,13 @@ require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/Config.php';
/**
* Validates the contents of the global HTML attribute class.
* 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_Class extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef
{
function validate($string, $config, &$context) {
@@ -31,10 +35,10 @@ class HTMLPurifier_AttrDef_Class extends HTMLPurifier_AttrDef
if (empty($matches[1])) return false;
// reconstruct class string
// reconstruct string
$new_string = '';
foreach ($matches[1] as $class_names) {
$new_string .= $class_names . ' ';
foreach ($matches[1] as $token) {
$new_string .= $token . ' ';
}
$new_string = rtrim($new_string);

View File

@@ -5,7 +5,7 @@ require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates an integer representation of pixels according to the HTML spec.
*/
class HTMLPurifier_AttrDef_Pixels extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef
{
function validate($string, $config, &$context) {

View File

@@ -46,7 +46,7 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
// process second subtag : $subtags[1]
$length = strlen($subtags[1]);
if ($length == 0 || $length == 1 || $length > 8 || !ctype_alnum($subtags[1])) {
if ($length == 0 || ($length == 1 && $subtags[1] != 'x') || $length > 8 || !ctype_alnum($subtags[1])) {
return $new_string;
}
if (!ctype_lower($subtags[1])) $subtags[1] = strtolower($subtags[1]);

View File

@@ -3,7 +3,7 @@
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/URIScheme.php';
require_once 'HTMLPurifier/URISchemeRegistry.php';
require_once 'HTMLPurifier/AttrDef/Host.php';
require_once 'HTMLPurifier/AttrDef/URI/Host.php';
require_once 'HTMLPurifier/PercentEncoder.php';
HTMLPurifier_ConfigSchema::define(
@@ -77,6 +77,14 @@ HTMLPurifier_ConfigSchema::define(
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'Disable', false, 'bool',
'Disables all URIs in all forms. Not sure why you\'d want to do that '.
'(after all, the Internet\'s founded on the notion of a hyperlink). '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::defineAlias('Attr', 'DisableURI', 'URI', 'Disable');
/**
* Validates a URI as defined by RFC 3986.
* @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
@@ -92,7 +100,7 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
* @param $embeds_resource_resource Does the URI here result in an extra HTTP request?
*/
function HTMLPurifier_AttrDef_URI($embeds_resource = false) {
$this->host = new HTMLPurifier_AttrDef_Host();
$this->host = new HTMLPurifier_AttrDef_URI_Host();
$this->PercentEncoder = new HTMLPurifier_PercentEncoder();
$this->embeds_resource = (bool) $embeds_resource;
}
@@ -102,6 +110,8 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
// We'll write stack-based parsers later, for now, use regexps to
// get things working as fast as possible (irony)
if ($config->get('URI', 'Disable')) return false;
// parse as CDATA
$uri = $this->parseCDATA($uri);

View File

@@ -2,7 +2,7 @@
require_once 'HTMLPurifier/AttrDef.php';
class HTMLPurifier_AttrDef_Email extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_URI_Email extends HTMLPurifier_AttrDef
{
/**

View File

@@ -1,12 +1,12 @@
<?php
require_once 'HTMLPurifier/AttrDef/Email.php';
require_once 'HTMLPurifier/AttrDef/URI/Email.php';
/**
* Primitive email validation class based on the regexp found at
* http://www.regular-expressions.info/email.html
*/
class HTMLPurifier_AttrDef_Email_SimpleCheck extends HTMLPurifier_AttrDef_Email
class HTMLPurifier_AttrDef_URI_Email_SimpleCheck extends HTMLPurifier_AttrDef_URI_Email
{
function validate($string, $config, &$context) {

View File

@@ -1,28 +1,28 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/IPv4.php';
require_once 'HTMLPurifier/AttrDef/IPv6.php';
require_once 'HTMLPurifier/AttrDef/URI/IPv4.php';
require_once 'HTMLPurifier/AttrDef/URI/IPv6.php';
/**
* Validates a host according to the IPv4, IPv6 and DNS (future) specifications.
*/
class HTMLPurifier_AttrDef_Host extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
{
/**
* Instance of HTMLPurifier_AttrDef_IPv4 sub-validator
* Instance of HTMLPurifier_AttrDef_URI_IPv4 sub-validator
*/
var $ipv4;
/**
* Instance of HTMLPurifier_AttrDef_IPv6 sub-validator
* Instance of HTMLPurifier_AttrDef_URI_IPv6 sub-validator
*/
var $ipv6;
function HTMLPurifier_AttrDef_Host() {
$this->ipv4 = new HTMLPurifier_AttrDef_IPv4();
$this->ipv6 = new HTMLPurifier_AttrDef_IPv6();
function HTMLPurifier_AttrDef_URI_Host() {
$this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4();
$this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6();
}
function validate($string, $config, &$context) {

View File

@@ -6,7 +6,7 @@ require_once 'HTMLPurifier/AttrDef.php';
* Validates an IPv4 address
* @author Feyd @ forums.devnetwork.net (public domain)
*/
class HTMLPurifier_AttrDef_IPv4 extends HTMLPurifier_AttrDef
class HTMLPurifier_AttrDef_URI_IPv4 extends HTMLPurifier_AttrDef
{
/**
@@ -15,7 +15,7 @@ class HTMLPurifier_AttrDef_IPv4 extends HTMLPurifier_AttrDef
*/
var $ip4;
function HTMLPurifier_AttrDef_IPv4() {
function HTMLPurifier_AttrDef_URI_IPv4() {
$oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255
$this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})";
}

View File

@@ -1,6 +1,6 @@
<?php
require_once 'HTMLPurifier/AttrDef/IPv4.php';
require_once 'HTMLPurifier/AttrDef/URI/IPv4.php';
/**
* Validates an IPv6 address.
@@ -8,7 +8,7 @@ require_once 'HTMLPurifier/AttrDef/IPv4.php';
* @note This function requires brackets to have been removed from address
* in URI.
*/
class HTMLPurifier_AttrDef_IPv6 extends HTMLPurifier_AttrDef_IPv4
class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4
{
function validate($aIP, $config, &$context) {

View File

@@ -0,0 +1,41 @@
<?php
require_once 'HTMLPurifier/AttrDef/HTML/ID.php';
require_once 'HTMLPurifier/AttrDef/HTML/Length.php';
require_once 'HTMLPurifier/AttrDef/HTML/MultiLength.php';
require_once 'HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require_once 'HTMLPurifier/AttrDef/HTML/Pixels.php';
require_once 'HTMLPurifier/AttrDef/Integer.php';
require_once 'HTMLPurifier/AttrDef/Text.php';
require_once 'HTMLPurifier/AttrDef/URI.php';
/**
* Provides lookup array of attribute types to HTMLPurifier_AttrDef objects
*/
class HTMLPurifier_AttrTypes
{
/**
* Lookup array of attribute string identifiers to concrete implementations
* @public
*/
var $info = array();
/**
* Constructs the info array
*/
function HTMLPurifier_AttrTypes() {
$this->info['CDATA'] = new HTMLPurifier_AttrDef_Text();
$this->info['ID'] = new HTMLPurifier_AttrDef_HTML_ID();
$this->info['Length'] = new HTMLPurifier_AttrDef_HTML_Length();
$this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength();
$this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens();
$this->info['Pixels'] = new HTMLPurifier_AttrDef_HTML_Pixels();
$this->info['Text'] = new HTMLPurifier_AttrDef_Text();
$this->info['URI'] = new HTMLPurifier_AttrDef_URI();
// number is really a positive integer (one or more digits)
$this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true);
}
}
?>

View File

@@ -1,19 +1,19 @@
<?php
require_once 'HTMLPurifier/AttrDef/CSS/Background.php';
require_once 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require_once 'HTMLPurifier/AttrDef/CSS/Border.php';
require_once 'HTMLPurifier/AttrDef/CSS/Color.php';
require_once 'HTMLPurifier/AttrDef/CSS/Composite.php';
require_once 'HTMLPurifier/AttrDef/CSS/Font.php';
require_once 'HTMLPurifier/AttrDef/CSS/FontFamily.php';
require_once 'HTMLPurifier/AttrDef/CSS/Length.php';
require_once 'HTMLPurifier/AttrDef/CSS/ListStyle.php';
require_once 'HTMLPurifier/AttrDef/CSS/Multiple.php';
require_once 'HTMLPurifier/AttrDef/CSS/Percentage.php';
require_once 'HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require_once 'HTMLPurifier/AttrDef/CSS/URI.php';
require_once 'HTMLPurifier/AttrDef/Enum.php';
require_once 'HTMLPurifier/AttrDef/Color.php';
require_once 'HTMLPurifier/AttrDef/Composite.php';
require_once 'HTMLPurifier/AttrDef/CSSLength.php';
require_once 'HTMLPurifier/AttrDef/Percentage.php';
require_once 'HTMLPurifier/AttrDef/Multiple.php';
require_once 'HTMLPurifier/AttrDef/TextDecoration.php';
require_once 'HTMLPurifier/AttrDef/FontFamily.php';
require_once 'HTMLPurifier/AttrDef/Font.php';
require_once 'HTMLPurifier/AttrDef/Border.php';
require_once 'HTMLPurifier/AttrDef/ListStyle.php';
require_once 'HTMLPurifier/AttrDef/CSSURI.php';
require_once 'HTMLPurifier/AttrDef/BackgroundPosition.php';
require_once 'HTMLPurifier/AttrDef/Background.php';
/**
* Defines allowed CSS attributes and what their values are.
@@ -43,7 +43,7 @@ class HTMLPurifier_CSSDefinition
array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
'groove', 'ridge', 'inset', 'outset'), false);
$this->info['border-style'] = new HTMLPurifier_AttrDef_Multiple($border_style);
$this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
$this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
array('none', 'left', 'right', 'both'), false);
@@ -54,10 +54,10 @@ class HTMLPurifier_CSSDefinition
$this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
array('normal', 'small-caps'), false);
$uri_or_none = new HTMLPurifier_AttrDef_Composite(
$uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
array(
new HTMLPurifier_AttrDef_Enum(array('none')),
new HTMLPurifier_AttrDef_CSSURI()
new HTMLPurifier_AttrDef_CSS_URI()
)
);
@@ -68,11 +68,11 @@ class HTMLPurifier_CSSDefinition
'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false);
$this->info['list-style-image'] = $uri_or_none;
$this->info['list-style'] = new HTMLPurifier_AttrDef_ListStyle($config);
$this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
$this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
array('capitalize', 'uppercase', 'lowercase', 'none'), false);
$this->info['color'] = new HTMLPurifier_AttrDef_Color();
$this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
$this->info['background-image'] = $uri_or_none;
$this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
@@ -81,96 +81,96 @@ class HTMLPurifier_CSSDefinition
$this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
array('scroll', 'fixed')
);
$this->info['background-position'] = new HTMLPurifier_AttrDef_BackgroundPosition();
$this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
$border_color =
$this->info['border-top-color'] =
$this->info['border-bottom-color'] =
$this->info['border-left-color'] =
$this->info['border-right-color'] =
$this->info['background-color'] = new HTMLPurifier_AttrDef_Composite(array(
$this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_Enum(array('transparent')),
new HTMLPurifier_AttrDef_Color()
new HTMLPurifier_AttrDef_CSS_Color()
));
$this->info['background'] = new HTMLPurifier_AttrDef_Background($config);
$this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
$this->info['border-color'] = new HTMLPurifier_AttrDef_Multiple($border_color);
$this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
$border_width =
$this->info['border-top-width'] =
$this->info['border-bottom-width'] =
$this->info['border-left-width'] =
$this->info['border-right-width'] = new HTMLPurifier_AttrDef_Composite(array(
$this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
new HTMLPurifier_AttrDef_CSSLength(true) //disallow negative
new HTMLPurifier_AttrDef_CSS_Length(true) //disallow negative
));
$this->info['border-width'] = new HTMLPurifier_AttrDef_Multiple($border_width);
$this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
$this->info['letter-spacing'] = new HTMLPurifier_AttrDef_Composite(array(
$this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_Enum(array('normal')),
new HTMLPurifier_AttrDef_CSSLength()
new HTMLPurifier_AttrDef_CSS_Length()
));
$this->info['word-spacing'] = new HTMLPurifier_AttrDef_Composite(array(
$this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_Enum(array('normal')),
new HTMLPurifier_AttrDef_CSSLength()
new HTMLPurifier_AttrDef_CSS_Length()
));
$this->info['font-size'] = new HTMLPurifier_AttrDef_Composite(array(
$this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small',
'small', 'medium', 'large', 'x-large', 'xx-large',
'larger', 'smaller')),
new HTMLPurifier_AttrDef_Percentage(),
new HTMLPurifier_AttrDef_CSSLength()
new HTMLPurifier_AttrDef_CSS_Percentage(),
new HTMLPurifier_AttrDef_CSS_Length()
));
$this->info['line-height'] = new HTMLPurifier_AttrDef_Composite(array(
$this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_Enum(array('normal')),
new HTMLPurifier_AttrDef_Number(true), // no negatives
new HTMLPurifier_AttrDef_CSSLength(true),
new HTMLPurifier_AttrDef_Percentage(true)
new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
new HTMLPurifier_AttrDef_CSS_Length(true),
new HTMLPurifier_AttrDef_CSS_Percentage(true)
));
$margin =
$this->info['margin-top'] =
$this->info['margin-bottom'] =
$this->info['margin-left'] =
$this->info['margin-right'] = new HTMLPurifier_AttrDef_Composite(array(
new HTMLPurifier_AttrDef_CSSLength(),
new HTMLPurifier_AttrDef_Percentage(),
$this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_CSS_Length(),
new HTMLPurifier_AttrDef_CSS_Percentage(),
new HTMLPurifier_AttrDef_Enum(array('auto'))
));
$this->info['margin'] = new HTMLPurifier_AttrDef_Multiple($margin);
$this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
// non-negative
$padding =
$this->info['padding-top'] =
$this->info['padding-bottom'] =
$this->info['padding-left'] =
$this->info['padding-right'] = new HTMLPurifier_AttrDef_Composite(array(
new HTMLPurifier_AttrDef_CSSLength(true),
new HTMLPurifier_AttrDef_Percentage(true)
$this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_CSS_Length(true),
new HTMLPurifier_AttrDef_CSS_Percentage(true)
));
$this->info['padding'] = new HTMLPurifier_AttrDef_Multiple($padding);
$this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
$this->info['text-indent'] = new HTMLPurifier_AttrDef_Composite(array(
new HTMLPurifier_AttrDef_CSSLength(),
new HTMLPurifier_AttrDef_Percentage()
$this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_CSS_Length(),
new HTMLPurifier_AttrDef_CSS_Percentage()
));
$this->info['width'] = new HTMLPurifier_AttrDef_Composite(array(
new HTMLPurifier_AttrDef_CSSLength(true),
new HTMLPurifier_AttrDef_Percentage(true),
$this->info['width'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_CSS_Length(true),
new HTMLPurifier_AttrDef_CSS_Percentage(true),
new HTMLPurifier_AttrDef_Enum(array('auto'))
));
$this->info['text-decoration'] = new HTMLPurifier_AttrDef_TextDecoration();
$this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
$this->info['font-family'] = new HTMLPurifier_AttrDef_FontFamily();
$this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
// this could use specialized code
$this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
@@ -179,14 +179,14 @@ class HTMLPurifier_CSSDefinition
// MUST be called after other font properties, as it references
// a CSSDefinition object
$this->info['font'] = new HTMLPurifier_AttrDef_Font($config);
$this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
// same here
$this->info['border'] =
$this->info['border-bottom'] =
$this->info['border-top'] =
$this->info['border-left'] =
$this->info['border-right'] = new HTMLPurifier_AttrDef_Border($config);
$this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
$this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array(
'collapse', 'seperate'));
@@ -197,11 +197,11 @@ class HTMLPurifier_CSSDefinition
$this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array(
'auto', 'fixed'));
$this->info['vertical-align'] = new HTMLPurifier_AttrDef_Composite(array(
$this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super',
'top', 'text-top', 'middle', 'bottom', 'text-bottom')),
new HTMLPurifier_AttrDef_CSSLength(),
new HTMLPurifier_AttrDef_Percentage()
new HTMLPurifier_AttrDef_CSS_Length(),
new HTMLPurifier_AttrDef_CSS_Percentage()
));
}

View File

@@ -38,22 +38,13 @@ class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef
}
function validateChildren($tokens_of_children, $config, &$context) {
$parent_type = $context->get('ParentType');
switch ($parent_type) {
case 'unknown':
case 'inline':
$result = $this->inline->validateChildren(
$tokens_of_children, $config, $context);
break;
case 'block':
$result = $this->block->validateChildren(
$tokens_of_children, $config, $context);
break;
default:
trigger_error('Invalid context', E_USER_ERROR);
return false;
if ($context->get('IsInline') === false) {
return $this->block->validateChildren(
$tokens_of_children, $config, $context);
} else {
return $this->inline->validateChildren(
$tokens_of_children, $config, $context);
}
return $result;
}
}

View File

@@ -20,10 +20,13 @@ class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
$elements = str_replace(' ', '', $elements);
$elements = explode('|', $elements);
}
$elements = array_flip($elements);
foreach ($elements as $i => $x) {
$elements[$i] = true;
if (empty($i)) unset($elements[$i]);
$keys = array_keys($elements);
if ($keys == array_keys($keys)) {
$elements = array_flip($elements);
foreach ($elements as $i => $x) {
$elements[$i] = true;
if (empty($i)) unset($elements[$i]);
}
}
$this->elements = $elements;
$this->gen = new HTMLPurifier_Generator();

View File

@@ -4,27 +4,31 @@ require_once 'HTMLPurifier/ChildDef/Required.php';
/**
* Takes the contents of blockquote when in strict and reformats for validation.
*
* From XHTML 1.0 Transitional to Strict, there is a notable change where
*/
class HTMLPurifier_ChildDef_StrictBlockquote
extends HTMLPurifier_ChildDef_Required
{
var $real_elements;
var $fake_elements;
var $allow_empty = true;
var $type = 'strictblockquote';
var $init = false;
function HTMLPurifier_ChildDef_StrictBlockquote() {}
function validateChildren($tokens_of_children, $config, &$context) {
$def = $config->getHTMLDefinition();
if (!$this->init) {
// allow all inline elements
$this->elements = $def->info_flow_elements;
$this->elements['#PCDATA'] = true;
$this->real_elements = $this->elements;
$this->fake_elements = $def->info_content_sets['Flow'];
$this->fake_elements['#PCDATA'] = true;
$this->init = true;
}
// trick the parent class into thinking it allows more
$this->elements = $this->fake_elements;
$result = parent::validateChildren($tokens_of_children, $config, $context);
$this->elements = $this->real_elements;
if ($result === false) return array();
if ($result === true) $result = $tokens_of_children;
@@ -40,8 +44,10 @@ extends HTMLPurifier_ChildDef_Required
// ifs are nested for readability
if (!$is_inline) {
if (!$depth) {
if (($token->type == 'text') ||
($def->info[$token->name]->type == 'inline')) {
if (
$token->type == 'text' ||
!isset($this->elements[$token->name])
) {
$is_inline = true;
$ret[] = $block_wrap_start;
}
@@ -50,7 +56,7 @@ extends HTMLPurifier_ChildDef_Required
if (!$depth) {
// starting tokens have been inline text / empty
if ($token->type == 'start' || $token->type == 'empty') {
if ($def->info[$token->name]->type == 'block') {
if (isset($this->elements[$token->name])) {
// ended
$ret[] = $block_wrap_end;
$is_inline = false;

View File

@@ -149,23 +149,36 @@ class HTMLPurifier_Config
return;
}
$this->conf[$namespace][$key] = $value;
if ($namespace == 'HTML' || $namespace == 'Attr') {
// reset HTML definition if relevant attributes changed
$this->html_definition = null;
}
if ($namespace == 'CSS') {
$this->css_definition = null;
}
}
/**
* Retrieves a copy of the HTML definition.
* Retrieves reference to the HTML definition.
* @param $raw Return a copy that has not been setup yet. Must be
* called before it's been setup, otherwise won't work.
*/
function getHTMLDefinition() {
if ($this->html_definition === null) {
$this->html_definition = new HTMLPurifier_HTMLDefinition();
$this->html_definition->setup($this);
function &getHTMLDefinition($raw = false) {
if (
empty($this->html_definition) || // hasn't ever been setup
($raw && $this->html_definition->setup) // requesting new one
) {
$this->html_definition = new HTMLPurifier_HTMLDefinition($this);
if ($raw) return $this->html_definition; // no setup!
}
if (!$this->html_definition->setup) $this->html_definition->setup();
return $this->html_definition;
}
/**
* Retrieves a copy of the CSS definition
* Retrieves reference to the CSS definition
*/
function getCSSDefinition() {
function &getCSSDefinition() {
if ($this->css_definition === null) {
$this->css_definition = new HTMLPurifier_CSSDefinition();
$this->css_definition->setup($this);

View File

@@ -0,0 +1,10 @@
<?php
/**
* Base class for configuration entity
*/
class HTMLPurifier_ConfigDef {
var $class = false;
}
?>

View File

@@ -0,0 +1,74 @@
<?php
require_once 'HTMLPurifier/ConfigDef.php';
/**
* Structure object containing definition of a directive.
* @note This structure does not contain default values
*/
class HTMLPurifier_ConfigDef_Directive extends HTMLPurifier_ConfigDef
{
var $class = 'directive';
function HTMLPurifier_ConfigDef_Directive(
$type = null,
$descriptions = null,
$allow_null = null,
$allowed = null,
$aliases = null
) {
if ( $type !== null) $this->type = $type;
if ($descriptions !== null) $this->descriptions = $descriptions;
if ( $allow_null !== null) $this->allow_null = $allow_null;
if ( $allowed !== null) $this->allowed = $allowed;
if ( $aliases !== null) $this->aliases = $aliases;
}
/**
* Allowed type of the directive. Values are:
* - string
* - istring (case insensitive string)
* - int
* - float
* - bool
* - lookup (array of value => true)
* - list (regular numbered index array)
* - hash (array of key => value)
* - mixed (anything goes)
*/
var $type = 'mixed';
/**
* Plaintext descriptions of the configuration entity is. Organized by
* file and line number, so multiple descriptions are allowed.
*/
var $descriptions = array();
/**
* Is null allowed? Has no effect for mixed type.
* @bool
*/
var $allow_null = false;
/**
* Lookup table of allowed values of the element, bool true if all allowed.
*/
var $allowed = true;
/**
* Hash of value aliases, i.e. values that are equivalent.
*/
var $aliases = array();
/**
* Adds a description to the array
*/
function addDescription($file, $line, $description) {
if (!isset($this->descriptions[$file])) $this->descriptions[$file] = array();
$this->descriptions[$file][$line] = $description;
}
}
?>

View File

@@ -0,0 +1,27 @@
<?php
require_once 'HTMLPurifier/ConfigDef.php';
/**
* Structure object describing a directive alias
*/
class HTMLPurifier_ConfigDef_DirectiveAlias extends HTMLPurifier_ConfigDef
{
var $class = 'alias';
/**
* Namespace being aliased to
*/
var $namespace;
/**
* Directive being aliased to
*/
var $name;
function HTMLPurifier_ConfigDef_DirectiveAlias($namespace, $name) {
$this->namespace = $namespace;
$this->name = $name;
}
}
?>

View File

@@ -0,0 +1,23 @@
<?php
require_once 'HTMLPurifier/ConfigDef.php';
/**
* Structure object describing of a namespace
*/
class HTMLPurifier_ConfigDef_Namespace extends HTMLPurifier_ConfigDef {
function HTMLPurifier_ConfigDef_Namespace($description = null) {
$this->description = $description;
}
var $class = 'namespace';
/**
* String description of what kinds of directives go in this namespace.
*/
var $description;
}
?>

View File

@@ -1,6 +1,10 @@
<?php
require_once 'HTMLPurifier/Error.php';
require_once 'HTMLPurifier/ConfigDef.php';
require_once 'HTMLPurifier/ConfigDef/Namespace.php';
require_once 'HTMLPurifier/ConfigDef/Directive.php';
require_once 'HTMLPurifier/ConfigDef/DirectiveAlias.php';
/**
* Configuration definition, defines directives and their defaults.
@@ -138,7 +142,7 @@ class HTMLPurifier_ConfigSchema {
return;
}
$def->info[$namespace][$name] =
new HTMLPurifier_ConfigEntity_Directive();
new HTMLPurifier_ConfigDef_Directive();
$def->info[$namespace][$name]->type = $type;
$def->info[$namespace][$name]->allow_null = $allow_null;
$def->defaults[$namespace][$name] = $default;
@@ -172,7 +176,7 @@ class HTMLPurifier_ConfigSchema {
return;
}
$def->info[$namespace] = array();
$def->info_namespace[$namespace] = new HTMLPurifier_ConfigEntity_Namespace();
$def->info_namespace[$namespace] = new HTMLPurifier_ConfigDef_Namespace();
$def->info_namespace[$namespace]->description = $description;
$def->defaults[$namespace] = array();
}
@@ -284,7 +288,7 @@ class HTMLPurifier_ConfigSchema {
return;
}
$def->info[$namespace][$name] =
new HTMLPurifier_ConfigEntity_DirectiveAlias(
new HTMLPurifier_ConfigDef_DirectiveAlias(
$new_namespace, $new_name);
}
@@ -379,120 +383,4 @@ class HTMLPurifier_ConfigSchema {
}
}
/**
* Base class for configuration entity
*/
class HTMLPurifier_ConfigEntity {
var $class = false;
}
/**
* Structure object describing of a namespace
*/
class HTMLPurifier_ConfigEntity_Namespace extends HTMLPurifier_ConfigEntity {
function HTMLPurifier_ConfigEntity_Namespace($description = null) {
$this->description = $description;
}
var $class = 'namespace';
/**
* String description of what kinds of directives go in this namespace.
*/
var $description;
}
/**
* Structure object containing definition of a directive.
* @note This structure does not contain default values
*/
class HTMLPurifier_ConfigEntity_Directive extends HTMLPurifier_ConfigEntity
{
var $class = 'directive';
function HTMLPurifier_ConfigEntity_Directive(
$type = null,
$descriptions = null,
$allow_null = null,
$allowed = null,
$aliases = null
) {
if ( $type !== null) $this->type = $type;
if ($descriptions !== null) $this->descriptions = $descriptions;
if ( $allow_null !== null) $this->allow_null = $allow_null;
if ( $allowed !== null) $this->allowed = $allowed;
if ( $aliases !== null) $this->aliases = $aliases;
}
/**
* Allowed type of the directive. Values are:
* - string
* - istring (case insensitive string)
* - int
* - float
* - bool
* - lookup (array of value => true)
* - list (regular numbered index array)
* - hash (array of key => value)
* - mixed (anything goes)
*/
var $type = 'mixed';
/**
* Plaintext descriptions of the configuration entity is. Organized by
* file and line number, so multiple descriptions are allowed.
*/
var $descriptions = array();
/**
* Is null allowed? Has no effect for mixed type.
* @bool
*/
var $allow_null = false;
/**
* Lookup table of allowed values of the element, bool true if all allowed.
*/
var $allowed = true;
/**
* Hash of value aliases, i.e. values that are equivalent.
*/
var $aliases = array();
/**
* Adds a description to the array
*/
function addDescription($file, $line, $description) {
if (!isset($this->descriptions[$file])) $this->descriptions[$file] = array();
$this->descriptions[$file][$line] = $description;
}
}
/**
* Structure object describing a directive alias
*/
class HTMLPurifier_ConfigEntity_DirectiveAlias extends HTMLPurifier_ConfigEntity
{
var $class = 'alias';
/**
* Namespace being aliased to
*/
var $namespace;
/**
* Directive being aliased to
*/
var $name;
function HTMLPurifier_ConfigEntity_DirectiveAlias($namespace, $name) {
$this->namespace = $namespace;
$this->name = $name;
}
}
?>

View File

@@ -0,0 +1,148 @@
<?php
// common defs that we'll support by default
require_once 'HTMLPurifier/ChildDef.php';
require_once 'HTMLPurifier/ChildDef/Empty.php';
require_once 'HTMLPurifier/ChildDef/Required.php';
require_once 'HTMLPurifier/ChildDef/Optional.php';
class HTMLPurifier_ContentSets
{
/**
* List of content set strings (pipe seperators) indexed by name.
* @public
*/
var $info = array();
/**
* List of content set lookups (element => true) indexed by name.
* @note This is in HTMLPurifier_HTMLDefinition->info_content_sets
* @public
*/
var $lookup = array();
/**
* Synchronized list of defined content sets (keys of info)
*/
var $keys = array();
/**
* Synchronized list of defined content values (values of info)
*/
var $values = array();
/**
* Merges in module's content sets, expands identifiers in the content
* sets and populates the keys, values and lookup member variables.
* @param $modules List of HTMLPurifier_HTMLModule
*/
function HTMLPurifier_ContentSets($modules) {
if (!is_array($modules)) $modules = array($modules);
// populate content_sets based on module hints
// sorry, no way of overloading
foreach ($modules as $module_i => $module) {
foreach ($module->content_sets as $key => $value) {
if (isset($this->info[$key])) {
// add it into the existing content set
$this->info[$key] = $this->info[$key] . ' | ' . $value;
} else {
$this->info[$key] = $value;
}
}
}
// perform content_set expansions
$this->keys = array_keys($this->info);
foreach ($this->info as $i => $set) {
// only performed once, so infinite recursion is not
// a problem
$this->info[$i] =
str_replace(
$this->keys,
// must be recalculated each time due to
// changing substitutions
array_values($this->info),
$set);
}
$this->values = array_values($this->info);
// generate lookup tables
foreach ($this->info as $name => $set) {
$this->lookup[$name] = $this->convertToLookup($set);
}
}
/**
* Accepts a definition; generates and assigns a ChildDef for it
* @param $def HTMLPurifier_ElementDef reference
* @param $module Module that defined the ElementDef
*/
function generateChildDef(&$def, $module) {
if (!empty($def->child)) return; // already done!
$content_model = $def->content_model;
if (is_string($content_model)) {
$def->content_model = str_replace(
$this->keys, $this->values, $content_model);
}
$def->child = $this->getChildDef($def, $module);
}
/**
* Instantiates a ChildDef based on content_model and content_model_type
* member variables in HTMLPurifier_ElementDef
* @note This will also defer to modules for custom HTMLPurifier_ChildDef
* subclasses that need content set expansion
* @param $def HTMLPurifier_ElementDef to have ChildDef extracted
* @return HTMLPurifier_ChildDef corresponding to ElementDef
*/
function getChildDef($def, $module) {
$value = $def->content_model;
if (is_object($value)) {
trigger_error(
'Literal object child definitions should be stored in '.
'ElementDef->child not ElementDef->content_model',
E_USER_NOTICE
);
return $value;
}
switch ($def->content_model_type) {
case 'required':
return new HTMLPurifier_ChildDef_Required($value);
case 'optional':
return new HTMLPurifier_ChildDef_Optional($value);
case 'empty':
return new HTMLPurifier_ChildDef_Empty();
case 'custom':
return new HTMLPurifier_ChildDef_Custom($value);
}
// defer to its module
$return = false;
if ($module->defines_child_def) { // save a func call
$return = $module->getChildDef($def);
}
if ($return !== false) return $return;
// error-out
trigger_error(
'Could not determine which ChildDef class to instantiate',
E_USER_ERROR
);
return false;
}
/**
* Converts a string list of elements separated by pipes into
* a lookup array.
* @param $string List of elements
* @return Lookup array of elements
*/
function convertToLookup($string) {
$array = explode('|', str_replace(' ', '', $string));
$ret = array();
foreach ($array as $i => $k) {
$ret[$k] = true;
}
return $ret;
}
}
?>

View File

@@ -0,0 +1,122 @@
<?php
/**
* Structure that stores an HTML element definition. Used by
* HTMLPurifier_HTMLDefinition and HTMLPurifier_HTMLModule.
*/
class HTMLPurifier_ElementDef
{
/**
* Does the definition work by itself, or is it created solely
* for the purpose of merging into another definition?
*/
var $standalone = true;
/**
* Associative array of attribute name to HTMLPurifier_AttrDef
* @note Before being processed by HTMLPurifier_AttrCollections
* when modules are finalized during
* HTMLPurifier_HTMLDefinition->setup(), this array may also
* contain an array at index 0 that indicates which attribute
* collections to load into the full array. It may also
* contain string indentifiers in lieu of HTMLPurifier_AttrDef,
* see HTMLPurifier_AttrTypes on how they are expanded during
* HTMLPurifier_HTMLDefinition->setup() processing.
* @public
*/
var $attr = array();
/**
* Indexed list of tag's HTMLPurifier_AttrTransform to be done before validation
* @public
*/
var $attr_transform_pre = array();
/**
* Indexed list of tag's HTMLPurifier_AttrTransform to be done after validation
* @public
*/
var $attr_transform_post = array();
/**
* HTMLPurifier_ChildDef of this tag.
* @public
*/
var $child;
/**
* Abstract string representation of internal ChildDef rules. See
* HTMLPurifier_ContentSets for how this is parsed and then transformed
* into an HTMLPurifier_ChildDef.
* @public
*/
var $content_model;
/**
* Value of $child->type, used to determine which ChildDef to use,
* used in combination with $content_model.
* @public
*/
var $content_model_type;
/**
* Lookup table of tags that close this tag. Used during parsing
* to make sure we don't attempt to nest unclosed tags.
* @public
*/
var $auto_close = array();
/**
* Does the element have a content model (#PCDATA | Inline)*? This
* is important for chameleon ins and del processing in
* HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't
* have to worry about this one.
* @public
*/
var $descendants_are_inline;
/**
* Lookup table of tags excluded from all descendants of this tag.
* @public
*/
var $excludes = array();
/**
* Merges the values of another element definition into this one.
* Values from the new element def take precedence if a value is
* not mergeable.
*/
function mergeIn($def) {
// later keys takes precedence
foreach($def->attr as $k => $v) {
if ($k == 0) {
// merge in the includes
// sorry, no way to override an include
foreach ($v as $v2) {
$def->attr[0][] = $v2;
}
continue;
}
$this->attr[$k] = $v;
}
foreach($def->attr_transform_pre as $k => $v) $this->attr_transform_pre[$k] = $v;
foreach($def->attr_transform_post as $k => $v) $this->attr_transform_post[$k] = $v;
foreach($def->auto_close as $k => $v) $this->auto_close[$k] = $v;
foreach($def->excludes as $k => $v) $this->excludes[$k] = $v;
if(!is_null($def->child)) $this->child = $def->child;
if(!empty($def->content_model)) $this->content_model .= ' | ' . $def->content_model;
if(!empty($def->content_model_type)) $this->content_model_type = $def->content_model_type;
if(!is_null($def->descendants_are_inline)) $this->descendants_are_inline = $def->descendants_are_inline;
}
}
?>

View File

@@ -9,7 +9,7 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
function preFilter($html, $config, &$context) {
$pre_regex = '#<object[^>]+>.+?'.
'http://www.youtube.com/v/([A-Za-z0-9\-_]+).+?</object>#';
'http://www.youtube.com/v/([A-Za-z0-9\-_]+).+?</object>#s';
$pre_replace = '<span class="youtube-embed">\1</span>';
return preg_replace($pre_regex, $pre_replace, $html);
}

View File

@@ -1,656 +1,250 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Enum.php';
require_once 'HTMLPurifier/AttrDef/ID.php';
require_once 'HTMLPurifier/AttrDef/Class.php';
require_once 'HTMLPurifier/AttrDef/Text.php';
require_once 'HTMLPurifier/AttrDef/Lang.php';
require_once 'HTMLPurifier/AttrDef/Pixels.php';
require_once 'HTMLPurifier/AttrDef/Length.php';
require_once 'HTMLPurifier/AttrDef/MultiLength.php';
require_once 'HTMLPurifier/AttrDef/Integer.php';
require_once 'HTMLPurifier/AttrDef/URI.php';
require_once 'HTMLPurifier/AttrDef/CSS.php';
require_once 'HTMLPurifier/AttrTransform.php';
require_once 'HTMLPurifier/AttrTransform/Lang.php';
require_once 'HTMLPurifier/AttrTransform/TextAlign.php';
require_once 'HTMLPurifier/AttrTransform/BdoDir.php';
require_once 'HTMLPurifier/AttrTransform/ImgRequired.php';
require_once 'HTMLPurifier/ChildDef.php';
require_once 'HTMLPurifier/ChildDef/Chameleon.php';
require_once 'HTMLPurifier/ChildDef/Empty.php';
require_once 'HTMLPurifier/ChildDef/Required.php';
require_once 'HTMLPurifier/ChildDef/Optional.php';
require_once 'HTMLPurifier/ChildDef/Table.php';
require_once 'HTMLPurifier/ChildDef/StrictBlockquote.php';
require_once 'HTMLPurifier/Generator.php';
require_once 'HTMLPurifier/Token.php';
require_once 'HTMLPurifier/TagTransform.php';
HTMLPurifier_ConfigSchema::define(
'HTML', 'EnableAttrID', false, 'bool',
'Allows the ID attribute in HTML. This is disabled by default '.
'due to the fact that without proper configuration user input can '.
'easily break the validation of a webpage by specifying an ID that is '.
'already on the surrounding HTML. If you don\'t mind throwing caution to '.
'the wind, enable this directive, but I strongly recommend you also '.
'consider blacklisting IDs you use (%Attr.IDBlacklist) or prefixing all '.
'user supplied IDs (%Attr.IDPrefix). This directive has been available '.
'since 1.2.0, and when set to true reverts to the behavior of pre-1.2.0 '.
'versions.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'Strict', false, 'bool',
'Determines whether or not to use Transitional (loose) or Strict rulesets. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'BlockWrapper', 'p', 'string',
'String name of element to wrap inline elements that are inside a block '.
'context. This only occurs in the children of blockquote in strict mode. '.
'Example: by default value, <code>&lt;blockquote&gt;Foo&lt;/blockquote&gt;</code> '.
'would become <code>&lt;blockquote&gt;&lt;p&gt;Foo&lt;/p&gt;&lt;/blockquote&gt;</code>. The '.
'<code>&lt;p&gt;</code> tags can be replaced '.
'with whatever you desire, as long as it is a block level element. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'Parent', 'div', 'string',
'String name of element that HTML fragment passed to library will be '.
'inserted in. An interesting variation would be using span as the '.
'parent element, meaning that only inline tags would be allowed. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'AllowedElements', null, 'lookup/null',
'If HTML Purifier\'s tag set is unsatisfactory for your needs, you '.
'can overload it with your own list of tags to allow. Note that this '.
'method is subtractive: it does its job by taking away from HTML Purifier '.
'usual feature set, so you cannot add a tag that HTML Purifier never '.
'supported in the first place (like embed, form or head). If you change this, you '.
'probably also want to change %HTML.AllowedAttributes. '.
'<strong>Warning:</strong> If another directive conflicts with the '.
'elements here, <em>that</em> directive will win and override. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'AllowedAttributes', null, 'lookup/null',
'IF HTML Purifier\'s attribute set is unsatisfactory, overload it! '.
'The syntax is \'tag.attr\' or \'*.attr\' for the global attributes '.
'(style, id, class, dir, lang, xml:lang).'.
'<strong>Warning:</strong> If another directive conflicts with the '.
'elements here, <em>that</em> directive will win and override. For '.
'example, %HTML.EnableAttrID will take precedence over *.id in this '.
'directive. You must set that directive to true before you can use '.
'IDs at all. This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'Attr', 'DisableURI', false, 'bool',
'Disables all URIs in all forms. Not sure why you\'d want to do that '.
'(after all, the Internet\'s founded on the notion of a hyperlink). '.
'This directive has been available since 1.3.0.'
);
/**
* Defines the purified HTML type with large amounts of objects.
*
* The main function of this object is its $info array, which is an
* associative array of all the child and attribute definitions for
* each allowed element. It also contains special use information (always
* prefixed by info) for intelligent tag closing and global attributes.
*
* For optimization, the definition generation may be moved to
* a maintenance script and stipulate that definition be created
* by a factory method that unserializes a serialized version of Definition.
* Customization would entail copying the maintenance script, making the
* necessary changes, generating the serialized object, and then hooking it
* in via the factory method. We would also offer a LiveDefinition for
* automatic recompilation, suggesting that we would have a DefinitionGenerator.
*/
class HTMLPurifier_HTMLDefinition
{
/**
* Associative array of element names to HTMLPurifier_ElementDef
* @public
*/
var $info = array();
/**
* Associative array of global attribute name to attribute definition.
* @public
*/
var $info_global_attr = array();
/**
* String name of parent element HTML will be going into.
* @public
*/
var $info_parent = 'div';
/**
* Definition for parent element, allows parent element to be a
* tag that's not allowed inside the HTML fragment.
* @public
*/
var $info_parent_def;
/**
* String name of element used to wrap inline elements in block context
* @note This is rarely used except for BLOCKQUOTEs in strict mode
* @public
*/
var $info_block_wrapper = 'p';
/**
* Associative array of deprecated tag name to HTMLPurifier_TagTransform
* @public
*/
var $info_tag_transform = array();
/**
* List of HTMLPurifier_AttrTransform to be performed before validation.
* @public
*/
var $info_attr_transform_pre = array();
/**
* List of HTMLPurifier_AttrTransform to be performed after validation/
* @public
*/
var $info_attr_transform_post = array();
/**
* Lookup table of flow elements
* @public
*/
var $info_flow_elements = array();
/**
* Boolean is a strict definition?
* @public
*/
var $strict;
/**
* Initializes the definition, the meat of the class.
*/
function setup($config) {
// some cached config values
$this->strict = $config->get('HTML', 'Strict');
//////////////////////////////////////////////////////////////////////
// info[] : initializes the definition objects
// if you attempt to define rules later on for a tag not in this array
// PHP will create an stdclass
$allowed_tags =
array(
'ins', 'del', 'blockquote', 'dd', 'li', 'div', 'em', 'strong',
'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym',
'q', 'sub', 'tt', 'sup', 'i', 'b', 'big', 'small',
'bdo', 'span', 'dt', 'p', 'h1', 'h2', 'h3', 'h4',
'h5', 'h6', 'ol', 'ul', 'dl', 'address', 'img', 'br', 'hr',
'pre', 'a', 'table', 'caption', 'thead', 'tfoot', 'tbody',
'colgroup', 'col', 'td', 'th', 'tr'
);
if (!$this->strict) {
$allowed_tags[] = 'u';
$allowed_tags[] = 's';
$allowed_tags[] = 'strike';
}
foreach ($allowed_tags as $tag) {
$this->info[$tag] = new HTMLPurifier_ElementDef();
}
//////////////////////////////////////////////////////////////////////
// info[]->child : defines allowed children for elements
// emulates the structure of the DTD
// however, these are condensed, with bad stuff taken out
// screening process was done by hand
// entities: prefixed with e_ and _ replaces . from DTD
// double underlines are entities we made up
// we don't use an array because that complicates interpolation
// strings are used instead of arrays because if you use arrays,
// you have to do some hideous manipulation with array_merge()
// todo: determine whether or not having allowed children
// that aren't allowed globally affects security (it shouldn't)
// if above works out, extend children definitions to include all
// possible elements (allowed elements will dictate which ones
// get dropped
$e_special_extra = 'img';
$e_special_basic = 'br | span | bdo';
$e_special = "$e_special_basic | $e_special_extra";
$e_fontstyle_extra = 'big | small';
$e_fontstyle_basic = 'tt | i | b | u | s | strike';
$e_fontstyle = "$e_fontstyle_basic | $e_fontstyle_extra";
$e_phrase_extra = 'sub | sup';
$e_phrase_basic = 'em | strong | dfn | code | q | samp | kbd | var'.
' | cite | abbr | acronym';
$e_phrase = "$e_phrase_basic | $e_phrase_extra";
$e_misc_inline = 'ins | del';
$e_misc = "$e_misc_inline";
$e_inline = "a | $e_special | $e_fontstyle | $e_phrase";
// pseudo-property we created for convenience, see later on
$e__inline = "#PCDATA | $e_inline | $e_misc_inline";
// note the casing
$e_Inline = new HTMLPurifier_ChildDef_Optional($e__inline);
$e_heading = 'h1|h2|h3|h4|h5|h6';
$e_lists = 'ul | ol | dl';
$e_blocktext = 'pre | hr | blockquote | address';
$e_block = "p | $e_heading | div | $e_lists | $e_blocktext | table";
$e_Block = new HTMLPurifier_ChildDef_Optional($e_block);
$e__flow = "#PCDATA | $e_block | $e_inline | $e_misc";
$e_Flow = new HTMLPurifier_ChildDef_Optional($e__flow);
$e_a_content = new HTMLPurifier_ChildDef_Optional("#PCDATA".
" | $e_special | $e_fontstyle | $e_phrase | $e_misc_inline");
$e_pre_content = new HTMLPurifier_ChildDef_Optional("#PCDATA | a".
" | $e_special_basic | $e_fontstyle_basic | $e_phrase_basic".
" | $e_misc_inline");
$e_form_content = new HTMLPurifier_ChildDef_Optional('');//unused
$e_form_button_content = new HTMLPurifier_ChildDef_Optional('');//unused
$this->info['ins']->child =
$this->info['del']->child =
new HTMLPurifier_ChildDef_Chameleon($e__inline, $e__flow);
$this->info['dd']->child =
$this->info['li']->child =
$this->info['div']->child = $e_Flow;
if ($this->strict) {
$this->info['blockquote']->child = new HTMLPurifier_ChildDef_StrictBlockquote();
} else {
$this->info['blockquote']->child = $e_Flow;
}
$this->info['caption']->child =
$this->info['em']->child =
$this->info['strong']->child =
$this->info['dfn']->child =
$this->info['code']->child =
$this->info['samp']->child =
$this->info['kbd']->child =
$this->info['var']->child =
$this->info['cite']->child =
$this->info['abbr']->child =
$this->info['acronym']->child =
$this->info['q']->child =
$this->info['sub']->child =
$this->info['tt']->child =
$this->info['sup']->child =
$this->info['i']->child =
$this->info['b']->child =
$this->info['big']->child =
$this->info['small']->child=
$this->info['bdo']->child =
$this->info['span']->child =
$this->info['dt']->child =
$this->info['p']->child =
$this->info['h1']->child =
$this->info['h2']->child =
$this->info['h3']->child =
$this->info['h4']->child =
$this->info['h5']->child =
$this->info['h6']->child = $e_Inline;
if (!$this->strict) {
$this->info['u']->child =
$this->info['s']->child =
$this->info['strike']->child = $e_Inline;
}
// the only three required definitions, besides custom table code
$this->info['ol']->child =
$this->info['ul']->child = new HTMLPurifier_ChildDef_Required('li');
$this->info['dl']->child = new HTMLPurifier_ChildDef_Required('dt|dd');
if ($this->strict) {
$this->info['address']->child = $e_Inline;
} else {
$this->info['address']->child =
new HTMLPurifier_ChildDef_Optional("#PCDATA | p | $e_inline".
" | $e_misc_inline");
}
$this->info['img']->child =
$this->info['br']->child =
$this->info['hr']->child = new HTMLPurifier_ChildDef_Empty();
$this->info['pre']->child = $e_pre_content;
$this->info['a']->child = $e_a_content;
$this->info['table']->child = new HTMLPurifier_ChildDef_Table();
// not a real entity, watch the double underscore
$e__row = new HTMLPurifier_ChildDef_Required('tr');
$this->info['thead']->child = $e__row;
$this->info['tfoot']->child = $e__row;
$this->info['tbody']->child = $e__row;
$this->info['colgroup']->child = new HTMLPurifier_ChildDef_Optional('col');
$this->info['col']->child = new HTMLPurifier_ChildDef_Empty();
$this->info['tr']->child = new HTMLPurifier_ChildDef_Required('th | td');
$this->info['th']->child = $e_Flow;
$this->info['td']->child = $e_Flow;
//////////////////////////////////////////////////////////////////////
// info[]->type : defines the type of the element (block or inline)
// reuses $e_Inline and $e_Block
foreach ($e_Inline->elements as $name => $bool) {
if ($name == '#PCDATA') continue;
if (!isset($this->info[$name])) continue;
$this->info[$name]->type = 'inline';
}
foreach ($e_Block->elements as $name => $bool) {
if (!isset($this->info[$name])) continue;
$this->info[$name]->type = 'block';
}
foreach ($e_Flow->elements as $name => $bool) {
$this->info_flow_elements[$name] = true;
}
//////////////////////////////////////////////////////////////////////
// info[]->excludes : defines elements that aren't allowed in here
// make sure you test using isset() and not !empty()
$this->info['a']->excludes = array('a' => true);
$this->info['pre']->excludes = array_flip(array('img', 'big', 'small',
// technically useless, but good to be indepth
'object', 'applet', 'font', 'basefont'));
//////////////////////////////////////////////////////////////////////
// info[]->attr : defines allowed attributes for elements
// this doesn't include REQUIRED declarations, those are handled
// by the transform classes. It will, however, do simple and slightly
// complex attribute value substitution
// the question of varying allowed attributes is more entangling.
$e_Text = new HTMLPurifier_AttrDef_Text();
// attrs, included in almost every single one except for a few,
// which manually override these in their local definitions
$this->info_global_attr = array(
// core attrs
'class' => new HTMLPurifier_AttrDef_Class(),
'title' => $e_Text,
'style' => new HTMLPurifier_AttrDef_CSS(),
// i18n
'dir' => new HTMLPurifier_AttrDef_Enum(array('ltr','rtl'), false),
'lang' => new HTMLPurifier_AttrDef_Lang(),
'xml:lang' => new HTMLPurifier_AttrDef_Lang(),
);
if ($config->get('HTML', 'EnableAttrID')) {
$this->info_global_attr['id'] = new HTMLPurifier_AttrDef_ID();
}
// required attribute stipulation handled in attribute transformation
$this->info['bdo']->attr = array(); // nothing else
$this->info['br']->attr['dir'] = false;
$this->info['br']->attr['lang'] = false;
$this->info['br']->attr['xml:lang'] = false;
$this->info['td']->attr['abbr'] = $e_Text;
$this->info['th']->attr['abbr'] = $e_Text;
$this->setAttrForTableElements('align', new HTMLPurifier_AttrDef_Enum(
array('left', 'center', 'right', 'justify', 'char'), false));
$this->setAttrForTableElements('valign', new HTMLPurifier_AttrDef_Enum(
array('top', 'middle', 'bottom', 'baseline'), false));
$this->info['img']->attr['alt'] = $e_Text;
$e_TFrame = new HTMLPurifier_AttrDef_Enum(array('void', 'above',
'below', 'hsides', 'lhs', 'rhs', 'vsides', 'box', 'border'), false);
$this->info['table']->attr['frame'] = $e_TFrame;
$e_TRules = new HTMLPurifier_AttrDef_Enum(array('none', 'groups',
'rows', 'cols', 'all'), false);
$this->info['table']->attr['rules'] = $e_TRules;
$this->info['table']->attr['summary'] = $e_Text;
$this->info['table']->attr['border'] =
new HTMLPurifier_AttrDef_Pixels();
$e_Length = new HTMLPurifier_AttrDef_Length();
$this->info['table']->attr['cellpadding'] =
$this->info['table']->attr['cellspacing'] =
$this->info['table']->attr['width'] =
$this->info['img']->attr['height'] =
$this->info['img']->attr['width'] = $e_Length;
$this->setAttrForTableElements('charoff', $e_Length);
$e_MultiLength = new HTMLPurifier_AttrDef_MultiLength();
$this->info['col']->attr['width'] =
$this->info['colgroup']->attr['width'] = $e_MultiLength;
$e__NumberSpan = new HTMLPurifier_AttrDef_Integer(false, false, true);
$this->info['colgroup']->attr['span'] =
$this->info['col']->attr['span'] =
$this->info['td']->attr['rowspan'] =
$this->info['th']->attr['rowspan'] =
$this->info['td']->attr['colspan'] =
$this->info['th']->attr['colspan'] = $e__NumberSpan;
if (!$config->get('Attr', 'DisableURI')) {
$e_URI = new HTMLPurifier_AttrDef_URI();
$this->info['a']->attr['href'] =
$this->info['img']->attr['longdesc'] =
$this->info['del']->attr['cite'] =
$this->info['ins']->attr['cite'] =
$this->info['blockquote']->attr['cite'] =
$this->info['q']->attr['cite'] = $e_URI;
// URI that causes HTTP request
$this->info['img']->attr['src'] = new HTMLPurifier_AttrDef_URI(true);
}
if (!$this->strict) {
$this->info['li']->attr['value'] = new HTMLPurifier_AttrDef_Integer();
$this->info['ol']->attr['start'] = new HTMLPurifier_AttrDef_Integer();
}
//////////////////////////////////////////////////////////////////////
// info_tag_transform : transformations of tags
$this->info_tag_transform['font'] = new HTMLPurifier_TagTransform_Font();
$this->info_tag_transform['menu'] = new HTMLPurifier_TagTransform_Simple('ul');
$this->info_tag_transform['dir'] = new HTMLPurifier_TagTransform_Simple('ul');
$this->info_tag_transform['center'] = new HTMLPurifier_TagTransform_Center();
//////////////////////////////////////////////////////////////////////
// info[]->auto_close : tags that automatically close another
// todo: determine whether or not SGML-like modeling based on
// mandatory/optional end tags would be a better policy
// make sure you test using isset() not !empty()
// these are all block elements: blocks aren't allowed in P
$this->info['p']->auto_close = array_flip(array(
'address', 'blockquote', 'dd', 'dir', 'div', 'dl', 'dt',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'ol', 'p', 'pre',
'table', 'ul'
));
$this->info['li']->auto_close = array('li' => true);
// we need TABLE and heading mismatch code
// we may need to make this more flexible for heading mismatch,
// or we can just create another info
//////////////////////////////////////////////////////////////////////
// info[]->attr_transform_* : attribute transformations in elements
// pre is applied before any validation is done, post is done after
$this->info['h1']->attr_transform_pre[] =
$this->info['h2']->attr_transform_pre[] =
$this->info['h3']->attr_transform_pre[] =
$this->info['h4']->attr_transform_pre[] =
$this->info['h5']->attr_transform_pre[] =
$this->info['h6']->attr_transform_pre[] =
$this->info['p'] ->attr_transform_pre[] =
new HTMLPurifier_AttrTransform_TextAlign();
$this->info['bdo']->attr_transform_post[] =
new HTMLPurifier_AttrTransform_BdoDir();
$this->info['img']->attr_transform_post[] =
new HTMLPurifier_AttrTransform_ImgRequired();
//////////////////////////////////////////////////////////////////////
// info_attr_transform_* : global attribute transformation that is
// unconditionally called. Good for transformations that have complex
// start conditions
// pre is applied before any validation is done, post is done after
$this->info_attr_transform_post[] = new HTMLPurifier_AttrTransform_Lang();
// protect against stdclasses floating around
foreach ($this->info as $key => $obj) {
if ($obj instanceof stdClass) {
unset($this->info[$key]);
}
}
//////////////////////////////////////////////////////////////////////
// info_block_wrapper : wraps inline elements in block context
$block_wrapper = $config->get('HTML', 'BlockWrapper');
if (isset($e_Block->elements[$block_wrapper])) {
$this->info_block_wrapper = $block_wrapper;
} else {
trigger_error('Cannot use non-block element as block wrapper.',
E_USER_ERROR);
}
//////////////////////////////////////////////////////////////////////
// info_parent : parent element of the HTML fragment
$parent = $config->get('HTML', 'Parent');
if (isset($this->info[$parent])) {
$this->info_parent = $parent;
} else {
trigger_error('Cannot use unrecognized element as parent.',
E_USER_ERROR);
}
$this->info_parent_def = $this->info[$this->info_parent];
//////////////////////////////////////////////////////////////////////
// %HTML.Allowed(Elements|Attributes) : cut non-allowed elements
$allowed_elements = $config->get('HTML', 'AllowedElements');
if (is_array($allowed_elements)) {
foreach ($this->info as $name => $d) {
if(!isset($allowed_elements[$name])) unset($this->info[$name]);
}
}
$allowed_attributes = $config->get('HTML', 'AllowedAttributes');
if (is_array($allowed_attributes)) {
foreach ($this->info_global_attr as $attr_key => $info) {
if (!isset($allowed_attributes["*.$attr_key"])) {
unset($this->info_global_attr[$attr_key]);
}
}
foreach ($this->info as $tag => $info) {
foreach ($info->attr as $attr => $attr_info) {
if (!isset($allowed_attributes["$tag.$attr"])) {
unset($this->info[$tag]->attr[$attr]);
}
}
}
}
}
function setAttrForTableElements($attr, $def) {
$this->info['col']->attr[$attr] =
$this->info['colgroup']->attr[$attr] =
$this->info['tbody']->attr[$attr] =
$this->info['td']->attr[$attr] =
$this->info['tfoot']->attr[$attr] =
$this->info['th']->attr[$attr] =
$this->info['thead']->attr[$attr] =
$this->info['tr']->attr[$attr] = $def;
}
}
/**
* Structure that stores an element definition.
*/
class HTMLPurifier_ElementDef
{
/**
* Associative array of attribute name to HTMLPurifier_AttrDef
* @public
*/
var $attr = array();
/**
* List of tag's HTMLPurifier_AttrTransform to be done before validation
* @public
*/
var $attr_transform_pre = array();
/**
* List of tag's HTMLPurifier_AttrTransform to be done after validation
* @public
*/
var $attr_transform_post = array();
/**
* Lookup table of tags that close this tag.
* @public
*/
var $auto_close = array();
/**
* HTMLPurifier_ChildDef of this tag.
* @public
*/
var $child;
/**
* Type of the tag: inline or block or unknown?
* @public
*/
var $type = 'unknown';
/**
* Lookup table of tags excluded from all descendants of this tag.
* @public
*/
var $excludes = array();
}
?>
<?php
// components
require_once 'HTMLPurifier/HTMLModuleManager.php';
// this definition and its modules MUST NOT define configuration directives
// outside of the HTML or Attr namespaces
// will be superceded by more accurate doctype declaration schemes
HTMLPurifier_ConfigSchema::define(
'HTML', 'Strict', false, 'bool',
'Determines whether or not to use Transitional (loose) or Strict rulesets. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'BlockWrapper', 'p', 'string',
'String name of element to wrap inline elements that are inside a block '.
'context. This only occurs in the children of blockquote in strict mode. '.
'Example: by default value, <code>&lt;blockquote&gt;Foo&lt;/blockquote&gt;</code> '.
'would become <code>&lt;blockquote&gt;&lt;p&gt;Foo&lt;/p&gt;&lt;/blockquote&gt;</code>. The '.
'<code>&lt;p&gt;</code> tags can be replaced '.
'with whatever you desire, as long as it is a block level element. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'Parent', 'div', 'string',
'String name of element that HTML fragment passed to library will be '.
'inserted in. An interesting variation would be using span as the '.
'parent element, meaning that only inline tags would be allowed. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'AllowedElements', null, 'lookup/null',
'If HTML Purifier\'s tag set is unsatisfactory for your needs, you '.
'can overload it with your own list of tags to allow. Note that this '.
'method is subtractive: it does its job by taking away from HTML Purifier '.
'usual feature set, so you cannot add a tag that HTML Purifier never '.
'supported in the first place (like embed, form or head). If you change this, you '.
'probably also want to change %HTML.AllowedAttributes. '.
'<strong>Warning:</strong> If another directive conflicts with the '.
'elements here, <em>that</em> directive will win and override. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'AllowedAttributes', null, 'lookup/null',
'IF HTML Purifier\'s attribute set is unsatisfactory, overload it! '.
'The syntax is \'tag.attr\' or \'*.attr\' for the global attributes '.
'(style, id, class, dir, lang, xml:lang).'.
'<strong>Warning:</strong> If another directive conflicts with the '.
'elements here, <em>that</em> directive will win and override. For '.
'example, %HTML.EnableAttrID will take precedence over *.id in this '.
'directive. You must set that directive to true before you can use '.
'IDs at all. This directive has been available since 1.3.0.'
);
/**
* Definition of the purified HTML that describes allowed children,
* attributes, and many other things.
*
* Conventions:
*
* All member variables that are prefixed with info
* (including the main $info array) are used by HTML Purifier internals
* and should not be directly edited when customizing the HTMLDefinition.
* They can usually be set via configuration directives or custom
* modules.
*
* On the other hand, member variables without the info prefix are used
* internally by the HTMLDefinition and MUST NOT be used by other HTML
* Purifier internals. Many of them, however, are public, and may be
* edited by userspace code to tweak the behavior of HTMLDefinition.
*
* HTMLPurifier_Printer_HTMLDefinition is a notable exception to this
* rule: in the interest of comprehensiveness, it will sniff everything.
*/
class HTMLPurifier_HTMLDefinition
{
/** FULLY-PUBLIC VARIABLES */
/**
* Associative array of element names to HTMLPurifier_ElementDef
* @public
*/
var $info = array();
/**
* Associative array of global attribute name to attribute definition.
* @public
*/
var $info_global_attr = array();
/**
* String name of parent element HTML will be going into.
* @public
*/
var $info_parent = 'div';
/**
* Definition for parent element, allows parent element to be a
* tag that's not allowed inside the HTML fragment.
* @public
*/
var $info_parent_def;
/**
* String name of element used to wrap inline elements in block context
* @note This is rarely used except for BLOCKQUOTEs in strict mode
* @public
*/
var $info_block_wrapper = 'p';
/**
* Associative array of deprecated tag name to HTMLPurifier_TagTransform
* @public
*/
var $info_tag_transform = array();
/**
* Indexed list of HTMLPurifier_AttrTransform to be performed before validation.
* @public
*/
var $info_attr_transform_pre = array();
/**
* Indexed list of HTMLPurifier_AttrTransform to be performed after validation.
* @public
*/
var $info_attr_transform_post = array();
/**
* Nested lookup array of content set name (Block, Inline) to
* element name to whether or not it belongs in that content set.
* @public
*/
var $info_content_sets = array();
/** PUBLIC BUT INTERNAL VARIABLES */
var $setup = false; /**< Has setup() been called yet? */
var $config; /**< Temporary instance of HTMLPurifier_Config */
var $manager; /**< Instance of HTMLPurifier_HTMLModuleManager */
/**
* Performs low-cost, preliminary initialization.
* @param $config Instance of HTMLPurifier_Config
*/
function HTMLPurifier_HTMLDefinition(&$config) {
$this->config =& $config;
$this->manager = new HTMLPurifier_HTMLModuleManager();
}
/**
* Processes internals into form usable by HTMLPurifier internals.
* Modifying the definition after calling this function should not
* be done.
*/
function setup() {
// multiple call guard
if ($this->setup) {return;} else {$this->setup = true;}
$this->processModules();
$this->setupConfigStuff();
unset($this->config);
unset($this->manager);
}
/**
* Extract out the information from the manager
*/
function processModules() {
$this->manager->setup($this->config);
foreach ($this->manager->activeModules as $module) {
foreach($module->info_tag_transform as $k => $v) $this->info_tag_transform[$k] = $v;
foreach($module->info_attr_transform_pre as $k => $v) $this->info_attr_transform_pre[$k] = $v;
foreach($module->info_attr_transform_post as $k => $v) $this->info_attr_transform_post[$k]= $v;
}
$this->info = $this->manager->getElements($this->config);
$this->info_content_sets = $this->manager->contentSets->lookup;
}
/**
* Sets up stuff based on config. We need a better way of doing this.
*/
function setupConfigStuff() {
$block_wrapper = $this->config->get('HTML', 'BlockWrapper');
if (isset($this->info_content_sets['Block'][$block_wrapper])) {
$this->info_block_wrapper = $block_wrapper;
} else {
trigger_error('Cannot use non-block element as block wrapper.',
E_USER_ERROR);
}
$parent = $this->config->get('HTML', 'Parent');
$def = $this->manager->getElement($parent, $this->config);
if ($def) {
$this->info_parent = $parent;
$this->info_parent_def = $def;
} else {
trigger_error('Cannot use unrecognized element as parent.',
E_USER_ERROR);
$this->info_parent_def = $this->manager->getElement(
$this->info_parent, $this->config);
}
// setup allowed elements, SubtractiveWhitelist module
$allowed_elements = $this->config->get('HTML', 'AllowedElements');
if (is_array($allowed_elements)) {
foreach ($this->info as $name => $d) {
if(!isset($allowed_elements[$name])) unset($this->info[$name]);
}
}
$allowed_attributes = $this->config->get('HTML', 'AllowedAttributes');
if (is_array($allowed_attributes)) {
foreach ($this->info_global_attr as $attr_key => $info) {
if (!isset($allowed_attributes["*.$attr_key"])) {
unset($this->info_global_attr[$attr_key]);
}
}
foreach ($this->info as $tag => $info) {
foreach ($info->attr as $attr => $attr_info) {
if (!isset($allowed_attributes["$tag.$attr"]) &&
!isset($allowed_attributes["*.$attr"])) {
unset($this->info[$tag]->attr[$attr]);
}
}
}
}
}
}
?>

View File

@@ -0,0 +1,125 @@
<?php
/**
* Represents an XHTML 1.1 module, with information on elements, tags
* and attributes.
* @note Even though this is technically XHTML 1.1, it is also used for
* regular HTML parsing. We are using modulization as a convenient
* way to represent the internals of HTMLDefinition, and our
* implementation is by no means conforming and does not directly
* use the normative DTDs or XML schemas.
* @note The public variables in a module should almost directly
* correspond to the variables in HTMLPurifier_HTMLDefinition.
* However, the prefix info carries no special meaning in these
* objects (include it anyway if that's the correspondence though).
*/
class HTMLPurifier_HTMLModule
{
/**
* Short unique string identifier of the module
*/
var $name;
/**
* Dynamically set integer that specifies when the module was loaded in.
*/
var $order;
/**
* Informally, a list of elements this module changes. Not used in
* any significant way.
* @protected
*/
var $elements = array();
/**
* Associative array of element names to element definitions.
* Some definitions may be incomplete, to be merged in later
* with the full definition.
* @public
*/
var $info = array();
/**
* Associative array of content set names to content set additions.
* This is commonly used to, say, add an A element to the Inline
* content set. This corresponds to an internal variable $content_sets
* and NOT info_content_sets member variable of HTMLDefinition.
* @public
*/
var $content_sets = array();
/**
* Associative array of attribute collection names to attribute
* collection additions. More rarely used for adding attributes to
* the global collections. Example is the StyleAttribute module adding
* the style attribute to the Core. Corresponds to HTMLDefinition's
* attr_collections->info, since the object's data is only info,
* with extra behavior associated with it.
* @public
*/
var $attr_collections = array();
/**
* Associative array of deprecated tag name to HTMLPurifier_TagTransform
* @public
*/
var $info_tag_transform = array();
/**
* List of HTMLPurifier_AttrTransform to be performed before validation.
* @public
*/
var $info_attr_transform_pre = array();
/**
* List of HTMLPurifier_AttrTransform to be performed after validation.
* @public
*/
var $info_attr_transform_post = array();
/**
* Boolean flag that indicates whether or not getChildDef is implemented.
* For optimization reasons: may save a call to a function. Be sure
* to set it if you do implement getChildDef(), otherwise it will have
* no effect!
* @public
*/
var $defines_child_def = false;
/**
* Retrieves a proper HTMLPurifier_ChildDef subclass based on
* content_model and content_model_type member variables of
* the HTMLPurifier_ElementDef class. There is a similar function
* in HTMLPurifier_HTMLDefinition.
* @param $def HTMLPurifier_ElementDef instance
* @return HTMLPurifier_ChildDef subclass
* @public
*/
function getChildDef($def) {return false;}
/**
* Hook method that lets module perform arbitrary operations on
* HTMLPurifier_HTMLDefinition before the module gets processed.
* @param $definition Reference to HTMLDefinition being setup
*/
function preProcess(&$definition) {}
/**
* Hook method that lets module perform arbitrary operations
* on HTMLPurifier_HTMLDefinition after the module gets processed.
* @param $definition Reference to HTMLDefinition being setup
*/
function postProcess(&$definition) {}
/**
* Hook method that is called when a module gets registered to
* the definition.
* @param $definition Reference to HTMLDefinition being setup
*/
function setup(&$definition) {}
}
?>

View File

@@ -0,0 +1,43 @@
<?php
require_once 'HTMLPurifier/HTMLModule.php';
require_once 'HTMLPurifier/AttrTransform/BdoDir.php';
/**
* XHTML 1.1 Bi-directional Text Module, defines elements that
* declare directionality of content. Text Extension Module.
*/
class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule
{
var $name = 'Bdo';
var $elements = array('bdo');
var $info = array();
var $content_sets = array('Inline' => 'bdo');
var $attr_collections = array(
'I18N' => array('dir' => false)
);
function HTMLPurifier_HTMLModule_Bdo() {
$dir = new HTMLPurifier_AttrDef_Enum(array('ltr','rtl'), false);
$this->attr_collections['I18N']['dir'] = $dir;
$this->info['bdo'] = new HTMLPurifier_ElementDef();
$this->info['bdo']->attr = array(
0 => array('Core', 'Lang'),
'dir' => $dir, // required
// The Abstract Module specification has the attribute
// inclusions wrong for bdo: bdo allows
// xml:lang too (and we'll toss in lang for good measure,
// though it is not allowed for XHTML 1.1, this will
// be managed with a global attribute transform)
);
$this->info['bdo']->content_model = '#PCDATA | Inline';
$this->info['bdo']->content_model_type = 'optional';
// provides fallback behavior if dir's missing (dir is required)
$this->info['bdo']->attr_transform_post['required-dir'] =
new HTMLPurifier_AttrTransform_BdoDir();
}
}
?>

View File

@@ -0,0 +1,31 @@
<?php
class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule
{
var $name = 'CommonAttributes';
var $attr_collections = array(
'Core' => array(
0 => array('Style'),
// 'xml:space' => false,
'class' => 'NMTOKENS',
'id' => 'ID',
'title' => 'CDATA',
),
'Lang' => array(
'xml:lang' => false, // see constructor
),
'I18N' => array(
0 => array('Lang'), // proprietary, for xml:lang/lang
),
'Common' => array(
0 => array('Core', 'I18N')
)
);
function HTMLPurifier_HTMLModule_CommonAttributes() {
$this->attr_collections['Lang']['xml:lang'] = new HTMLPurifier_AttrDef_Lang();
}
}
?>

View File

@@ -0,0 +1,46 @@
<?php
require_once 'HTMLPurifier/HTMLModule.php';
require_once 'HTMLPurifier/ChildDef/Chameleon.php';
/**
* XHTML 1.1 Edit Module, defines editing-related elements. Text Extension
* Module.
*/
class HTMLPurifier_HTMLModule_Edit extends HTMLPurifier_HTMLModule
{
var $name = 'Edit';
var $elements = array('del', 'ins');
var $info = array();
var $content_sets = array('Inline' => 'del | ins');
function HTMLPurifier_HTMLModule_Edit() {
foreach ($this->elements as $element) {
$this->info[$element] = new HTMLPurifier_ElementDef();
$this->info[$element]->attr = array(
0 => array('Common'),
'cite' => 'URI',
// 'datetime' => 'Datetime' // Datetime not implemented
);
// Inline context ! Block context (exclamation mark is
// separator, see getChildDef for parsing)
$this->info[$element]->content_model =
'#PCDATA | Inline ! #PCDATA | Flow';
// HTML 4.01 specifies that ins/del must not contain block
// elements when used in an inline context, chameleon is
// a complicated workaround to acheive this effect
$this->info[$element]->content_model_type = 'chameleon';
}
}
var $defines_child_def = true;
function getChildDef($def) {
if ($def->content_model_type != 'chameleon') return false;
$value = explode('!', $def->content_model);
return new HTMLPurifier_ChildDef_Chameleon($value[0], $value[1]);
}
}
?>

View File

@@ -0,0 +1,36 @@
<?php
require_once 'HTMLPurifier/HTMLModule.php';
/**
* XHTML 1.1 Hypertext Module, defines hypertext links. Core Module.
*/
class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule
{
var $name = 'Hypertext';
var $elements = array('a');
var $info = array();
var $content_sets = array('Inline' => 'a');
function HTMLPurifier_HTMLModule_Hypertext() {
$this->info['a'] = new HTMLPurifier_ElementDef();
$this->info['a']->attr = array(
0 => array('Common'),
// 'accesskey' => 'Character',
// 'charset' => 'Charset',
'href' => 'URI',
//'hreflang' => 'LanguageCode',
//'rel' => 'LinkTypes',
//'rev' => 'LinkTypes',
//'tabindex' => 'Number',
//'type' => 'ContentType',
);
$this->info['a']->content_model = '#PCDATA | Inline';
$this->info['a']->content_model_type = 'optional';
$this->info['a']->excludes = array('a' => true);
}
}
?>

View File

@@ -0,0 +1,38 @@
<?php
require_once 'HTMLPurifier/HTMLModule.php';
require_once 'HTMLPurifier/AttrDef/URI.php';
require_once 'HTMLPurifier/AttrTransform/ImgRequired.php';
/**
* XHTML 1.1 Image Module provides basic image embedding.
* @note There is specialized code for removing empty images in
* HTMLPurifier_Strategy_RemoveForeignElements
*/
class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule
{
var $name = 'Image';
var $elements = array('img');
var $info = array();
var $content_sets = array('Inline' => 'img');
function HTMLPurifier_HTMLModule_Image() {
$this->info['img'] = new HTMLPurifier_ElementDef();
$this->info['img']->attr = array(
0 => array('Common'),
'alt' => 'Text',
'height' => 'Length',
'longdesc' => 'URI',
'src' => new HTMLPurifier_AttrDef_URI(true), // embedded
'width' => 'Length'
);
$this->info['img']->content_model_type = 'empty';
$this->info['img']->attr_transform_post[] =
new HTMLPurifier_AttrTransform_ImgRequired();
}
}
?>

View File

@@ -0,0 +1,60 @@
<?php
/**
* XHTML 1.1 Legacy module defines elements that were previously
* deprecated.
*
* @note Not all legacy elements have been implemented yet, which
* is a bit of a reverse problem as compared to browsers! In
* addition, this legacy module may implement a bit more than
* mandated by XHTML 1.1.
*
* This module can be used in combination with TransformToStrict in order
* to transform as many deprecated elements as possible, but retain
* questionably deprecated elements that do not have good alternatives
* as well as transform elements that don't have an implementation.
* See docs/ref-strictness.txt for more details.
*/
class HTMLPurifier_HTMLModule_Legacy extends HTMLPurifier_HTMLModule
{
// incomplete
var $name = 'Legacy';
var $elements = array('u', 's', 'strike');
var $non_standalone_elements = array('li', 'ol', 'address', 'blockquote');
function HTMLPurifier_HTMLModule_Legacy() {
// setup new elements
foreach ($this->elements as $name) {
$this->info[$name] = new HTMLPurifier_ElementDef();
// for u, s, strike, as more elements get added, add
// conditionals as necessary
$this->info[$name]->content_model = 'Inline | #PCDATA';
$this->info[$name]->content_model_type = 'optional';
$this->info[$name]->attr[0] = array('Common');
}
// setup modifications to old elements
foreach ($this->non_standalone_elements as $name) {
$this->info[$name] = new HTMLPurifier_ElementDef();
$this->info[$name]->standalone = false;
}
$this->info['li']->attr['value'] = new HTMLPurifier_AttrDef_Integer();
$this->info['ol']->attr['start'] = new HTMLPurifier_AttrDef_Integer();
$this->info['address']->content_model = 'Inline | #PCDATA | p';
$this->info['address']->content_model_type = 'optional';
$this->info['address']->child = false;
$this->info['blockquote']->content_model = 'Flow | #PCDATA';
$this->info['blockquote']->content_model_type = 'optional';
$this->info['blockquote']->child = false;
}
}
?>

View File

@@ -0,0 +1,46 @@
<?php
require_once 'HTMLPurifier/HTMLModule.php';
/**
* XHTML 1.1 List Module, defines list-oriented elements. Core Module.
*/
class HTMLPurifier_HTMLModule_List extends HTMLPurifier_HTMLModule
{
var $name = 'List';
var $elements = array('dl', 'dt', 'dd', 'ol', 'ul', 'li');
var $info = array();
// According to the abstract schema, the List content set is a fully formed
// one or more expr, but it invariably occurs in an optional declaration
// so we're not going to do that subtlety. It might cause trouble
// if a user defines "List" and expects that multiple lists are
// allowed to be specified, but then again, that's not very intuitive.
// Furthermore, the actual XML Schema may disagree. Regardless,
// we don't have support for such nested expressions without using
// the incredibly inefficient and draconic Custom ChildDef.
var $content_sets = array('List' => 'dl | ol | ul', 'Flow' => 'List');
function HTMLPurifier_HTMLModule_List() {
foreach ($this->elements as $element) {
$this->info[$element] = new HTMLPurifier_ElementDef();
$this->info[$element]->attr = array(0 => array('Common'));
if ($element == 'li' || $element == 'dd') {
$this->info[$element]->content_model = '#PCDATA | Flow';
$this->info[$element]->content_model_type = 'optional';
} elseif ($element == 'ol' || $element == 'ul') {
$this->info[$element]->content_model = 'li';
$this->info[$element]->content_model_type = 'required';
}
}
$this->info['dt']->content_model = '#PCDATA | Inline';
$this->info['dt']->content_model_type = 'optional';
$this->info['dl']->content_model = 'dt | dd';
$this->info['dl']->content_model_type = 'required';
// this could be a LOT more robust
$this->info['li']->auto_close = array('li' => true);
}
}
?>

View File

@@ -0,0 +1,41 @@
<?php
require_once 'HTMLPurifier/HTMLModule.php';
/**
* XHTML 1.1 Presentation Module, defines simple presentation-related
* markup. Text Extension Module.
* @note The official XML Schema and DTD specs further divide this into
* two modules:
* - Block Presentation (hr)
* - Inline Presentation (b, big, i, small, sub, sup, tt)
* We have chosen not to heed this distinction, as content_sets
* provides satisfactory disambiguation.
*/
class HTMLPurifier_HTMLModule_Presentation extends HTMLPurifier_HTMLModule
{
var $name = 'Presentation';
var $elements = array('b', 'big', 'hr', 'i', 'small', 'sub', 'sup', 'tt');
var $info = array();
var $content_sets = array(
'Block' => 'hr',
'Inline' => 'b | big | i | small | sub | sup | tt'
);
function HTMLPurifier_HTMLModule_Presentation() {
foreach ($this->elements as $element) {
$this->info[$element] = new HTMLPurifier_ElementDef();
$this->info[$element]->attr = array(0 => array('Common'));
if ($element == 'hr') {
$this->info[$element]->content_model_type = 'empty';
} else {
$this->info[$element]->content_model = '#PCDATA | Inline';
$this->info[$element]->content_model_type = 'optional';
}
}
}
}
?>

View File

@@ -0,0 +1,27 @@
<?php
require_once 'HTMLPurifier/HTMLModule.php';
require_once 'HTMLPurifier/AttrDef/CSS.php';
/**
* XHTML 1.1 Edit Module, defines editing-related elements. Text Extension
* Module.
*/
class HTMLPurifier_HTMLModule_StyleAttribute extends HTMLPurifier_HTMLModule
{
var $name = 'StyleAttribute';
var $attr_collections = array(
// The inclusion routine differs from the Abstract Modules but
// is in line with the DTD and XML Schemas.
'Style' => array('style' => false), // see constructor
'Core' => array(0 => array('Style'))
);
function HTMLPurifier_HTMLModule_StyleAttribute() {
$this->attr_collections['Style']['style'] = new HTMLPurifier_AttrDef_CSS();
}
}
?>

View File

@@ -0,0 +1,88 @@
<?php
require_once 'HTMLPurifier/HTMLModule.php';
require_once 'HTMLPurifier/ChildDef/Table.php';
/**
* XHTML 1.1 Tables Module, fully defines accessible table elements.
*/
class HTMLPurifier_HTMLModule_Tables extends HTMLPurifier_HTMLModule
{
var $name = 'Tables';
var $elements = array('caption', 'table', 'td', 'th', 'tr', 'col',
'colgroup', 'tbody', 'thead', 'tfoot');
var $info = array();
var $content_sets = array('Block' => 'table');
function HTMLPurifier_HTMLModule_Tables() {
foreach ($this->elements as $e) {
$this->info[$e] = new HTMLPurifier_ElementDef();
$this->info[$e]->attr = array(0 => array('Common'));
$attr =& $this->info[$e]->attr;
if ($e == 'caption') continue;
if ($e == 'table'){
$attr['border'] = 'Pixels';
$attr['cellpadding'] = 'Length';
$attr['cellspacing'] = 'Length';
$attr['frame'] = new HTMLPurifier_AttrDef_Enum(array(
'void', 'above', 'below', 'hsides', 'lhs', 'rhs',
'vsides', 'box', 'border'
), false);
$attr['rules'] = new HTMLPurifier_AttrDef_Enum(array(
'none', 'groups', 'rows', 'cols', 'all'
), false);
$attr['summary'] = 'Text';
$attr['width'] = 'Length';
continue;
}
if ($e == 'col' || $e == 'colgroup') {
$attr['span'] = 'Number';
$attr['width'] = 'MultiLength';
}
if ($e == 'td' || $e == 'th') {
$attr['abbr'] = 'Text';
$attr['colspan'] = 'Number';
$attr['rowspan'] = 'Number';
}
$attr['align'] = new HTMLPurifier_AttrDef_Enum(array(
'left', 'center', 'right', 'justify', 'char'
), false);
$attr['valign'] = new HTMLPurifier_AttrDef_Enum(array(
'top', 'middle', 'bottom', 'baseline'
), false);
$attr['charoff'] = 'Length';
}
$this->info['caption']->content_model = '#PCDATA | Inline';
$this->info['caption']->content_model_type = 'optional';
// Is done directly because it doesn't leverage substitution
// mechanisms. True model is:
// 'caption?, ( col* | colgroup* ), (( thead?, tfoot?, tbody+ ) | ( tr+ ))'
$this->info['table']->child = new HTMLPurifier_ChildDef_Table();
$this->info['td']->content_model =
$this->info['th']->content_model = '#PCDATA | Flow';
$this->info['td']->content_model_type =
$this->info['th']->content_model_type = 'optional';
$this->info['tr']->content_model = 'td | th';
$this->info['tr']->content_model_type = 'required';
$this->info['col']->content_model_type = 'empty';
$this->info['colgroup']->content_model = 'col';
$this->info['colgroup']->content_model_type = 'optional';
$this->info['tbody']->content_model =
$this->info['thead']->content_model =
$this->info['tfoot']->content_model = 'tr';
$this->info['tbody']->content_model_type =
$this->info['thead']->content_model_type =
$this->info['tfoot']->content_model_type = 'required';
}
}
?>

View File

@@ -0,0 +1,78 @@
<?php
require_once 'HTMLPurifier/HTMLModule.php';
/**
* XHTML 1.1 Text Module, defines basic text containers. Core Module.
* @note In the normative XML Schema specification, this module
* is further abstracted into the following modules:
* - Block Phrasal (address, blockquote, pre, h1, h2, h3, h4, h5, h6)
* - Block Structural (div, p)
* - Inline Phrasal (abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var)
* - Inline Structural (br, span)
* We have elected not to follow suite, but this may change.
*/
class HTMLPurifier_HTMLModule_Text extends HTMLPurifier_HTMLModule
{
var $name = 'Text';
var $elements = array('abbr', 'acronym', 'address', 'blockquote',
'br', 'cite', 'code', 'dfn', 'div', 'em', 'h1', 'h2', 'h3',
'h4', 'h5', 'h6', 'kbd', 'p', 'pre', 'q', 'samp', 'span', 'strong',
'var');
var $info = array();
var $content_sets = array(
'Heading' => 'h1 | h2 | h3 | h4 | h5 | h6',
'Block' => 'address | blockquote | div | p | pre',
'Inline' => 'abbr | acronym | br | cite | code | dfn | em | kbd | q | samp | span | strong | var',
'Flow' => 'Heading | Block | Inline'
);
function HTMLPurifier_HTMLModule_Text() {
foreach ($this->elements as $element) {
$this->info[$element] = new HTMLPurifier_ElementDef();
// attributes
if ($element == 'br') {
$this->info[$element]->attr = array(0 => array('Core'));
} elseif ($element == 'blockquote' || $element == 'q') {
$this->info[$element]->attr = array(0 => array('Common'), 'cite' => 'URI');
} else {
$this->info[$element]->attr = array(0 => array('Common'));
}
// content models
if ($element == 'br') {
$this->info[$element]->content_model_type = 'empty';
} elseif ($element == 'blockquote') {
$this->info[$element]->content_model = 'Heading | Block | List';
$this->info[$element]->content_model_type = 'optional';
} elseif ($element == 'div') {
$this->info[$element]->content_model = '#PCDATA | Flow';
$this->info[$element]->content_model_type = 'optional';
} else {
$this->info[$element]->content_model = '#PCDATA | Inline';
$this->info[$element]->content_model_type = 'optional';
}
}
// SGML permits exclusions for all descendants, but this is
// not possible with DTDs or XML Schemas. W3C has elected to
// use complicated compositions of content_models to simulate
// exclusion for children, but we go the simpler, SGML-style
// route of flat-out exclusions. Note that the Abstract Module
// is blithely unaware of such distinctions.
$this->info['pre']->excludes = array_flip(array(
'img', 'big', 'small',
'object', 'applet', 'font', 'basefont' // generally not allowed
));
$this->info['p']->auto_close = array_flip(array(
'address', 'blockquote', 'dd', 'dir', 'div', 'dl', 'dt',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'ol', 'p', 'pre',
'table', 'ul'
));
}
}
?>

View File

@@ -0,0 +1,86 @@
<?php
require_once 'HTMLPurifier/ChildDef/StrictBlockquote.php';
require_once 'HTMLPurifier/TagTransform/Simple.php';
require_once 'HTMLPurifier/TagTransform/Center.php';
require_once 'HTMLPurifier/TagTransform/Font.php';
require_once 'HTMLPurifier/AttrTransform/Lang.php';
require_once 'HTMLPurifier/AttrTransform/TextAlign.php';
/**
* Proprietary module that transforms deprecated elements into Strict
* HTML (see HTML 4.01 and XHTML 1.0) when possible.
*/
class HTMLPurifier_HTMLModule_TransformToStrict extends HTMLPurifier_HTMLModule
{
var $name = 'TransformToStrict';
// we're actually modifying these elements, not defining them
var $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'blockquote');
var $info_tag_transform = array(
// placeholders, see constructor for definitions
'font' => false,
'menu' => false,
'dir' => false,
'center'=> false
);
var $attr_collections = array(
'Lang' => array(
'lang' => false // placeholder
)
);
var $info_attr_transform_post = array(
'lang' => false // placeholder
);
function HTMLPurifier_HTMLModule_TransformToStrict() {
// deprecated tag transforms
$this->info_tag_transform['font'] = new HTMLPurifier_TagTransform_Font();
$this->info_tag_transform['menu'] = new HTMLPurifier_TagTransform_Simple('ul');
$this->info_tag_transform['dir'] = new HTMLPurifier_TagTransform_Simple('ul');
$this->info_tag_transform['center'] = new HTMLPurifier_TagTransform_Center();
foreach ($this->elements as $name) {
$this->info[$name] = new HTMLPurifier_ElementDef();
$this->info[$name]->standalone = false;
}
// deprecated attribute transforms
$this->info['h1']->attr_transform_pre['align'] =
$this->info['h2']->attr_transform_pre['align'] =
$this->info['h3']->attr_transform_pre['align'] =
$this->info['h4']->attr_transform_pre['align'] =
$this->info['h5']->attr_transform_pre['align'] =
$this->info['h6']->attr_transform_pre['align'] =
$this->info['p'] ->attr_transform_pre['align'] =
new HTMLPurifier_AttrTransform_TextAlign();
// xml:lang <=> lang mirroring, implement in TransformToStrict,
// this is overridden in TransformToXHTML11
$this->info_attr_transform_post['lang'] = new HTMLPurifier_AttrTransform_Lang();
$this->attr_collections['Lang']['lang'] = new HTMLPurifier_AttrDef_Lang();
// this should not be applied to XHTML 1.0 Transitional, ONLY
// XHTML 1.0 Strict. We may need three classes
$this->info['blockquote']->content_model_type = 'strictblockquote';
$this->info['blockquote']->child = false; // recalculate please!
}
var $defines_child_def = true;
function getChildDef($def) {
if ($def->content_model_type != 'strictblockquote') return false;
return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model);
}
}
?>

View File

@@ -0,0 +1,30 @@
<?php
/**
* Proprietary module that transforms XHTML 1.0 deprecated aspects into
* XHTML 1.1 compliant ones, when possible. For maximum effectiveness,
* HTMLPurifier_HTMLModule_TransformToStrict must also be loaded
* (otherwise, elements that were deprecated from Transitional to Strict
* will not be transformed).
*
* XHTML 1.1 compliant document are automatically XHTML 1.0 compliant too,
* although they may not be as friendly to legacy browsers.
*/
class HTMLPurifier_HTMLModule_TransformToXHTML11 extends HTMLPurifier_HTMLModule
{
var $name = 'TransformToXHTML11';
var $attr_collections = array(
'Lang' => array(
'lang' => false // remove it
)
);
var $info_attr_transform_post = array(
'lang' => false // remove it
);
}
?>

View File

@@ -0,0 +1,558 @@
<?php
require_once 'HTMLPurifier/HTMLModule.php';
require_once 'HTMLPurifier/ElementDef.php';
require_once 'HTMLPurifier/ContentSets.php';
require_once 'HTMLPurifier/AttrTypes.php';
require_once 'HTMLPurifier/AttrCollections.php';
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Enum.php';
// W3C modules
require_once 'HTMLPurifier/HTMLModule/CommonAttributes.php';
require_once 'HTMLPurifier/HTMLModule/Text.php';
require_once 'HTMLPurifier/HTMLModule/Hypertext.php';
require_once 'HTMLPurifier/HTMLModule/List.php';
require_once 'HTMLPurifier/HTMLModule/Presentation.php';
require_once 'HTMLPurifier/HTMLModule/Edit.php';
require_once 'HTMLPurifier/HTMLModule/Bdo.php';
require_once 'HTMLPurifier/HTMLModule/Tables.php';
require_once 'HTMLPurifier/HTMLModule/Image.php';
require_once 'HTMLPurifier/HTMLModule/StyleAttribute.php';
require_once 'HTMLPurifier/HTMLModule/Legacy.php';
// proprietary modules
require_once 'HTMLPurifier/HTMLModule/TransformToStrict.php';
require_once 'HTMLPurifier/HTMLModule/TransformToXHTML11.php';
HTMLPurifier_ConfigSchema::define(
'HTML', 'Doctype', null, 'string/null',
'Doctype to use, valid values are HTML 4.01 Transitional, HTML 4.01 '.
'Strict, XHTML 1.0 Transitional, XHTML 1.0 Strict, XHTML 1.1. '.
'Technically speaking this is not actually a doctype (as it does '.
'not identify a corresponding DTD), but we are using this name '.
'for sake of simplicity. This will override any older directives '.
'like %Core.XHTML or %HTML.Strict.'
);
class HTMLPurifier_HTMLModuleManager
{
/**
* Array of HTMLPurifier_Module instances, indexed by module's class name.
* All known modules, regardless of use, are in this array.
*/
var $modules = array();
/**
* String doctype we will validate against. See $validModules for use.
*
* @note
* There is a special doctype '*' that acts both as the "default"
* doctype if a customized system only defines one doctype and
* also a catch-all doctype that gets merged into all the other
* module collections. When possible, use a private collection to
* share modules between doctypes: this special doctype is to
* make life more convenient for users.
*/
var $doctype;
var $doctypeAliases = array(); /**< Lookup array of strings to real doctypes */
/**
* Associative array: $collections[$type][$doctype] = list of modules.
* This is used to logically separate types of functionality so that
* based on the doctype and other configuration settings they may
* be easily switched and on and off. Custom setups may not need
* to use this abstraction, opting to have only one big collection
* with one valid doctype.
*/
var $collections = array();
/**
* Modules that may be used in a valid doctype of this kind.
* Correctional and leniency modules should not be placed in this
* array unless the user said so: don't stuff every possible lenient
* module for this doctype in here.
*/
var $validModules = array();
var $validCollections = array(); /**< Collections to merge into $validModules */
/**
* Modules that we will allow in input, subset of $validModules. Single
* element definitions may result in us consulting validModules.
*/
var $activeModules = array();
var $activeCollections = array(); /**< Collections to merge into $activeModules */
var $counter = 0; /**< Designates next available integer order for modules. */
var $initialized = false; /**< Says whether initialize() was called */
/**
* Specifies what doctype to siphon new modules from addModule() to,
* or false to disable the functionality. Must be used in conjunction
* with $autoCollection.
*/
var $autoDoctype = false;
/**
* Specifies what collection to siphon new modules from addModule() to,
* or false to disable the functionality. Must be used in conjunction
* with $autoCollection.
*/
var $autoCollection = false;
/** Associative array of element name to defining modules (always array) */
var $elementLookup = array();
/** List of prefixes we should use for resolving small names */
var $prefixes = array('HTMLPurifier_HTMLModule_');
var $contentSets; /**< Instance of HTMLPurifier_ContentSets */
var $attrTypes; /**< Instance of HTMLPurifier_AttrTypes */
var $attrCollections; /**< Instance of HTMLPurifier_AttrCollections */
/**
* @param $blank If true, don't do any initializing
*/
function HTMLPurifier_HTMLModuleManager($blank = false) {
// the only editable internal object. The rest need to
// be manipulated through modules
$this->attrTypes = new HTMLPurifier_AttrTypes();
if (!$blank) $this->initialize();
}
function initialize() {
$this->initialized = true;
// load default modules to the recognized modules list (not active)
$modules = array(
// define
'CommonAttributes',
'Text', 'Hypertext', 'List', 'Presentation',
'Edit', 'Bdo', 'Tables', 'Image', 'StyleAttribute',
// define-redefine
'Legacy',
// redefine
'TransformToStrict', 'TransformToXHTML11'
);
foreach ($modules as $module) {
$this->addModule($module);
}
// Safe modules for supported doctypes. These are included
// in the valid and active module lists by default
$this->collections['Safe'] = array(
'_Common' => array( // leading _ indicates private
'CommonAttributes', 'Text', 'Hypertext', 'List',
'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
'StyleAttribute'
),
// HTML definitions, defer to XHTML definitions
'HTML 4.01 Transitional' => array(array('XHTML 1.0 Transitional')),
'HTML 4.01 Strict' => array(array('XHTML 1.0 Strict')),
// XHTML definitions
'XHTML 1.0 Transitional' => array( array('XHTML 1.0 Strict'), 'Legacy' ),
'XHTML 1.0 Strict' => array(array('_Common')),
'XHTML 1.1' => array(array('_Common')),
);
// Modules that specify elements that are unsafe from untrusted
// third-parties. These should be registered in $validModules but
// almost never $activeModules unless you really know what you're
// doing.
$this->collections['Unsafe'] = array();
// Modules to import if lenient mode (attempt to convert everything
// to a valid representation) is on. These must not be in $validModules
// unless specified so.
$this->collections['Lenient'] = array(
'HTML 4.01 Strict' => array(array('XHTML 1.0 Strict')),
'XHTML 1.0 Strict' => array('TransformToStrict'),
'XHTML 1.1' => array(array('XHTML 1.0 Strict'), 'TransformToXHTML11')
);
// Modules to import if correctional mode (correct everything that
// is feasible to strict mode) is on. These must not be in $validModules
// unless specified so.
$this->collections['Correctional'] = array(
'HTML 4.01 Transitional' => array(array('XHTML 1.0 Transitional')),
'XHTML 1.0 Transitional' => array('TransformToStrict'), // probably want a different one
);
// User-space modules, custom code or whatever
$this->collections['Extension'] = array();
// setup active versus valid modules. ORDER IS IMPORTANT!
// definition modules
$this->makeCollectionActive('Safe');
$this->makeCollectionValid('Unsafe');
// redefinition modules
$this->makeCollectionActive('Lenient');
$this->makeCollectionActive('Correctional');
$this->autoDoctype = '*';
$this->autoCollection = 'Extension';
}
/**
* Adds a module to the recognized module list. This does not
* do anything else: the module must be added to a corresponding
* collection to be "activated".
* @param $module Mixed: string module name, with or without
* HTMLPurifier_HTMLModule prefix, or instance of
* subclass of HTMLPurifier_HTMLModule.
*/
function addModule($module) {
if (is_string($module)) {
$original_module = $module;
if (!class_exists($module)) {
foreach ($this->prefixes as $prefix) {
$module = $prefix . $original_module;
if (class_exists($module)) break;
}
}
if (!class_exists($module)) {
trigger_error($original_module . ' module does not exist',
E_USER_ERROR);
return;
}
$module = new $module();
}
$module->order = $this->counter++; // assign then increment
$this->modules[$module->name] = $module;
if ($this->autoDoctype !== false && $this->autoCollection !== false) {
$this->collections[$this->autoCollection][$this->autoDoctype][] = $module->name;
}
}
/**
* Makes a collection active, while also making it valid if not
* already done so. See $activeModules for the semantics of "active".
* @param $collection_name Name of collection to activate
*/
function makeCollectionActive($collection_name) {
if (!in_array($collection_name, $this->validCollections)) {
$this->makeCollectionValid($collection_name);
}
$this->activeCollections[] = $collection_name;
}
/**
* Makes a collection valid. See $validModules for the semantics of "valid"
*/
function makeCollectionValid($collection_name) {
$this->validCollections[] = $collection_name;
}
/**
* Adds a class prefix that addModule() will use to resolve a
* string name to a concrete class
*/
function addPrefix($prefix) {
$this->prefixes[] = (string) $prefix;
}
function setup($config) {
// load up the autocollection
if ($this->autoCollection !== false) {
$this->makeCollectionActive($this->autoCollection);
}
// retrieve the doctype
$this->doctype = $this->getDoctype($config);
if (isset($this->doctypeAliases[$this->doctype])) {
$this->doctype = $this->doctypeAliases[$this->doctype];
}
// process module collections to module name => module instance form
foreach ($this->collections as $col_i => $x) {
$this->processCollections($this->collections[$col_i]);
}
$this->validModules = $this->assembleModules($this->validCollections);
$this->activeModules = $this->assembleModules($this->activeCollections);
// setup lookup table based on all valid modules
foreach ($this->validModules as $module) {
foreach ($module->info as $name => $def) {
if (!isset($this->elementLookup[$name])) {
$this->elementLookup[$name] = array();
}
$this->elementLookup[$name][] = $module->name;
}
}
// note the different choice
$this->contentSets = new HTMLPurifier_ContentSets(
// content models that contain non-allowed elements are
// harmless because RemoveForeignElements will ensure
// they never get in anyway, and there is usually no
// reason why you should want to restrict a content
// model beyond what is mandated by the doctype.
// Note, however, that this means redefinitions of
// content models can't be tossed in validModels willy-nilly:
// that stuff still is regulated by configuration.
$this->validModules
);
$this->attrCollections = new HTMLPurifier_AttrCollections(
$this->attrTypes,
// only explicitly allowed modules are allowed to affect
// the global attribute collections. This mean's there's
// a distinction between loading the Bdo module, and the
// bdo element: Bdo will enable the dir attribute on all
// elements, while bdo will only define the bdo element,
// which will not have an editable directionality. This might
// catch people who are loading only elements by surprise, so
// we should consider loading an entire module if all the
// elements it defines are requested by the user, especially
// if it affects the global attribute collections.
$this->activeModules
);
}
/**
* Takes a list of collections and merges together all the defined
* modules for the current doctype from those collections.
* @param $collections List of collection suffixes we should grab
* modules from (like 'Safe' or 'Lenient')
*/
function assembleModules($collections) {
$modules = array();
$numOfCollectionsUsed = 0;
foreach ($collections as $name) {
$disable_global = false;
if (!isset($this->collections[$name])) {
trigger_error("$name collection is undefined", E_USER_ERROR);
continue;
}
$cols = $this->collections[$name];
if (isset($cols[$this->doctype])) {
if (isset($cols[$this->doctype]['*'])) {
unset($cols[$this->doctype]['*']);
$disable_global = true;
}
$modules += $cols[$this->doctype];
$numOfCollectionsUsed++;
}
// accept catch-all doctype
if (
$this->doctype !== '*' &&
isset($cols['*']) &&
!$disable_global
) {
$modules += $cols['*'];
}
}
if ($numOfCollectionsUsed < 1) {
// possible XSS injection if user-specified doctypes
// are allowed
trigger_error("Doctype {$this->doctype} does not exist, ".
"check for typos (if you desire a doctype that allows ".
"no elements, use an empty array collection)", E_USER_ERROR);
}
return $modules;
}
/**
* Takes a collection and performs inclusions and substitutions for it.
* @param $cols Reference to collections class member variable
*/
function processCollections(&$cols) {
// $cols is the set of collections
// $col_i is the name (index) of a collection
// $col is a collection/list of modules
// perform inclusions
foreach ($cols as $col_i => $col) {
$seen = array();
if (!empty($col[0]) && is_array($col[0])) {
$seen[$col_i] = true; // recursion reporting
$includes = $col[0];
unset($cols[$col_i][0]); // remove inclusions value, recursion guard
} else {
$includes = array();
}
if (empty($includes)) continue;
for ($i = 0; isset($includes[$i]); $i++) {
$inc = $includes[$i];
if (isset($seen[$inc])) {
trigger_error(
"Circular inclusion detected in $col_i collection",
E_USER_ERROR
);
continue;
} else {
$seen[$inc] = true;
}
if (!isset($cols[$inc])) {
trigger_error(
"Collection $col_i tried to include undefined ".
"collection $inc", E_USER_ERROR);
continue;
}
foreach ($cols[$inc] as $module) {
if (is_array($module)) { // another inclusion!
foreach ($module as $inc2) $includes[] = $inc2;
continue;
}
$cols[$col_i][] = $module; // merge in the other modules
}
}
}
// replace with real modules, invert module from list to
// assoc array of module name to module instance
foreach ($cols as $col_i => $col) {
$ignore_global = false;
$order = array();
foreach ($col as $module_i => $module) {
unset($cols[$col_i][$module_i]);
if (is_array($module)) {
trigger_error("Illegal inclusion array at index".
" $module_i found collection $col_i, inclusion".
" arrays must be at start of collection (index 0)",
E_USER_ERROR);
continue;
}
if ($module_i === '*' && $module === false) {
$ignore_global = true;
continue;
}
if (!isset($this->modules[$module])) {
trigger_error(
"Collection $col_i references undefined ".
"module $module",
E_USER_ERROR
);
continue;
}
$module = $this->modules[$module];
$cols[$col_i][$module->name] = $module;
$order[$module->name] = $module->order;
}
array_multisort(
$order, SORT_ASC, SORT_NUMERIC, $cols[$col_i]
);
if ($ignore_global) $cols[$col_i]['*'] = false;
}
// delete pseudo-collections
foreach ($cols as $col_i => $col) {
if ($col_i[0] == '_') unset($cols[$col_i]);
}
}
/**
* Retrieves the doctype from the configuration object
*/
function getDoctype($config) {
$doctype = $config->get('HTML', 'Doctype');
if ($doctype !== null) {
return $doctype;
}
if (!$this->initialized) {
// don't do HTML-oriented backwards compatibility stuff
// use either the auto-doctype, or the catch-all doctype
return $this->autoDoctype ? $this->autoDoctype : '*';
}
// this is backwards-compatibility stuff
if ($config->get('Core', 'XHTML')) {
$doctype = 'XHTML 1.0';
} else {
$doctype = 'HTML 4.01';
}
if ($config->get('HTML', 'Strict')) {
$doctype .= ' Strict';
} else {
$doctype .= ' Transitional';
}
return $doctype;
}
/**
* Retrieves merged element definitions for all active elements.
* @note We may want to generate an elements array during setup
* and pass that on, because a specific combination of
* elements may trigger the loading of a module.
* @param $config Instance of HTMLPurifier_Config, for determining
* stray elements.
*/
function getElements($config) {
$elements = array();
foreach ($this->activeModules as $module) {
foreach ($module->elements as $name) {
$elements[$name] = $this->getElement($name, $config);
}
}
// standalone elements now loaded
return $elements;
}
/**
* Retrieves a single merged element definition
* @param $name Name of element
* @param $config Instance of HTMLPurifier_Config, may not be necessary.
*/
function getElement($name, $config) {
$def = false;
$modules = $this->validModules;
if (!isset($this->elementLookup[$name])) {
return false;
}
foreach($this->elementLookup[$name] as $module_name) {
$module = $modules[$module_name];
$new_def = $module->info[$name];
if (!$def && $new_def->standalone) {
$def = $new_def;
} elseif ($def) {
$def->mergeIn($new_def);
} else {
// could "save it for another day":
// non-standalone definitions that don't have a standalone
// to merge into could be deferred to the end
continue;
}
// attribute value expansions
$this->attrCollections->performInclusions($def->attr);
$this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes);
// descendants_are_inline, for ChildDef_Chameleon
if (is_string($def->content_model) &&
strpos($def->content_model, 'Inline') !== false) {
if ($name != 'del' && $name != 'ins') {
// this is for you, ins/del
$def->descendants_are_inline = true;
}
}
$this->contentSets->generateChildDef($def, $module);
}
return $def;
}
}
?>

View File

@@ -0,0 +1,56 @@
<?php
require_once 'HTMLPurifier/LanguageFactory.php';
class HTMLPurifier_Language
{
/**
* ISO 639 language code of language. Prefers shortest possible version
*/
var $code = 'en';
/**
* Fallback language code
*/
var $fallback = false;
/**
* Array of localizable messages
*/
var $messages = array();
/**
* Has the language object been loaded yet?
* @private
*/
var $_loaded = false;
/**
* Loads language object with necessary info from factory cache
* @note This is a lazy loader
*/
function load() {
if ($this->_loaded) return;
$factory = HTMLPurifier_LanguageFactory::instance();
$factory->loadLanguage($this->code);
foreach ($factory->keys as $key) {
$this->$key = $factory->cache[$this->code][$key];
}
$this->_loaded = true;
}
/**
* Retrieves a localised message. Does not perform any operations.
* @param $key string identifier of message
* @return string localised message
*/
function getMessage($key) {
if (!$this->_loaded) $this->load();
if (!isset($this->messages[$key])) return '';
return $this->messages[$key];
}
}
?>

View File

@@ -0,0 +1,12 @@
<?php
// private class for unit testing
class HTMLPurifier_Language_en_x_test extends HTMLPurifier_Language
{
}
?>

View File

@@ -0,0 +1,11 @@
<?php
// private language message file for unit testing purposes
$fallback = 'en';
$messages = array(
'htmlpurifier' => 'HTML Purifier X'
);
?>

View File

@@ -0,0 +1,12 @@
<?php
$fallback = false;
$messages = array(
'htmlpurifier' => 'HTML Purifier',
'pizza' => 'Pizza', // for unit testing purposes
);
?>

View File

@@ -0,0 +1,196 @@
<?php
require_once 'HTMLPurifier/Language.php';
require_once 'HTMLPurifier/AttrDef/Lang.php';
/**
* Class responsible for generating HTMLPurifier_Language objects, managing
* caching and fallbacks.
* @note Thanks to MediaWiki for the general logic, although this version
* has been entirely rewritten
*/
class HTMLPurifier_LanguageFactory
{
/**
* Cache of language code information used to load HTMLPurifier_Language objects
* Structure is: $factory->cache[$language_code][$key] = $value
* @value array map
*/
var $cache;
/**
* Valid keys in the HTMLPurifier_Language object. Designates which
* variables to slurp out of a message file.
* @value array list
*/
var $keys = array('fallback', 'messages');
/**
* Instance of HTMLPurifier_AttrDef_Lang to validate language codes
* @value object HTMLPurifier_AttrDef_Lang
*/
var $validator;
/**
* Cached copy of dirname(__FILE__), directory of current file without
* trailing slash
* @value string filename
*/
var $dir;
/**
* Keys whose contents are a hash map and can be merged
* @value array lookup
*/
var $mergeable_keys_map = array('messages' => true);
/**
* Keys whose contents are a list and can be merged
* @value array lookup
*/
var $mergeable_keys_list = array();
/**
* Retrieve sole instance of the factory.
* @static
* @param $prototype Optional prototype to overload sole instance with,
* or bool true to reset to default factory.
*/
static function &instance($prototype = null) {
static $instance = null;
if ($prototype !== null) {
$instance = $prototype;
} elseif ($instance === null || $prototype == true) {
$instance = new HTMLPurifier_LanguageFactory();
$instance->setup();
}
return $instance;
}
/**
* Sets up the singleton, much like a constructor
* @note Prevents people from getting this outside of the singleton
*/
function setup() {
$this->validator = new HTMLPurifier_AttrDef_Lang();
$this->dir = dirname(__FILE__);
}
/**
* Creates a language object, handles class fallbacks
* @param $code string language code
*/
function create($code) {
$config = $context = false; // hope it doesn't use these!
$code = $this->validator->validate($code, $config, $context);
if ($code === false) $code = 'en'; // malformed code becomes English
$pcode = str_replace('-', '_', $code); // make valid PHP classname
static $depth = 0; // recursion protection
if ($code == 'en') {
$class = 'HTMLPurifier_Language';
$file = $this->dir . '/Language.php';
} else {
$class = 'HTMLPurifier_Language_' . $pcode;
$file = $this->dir . '/Language/classes/' . $code . '.php';
// PHP5/APC deps bug workaround can go here
// you can bypass the conditional include by loading the
// file yourself
if (file_exists($file) && !class_exists($class)) {
include_once $file;
}
}
if (!class_exists($class)) {
// go fallback
$fallback = HTMLPurifier_Language::getFallbackFor($code);
$depth++;
$lang = Language::factory( $fallback );
$depth--;
} else {
$lang = new $class;
}
$lang->code = $code;
return $lang;
}
/**
* Returns the fallback language for language
* @note Loads the original language into cache
* @param $code string language code
*/
function getFallbackFor($code) {
$this->loadLanguage($code);
return $this->cache[$code]['fallback'];
}
/**
* Loads language into the cache, handles message file and fallbacks
* @param $code string language code
*/
function loadLanguage($code) {
static $languages_seen = array(); // recursion guard
// abort if we've already loaded it
if (isset($this->cache[$code])) return;
// generate filename
$filename = $this->dir . '/Language/messages/' . $code . '.php';
// default fallback : may be overwritten by the ensuing include
$fallback = ($code != 'en') ? 'en' : false;
// load primary localisation
if (!file_exists($filename)) {
// skip the include: will rely solely on fallback
$filename = $this->dir . '/Language/messages/en.php';
$cache = array();
} else {
include $filename;
$cache = compact($this->keys);
}
// load fallback localisation
if (!empty($fallback)) {
// infinite recursion guard
if (isset($languages_seen[$code])) {
trigger_error('Circular fallback reference in language ' .
$code, E_USER_ERROR);
$fallback = 'en';
}
$language_seen[$code] = true;
// load the fallback recursively
$this->loadLanguage($fallback);
$fallback_cache = $this->cache[$fallback];
// merge fallback with current language
foreach ( $this->keys as $key ) {
if (isset($cache[$key]) && isset($fallback_cache[$key])) {
if (isset($this->mergeable_keys_map[$key])) {
$cache[$key] = $cache[$key] + $fallback_cache[$key];
} elseif (isset($this->mergeable_keys_list[$key])) {
$cache[$key] = array_merge( $fallback_cache[$key], $cache[$key] );
}
} else {
$cache[$key] = $fallback_cache[$key];
}
}
}
// save to cache for later retrieval
$this->cache[$code] = $cache;
return;
}
}
?>

View File

@@ -151,7 +151,8 @@ class HTMLPurifier_Lexer
$lexer = $prototype;
}
if (empty($lexer)) {
if (class_exists('DOMDocument')) { // check for DOM support
if (version_compare(PHP_VERSION, "5", ">=") && // check for PHP5
class_exists('DOMDocument')) { // check for DOM support
require_once 'HTMLPurifier/Lexer/DOMLex.php';
$lexer = new HTMLPurifier_Lexer_DOMLex();
} else {

View File

@@ -21,7 +21,7 @@ require_once 'HTMLPurifier/TokenFactory.php';
*
* @warning DOM tends to drop whitespace, which may wreak havoc on indenting.
* If this is a huge problem, due to the fact that HTML is hand
* edited and youa re unable to get a parser cache that caches the
* edited and you are unable to get a parser cache that caches the
* the output of HTML Purifier while keeping the original HTML lying
* around, you may want to run Tidy on the resulting output or use
* HTMLPurifier_DirectLex
@@ -54,7 +54,13 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
$doc = new DOMDocument();
$doc->encoding = 'UTF-8'; // technically does nothing, but whatever
@$doc->loadHTML($string); // mute all errors, handle it transparently
// DOM will toss errors if the HTML its parsing has really big
// problems, so we're going to mute them. This can cause problems
// if a custom error handler that doesn't implement error_reporting
// is set, as noted by a Drupal plugin of HTML Purifier. Consider
// making our own error reporter to temporarily load in
@$doc->loadHTML($string);
$tokens = array();
$this->tokenizeDOM(

View File

@@ -13,6 +13,7 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
function render($config) {
$ret = '';
$this->config =& $config;
$this->def = $config->getHTMLDefinition();
$def =& $this->def;
@@ -21,16 +22,14 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
$ret .= $this->element('caption', 'Environment');
$ret .= $this->row('Parent of fragment', $def->info_parent);
$ret .= $this->row('Strict mode', $def->strict);
if ($def->strict) $ret .= $this->row('Block wrap name', $def->info_block_wrapper);
$ret .= $this->renderChildren($def->info_parent_def->child);
$ret .= $this->row('Block wrap name', $def->info_block_wrapper);
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Global attributes');
$ret .= $this->element('td', $this->listifyAttr($def->info_global_attr),0,0);
$ret .= $this->end('tr');
$ret .= $this->renderChildren($def->info_parent_def->child);
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Tag transforms');
$list = array();
@@ -81,8 +80,8 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
$ret .= $this->element('th', "<$name>", array('class'=>'heavy', 'colspan' => 2));
$ret .= $this->end('tr');
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Type');
$ret .= $this->element('td', ucfirst($def->type));
$ret .= $this->element('th', 'Inline content');
$ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No');
$ret .= $this->end('tr');
if (!empty($def->excludes)) {
$ret .= $this->start('tr');
@@ -130,15 +129,17 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
$elements = array();
$attr = array();
if (isset($def->elements)) {
if ($def->type == 'strictblockquote') $def->validateChildren(array(), $this->config, $context);
if ($def->type == 'strictblockquote') {
$def->validateChildren(array(), $this->config, $context);
}
$elements = $def->elements;
} elseif ($def->type == 'chameleon') {
$attr['rowspan'] = 2;
} elseif ($def->type == 'empty') {
$elements = array();
} elseif ($def->type == 'table') {
$elements = array('col', 'caption', 'colgroup', 'thead',
'tfoot', 'tbody', 'tr');
$elements = array_flip(array('col', 'caption', 'colgroup', 'thead',
'tfoot', 'tbody', 'tr'));
}
$ret .= $this->element('th', 'Allowed children', $attr);
@@ -167,6 +168,7 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
* @param $array Tag lookup array in form of array('tagname' => true)
*/
function listifyTagLookup($array) {
ksort($array);
$list = array();
foreach ($array as $name => $discard) {
if ($name !== '#PCDATA' && !isset($this->def->info[$name])) continue;
@@ -181,6 +183,7 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
* @todo Also add information about internal state
*/
function listifyObjectList($array) {
ksort($array);
$list = array();
foreach ($array as $discard => $obj) {
$list[] = $this->getClass($obj, 'AttrTransform_');
@@ -193,6 +196,7 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
* @param $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef)
*/
function listifyAttr($array) {
ksort($array);
$list = array();
foreach ($array as $name => $obj) {
if ($obj === false) continue;

View File

@@ -49,8 +49,8 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
$tokens[] = new HTMLPurifier_Token_End($parent_name);
// setup the context variables
$parent_type = 'unknown'; // reference var that we alter
$context->register('ParentType', $parent_type);
$is_inline = false; // reference var that we alter
$context->register('IsInline', $is_inline);
//####################################################################//
// Loop initialization
@@ -115,11 +115,16 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
}
// calculate context
if (isset($parent_def)) {
$parent_type = $parent_def->type;
if ($is_inline === false) {
// check if conditions make it inline
if (!empty($parent_def) && $parent_def->descendants_are_inline) {
$is_inline = $count - 1;
}
} else {
// generally found in specialized elements like UL
$parent_type = 'unknown';
// check if we're out of inline
if ($count === $is_inline) {
$is_inline = false;
}
}
//################################################################//
@@ -273,7 +278,7 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
array_pop($tokens);
// remove context variables
$context->destroy('ParentType');
$context->destroy('IsInline');
//####################################################################//
// Return

View File

@@ -1,6 +1,6 @@
<?php
require_once('HTMLPurifier/Token.php');
require_once 'HTMLPurifier/Token.php';
/**
* Defines a mutation of an obsolete tag into a valid tag.
@@ -26,132 +26,4 @@ class HTMLPurifier_TagTransform
}
/**
* Simple transformation, just change tag name to something else.
*/
class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform
{
/**
* @param $transform_to Tag name to transform to.
*/
function HTMLPurifier_TagTransform_Simple($transform_to) {
$this->transform_to = $transform_to;
}
function transform($tag, $config, &$context) {
$new_tag = $tag->copy();
$new_tag->name = $this->transform_to;
return $new_tag;
}
}
/**
* Transforms CENTER tags into proper version (DIV with text-align CSS)
*
* Takes a CENTER tag, parses the align attribute, and then if it's valid
* assigns it to the CSS property text-align.
*/
class HTMLPurifier_TagTransform_Center extends HTMLPurifier_TagTransform
{
var $transform_to = 'div';
function transform($tag, $config, &$context) {
if ($tag->type == 'end') {
$new_tag = new HTMLPurifier_Token_End($this->transform_to);
return $new_tag;
}
$attr = $tag->attr;
$prepend_css = 'text-align:center;';
if (isset($attr['style'])) {
$attr['style'] = $prepend_css . $attr['style'];
} else {
$attr['style'] = $prepend_css;
}
$new_tag = $tag->copy();
$new_tag->name = $this->transform_to;
$new_tag->attr = $attr;
return $new_tag;
}
}
/**
* Transforms FONT tags to the proper form (SPAN with CSS styling)
*
* This transformation takes the three proprietary attributes of FONT and
* transforms them into their corresponding CSS attributes. These are color,
* face, and size.
*
* @note Size is an interesting case because it doesn't map cleanly to CSS.
* Thanks to
* http://style.cleverchimp.com/font_size_intervals/altintervals.html
* for reasonable mappings.
*/
class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
{
var $transform_to = 'span';
var $_size_lookup = array(
'1' => 'xx-small',
'2' => 'small',
'3' => 'medium',
'4' => 'large',
'5' => 'x-large',
'6' => 'xx-large',
'7' => '300%',
'-1' => 'smaller',
'+1' => 'larger',
'-2' => '60%',
'+2' => '150%',
'+4' => '300%'
);
function transform($tag, $config, &$context) {
if ($tag->type == 'end') {
$new_tag = new HTMLPurifier_Token_End($this->transform_to);
return $new_tag;
}
$attr = $tag->attr;
$prepend_style = '';
// handle color transform
if (isset($attr['color'])) {
$prepend_style .= 'color:' . $attr['color'] . ';';
unset($attr['color']);
}
// handle face transform
if (isset($attr['face'])) {
$prepend_style .= 'font-family:' . $attr['face'] . ';';
unset($attr['face']);
}
// handle size transform
if (isset($attr['size'])) {
if (isset($this->_size_lookup[$attr['size']])) {
$prepend_style .= 'font-size:' .
$this->_size_lookup[$attr['size']] . ';';
}
unset($attr['size']);
}
if ($prepend_style) {
$attr['style'] = isset($attr['style']) ?
$prepend_style . $attr['style'] :
$prepend_style;
}
$new_tag = $tag->copy();
$new_tag->name = $this->transform_to;
$new_tag->attr = $attr;
return $new_tag;
}
}
?>

View File

@@ -0,0 +1,34 @@
<?php
require_once 'HTMLPurifier/TagTransform.php';
/**
* Transforms CENTER tags into proper version (DIV with text-align CSS)
*
* Takes a CENTER tag, parses the align attribute, and then if it's valid
* assigns it to the CSS property text-align.
*/
class HTMLPurifier_TagTransform_Center extends HTMLPurifier_TagTransform
{
var $transform_to = 'div';
function transform($tag, $config, &$context) {
if ($tag->type == 'end') {
$new_tag = new HTMLPurifier_Token_End($this->transform_to);
return $new_tag;
}
$attr = $tag->attr;
$prepend_css = 'text-align:center;';
if (isset($attr['style'])) {
$attr['style'] = $prepend_css . $attr['style'];
} else {
$attr['style'] = $prepend_css;
}
$new_tag = $tag->copy();
$new_tag->name = $this->transform_to;
$new_tag->attr = $attr;
return $new_tag;
}
}
?>

View File

@@ -0,0 +1,83 @@
<?php
require_once 'HTMLPurifier/TagTransform.php';
/**
* Transforms FONT tags to the proper form (SPAN with CSS styling)
*
* This transformation takes the three proprietary attributes of FONT and
* transforms them into their corresponding CSS attributes. These are color,
* face, and size.
*
* @note Size is an interesting case because it doesn't map cleanly to CSS.
* Thanks to
* http://style.cleverchimp.com/font_size_intervals/altintervals.html
* for reasonable mappings.
*/
class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
{
var $transform_to = 'span';
var $_size_lookup = array(
'1' => 'xx-small',
'2' => 'small',
'3' => 'medium',
'4' => 'large',
'5' => 'x-large',
'6' => 'xx-large',
'7' => '300%',
'-1' => 'smaller',
'+1' => 'larger',
'-2' => '60%',
'+2' => '150%',
'+4' => '300%'
);
function transform($tag, $config, &$context) {
if ($tag->type == 'end') {
$new_tag = new HTMLPurifier_Token_End($this->transform_to);
return $new_tag;
}
$attr = $tag->attr;
$prepend_style = '';
// handle color transform
if (isset($attr['color'])) {
$prepend_style .= 'color:' . $attr['color'] . ';';
unset($attr['color']);
}
// handle face transform
if (isset($attr['face'])) {
$prepend_style .= 'font-family:' . $attr['face'] . ';';
unset($attr['face']);
}
// handle size transform
if (isset($attr['size'])) {
if (isset($this->_size_lookup[$attr['size']])) {
$prepend_style .= 'font-size:' .
$this->_size_lookup[$attr['size']] . ';';
}
unset($attr['size']);
}
if ($prepend_style) {
$attr['style'] = isset($attr['style']) ?
$prepend_style . $attr['style'] :
$prepend_style;
}
$new_tag = $tag->copy();
$new_tag->name = $this->transform_to;
$new_tag->attr = $attr;
return $new_tag;
}
}
?>

View File

@@ -0,0 +1,26 @@
<?php
require_once 'HTMLPurifier/TagTransform.php';
/**
* Simple transformation, just change tag name to something else.
*/
class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform
{
/**
* @param $transform_to Tag name to transform to.
*/
function HTMLPurifier_TagTransform_Simple($transform_to) {
$this->transform_to = $transform_to;
}
function transform($tag, $config, &$context) {
$new_tag = $tag->copy();
$new_tag->name = $this->transform_to;
return $new_tag;
}
}
?>

View File

@@ -10,7 +10,7 @@ HTMLPurifier_ConfigSchema::define(
'irc' => true, // "Internet Relay Chat", usually needs another app
// for Usenet, these two are similar, but distinct
'nntp' => true, // individual Netnews articles
'news' => true // newsgroup or individual Netnews articles),
'news' => true // newsgroup or individual Netnews articles
), 'lookup',
'Whitelist that defines the schemes that a URI is allowed to have. This '.
'prevents XSS attacks from using pseudo-schemes like javascript or mocha.'