1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-08-04 21:28:06 +02:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Edward Z. Yang
3af239c70f [1.2.0] [merge] Bump TODO items.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/branches/1.2@545 48356398-32a2-884e-a903-53898d9a118a
2006-11-20 03:20:08 +00:00
Edward Z. Yang
c6d5016626 Branch 1.2 series.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/branches/1.2@544 48356398-32a2-884e-a903-53898d9a118a
2006-11-20 03:18:47 +00:00
138 changed files with 1376 additions and 5140 deletions

View File

@@ -4,7 +4,7 @@
# Project related configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = HTML Purifier
PROJECT_NUMBER = 1.4.1
PROJECT_NUMBER = 1.2.0
OUTPUT_DIRECTORY = "C:/Documents and Settings/Edward/My Documents/My Webs/htmlpurifier/docs/doxygen"
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English

View File

@@ -8,7 +8,6 @@ installation GUI, you've come to the wrong place!) The impatient can scroll
down to the bottom of this INSTALL document to see the code, but you really
should make sure a few things are properly done.
Todo: Convert to using the array syntax for configuration.
1. Compatibility

75
NEWS
View File

@@ -9,81 +9,6 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
. Internal change
==========================
1.4.1, released 2007-01-21
! docs/enduser-youtube.html updated according to new functionality
- YouTube IDs can have underscores and dashes
1.4.0, released 2007-01-21
! Implemented list-style-image, URIs now allowed in list-style
! Implemented background-image, background-repeat, background-attachment
and background-position CSS properties. Shorthand property background
supports all of these properties.
! Configuration documentation looks nicer
! Added %Core.EscapeNonASCIICharacters to workaround loss of Unicode
characters while %Core.Encoding is set to a non-UTF-8 encoding.
! Support for configuration directive aliases added
! Config object can now be instantiated from ini files
! YouTube preservation code added to the core, with two lines of code
you can add it as a filter to your code. See smoketests/preserveYouTube.php
for sample code.
! Moved SLOW to docs/enduser-slow.html and added code examples
- Replaced version check with functionality check for DOM (thanks Stephen
Khoo)
. Added smoketest 'all.php', which loads all other smoketests via frames
. Implemented AttrDef_CSSURI for url(http://google.com) style declarations
. Added convenient single test selector form on test runner
1.3.2, released 2006-12-25
! HTMLPurifier object now accepts configuration arrays, no need to manually
instantiate a configuration object
! Context object now accessible to outside
! Added enduser-youtube.html, explains how to embed YouTube videos. See
also corresponding smoketest preserveYouTube.php.
! Added purifyArray(), which takes a list of HTML and purifies it all
! Added static member variable $version to HTML Purifier with PHP-compatible
version number string.
- Fixed fatal error thrown by upper-cased language attributes
- printDefinition.php: added labels, added better clarification
. HTMLPurifier_Config::create() added, takes mixed variable and converts into
a HTMLPurifier_Config object.
1.3.1, released 2006-12-06
! Added HTMLPurifier.func.php stub for a convenient function to call the library
- Fixed bug in RemoveInvalidImg code that caused all images to be dropped
(thanks to .mario for reporting this)
. Standardized all attribute handling variables to attr, made it plural
1.3.0, released 2006-11-26
# Invalid images are now removed, rather than replaced with a dud
<img src="" alt="Invalid image" />. Previous behavior can be restored
with new directive %Core.RemoveInvalidImg set to false.
! (X)HTML Strict now supported
+ Transparently handles inline elements in block context (blockquote)
! Added GET method to demo for easier validation, added 50kb max input size
! New directive %HTML.BlockWrapper, for block-ifying inline elements
! New directive %HTML.Parent, allows you to only allow inline content
! New directives %HTML.AllowedElements and %HTML.AllowedAttributes to let
users narrow the set of allowed tags
! <li value="4"> and <ul start="2"> now allowed in loose mode
! New directives %URI.DisableExternalResources and %URI.DisableResources
! New directive %Attr.DisableURI, which eliminates all hyperlinking
! New directive %URI.Munge, munges URI so you can use some sort of redirector
service to avoid PageRank leaks or warn users that they are exiting your site.
! Added spiffy new smoketest printDefinition.php, which lets you twiddle with
the configuration settings and see how the internal rules are affected.
! New directive %URI.HostBlacklist for blocking links to bad hosts.
xssAttacks.php smoketest updated accordingly.
- Added missing type to ChildDef_Chameleon
- Remove Tidy option from demo if there is not Tidy available
. ChildDef_Required guards against empty tags
. Lookup table HTMLDefinition->info_flow_elements added
. Added peace-of-mind variable initialization to Strategy_FixNesting
. Added HTMLPurifier->info_parent_def, parent child processing made special
. Added internal documents briefly summarizing future progression of HTML
. HTMLPurifier_Config->getBatch($namespace) added
. More lenient casting to bool from string in HTMLPurifier_ConfigSchema
. Refactored ChildDef classes into their own files
1.2.0, released 2006-11-19
# ID attributes now disabled by default. New directives:
+ %HTML.EnableAttrID - restores old behavior by allowing IDs

25
README
View File

@@ -1,22 +1,13 @@
README
All about HTML Purifier
All about HTMLPurifier
HTML Purifier is an HTML filtering solution that uses a unique combination
of robust whitelists and agressive parsing to ensure that not only are
XSS attacks thwarted, but the resulting HTML is standards compliant.
HTMLPurifier is an HTML filtering solution. It uses a unique combination of
robust whitelists and agressive parsing to ensure that not only are XSS
attacks thwarted, but the resulting HTML is standards compliant.
HTML Purifier is oriented towards richly formatted documents from
untrusted sources that require CSS and a full tag-set. This library can
be configured to accept a more restrictive set of tags, but it won't be
as efficient as more bare-bones parsers. It will, however, do the job
right, which may be more important.
See INSTALL on how to use the library. See docs/ for more developer-oriented
documentation as well as some code examples. Users of TinyMCE or FCKeditor
may be especially interested in WYSIWYG.
Places to go:
* See INSTALL for a quick installation guide
* See docs/ for developer-oriented documentation, code examples and
an in-depth installation guide.
* See WYSIWYG for information on editors like TinyMCE and FCKeditor
HTML Purifier can be found on the web at: http://hp.jpsband.org/
HTMLPurifier can be found on the web at: http://hp.jpsband.org/

40
SLOW Normal file
View File

@@ -0,0 +1,40 @@
SLOW
also known as the HELP ME LIBRARY IS TOO SLOW MY PAGE TAKE TOO LONG LOAD page
HTML Purifier is a very powerful library. But with power comes great
responsibility, or, at least, longer execution times. Remember, this
library isn't lightly grazing over submitted HTML: it's deconstructing
the whole thing, rigorously checking the parts, and then putting it
back together.
So, if it so turns out that HTML Purifier is kinda too slow for outbound
filtering, you've got a few options:
1. Inbound filtering - perform filtering of HTML when it's submitted by the
user. Since the user is already submitting something, an extra half a
second tacked on to the load time probably isn't going to be that huge of
a problem. Then, displaying the content is a simple a manner of outputting
it directly from your database/filesystem. The trouble with this method is
that your user loses the original text, and when doing edits, will be
handling the filtered text. While this may be a good thing, especially if
you're using a WYSIWYG editor, it can also result in data-loss if a user
makes a typo.
2. Caching the filtered output - accept the submitted text and put it
unaltered into the database, but then also generate a filtered version and
stash that in the database. Serve the filtered version to readers, and the
unaltered version to editors. If need be, you can invalidate the cache and
have the cached filtered version be regenerated on the first page view. Pros?
Full data retention. Cons? It's more complicated, and opens other editors
up to XSS if they are using a WYSIWYG editor (to fix that, they'd have to
be able to get their hands on the *really* original text served in plaintext
mode).
In short, inbound filtering is almost as simple as outbound filtering, but
it has some drawbacks which cannot be fixed unless you save both the original
and the filtered versions.
There is a third option: profile and optimize HTMLPurifier yourself. Be sure
to report back your results if you decide to do that! Especially if you
port HTML Purifier to C++. ;-)

123
TODO
View File

@@ -1,89 +1,72 @@
TODO List
= KEY ====================
# Flagship
- Regular
? At-risk
==========================
1.3 release
- Make URI validation routines tighter (especially mailto)
- More extensive URI filtering schemes (see docs/proposal-new-directives.txt)
- Allow for background-image and list-style-image (see above)
- Error logging for filtering/cleanup procedures
- Rich set* methods and config file loaders for HTMLPurifier_Config
1.5 release
# Implement all non-essential attribute transforms, configurable
# URI validation routines tighter (see docs/dev-code-quality.html) (COMPLEX)
# Advanced URI filtering schemes (see docs/proposal-new-directives.txt)
# Error logging for filtering/cleanup procedures
- Requires I18N facilities to be created first (COMPLEX)
? Configuration profiles: sets of directives that get set with one func call
- XSS-attempt detection
1.6 release
# Add pre-packaged "levels" of cleaning (custom behavior already done)
1.4 release
- Add various "levels" of cleaning
- Related: Allow strict (X)HTML
- More fine-grained control over escaping behavior
- Silently drop content inbetween SCRIPT tags (can be generalized to allow
specification of elements that, when detected as foreign, trigger removal
of children, although unbalanced tags could wreck havoc (or at least
delete the rest of the document)).
- Allow specifying global attributes on a tag-by-tag basis in
%HTML.AllowAttributes
? More user-friendly warnings when %HTML.Allow* attempts to specify a
tag or attribute that is not supported
- Parse TinyMCE whitelist into our %HTML.Allow* whitelists
1.7 release
# Additional support for poorly written HTML
- Microsoft Word HTML cleaning (i.e. MsoNormal, but research essential!)
- Friendly strict handling of <address> (block -> <br>)
1.5 release
- Additional support for poorly written HTML
- Implement all non-essential attribute transforms
- Microsoft Word HTML cleaning (i.e. MsoNormal)
2.0 release
- Formatters for plaintext
- Auto-paragraphing (be sure to leverage fact that we know when things
shouldn't be paragraphed, such as lists and tables).
- Linkify URLs
- Smileys
- Linkification for HTML Purifier docs: notably configuration and
class names
3.0 release
- Extended HTML capabilities based on namespacing and tag transforms
- Hooks for adding custom processors to custom namespaced tags and
attributes, offer default implementation
- Lots of documentation and samples
Ongoing
- Lots of profiling, make it faster!
- Plugins for major CMSes (very tricky issue)
Unknown release (on a scratch-an-itch basis)
- Fixes for Firefox's inability to handle COL alignment props (Bug 915)
- Automatically add non-breaking spaces to empty table cells when
empty-cells:show is applied to have compatibility with Internet Explorer
- Convert RTL/LTR override characters to <bdo> tags, or vice versa on demand.
Also, enable disabling of directionality
- Append something to duplicate IDs so they're still usable (impl. note: the
dupe detector would also need to detect the suffix as well)
- Have 'lang' attribute be checked against official lists
Encoding workarounds
- Non-lossy dumb alternate character encoding transformations, achieved by
numerically encoding all non-ASCII characters
- Semi-lossy dumb alternate character encoding transformations, achieved by
encoding all characters that have string entity equivalents
Requested
- Native content compression, whitespace stripping (don't rely on Tidy, make
sure we don't remove from <pre> or related tags)
- Win32 Phalanger C# binaries (?)
- Remove redundant tags, ex. <u><u>Underlined</u></u>. Implementation notes:
1. Analyzing which tags to remove duplicants
2. Ensure attributes are merged into the parent tag
3. Extend the tag exclusion system to specify whether or not the
contents should be dropped or not (currently, there's code that could do
something like this if it didn't drop the inner text too.)
- Remove <span> tags that don't do anything (no attributes)
- Remove empty inline tags<i></i>
- Append something to duplicate IDs so they're still usable (impl. note: the
dupe detector would also need to detect the suffix as well)
2.0 release
# Legit token based CSS parsing (will require revamping almost every
AttrDef class)
# Formatters for plaintext (COMPLEX)
- Auto-paragraphing (be sure to leverage fact that we know when things
shouldn't be paragraphed, such as lists and tables).
- Linkify URLs
- Smileys
- Linkification for HTML Purifier docs: notably configuration and classes
3.0 release
- Extended HTML capabilities based on namespacing and tag transforms (COMPLEX)
- Hooks for adding custom processors to custom namespaced tags and
attributes, offer default implementation
- Lots of documentation and samples
- Allow tags to be "armored", an internal flag that protects them
from validation and passes them out unharmed
- XHTML 1.1 support
- Fixes for Firefox's inability to handle COL alignment props (Bug 915)
- Automatically add non-breaking spaces to empty table cells when
empty-cells:show is applied to have compatibility with Internet Explorer
- Convert RTL/LTR override characters to <bdo> tags, or vice versa on demand.
Also, enable disabling of directionality
Ongoing
- Lots of profiling, make it faster!
- Plugins for major CMSes (COMPLEX)
- WordPress
- eFiction
- more! (look for ones that use WYSIWYGs)
Unknown release (on a scratch-an-itch basis)
- Upgrade SimpleTest testing code to newest versions
- Have 'lang' attribute be checked against official lists
? Semi-lossy dumb alternate character encoding transformations, achieved by
encoding all characters that have string entity equivalents
Requested
? Native content compression, whitespace stripping (don't rely on Tidy, make
sure we don't remove from <pre> or related tags)
Wontfix
- Non-lossy smart alternate character encoding transformations (unless

View File

@@ -18,5 +18,4 @@ HTML Purifier is perfect for filtering pure-HTML input from WYSIWYG editors.
Enough said.
There is a proof-of-concept integration of HTML Purifier with the Mantis
bugtracker at http://hp.jpsband.org/mantis/ You can see notes on how
this integration was acheived at http://hp.jpsband.org/mantis_notes.txt
bugtracker at http://hp.jpsband.org/mantis/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -99,8 +99,6 @@ foreach($schema->info as $namespace_name => $namespace_info) {
foreach ($namespace_info as $name => $info) {
if ($info->class == 'alias') continue;
$dom_directive = $dom_document->createElement('directive');
$dom_namespace->appendChild($dom_directive);

View File

@@ -1,6 +1,3 @@
body {margin:1em 4em;}
table {border-collapse:collapse;}
table td, table th {padding:0.2em;}
@@ -11,14 +8,3 @@ table.constraints td pre {margin:0;}
#toc {list-style-type:none; font-weight:bold;}
#toc ul {list-style-type:disc; font-weight:normal;}
.description p {margin-top:0;margin-bottom:1em;}
#library, h1 {text-align:center; font-family:Garamond, serif;
font-variant:small-caps;}
#library {font-size:1em;}
h1 {margin-top:0;}
h2 {border-bottom:1px solid #CCC; font-family:sans-serif; font-weight:normal;
font-size:1.3em;}
h3 {font-family:sans-serif; font-size:1.1em; font-weight:bold; }
h4 {font-family:sans-serif; font-size:0.9em; font-weight:bold; }

View File

@@ -18,13 +18,12 @@
<xsl:template match="/">
<html lang="en" xml:lang="en">
<head>
<title>Configuration Documentation - <xsl:value-of select="/configdoc/title" /></title>
<title><xsl:value-of select="/configdoc/title" /> Configuration Documentation</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="styles/plain.css" />
</head>
<body>
<div id="library"><xsl:value-of select="/configdoc/title" /></div>
<h1>Configuration Documentation</h1>
<h1><xsl:value-of select="/configdoc/title" /> Configuration Documentation</h1>
<h2>Table of Contents</h2>
<ul id="toc">
<xsl:apply-templates mode="toc" />

View File

@@ -14,7 +14,6 @@
<div id="filing">Filed under Development</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
<p>Okay, face it. Programmers can get lazy, cut corners, or make mistakes. They
also can do quick prototypes, and then forget to rewrite them later. Well,
@@ -23,8 +22,6 @@ of code that should be aggressively refactored. This does not list
optimization issues, that needs to be done after intense profiling.</p>
<pre>
docs/examples/demo.php - ad hoc HTML/PHP soup to the extreme
AttrDef
Class - doesn't support Unicode characters (fringe); uses regular
expressions
@@ -35,8 +32,7 @@ AttrDef
Number - constructor interface inconsistent with Integer
ConfigSchema - redefinition is a mess
Strategy
FixNesting - cannot bubble nodes out of structures, duplicated checks
for special-case parent node
FixNesting - cannot bubble nodes out of structures
MakeWellFormed - insufficient automatic closing definitions (check HTML
spec for optional end tags, also, closing based on type (block/inline)
might be efficient).

View File

@@ -14,7 +14,6 @@
<div id="filing">Filed under Development</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
<p>The classes in this library follow a few naming conventions, which may
help you find the correct functionality more quickly. Here they are:</p>
@@ -55,9 +54,8 @@ help you find the correct functionality more quickly. Here they are:</p>
abbreviated version is more readable than the full version. Here, we
list common abbreviations:
<ul>
<li>Attr to Attributes (note that it is plural, i.e. <code>$attr = array()</code>)</li>
<li>Attr(s) to Attribute(s)</li>
<li>Def to Definition</li>
<li><code>$ret</code> is the value to be returned in a function</li>
</ul>
</dd>

View File

@@ -14,7 +14,6 @@
<div id="filing">Filed under Development</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
<p>Here are some possible optimization techniques we can apply to code sections if
they turn out to be slow. Be sure not to prematurely optimize: if you get

View File

@@ -32,7 +32,6 @@ thead th {text-align:left;padding:0.1em;background-color:#EEE;}
<div id="filing">Filed under Development</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
<h2>Key</h2>
@@ -60,7 +59,7 @@ thead th {text-align:left;padding:0.1em;background-color:#EEE;}
<tbody>
<tr><th colspan="2">Standard</th></tr>
<tr class="css1 impl-yes"><td>background-color</td><td>COMPOSITE(&lt;color&gt;, transparent)</td></tr>
<tr class="css1 impl-yes"><td>background</td><td>SHORTHAND, currently alias for background-color</td></tr>
<tr class="css1 impl-yes"><td>background</td><td>SHORTHAND, only for color, see below for info on background-image and friends</td></tr>
<tr class="css1 impl-yes"><td>border</td><td>SHORTHAND, MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>border-color</td><td>MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>border-style</td><td>MULTIPLE</td></tr>
@@ -129,30 +128,29 @@ thead th {text-align:left;padding:0.1em;background-color:#EEE;}
<tbody>
<tr><th colspan="2">Absolute positioning, unknown release milestone</th></tr>
<tr class="danger impl-no"><td>bottom</td><td rowspan="4">Dangerous, must be non-negative to even be considered,
but it's still possible to arbitrarily position by running over.</td></tr>
<tr class="danger impl-no"><td>left</td></tr>
<tr class="danger impl-no"><td>right</td></tr>
<tr class="danger impl-no"><td>top</td></tr>
<tr class="impl-no"><td>clip</td><td>-</td></tr>
<tr class="danger impl-no"><td>position</td><td>ENUM(static, relative, absolute, fixed)
<tr class="danger"><td>bottom</td><td rowspan="4">Dangerous, must be non-negative</td></tr>
<tr class="danger"><td>left</td></tr>
<tr class="danger"><td>right</td></tr>
<tr class="danger"><td>top</td></tr>
<tr><td>clip</td><td>-</td></tr>
<tr class="danger"><td>position</td><td>ENUM(static, relative, absolute, fixed), permit
relative not absolute?</td></tr>
<tr class="danger impl-no"><td>z-index</td><td>Dangerous</td></tr>
<tr class="danger"><td>z-index</td><td>Dangerous</td></tr>
</tbody>
<tbody>
<tr><th colspan="2">Unknown</th></tr>
<tr class="danger css1 impl-yes"><td>background-image</td><td>Dangerous, target milestone 1.3</td></tr>
<tr class="css1 impl-yes"><td>background-attachment</td><td>ENUM(scroll, fixed),
<tr class="danger css1"><td>background-image</td><td>Dangerous, target milestone 1.2</td></tr>
<tr class="css1"><td>background-attachment</td><td>ENUM(scroll, fixed),
Depends on background-image</td></tr>
<tr class="css1 impl-yes"><td>background-position</td><td>Depends on background-image</td></tr>
<tr class="css1"><td>background-position</td><td>Depends on background-image</td></tr>
<tr class="danger impl-no"><td>cursor</td><td>Dangerous but fluffy</td></tr>
<tr class="danger css1"><td>display</td><td>ENUM(...), Dangerous but interesting;
will not implement list-item, run-in (Opera only) or table (no IE);
inline-block has incomplete IE6 support and requires -moz-inline-box
for Mozilla. Unknown target milestone.</td></tr>
<tr class="css1"><td>height</td><td>Interesting, why use it? Unknown target milestone.</td></tr>
<tr class="danger css1 impl-yes"><td>list-style-image</td><td>Dangerous?</td></tr>
<tr><td class="css1">height</td><td>Interesting, why use it? Unknown target milestone.</td></tr>
<tr class="danger css1"><td>list-style-image</td><td>Dangerous? Target milestone 1.2</td></tr>
<tr class="impl-no"><td>max-height</td><td rowspan="4">No IE 5/6</td></tr>
<tr class="impl-no"><td>min-height</td></tr>
<tr class="impl-no"><td>max-width</td></tr>
@@ -231,14 +229,14 @@ Mozilla on inside and needs -moz-outline, no IE support.</td></tr>
<tbody>
<tr><th colspan="3">CSS</th></tr>
<tr class="impl-yes"><td>style</td><td>All</td><td>Parser is reasonably functional. Status here doesn't count individual properties.</td></tr>
<tr class="impl-yes"><td>style</td><td>All</td><td>Not all properties may be implemented, parser is good though.</td></tr>
</tbody>
<tbody>
<tr><th colspan="3">Questionable</th></tr>
<tr class="impl-no"><td>accesskey</td><td>A</td><td>May interfere with main interface</td></tr>
<tr class="impl-no"><td>tabindex</td><td>A</td><td>May interfere with main interface</td></tr>
<tr><td>target</td><td>A</td><td>Config enabled, only useful for frame layouts, disallowed in strict</td></tr>
<tr><td>target</td><td>A</td><td>Config enabled, only useful for frame layouts</td></tr>
</tbody>
<tbody>
@@ -266,13 +264,13 @@ Mozilla on inside and needs -moz-outline, no IE support.</td></tr>
<tr><td rowspan="5">align</td><td>CAPTION</td><td>Near-equiv style 'caption-side', drop left and right</td></tr>
<tr><td>IMG</td><td rowspan="2">Margin-left and margin-right = auto or parent div</td></tr>
<tr><td>TABLE</td></tr>
<tr><td>HR</td><td>Near-equivalent style 'text-align' (Works for IE and Opera, but not Firefox). Also try <code>margin-right:auto; margin-left:0;</code> for left or <code>margin-right:0; margin-left:auto;</code> for right (optionally replacing 0 with the original margin for that side)</td></tr>
<tr><td>HR</td><td>Equivalent style 'text-align' (IE tested)</td></tr>
<tr class="impl-yes"><td>H1, H2, H3, H4, H5, H6, P</td><td>Equivalent style 'text-align'</td></tr>
<tr class="required impl-yes"><td>alt</td><td>IMG</td><td>Required, insert image filename if src is present or default invalid image text</td></tr>
<tr><td rowspan="3">bgcolor</td><td>TABLE</td><td>Equivalent style 'background-color'</td></tr>
<tr><td>TR</td><td>Equivalent style 'background-color'</td></tr>
<tr><td rowspan="3">bgcolor</td><td>TABLE</td><td>Equivalent style 'background-color' (IE tested)</td></tr>
<tr><td>TR</td><td>Equivalent style 'background-color' (IE tested)</td></tr>
<tr><td>TD, TH</td><td>Equivalent style 'background-color'</td></tr>
<tr><td>border</td><td>IMG</td><td>Near equivalent style 'border-width', as it only applies when link present</td></tr>
<tr><td>border</td><td>IMG</td><td>Equivalent style 'border-width', only applies when link present</td></tr>
<tr><td>clear</td><td>BR</td><td>Near-equiv style 'clear', transform 'all' into 'both'</td></tr>
<tr class="impl-no"><td>compact</td><td>DL, OL, UL</td><td>Boolean, needs custom CSS class; rarely used anyway</td></tr>
<tr class="required impl-yes"><td>dir</td><td>BDO</td><td>Required, insert ltr (or configuration value) if none</td></tr>
@@ -285,11 +283,11 @@ Mozilla on inside and needs -moz-outline, no IE support.</td></tr>
<tr><td>nowrap</td><td>TD, TH</td><td>Boolean, style 'white-space:nowrap;' (not compat with IE5)</td></tr>
<tr><td>size</td><td>HR</td><td>Near-equiv 'width', needs px suffix if original was pixels</td></tr>
<tr class="required impl-yes"><td>src</td><td>IMG</td><td>Required, insert blank or default img if not set</td></tr>
<tr class="impl-yes"><td>start</td><td>OL</td><td>Poorly supported 'counter-reset', allowed in loose, dropped in strict</td></tr>
<tr><td>start</td><td>OL</td><td>Poorly supported 'counter-reset', transform may not be desirable</td></tr>
<tr><td rowspan="3">type</td><td>LI</td><td rowspan="3">Equivalent style 'list-style-type', different allowed values though. (needs testing)</td></tr>
<tr><td>OL</td></tr>
<tr><td>UL</td></tr>
<tr class="impl-yes"><td>value</td><td>LI</td><td>Poorly supported 'counter-reset', allowed in loose, dropped in strict</td></tr>
<tr><td>value</td><td>LI</td><td>Poorly supported 'counter-reset', transform may not be desirable, see ol.start. Configurable.</td></tr>
<tr><td>vspace</td><td>IMG</td><td>Near-equiv styles 'margin-left' and 'margin-right', needs px suffix, see hspace</td></tr>
<tr><td rowspan="2">width</td><td>HR</td><td rowspan="2">Near-equiv style 'width', needs px suffix if original was pixels</td></tr>
<tr><td>TD, TH</td></tr>

View File

@@ -15,7 +15,6 @@
<div id="filing">Filed under End-User</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
<p>Prior to HTML Purifier 1.2.0, this library blithely accepted user input that
looked like this:</p>

View File

@@ -7,7 +7,6 @@ and it's up to you to provide it the proper information and proper context
to be effective. Things to remember:
1. Character Encoding: UTF-8.
This segment will soon be obsoleted by enduser-utf8.html
Currently, the parser runs under the assumption that it is dealing
with UTF-8. Not ISO-8859-1 or Windows-1252, UTF-8. And definitely not "no
character encoding explicitly stated" or UTF-7. If you're not using UTF-8 as
@@ -28,7 +27,6 @@ this may be configurable in the future. Do you want standards compliance?
The doctype is a good place to start.
3. IDs
This segment is obsoleted by enduser-id.html
They need to be unique, but without some knowledge of the
rest of the document, it's difficult to know what's unique. %Attr.IDBlacklist
needs to be set: we may want to consider disallowing IDs by default to

View File

@@ -1,117 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Explains how to speed up HTML Purifier through caching or inbound filtering." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Speeding up HTML Purifier - HTML Purifier</title>
</head><body>
<h1 class="subtitled">Speeding up HTML Purifier</h1>
<div class="subtitle">...also known as the HELP ME LIBRARY IS TOO SLOW MY PAGE TAKE TOO LONG page</div>
<div id="filing">Filed under End-User</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
<p>HTML Purifier is a very powerful library. But with power comes great
responsibility, in the form of longer execution times. Remember, this
library isn't lightly grazing over submitted HTML: it's deconstructing
the whole thing, rigorously checking the parts, and then putting it back
together. </p>
<p>So, if it so turns out that HTML Purifier is kinda too slow for outbound
filtering, you've got a few options: </p>
<h2>Inbound filtering</h2>
<p>Perform filtering of HTML when it's submitted by the user. Since the
user is already submitting something, an extra half a second tacked on
to the load time probably isn't going to be that huge of a problem.
Then, displaying the content is a simple a manner of outputting it
directly from your database/filesystem. The trouble with this method is
that your user loses the original text, and when doing edits, will be
handling the filtered text. While this may be a good thing, especially
if you're using a WYSIWYG editor, it can also result in data-loss if a
user makes a typo. </p>
<p>Example (non-functional):</p>
<pre>&lt;?php
/**
* FORM SUBMISSION PAGE
* display_error($message) : displays nice error page with message
* display_success() : displays a nice success page
* display_form() : displays the HTML submission form
* database_insert($html) : inserts data into database as new row
*/
if (!empty($_POST)) {
require_once '/path/to/library/HTMLPurifier.auto.php';
require_once 'HTMLPurifier.func.php';
$dirty_html = isset($_POST['html']) ? $_POST['html'] : false;
if (!$dirty_html) {
display_error('You must write some HTML!');
}
$html = HTMLPurifier($dirty_html);
database_insert($html);
display_success();
// notice that $dirty_html is *not* saved
} else {
display_form();
}
?&gt;</pre>
<h2>Caching the filtered output</h2>
<p>Accept the submitted text and put it unaltered into the database, but
then also generate a filtered version and stash that in the database.
Serve the filtered version to readers, and the unaltered version to
editors. If need be, you can invalidate the cache and have the cached
filtered version be regenerated on the first page view. Pros? Full data
retention. Cons? It's more complicated, and opens other editors up to
XSS if they are using a WYSIWYG editor (to fix that, they'd have to be
able to get their hands on the *really* original text served in
plaintext mode). </p>
<p>Example (non-functional):</p>
<pre>&lt;?php
/**
* VIEW PAGE
* display_error($message) : displays nice error page with message
* cache_get($id) : retrieves HTML from fast cache (db or file)
* cache_insert($id, $html) : inserts good HTML into cache system
* database_get($id) : retrieves raw HTML from database
*/
$id = isset($_GET['id']) ? (int) $_GET['id'] : false;
if (!$id) {
display_error('Must specify ID.');
exit;
}
$html = cache_get($id); // filesystem or database
if ($html === false) {
// cache didn't have the HTML, generate it
$raw_html = database_get($id);
require_once '/path/to/library/HTMLPurifier.auto.php';
require_once 'HTMLPurifier.func.php';
$html = HTMLPurifier($raw_html);
cache_insert($id, $html);
}
echo $html;
?&gt;</pre>
<h2>Summary</h2>
<p>In short, inbound filtering is the simple option and caching is the
robust option (albeit with bigger storage requirements). </p>
<p>There is a third option, independent of the two we've discussed: profile
and optimize HTMLPurifier yourself. Be sure to report back your results
if you decide to do that! Especially if you port HTML Purifier to C++.
<tt>;-)</tt></p>
</body>
</html>

View File

@@ -1,640 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Describes the rationale for using UTF-8, the ramifications otherwise, and how to make the switch." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<script defer="defer" type="text/javascript" src="./toc-gen.js"></script>
<style type="text/css">
.minor td {font-style:italic;}
</style>
<title>UTF-8 - HTML Purifier</title>
<!-- Note to users: this document, though professing to be UTF-8, attempts
to use only ASCII characters, because most webservers are configured
to send HTML as ISO-8859-1. So I will, many times, go against my
own advice for sake of portability. -->
</head><body>
<h1>UTF-8</h1>
<div id="filing">Filed under End-User</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
<p>Character encoding and character sets, in truth, are not that
difficult to understand. But if you don't understand them, you are going
to be caught by surprise by some of HTML Purifier's behavior, namely
the fact that it operates UTF-8 or the limitations of the character
encoding transformations it does. This document will walk you through
determining the encoding of your system and how you should handle
this information. It will stay away from excessive discussion on
the internals of character encoding, but offer the information in
asides that can easily be skipped.</p>
<blockquote class="aside">
<div class="label">Asides</div>
<p>Text in this formatting is an <strong>aside</strong>,
interesting tidbits for the curious but not strictly necessary material to
do the tutorial. If you read this text, you'll come out
with a greater understanding of the underlying issues.</p>
</blockquote>
<h2 id="findcharset">Finding the real encoding</h2>
<p>In the beginning, there was ASCII, and things were simple. But they
weren't good, for no one could write in Cryllic or Thai. So there
exploded a proliferation of character encodings to remedy the problem
by extending the characters ASCII could express. This ridiculously
simplified version of the history of character encodings shows us that
there are now many character encodings floating around.</p>
<blockquote class="aside">
<p>A <strong>character encoding</strong> tells the computer how to
interpret raw zeroes and ones into real characters. It
usually does this by pairing numbers with characters.</p>
<p>There are many different types of character encodings floating
around, but the ones we deal most frequently with are ASCII,
8-bit encodings, and Unicode-based encodings.</p>
<ul>
<li><strong>ASCII</strong> is a 7-bit encoding based on the
English alphabet.</li>
<li><strong>8-bit encodings</strong> are extensions to ASCII
that add a potpourri of useful, non-standard characters
like &eacute; and &aelig;. They can only add 127 characters,
so usually only support one script at a time. When you
see a page on the web, chances are it's encoded in one
of these encodings.</li>
<li><strong>Unicode-based encodings</strong> implement the
Unicode standard and include UTF-8, UCS-2 and UTF-16.
They go beyond 8-bits (the first two are variable length,
while the second one uses 16-bits), and support almost
every language in the world. UTF-8 is gaining traction
as the dominant international encoding of the web.</li>
</ul>
</blockquote>
<p>The first step of our journey is to find out what the encoding of
your website is. The most reliable way is to ask your
browser:</p>
<dl>
<dt>Mozilla Firefox</dt>
<dd>Tools &gt; Page Info: Encoding</dd>
<dt>Internet Explorer</dt>
<dd>View &gt; Encoding: bulleted item is unofficial name</dd>
</dl>
<p>Internet Explorer won't give you the mime (i.e. useful/real) name of the
character encoding, so you'll have to look it up using their description.
Some common ones:</p>
<table class="table">
<thead><tr>
<th>IE's Description</th>
<th>Mime Name</th>
</tr></thead>
<tbody>
<tr><th colspan="2">Windows</th></tr>
<tr><td>Arabic (Windows)</td><td>Windows-1256</td></tr>
<tr><td>Baltic (Windows)</td><td>Windows-1257</td></tr>
<tr><td>Central European (Windows)</td><td>Windows-1250</td></tr>
<tr><td>Cyrillic (Windows)</td><td>Windows-1251</td></tr>
<tr><td>Greek (Windows)</td><td>Windows-1253</td></tr>
<tr><td>Hebrew (Windows)</td><td>Windows-1255</td></tr>
<tr><td>Thai (Windows)</td><td>TIS-620</td></tr>
<tr><td>Turkish (Windows)</td><td>Windows-1254</td></tr>
<tr><td>Vietnamese (Windows)</td><td>Windows-1258</td></tr>
<tr><td>Western European (Windows)</td><td>Windows-1252</td></tr>
</tbody>
<tbody>
<tr><th colspan="2">ISO</th></tr>
<tr><td>Arabic (ISO)</td><td>ISO-8859-6</td></tr>
<tr><td>Baltic (ISO)</td><td>ISO-8859-4</td></tr>
<tr><td>Central European (ISO)</td><td>ISO-8859-2</td></tr>
<tr><td>Cyrillic (ISO)</td><td>ISO-8859-5</td></tr>
<tr class="minor"><td>Estonian (ISO)</td><td>ISO-8859-13</td></tr>
<tr class="minor"><td>Greek (ISO)</td><td>ISO-8859-7</td></tr>
<tr><td>Hebrew (ISO-Logical)</td><td>ISO-8859-8-l</td></tr>
<tr><td>Hebrew (ISO-Visual)</td><td>ISO-8859-8</td></tr>
<tr class="minor"><td>Latin 9 (ISO)</td><td>ISO-8859-15</td></tr>
<tr class="minor"><td>Turkish (ISO)</td><td>ISO-8859-9</td></tr>
<tr><td>Western European (ISO)</td><td>ISO-8859-1</td></tr>
</tbody>
<tbody>
<tr><th colspan="2">Other</th></tr>
<tr><td>Chinese Simplified (GB18030)</td><td>GB18030</td></tr>
<tr><td>Chinese Simplified (GB2312)</td><td>GB2312</td></tr>
<tr><td>Chinese Simplified (HZ)</td><td>HZ</td></tr>
<tr><td>Chinese Traditional (Big5)</td><td>Big5</td></tr>
<tr><td>Japanese (Shift-JIS)</td><td>Shift_JIS</td></tr>
<tr><td>Japanese (EUC)</td><td>EUC-JP</td></tr>
<tr><td>Korean</td><td>EUC-KR</td></tr>
<tr><td>Unicode (UTF-8)</td><td>UTF-8</td></tr>
</tbody>
</table>
<p>Internet Explorer does not recognize some of the more obscure
character encodings, and having to lookup the real names with a table
is a pain, so I recommend using Mozilla Firefox to find out your
character encoding.</p>
<h2 id="findmetacharset">Finding the embedded encoding</h2>
<p>At this point, you may be asking, &quot;Didn't we already find out our
encoding?&quot; Well, as it turns out, there are multiple places where
a web developer can specify a character encoding, and one such place
is in a <code>META</code> tag:</p>
<pre>&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;</pre>
<p>You'll find this in the <code>HEAD</code> section of an HTML document.
The text to the right of <code>charset=</code> is the &quot;claimed&quot;
encoding: the HTML claims to be this encoding, but whether or not this
is actually the case depends on other factors. For now, take note
if your <code>META</code> tag claims that either:</p>
<ol>
<li>The character encoding is the same as the one reported by the
browser,</li>
<li>The character encoding is different from the browser's, or</li>
<li>There is no <code>META</code> tag at all! (horror, horror!)</li>
</ol>
<h2 id="fixcharset">Fixing the encoding</h2>
<p>If your <code>META</code> encoding and your real encoding match,
savvy! You can skip this section. If they don't...</p>
<h3 id="fixcharset-none">No embedded encoding</h3>
<p>If this is the case, you'll want to add in the appropriate
<code>META</code> tag to your website. It's as simple as copy-pasting
the code snippet above and replacing UTF-8 with whatever is the mime name
of your real encoding.</p>
<blockquote class="aside">
<p>For all those skeptics out there, there is a very good reason
why the character encoding should be explicitly stated. When the
browser isn't told what the character encoding of a text is, it
has to guess: and sometimes the guess is wrong. Hackers can manipulate
this guess in order to slip XSS pass filters and then fool the
browser into executing it as active code. A great example of this
is the <a href="http://shiflett.org/archive/177">Google UTF-7
exploit</a>.</p>
<p>You might be able to get away with not specifying a character
encoding with the <code>META</code> tag as long as your webserver
sends the right Content-Type header, but why risk it? Besides, if
the user downloads the HTML file, there is no longer any webserver
to define the character encoding.</p>
</blockquote>
<h3 id="fixcharset-diff">Embedded encoding disagrees</h3>
<p>This is an extremely common mistake: another source is telling
the browser what the
character encoding is and is overriding the embedded encoding. This
source usually is the Content-Type HTTP header that the webserver (i.e.
Apache) sends. A usual Content-Type header sent with a page might
look like this:</p>
<pre>Content-Type: text/html; charset=ISO-8859-1</pre>
<p>Notice how there is a charset parameter: this is the webserver's
way of telling a browser what the character encoding is, much like
the <code>META</code> tags we touched upon previously.</p>
<blockquote class="aside"><p>In fact, the <code>META</code> tag is
designed as a substitute for the HTTP header for contexts where
sending headers is impossible (such as locally stored files without
a webserver). Thus the name <code>http-equiv</code> (HTTP equivalent).
</p></blockquote>
<p>There are two ways to go about fixing this: changing the <code>META</code>
tag to match the HTTP header, or changing the HTTP header to match
the <code>META</code> tag. How do we know which to do? It depends
on the website's content: after all, headers and tags are only ways of
describing the actual characters on the web page.</p>
<p>If your website:</p>
<dl>
<dt>...only uses ASCII characters,</dt>
<dd>Either way is fine, but I recommend switching both to
UTF-8 (more on this later).</dd>
<dt>...uses special characters, and they display
properly,</dt>
<dd>Change the embedded encoding to the server encoding.</dd>
<dt>...uses special characters, but users often complain that
they come out garbled,</dt>
<dd>Change the server encoding to the embedded encoding.</dd>
</dl>
<p>Changing a META tag is easy: just swap out the old encoding
for the new. Changing the server (HTTP header) encoding, however,
is slightly more difficult.</p>
<h3 id="fixcharset-server">Changing the server encoding</h3>
<h4 id="fixcharset-server-php">PHP header() function</h4>
<p>The simplest way to handle this problem is to send the encoding
yourself, via your programming language. Since you're using HTML
Purifier, I'll assume PHP, although it's not too difficult to do
similar things in
<a href="http://www.w3.org/International/O-HTTP-charset#scripting">other
languages</a>. The appropriate code is:</p>
<pre><a href="http://php.net/function.header">header</a>('Content-Type:text/html; charset=UTF-8');</pre>
<p>...replacing UTF-8 with whatever your embedded encoding is.
This code must come before any output, so be careful about
stray whitespace in your application.</p>
<h4 id="fixcharset-server-phpini">PHP ini directive</h4>
<p>PHP also has a neat little ini directive that can save you a
header call: <code><a href="http://php.net/ini.core#ini.default-charset">default_charset</a></code>. Using this code:</p>
<pre><a href="http://php.net/function.ini_set">ini_set</a>('default_charset', 'UTF-8');</pre>
<p>...will also do the trick. If PHP is running as an Apache module (and
not as FastCGI, consult
<a href="http://php.net/phpinfo">phpinfo</a>() for details), you can even use htaccess do apply this property
globally:</p>
<pre><a href="http://php.net/configuration.changes#configuration.changes.apache">php_value</a> default_charset &quot;UTF-8&quot;</pre>
<blockquote class="aside"><p>As with all INI directives, this can
also go in your php.ini file. Some hosting providers allow you to customize
your own php.ini file, ask your support for details. Use:</p>
<pre>default_charset = &quot;utf-8&quot;</pre></blockquote>
<h4 id="fixcharset-server-nophp">Non-PHP</h4>
<p>You may, for whatever reason, may need to set the character encoding
on non-PHP files, usually plain ol' HTML files. Doing this
is more of a hit-or-miss process: depending on the software being
used as a webserver and the configuration of that software, certain
techniques may work, or may not work.</p>
<h4 id="fixcharset-server-htaccess">.htaccess</h4>
<p>On Apache, you can use an .htaccess file to change the character
encoding. I'll defer to
<a href="http://www.w3.org/International/questions/qa-htaccess-charset">W3C</a>
for the in-depth explanation, but it boils down to creating a file
named .htaccess with the contents:</p>
<pre><a href="http://httpd.apache.org/docs/1.3/mod/mod_mime.html#addcharset">AddCharset</a> UTF-8 .html</pre>
<p>Where UTF-8 is replaced with the character encoding you want to
use and .html is a file extension that this will be applied to. This
character encoding will then be set for any file directly in
or in the subdirectories of directory you place this file in.</p>
<p>If you're feeling particularly courageous, you can use:</p>
<pre><a href="http://httpd.apache.org/docs/1.3/mod/core.html#adddefaultcharset">AddDefaultCharset</a> UTF-8</pre>
<p>...which changes the character set Apache adds to any document that
doesn't have any Content-Type parameters. This directive, which the
default configuration file sets to iso-8859-1 for security
reasons, is probably why your headers mismatch
with the <code>META</code> tag. If you would prefer Apache not to be
butting in on your character encodings, you can tell it not
to send anything at all:</p>
<pre><a href="http://httpd.apache.org/docs/1.3/mod/core.html#adddefaultcharset">AddDefaultCharset</a> Off</pre>
<p>...making your <code>META</code> tags the sole source of
character encoding information. In these cases, it is
<em>especially</em> important to make sure you have valid <code>META</code>
tags on your pages and all the text before them is ASCII.</p>
<blockquote class="aside"><p>These directives can also be
placed in httpd.conf file for Apache, but
in most shared hosting situations you won't be able to edit this file.
</p></blockquote>
<h4 id="fixcharset-server-ext">File extensions</h4>
<p>If you're not allowed to use .htaccess files, you can often
piggy-back off of Apache's default AddCharset declarations to get
your files in the proper extension. Here are Apache's default
character set declarations:</p>
<table class="table">
<thead><tr>
<th>Charset</th>
<th>File extension(s)</th>
</tr></thead>
<tbody>
<tr><td>ISO-8859-1</td><td>.iso8859-1 .latin1</td></tr>
<tr><td>ISO-8859-2</td><td>.iso8859-2 .latin2 .cen</td></tr>
<tr><td>ISO-8859-3</td><td>.iso8859-3 .latin3</td></tr>
<tr><td>ISO-8859-4</td><td>.iso8859-4 .latin4</td></tr>
<tr><td>ISO-8859-5</td><td>.iso8859-5 .latin5 .cyr .iso-ru</td></tr>
<tr><td>ISO-8859-6</td><td>.iso8859-6 .latin6 .arb</td></tr>
<tr><td>ISO-8859-7</td><td>.iso8859-7 .latin7 .grk</td></tr>
<tr><td>ISO-8859-8</td><td>.iso8859-8 .latin8 .heb</td></tr>
<tr><td>ISO-8859-9</td><td>.iso8859-9 .latin9 .trk</td></tr>
<tr><td>ISO-2022-JP</td><td>.iso2022-jp .jis</td></tr>
<tr><td>ISO-2022-KR</td><td>.iso2022-kr .kis</td></tr>
<tr><td>ISO-2022-CN</td><td>.iso2022-cn .cis</td></tr>
<tr><td>Big5</td><td>.Big5 .big5 .b5</td></tr>
<tr><td>WINDOWS-1251</td><td>.cp-1251 .win-1251</td></tr>
<tr><td>CP866</td><td>.cp866</td></tr>
<tr><td>KOI8-r</td><td>.koi8-r .koi8-ru</td></tr>
<tr><td>KOI8-ru</td><td>.koi8-uk .ua</td></tr>
<tr><td>ISO-10646-UCS-2</td><td>.ucs2</td></tr>
<tr><td>ISO-10646-UCS-4</td><td>.ucs4</td></tr>
<tr><td>UTF-8</td><td>.utf8</td></tr>
<tr><td>GB2312</td><td>.gb2312 .gb </td></tr>
<tr><td>utf-7</td><td>.utf7</td></tr>
<tr><td>EUC-TW</td><td>.euc-tw</td></tr>
<tr><td>EUC-JP</td><td>.euc-jp</td></tr>
<tr><td>EUC-KR</td><td>.euc-kr</td></tr>
<tr><td>shift_jis</td><td>.sjis</td></tr>
</tbody>
</table>
<p>So, for example, a file named <code>page.utf8.html</code> or
<code>page.html.utf8</code> will probably be sent with the UTF-8 charset
attached, the difference being that if there is an
<code>AddCharset charset .html</code> declaration, it will override
the .utf8 extension in <code>page.utf8.html</code> (precedence moves
from right to left). By default, Apache has no such declaration.</p>
<h4 id="fixcharset-server-iis">Microsoft IIS</h4>
<p>If anyone can contribute information on how to configure Microsoft
IIS to change character encodings, I'd be grateful.</p>
<h3 id="fixcharset-xml">XML</h3>
<p><code>META</code> tags are the most common source of embedded
encodings, but they can also come from somewhere else: XML
processing instructions. They look like:</p>
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</pre>
<p>...and are most often found in XML documents (including XHTML).</p>
<p>For XHTML, this processing instruction theoretically
overrides the <code>META</code> tag. In reality, this happens only when the
XHTML is actually served as legit XML and not HTML, which is almost
always never due to Internet Explorer's lack of support for
<code>application/xhtml+xml</code> (even though doing so is often
argued to be <a href="http://www.hixie.ch/advocacy/xhtml">good practice</a>).</p>
<p>For XML, however, this processing instruction is extremely important.
Since most webservers are not configured to send charsets for .xml files,
this is the only thing a parser has to go on. Furthermore, the default
for XML files is UTF-8, which often butts heads with more common
ISO-8859-1 encoding (you see this in garbled RSS feeds).</p>
<p>In short, if you use XHTML and have gone through the
trouble of adding the XML header, be sure to make sure it jives
with your <code>META</code> tags and HTTP headers.</p>
<h3>Inside the process</h3>
<p>This section is not required reading,
but may answer some of your questions on what's going on in all
this character encoding hocus pocus. If you're interested in
moving on to the next phase, skip this section.</p>
<p>A logical question that follows all of our wheeling and dealing
with multiple sources of character encodings is &quot;Why are there
so many options?&quot; To answer this question, we have to turn
back our definition of character encodings: they allow a program
to interpret bytes into human-readable characters.</p>
<p>Thus, a chicken-egg problem: a character encoding
is necessary to interpret the
text of a document. A <code>META</code> tag is in the text of a document.
The <code>META</code> tag gives the character encoding. How can we
determine the contents of a <code>META</code> tag, inside the text,
if we don't know it's character encoding? And how do we figure out
the character encoding, if we don't know the contents of the
<code>META</code> tag?</p>
<p>Fortunantely for us, the characters we need to write the
<code>META</code> are in ASCII, which is pretty much universal
over every character encoding that is in common use today. So,
all the web-browser has to do is parse all the way down until
it gets to the Content-Type tag, extract the character encoding
tag, then re-parse the document according to this new information.</p>
<p>Obviously this is complicated, so browsers prefer the simpler
and more efficient solution: get the character encoding from a
somewhere other than the document itself, i.e. the HTTP headers,
much to the chagrin of HTML authors who can't set these headers.</p>
<h2 id="whyutf8">Why UTF-8?</h2>
<p>So, you've gone through all the trouble of ensuring that your
server and embedded characters all line up properly and are
present. Good job: at
this point, you could quit and rest easy knowing that your pages
are not vulnerable to character encoding style XSS attacks.
However, just as having a character encoding is better than
having no character encoding at all, having UTF-8 as your
character encoding is better than having some other random
character encoding, and the next step is to convert to UTF-8.
But why?</p>
<h3 id="whyutf8-i18n">Internationalization</h3>
<p>Many software projects, at one point or another, suddenly realize
that they should be supporting more than one language. Even regular
usage in one language sometimes requires the occasional special character
that, without surprise, is not available in your character set. Sometimes
developers get around this by adding support for multiple encodings: when
using Chinese, use Big5, when using Japanese, use Shift-JIS, when
using Greek, etc. Other times, they use character entities with great
zeal.</p>
<p>UTF-8, however, obviates the need for any of these complicated
measures. After getting the system to use UTF-8 and adjusting for
sources that are outside the hand of the browser (more on this later),
UTF-8 just works. You can use it for any language, even many languages
at once, you don't have to worry about managing multiple encodings,
you don't have to use those user-unfriendly entities.</p>
<h3 id="whyutf8-user">User-friendly</h3>
<p>Websites encoded in Latin-1 (ISO-8859-1) which ocassionally need
a special character outside of their scope often will use a character
entity to achieve the desired effect. For instance, &theta; can be
written <code>&amp;theta;</code>, regardless of the character encoding's
support of Greek letters.</p>
<p>This works nicely for limited use of special characters, but
say you wanted this sentence of Chinese text: &#28608;&#20809;,
&#36889;&#20841;&#20491;&#23383;&#26159;&#29978;&#40636;&#24847;&#24605;.
The entity-ized version would look like this:</p>
<pre>&amp;#28608;&amp;#20809;, &amp;#36889;&amp;#20841;&amp;#20491;&amp;#23383;&amp;#26159;&amp;#29978;&amp;#40636;&amp;#24847;&amp;#24605;</pre>
<p>Extremely inconvenient for those of us who actually know what
character entities are, totally unintelligible to poor users who don't!
Even the slightly more user-friendly, &quot;intelligible&quot; character
entities like <code>&amp;theta;</code> will leave users who are
uninterested in learning HTML scratching their heads. On the other
hand, if they see &theta; in an edit box, they'll know that it's a
special character, and treat it accordingly, even if they don't know
how to write that character themselves.</p>
<blockquote class="aside"><p>Wikipedia is a great case study for
an application that originally used ISO-8859-1 but switched to UTF-8
when it became far to cumbersome to support foreign languages. Bots
will now actually go through articles and convert character entities
to their corresponding real characters for the sake of user-friendliness
and searcheability. See
<a href="http://meta.wikimedia.org/wiki/Help:Special_characters">Meta's
page on special characters</a> for more details.
</p></blockquote>
<h3 id="whyutf8-forms">Forms</h3>
<p>While we're on the tack of users, how do non-UTF-8 web forms deal
with characters that our outside of their character set? Rather than
discuss what UTF-8 does right, we're going to show what could go wrong
if you didn't use UTF-8 and people tried to use characters outside
of your character encoding.</p>
<p>The troubles are large, extensive, and extremely difficult to fix (or,
at least, difficult enough that if you had the time and resources to invest
in doing the fix, you would be probably better off migrating to UTF-8).
There are two types of form submission: <code>application/x-www-form-urlencoded</code>
which is used for GET and by default for POST, and <code>multipart/form-data</code>
which may be used by POST, and is required when you want to upload
files.</p>
<p>The following is a summarization of notes from
<a href="http://ppewww.physics.gla.ac.uk/~flavell/charset/form-i18n.html">
<code>FORM</code> submission and i18n</a>. That document contains lots
of useful information, but is written in a rambly manner, so
here I try to get right to the point.</p>
<h4 id="whyutf8-forms-urlencoded"><code>application/x-www-form-urlencoded</code></h4>
<p>This is the Content-Type that GET requests must use, and POST requests
use by default. It involves the ubiquituous percent encoding format that
looks something like: <code>%C3%86</code>. There is no official way of
determining the character encoding of such a request, since the percent
encoding operates on a byte level, so it is usually assumed that it
is the same as the encoding the page containing the form was submitted
in. You'll run into very few problems if you only use characters in
the character encoding you chose.</p>
<p>However, once you start adding characters outside of your encoding
(and this is a lot more common than you may think: take curly
&quot;smart&quot; quotes from Microsoft as an example),
a whole manner of strange things start to happen. Depending on the
browser you're using, they might:</p>
<ul>
<li>Replace the unsupported characters with useless question marks,</li>
<li>Attempt to fix the characters (example: smart quotes to regular quotes),</li>
<li>Replace the character with a character entity, or</li>
<li>Send it anyway as a different character encoding mixed in
with the original encoding (usually Windows-1252 rather than
iso-8859-1 or UTF-8 interspersed in 8-bit)</li>
</ul>
<p>To properly guard against these behaviors, you'd have to sniff out
the browser agent, compile a database of different behaviors, and
take appropriate conversion action against the string (disregarding
a spate of extremely mysterious, random and devastating bugs Internet
Explorer manifests every once in a while). Or you could
use UTF-8 and rest easy knowing that none of this could possibly happen
since UTF-8 supports every character.</p>
<h4 id="whyutf8-forms-multipart"><code>multipart/form-data</code></h4>
<p>Multipart form submission takes a way a lot of the ambiguity
that percent-encoding had: the server now can explicitly ask for
certain encodings, and the client can explicitly tell the server
during the form submission what encoding the fields are in.</p>
<p>There are two ways you go with this functionality: leave it
unset and have the browser send in the same encoding as the page,
or set it to UTF-8 and then do another conversion server-side.
Each method has deficiencies, especially the former.</p>
<p>If you tell the browser to send the form in the same encoding as
the page, you still have the trouble of what to do with characters
that are outside of the character encoding's range. The behavior, once
again, varies: Firefox 2.0 entity-izes them while Internet Explorer
7.0 mangles them beyond intelligibility. For serious I18N purposes,
this is not an option.</p>
<p>The other possibility is to set Accept-Encoding to UTF-8, which
begs the question: Why aren't you using UTF-8 for everything then?
This route is more palatable, but there's a notable caveat: your data
will come in as UTF-8, so you will have to explicitly convert it into
your favored local character encoding.</p>
<p>I object to this approach on idealogical grounds: you're
digging yourself deeper into
the hole when you could have been converting to UTF-8
instead. And, of course, you can't use this method for GET requests.</p>
<h3 id="whyutf8-support">Well supported</h3>
<p>Almost every modern browser in the wild today has full UTF-8 and Unicode
support: the number of troublesome cases can be counted with the
fingers of one hand, and these browsers usually have trouble with
other character encodings too. Problems users usually encounter stem
from the lack of appropriate fonts to display the characters (once
again, this applies to all character encodings and HTML entities) or
Internet Explorer's lack of intelligent font picking (which can be
worked around).</p>
<p>We will go into more detail about how to deal with edge cases in
the browser world in the Migration section, but rest assured that
converting to UTF-8, if done correctly, will not result in users
hounding you about broken pages.</p>
<h3 id="whyutf8-htmlpurifier">HTML Purifier</h3>
<p>And finally, we get to HTML Purifier.</p>
<h2 id="migrate">Migrate to UTF-8</h2>
<h3 id="migrate-editor">Text editor</h3>
<h3 id="migrate-db">Configuring your database</h3>
<h3 id="migrate-convert">Convert old text</h3>
<h3 id="migrate-bom">Byte Order Mark (headers already sent!)</h3>
<h3 id="migrate-variablewidth">Dealing with variable width in functions</h3>
<h2 id="externallinks">Further Reading</h2>
<p>Many other developers have already discussed the subject of Unicode,
UTF-8 and internationalization, and I would like to defer to them for
a more in-depth look into character sets and encodings.</p>
<ul>
<li><a href="http://www.joelonsoftware.com/articles/Unicode.html">
The Absolute Minimum Every Software Developer Absolutely,
Positively Must Know About Unicode and Character Sets
(No Excuses!)</a> by Joel Spolsky, provides a <em>very</em>
good high-level look at Unicode and character sets in general.</li>
<li><a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8 on Wikipedia</a>,
provides a lot of useful details into the innards of UTF-8, although
it may be a little off-putting to people who don't know much
about Unicode to begin with.</li>
</ul>
</body>
</html>

View File

@@ -1,152 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Explains how to safely allow the embedding of flash from trusted sites in HTML Purifier." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Embedding YouTube Videos - HTML Purifier</title>
</head><body>
<h1 class="subtitled">Embedding YouTube Videos</h1>
<div class="subtitle">...as well as other dangerous active content</div>
<div id="filing">Filed under End-User</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
<p>Clients like their YouTube videos. It gives them a warm fuzzy feeling when
they see a neat little embedded video player on their websites that can play
the latest clips from their documentary &quot;Fido and the Bones of Spring&quot;.
All joking aside, the ability to embed YouTube videos or other active
content in their pages is something that a lot of people like.</p>
<p>This is a <em>bad</em> idea. The moment you embed anything untrusted,
you will definitely be slammed by a manner of nasties that can be
embedded in things from your run of the mill Flash movie to
<a href="http://blog.spywareguide.com/2006/12/myspace_phish_attack_leads_use.html">Quicktime movies</a>.
Even <code>img</code> tags, which HTML Purifier allows by default, can be
dangerous. Be distrustful of anything that tells a browser to load content
from another website automatically.</p>
<p>Luckily for us, however, whitelisting saves the day. Sure, letting users
include any old random flash file could be dangerous, but if it's
from a specific website, it probably is okay. If no amount of pleading will
convince the people upstairs that they should just settle with just linking
to their movies, you may find this technique very useful.</p>
<h2>Looking in</h2>
<p>Below is custom code that allows users to embed
YouTube videos. This is not favoritism: this trick can easily be adapted for
other forms of embeddable content.</p>
<p>Usually, websites like YouTube give us boilerplate code that you can insert
into your documents. YouTube's code goes like this:</p>
<pre>
&lt;object width=&quot;425&quot; height=&quot;350&quot;&gt;
&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/AyPzM5WK8ys&quot; /&gt;
&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot; /&gt;
&lt;embed src=&quot;http://www.youtube.com/v/AyPzM5WK8ys&quot;
type=&quot;application/x-shockwave-flash&quot;
wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;350&quot; /&gt;
&lt;/object&gt;
</pre>
<p>There are two things to note about this code:</p>
<ol>
<li><code>&lt;embed&gt;</code> is not recognized by W3C, so if you want
standards-compliant code, you'll have to get rid of it.</li>
<li>The code is exactly the same for all instances, except for the
identifier <tt>AyPzM5WK8ys</tt> which tells us which movie file
to retrieve.</li>
</ol>
<p>What point 2 means is that if we have code like <code>&lt;span
class=&quot;embed-youtube&quot;&gt;AyPzM5WK8ys&lt;/span&gt;</code> your
application can reconstruct the full object from this small snippet that
passes through HTML Purifier <em>unharmed</em>.
<a href="http://hp.jpsband.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/Filter/YouTube.php">Show me the code!</a></p>
<p>And the corresponding usage:</p>
<pre>&lt;?php
// assuming $purifier is an instance of HTMLPurifier
require_once 'HTMLPurifier/Filter/YouTube.php';
$purifier-&gt;addFilter(new HTMLPurifier_Filter_YouTube());
?&gt;</pre>
<p>There is a bit going in the two code snippets, so let's explain.</p>
<ol>
<li>This is a Filter object, which intercepts the HTML that is
coming into and out of the purifier. You can add as many
filter objects as you like. <code>preFilter()</code>
processes the code before it gets purified, and <code>postFilter()</code>
processes the code afterwards. So, we'll use <code>preFilter()</code> to
replace the object tag with a <code>span</code>, and <code>postFilter()</code>
to restore it.</li>
<li>The first preg_replace call replaces any YouTube code users may have
embedded into the benign span tag. Span is used because it is inline,
and objects are inline too. We are very careful to be extremely
restrictive on what goes inside the span tag, as if an errant code
gets in there it could get messy.</li>
<li>The HTML is then purified as usual.</li>
<li>Then, another preg_replace replaces the span tag with a fully fledged
object. Note that the embed is removed, and, in its place, a data
attribute was added to the object. This makes the tag standards
compliant! It also breaks Internet Explorer, so we add in a bit of
conditional comments with the old embed code to make it work again.
It's all quite convoluted but works.</li>
</ol>
<h2>Warning</h2>
<p>There are a number of possible problems with the code above, depending
on how you look at it.</p>
<h3>Cannot change width and height</h3>
<p>The width and height of the final YouTube movie cannot be adjusted. This
is because I am lazy. If you really insist on letting users change the size
of the movie, what you need to do is package up the attributes inside the
span tag (along with the movie ID). It gets complicated though: a malicious
user can specify an outrageously large height and width and attempt to crash
the user's operating system/browser. You need to either cap it by limiting
the amount of digits allowed in the regex or using a callback to check the
number.</p>
<h3>Trusts media's host's security</h3>
<p>By allowing this code onto our website, we are trusting that YouTube has
tech-savvy enough people not to allow their users to inject malicious
code into the Flash files. An exploit on YouTube means an exploit on your
site. Even though YouTube is run by the reputable Google, it
<a href="http://ha.ckers.org/blog/20061213/google-xss-vuln/">doesn't</a>
mean they are
<a href="http://ha.ckers.org/blog/20061208/xss-in-googles-orkut/">invulnerable.</a>
You're putting a certain measure of the job on an external provider (just as
you have by entrusting your user input to HTML Purifier), and
it is important that you are cognizant of the risk.</p>
<h3>Poorly written adaptations compromise security</h3>
<p>This should go without saying, but if you're going to adapt this code
for Google Video or the like, make sure you do it <em>right</em>. It's
extremely easy to allow a character too many in <code>postFilter()</code> and
suddenly you're introducing XSS into HTML Purifier's XSS free output. HTML
Purifier may be well written, but it cannot guard against vulnerabilities
introduced after it has finished.</p>
<h2>Help out!</h2>
<p>If you write a filter for your favorite video destination (or anything
like that, for that matter), send it over and it might get included
with the core!</p>
</body>
</html>

View File

@@ -1,14 +1,15 @@
<?php exit;
<?php
// This file demonstrates basic usage of HTMLPurifier.
require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';
exit; // not to be called directly, it will fail fantastically!
set_include_path('/path/to/htmlpurifier/library' . PATH_SEPARATOR . get_include_path());
require_once 'HTMLPurifier.php';
$purifier = new HTMLPurifier();
$html = '<b>Simple and short';
$pure_html = $purifier->purify($html);
echo $pure_html;
?>

View File

@@ -1,66 +1,34 @@
<?php
// using _REQUEST because we accept GET and POST requests
header('Content-type:text/html;charset=UTF-8');
$content = empty($_REQUEST['xml']) ? 'text/html' : 'application/xhtml+xml';
header("Content-type:$content;charset=UTF-8");
// prevent PHP versions with shorttags from barfing
echo '<?xml version="1.0" encoding="UTF-8" ?>
';
function getFormMethod() {
return (isset($_REQUEST['post'])) ? 'post' : 'get';
}
if (empty($_REQUEST['strict'])) {
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
?><!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<?php
} else {
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<?php
}
?>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<html>
<head>
<title>HTML Purifier Live Demo</title>
<title>HTMLPurifier Live Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>HTML Purifier Live Demo</h1>
<h1>HTMLPurifier Live Demo</h1>
<?php
require_once '../../library/HTMLPurifier.auto.php';
set_include_path('../../library' . PATH_SEPARATOR . get_include_path());
require_once 'HTMLPurifier.php';
if (!empty($_REQUEST['html'])) { // start result
if (!empty($_POST['html'])) {
if (strlen($_REQUEST['html']) > 50000) {
?>
<p>Request exceeds maximum allowed text size of 50kb.</p>
<?php
} else { // start main processing
$html = get_magic_quotes_gpc() ? stripslashes($_REQUEST['html']) : $_REQUEST['html'];
$html = get_magic_quotes_gpc() ? stripslashes($_POST['html']) : $_POST['html'];
$config = HTMLPurifier_Config::createDefault();
$config->set('Core', 'TidyFormat', !empty($_REQUEST['tidy']));
$config->set('HTML', 'Strict', !empty($_REQUEST['strict']));
$config->set('Core', 'TidyFormat', !empty($_POST['tidy']));
$purifier = new HTMLPurifier($config);
$pure_html = $purifier->purify($html);
?>
<p>Here is your purified HTML:</p>
<div style="border:5px solid #CCC;margin:0 10%;padding:1em;">
<?php if(getFormMethod() == 'get') { ?>
<div style="float:right;">
<a href="http://validator.w3.org/check?uri=referer"><img
src="http://www.w3.org/Icons/valid-xhtml10"
alt="Valid XHTML 1.0 Transitional" height="31" width="88" style="border:0;" /></a>
</div>
<?php } ?>
<?php
echo $pure_html;
@@ -75,34 +43,23 @@ echo htmlspecialchars($pure_html, ENT_COMPAT, 'UTF-8');
?></pre>
<?php
if (getFormMethod() == 'post') { // start POST validation notice
?>
<p>If you would like to validate the code with
<a href="http://validator.w3.org/#validate-by-input">W3C's
validator</a>, copy and paste the <em>entire</em> demo page's source.</p>
<?php
} // end POST validation notice
} // end main processing
// end result
} else {
?>
<p>Welcome to the live demo. Enter some HTML and see how HTML Purifier
<p>Welcome to the live demo. Enter some HTML and see how HTMLPurifier
will filter it.</p>
<?php
}
?>
<form id="filter" action="demo.php<?php
echo '?' . getFormMethod();
if (isset($_REQUEST['profile']) || isset($_REQUEST['XDEBUG_PROFILE'])) {
echo '&amp;XDEBUG_PROFILE=1';
} ?>" method="<?php echo getFormMethod(); ?>">
<form name="filter" action="demo.php<?php
if (isset($_GET['profile']) || isset($_GET['XDEBUG_PROFILE'])) {
echo '?XDEBUG_PROFILE=1';
} ?>" method="post">
<fieldset>
<legend>HTML Purifier Input (<?php echo getFormMethod(); ?>)</legend>
<legend>HTML</legend>
<textarea name="html" cols="60" rows="15"><?php
if (isset($html)) {
@@ -110,27 +67,13 @@ if (isset($html)) {
HTMLPurifier_Encoder::cleanUTF8($html), ENT_COMPAT, 'UTF-8');
}
?></textarea>
<?php if (getFormMethod() == 'get') { ?>
<p><strong>Warning:</strong> GET request method can only hold
8129 characters (probably less depending on your browser).
If you need to test anything
larger than that, try the <a href="demo.php?post">POST form</a>.</p>
<?php } ?>
<?php if (extension_loaded('tidy')) { ?>
<div>Nicely format output with Tidy? <input type="checkbox" value="1"
name="tidy"<?php if (!empty($_REQUEST['tidy'])) echo ' checked="checked"'; ?> /></div>
<?php } ?>
<div>XHTML 1.0 Strict output? <input type="checkbox" value="1"
name="strict"<?php if (!empty($_REQUEST['strict'])) echo ' checked="checked"'; ?> /></div>
<div>Serve as application/xhtml+xml? (not for IE) <input type="checkbox" value="1"
name="xml"<?php if (!empty($_REQUEST['xml'])) echo ' checked="checked"'; ?> /></div>
<div>Nicely format output with Tidy? <input type="checkbox" value="1"
name="tidy"<?php if (!empty($_POST['tidy'])) echo ' checked="checked"'; ?> /></div>
<div>
<input type="submit" value="Submit" name="submit" class="button" />
</div>
</fieldset>
</form>
<p>Return to <a href="http://hp.jpsband.org/">HTML Purifier's home page</a>.
Try the form in <a href="demo.php?get">GET</a> and <a href="demo.php?post">POST</a> request
flavors (GET is easy to validate with W3C, but POST allows larger inputs).</p>
<p>Return to <a href="http://hp.jpsband.org/">HTMLPurifier's home page</a>.</p>
</body>
</html>

View File

@@ -13,7 +13,7 @@
<h1>Documentation</h1>
<p><strong><a href="http://hp.jpsband.org/">HTML Purifier</a></strong> has documentation for all types of people.
<p><strong>HTML Purifier</strong> has documentation for all types of people.
Here is an index of all of them.</p>
<h2>End-user</h2>
@@ -23,16 +23,7 @@ information for casual developers using HTML Purifier.</p>
<dl>
<dt><a href="enduser-id.html">IDs</a></dt>
<dd>Explains various methods for allowing IDs in documents safely.</dd>
<dt><a href="enduser-youtube.html">Embedding YouTube videos</a></dt>
<dd>Explains how to safely allow the embedding of flash from trusted sites.</dd>
<dt><a href="enduser-slow.html">Speeding up HTML Purifier</a></dt>
<dd>Explains how to speed up HTML Purifier through caching or inbound filtering.</dd>
<dt><a href="enduser-utf8.html">UTF-8</a></dt>
<dd>Describes the rationale for using UTF-8, the ramifications otherwise, and how to make the switch.</dd>
<dd>Explains various methods for allowing IDs in documents safely in HTML Purifier.</dd>
</dl>
@@ -74,85 +65,6 @@ that may not directly discuss HTML Purifier.</p>
<dd>Credits and links to DevNetwork forum topics.</dd>
</dl>
<h2>Internal memos</h2>
<p>Plaintext documents that are more for use by active developers of
the code. They may be upgraded to HTML files or stay as TXT scratchpads.</p>
<table class="table">
<thead><tr>
<th width="10%">Type</th>
<th width="20%">Name</th>
<th>Description</th>
</tr></thead>
<tbody>
<tr>
<td>End-user</td>
<td><a href="enduser-overview.txt">Overview</a></td>
<td>High level overview of the general control flow (mostly obsolete).</td>
</tr>
<tr>
<td>End-user</td>
<td><a href="enduser-security.txt">Security</a></td>
<td>Common security issues that may still arise (half-baked).</td>
</tr>
<tr>
<td>Proposal</td>
<td><a href="proposal-filter-levels.txt">Filter levels</a></td>
<td>Outlines details of projected configurable level of filtering.</td>
</tr>
<tr>
<td>Proposal</td>
<td><a href="proposal-language.txt">Language</a></td>
<td>Specification of I18N for error messages derived from MediaWiki (half-baked).</td>
</tr>
<tr>
<td>Proposal</td>
<td><a href="proposal-new-directives.txt">New directives</a></td>
<td>Assorted configuration options that could be implemented.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-loose-vs-strict.txt">Loose vs.Strict</a></td>
<td>Differences between HTML Strict and Transitional versions.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-proprietary-tags.txt">Proprietary tags</a></td>
<td>List of vendor-specific tags we may want to transform to W3C compliant markup.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-strictness.txt">Strictness</a></td>
<td>Short essay on how loose definition isn't really loose.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-xhtml-1.1.txt">XHTML 1.1</a></td>
<td>What we'd have to do to support XHTML 1.1.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-whatwg.txt">WHATWG</a></td>
<td>How WHATWG plays into what we need to do.</td>
</tr>
</tbody>
</table>
<div id="version">$Id$</div>
</body>
</html>

View File

@@ -15,7 +15,6 @@
<div id="filing">Filed under Proposals</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
<p>Your website probably has a color-scheme.
<span style="color:#090; background:#FFF;">Green on white</span>,

View File

@@ -14,15 +14,15 @@ Since configuration is dependant on context, internal classes require a
configuration object to be passed as a parameter. (They also require a
Context object).
In relation to HTMLDefinition and CSSDefinition, there could be a special class
In relation to HTMLDefinition and CSSDefinition, there is a special class
of directives that influence the *construction* of the Definition object.
A theoretical call pattern would look like:
A standard call pattern would look like:
1. Client calls Config->getHTMLDefinition()
2. Config calls HTMLDefinition->createNew(this)
3. HTMLDefinition constructs itself with base configuration
4. HTMLDefinition calls Config->get('HTML')
5. Config returns array of directives
4. HTMLDefinition calls Config->get('HTMLDefinition')
5. Config returns array of directives that later construction
6. HTMLDefinition performs operations and changes specified by directives
7. HTMLPurifier returns constructed definition
8. Config caches definition so it doesn't have to be generated again
@@ -33,7 +33,3 @@ custom copy, which OVERRIDES all directives. Only the base, vanilla copy
is the Singleton, the object actually interfaced with is a operated-upon
clone of that object. Also, if an update to the directives would update
the definition, you'd have to force reconstruction.
In practice, the pulling directives from the config object are
solely need-based, and the flex points are littered throughout the
setup() function. Some sort of refactoring is likely in order.

View File

@@ -8,17 +8,14 @@ could go into this definition: the set of HTML good for blog entries is
definitely too large for HTML that would be allowed in blog comments. Going
from Transitional to Strict requires changes to the definition.
Allowing users to specify their own whitelists is one step (implemented, btw),
but I have doubts on only doing this. Simply put, the typical programmer is too
lazy to actually go through the trouble of investigating which tags, attributes
and properties to allow. HTMLDefinition makes a big part of what HTMLPurifier
is.
However, allowing users to specify their own whitelists was an idea I
rejected from the start. Simply put, the typical programmer is too lazy
to actually go through the trouble of investigating which tags, attributes
and properties to allow. HTMLDefinition makes a big part of what HTMLPurifier
is.
The idea, then, is to setup fundamentally different set of definitions, which
can further be customized using simpler configuration options. Alternatively,
they could be implemented as configuration profiles, which simply load
a set of recommended directives to acheive a desired affect (no simpler
config options though).
can further be customized using simpler configuration options.
Here are some fuzzy levels you could set:
@@ -31,7 +28,7 @@ Here are some fuzzy levels you could set:
to be useful)
3. Pages - As permissive as possible without allowing XSS. No protection
against bad design sense, unfortunantely. Suitable for wiki and page
environments. (probably what we have now)
environments.
4. Lint - Accept everything in the spec, a Tidy wannabe. (This probably won't
get implemented as it would require routines for things like <object>
and friends to be implemented, which is a lot of work for not a lot of

View File

@@ -4,6 +4,8 @@ Configuration Ideas
Here are some theoretical configuration ideas that we could implement some
time. Note the naming convention: %Namespace.Directive
%Attr.IDPrefix - prefix all ids with this
%Attr.RewriteFragments - if there's %Attr.IDPrefix we may want to transparently
rewrite the URLs we parse too. However, we can only do it when it's a pure
anchor link, so it's not foolproof
@@ -19,11 +21,20 @@ time. Note the naming convention: %Namespace.Directive
%Attr.MaxHeight - caps for width and height related checks.
(the hack in Pixels for an image crashing attack could be replaced by this)
%URI.Munge - will munge all external URIs to a different URI, which redirects
the user to the applicable page. A urlencoded version of the URI
will replace any instances of %s in the string. One possible
string is 'http://www.google.com/url?q=%s'. Useful for preventing
pagerank from being sent to other sites, but can also be used to
redirect to a splash page notifying user that they are leaving your
website.
%URI.AddRelNofollow - will add rel="nofollow" to all links, preventing the
spread of ill-gotten pagerank
%URI.RelativeToAbsolute - transforms all relative URIs to absolute form
%URI.HostBlacklist - strings that if found in the host of a URI are disallowed
%URI.HostBlacklistRegex - regexes that if matching the host are disallowed
%URI.HostWhitelist - domain names that are excluded from the host blacklist
%URI.HostPolicy - determines whether or not its reject all and then whitelist
@@ -42,3 +53,7 @@ time. Note the naming convention: %Namespace.Directive
absolute DNS. While this is actually the preferred method according to
the RFC, most people opt to use a relative domain name relative to . (root).
%URI.DisableExternalResources - disallow resource links (i.e. URIs that result
in immediate requests, such as src in IMG) to external websites
%HTML.DisableImg - disables all images

View File

@@ -15,7 +15,6 @@
<div id="filing">Filed under Reference</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
<p>Many thanks to the DevNetwork community for answering questions,
theorizing about design, and offering encouragement during

View File

@@ -1,37 +0,0 @@
Loose versus Strict
Changes from one doctype to another
There are changes. Wow, how insightful. Not everything changed is relevant
to HTML Purifier, though, so let's take a look:
== Major incompatibilities ==
[done] BLOCKQUOTE changes from 'flow' to 'block'
current behavior: inline inner contents should not be nuked, block-ify as necessary
[partially-done] U, S, STRIKE cut
current behavior: removed completely
projected behavior: replace with appropriate inline span + CSS
[done] ADDRESS from potpourri to Inline (removes p tags)
current behavior: block tags silently dropped
ideal behavior: replace tags with something like <br>. (not high priority)
== Things we can loosen up ==
Tags DIR, MENU, CENTER, ISINDEX, FONT, BASEFONT? allowed in loose
current behavior: transform to strict-valid forms
Attributes allowed in loose (see attribute transforms in 'dev-progress.html')
current behavior: projected to transform into strict-valid forms
== Periphery issues ==
A tag's attribute 'target' (for selecting frames) cut
current behavior: not allowed at all
projected behavior: use loose doctype if needed, needs valid values
[done] OL/LI tag's attribute 'start'/'value' (for renumbering lists) cut
current behavior: no substitute, just delete when in strict, allow in loose
Attribute 'name' deprecated in favor of 'id'
current behavior: dropped silently
projected behavior: create proper AttrTransform (currently not allowed at all)
[done] PRE tag allows SUB/SUP? (strict dtd comment vs syntax, loose disallows)
current behavior: disallow as usual

View File

@@ -1,22 +0,0 @@
Proprietary Tags
<nobr> and friends
Here are some proprietary tags that W3C does not define but occasionally show
up in the wild. We have only included tags that would make sense in an
HTML Purifier context.
<align>, block element that aligns (extremely rare)
<blackface>, inline that double-bolds text (extremely rare)
<comment>, hidden comment for IE and WebTV
<multicol cols=number gutter=pixels width=pixels>, multiple columns
<nobr>, no linebreaks
<spacer align=* type="vertical|horizontal|block">, whitespace in doc,
use width/height for block and size for vertical/horizontal (attributes)
(extremely rare)
<wbr>, potential word break point: allows linebreaks. Only works in <nobr>
<listing>, monospace pre-variant (extremely rare)
<plaintext>, escapes all tags to the end of document
<ruby> and friends, (more research needed, appears to be XHTML 1.1 markup)
<xmp>, monospace, replace with pre

View File

@@ -2,8 +2,8 @@
Is HTML Purifier Strict or Transitional?
A little bit of helpful guidance
Despite the fact that HTML Purifier professes to support both transitional and
strict HTML, it rejects a lot of attributes and elements that are actually, indeed,
Despite the fact that HTML Purifier professes only to support transitional
HTML, it rejects a lot of attributes and elements that are actually, indeed,
valid. You can investigate progress.html to find out precisely what we
are doing to these *deprecated* attributes.
@@ -11,8 +11,8 @@ However, users have found that Strict HTML imposes some quite unreasonable
restrictions on certain things. The start and value attributes in ol and
li (respectively) perhaps are the most contested. There's is currently no
widely supported browser method short of JavaScript that can replace these
two deprecated elements. It behooves us to allow these deprecated
attributes when the output is transitional.
two deprecated elements. HTML Purifier does not currently support them, but
it might behoove us to do so while our output is still transitional.
Fortunantely, that's the only real bugger case. The others have near-perfect
CSS equivalents, and were presentational anyway. However, the other question
@@ -22,16 +22,4 @@ whole point about CSS is to seperate styling from content, so inline styling
doesn't solve that problem.
It's an icky question, and we'll have to deal with it as more and more
transforms get implemented. As of right now, however, we currently support
these loose-only constructs in loose mode:
- <ul start="1">, <li value="1"> attributes
- <u>, <strike>, <s> tags
- flow children in <blockquote>
- mixed children in <address>
The changed child definitions as well as the ul.start li.value are the most
compelling reasons why loose should be used. We may want offer disabling <u>,
<strike> and <s> by themselves. We may also want to offer no pre-emptive
deprecated conversions. This all must be unified.
transforms get implemented.

View File

@@ -1,9 +0,0 @@
Web Hypertext Application Technology Working Group
WHATWG
I don't think we need to worry about them. Untrusted users shouldn't be
submitting applications, eh? But if some interesting attribute pops up in
their spec, and might be worth supporting, stick it here.
(none so far, as you can see)

View File

@@ -1,21 +0,0 @@
Getting XHTML 1.1 Working
It's quite simple, according to <http://www.w3.org/TR/xhtml11/changes.html>
1. Scratch lang entirely in favor of xml:lang
2. Scratch name entirely in favor of id (partially-done)
3. Support Ruby <http://www.w3.org/TR/2001/REC-ruby-20010531/>
...but that's only an informative section. More things to do:
1. Scratch style attribute (it's deprecated)
2. Be module-aware (this might entail intelligent grouping in the definition
and allowing users to specifically remove certain modules (see 5))
3. Cross-reference minimal content models with existing DTDs and determine
changes (todo)
4. Watch out for the Legacy Module
<http://www.w3.org/TR/2001/REC-xhtml-modularization-20010410/abstract_modules.html#s_legacymodule>
5. Let users specify their own custom modules
6. Study Modularization document
<http://www.w3.org/TR/2001/REC-xhtml-modularization-20010410/>

View File

@@ -23,8 +23,6 @@ h4 {font-family:sans-serif; font-size:0.9em; font-weight:bold; }
/* Marks off asides, discussions on why something is the way it is */
.aside {margin-left:2em; font-family:sans-serif; font-size:0.9em; }
blockquote .label {font-weight:bold; font-size:1em; margin:0 0 .1em;
border-bottom:1px solid #CCC;}
/* A regular table */
.table {border-collapse:collapse; border-bottom:2px solid #888; margin-left:2em; }
@@ -38,7 +36,5 @@ blockquote .label {font-weight:bold; font-size:1em; margin:0 0 .1em;
/* Contains, without exception, Return to index. */
#index {font-size:smaller; }
#home {font-size:smaller;}
/* Contains, without exception, $Id$, for SVN version info. */
#version {text-align:right; font-style:italic; margin:2em 0;}
#version {text-align:right; font-style:italic; margin:2em 0;}

View File

@@ -1,21 +0,0 @@
<?php
/**
* Function wrapper for HTML Purifier for quick use.
* @note This function only includes the library when it is called. While
* this is efficient for instances when you only use HTML Purifier
* on a few of your pages, it murders bytecode caching. You still
* need to add HTML Purifier to your path.
* @note ''HTMLPurifier()'' is NOT the same as ''new HTMLPurifier()''
*/
function HTMLPurifier($html, $config = null) {
static $purifier = false;
if (!$purifier) {
require_once 'HTMLPurifier.php';
$purifier = new HTMLPurifier();
}
return $purifier->purify($html, $config);
}
?>

View File

@@ -22,7 +22,7 @@
*/
/*
HTML Purifier 1.4.1 - Standards Compliant HTML Filtering
HTML Purifier 1.2.0 - Standards Compliant HTML Filtering
Copyright (C) 2006 Edward Z. Yang
This library is free software; you can redistribute it and/or
@@ -64,107 +64,52 @@ require_once 'HTMLPurifier/Encoder.php';
class HTMLPurifier
{
var $version = '1.4.1';
var $config;
var $filters;
var $lexer, $strategy, $generator;
/**
* Final HTMLPurifier_Context of last run purification. Might be an array.
* @public
*/
var $context;
/**
* Initializes the purifier.
* @param $config Optional HTMLPurifier_Config object for all instances of
* the purifier, if omitted, a default configuration is
* supplied (which can be overridden on a per-use basis).
* The parameter can also be any type that
* HTMLPurifier_Config::create() supports.
*/
function HTMLPurifier($config = null) {
$this->config = HTMLPurifier_Config::create($config);
$this->config = $config ? $config : HTMLPurifier_Config::createDefault();
$this->lexer = HTMLPurifier_Lexer::create();
$this->strategy = new HTMLPurifier_Strategy_Core();
$this->generator = new HTMLPurifier_Generator();
$this->encoder = new HTMLPurifier_Encoder();
}
/**
* Adds a filter to process the output. First come first serve
* @param $filter HTMLPurifier_Filter object
*/
function addFilter($filter) {
$this->filters[] = $filter;
}
/**
* Filters an HTML snippet/document to be XSS-free and standards-compliant.
*
* @param $html String of HTML to purify
* @param $config HTMLPurifier_Config object for this operation, if omitted,
* defaults to the config object specified during this
* object's construction. The parameter can also be any type
* that HTMLPurifier_Config::create() supports.
* object's construction.
* @return Purified HTML
*/
function purify($html, $config = null) {
$config = $config ? HTMLPurifier_Config::create($config) : $this->config;
$context = new HTMLPurifier_Context();
$html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context);
for ($i = 0, $size = count($this->filters); $i < $size; $i++) {
$html = $this->filters[$i]->preFilter($html, $config, $context);
}
// purified HTML
$config = $config ? $config : $this->config;
$context =& new HTMLPurifier_Context();
$html = $this->encoder->convertToUTF8($html, $config, $context);
$html =
$this->generator->generateFromTokens(
// list of tokens
$this->strategy->execute(
// list of un-purified tokens
$this->lexer->tokenizeHTML(
// un-purified HTML
$html, $config, $context
),
$this->lexer->tokenizeHTML($html, $config, $context),
$config, $context
),
$config, $context
);
for ($i = $size - 1; $i >= 0; $i--) {
$html = $this->filters[$i]->postFilter($html, $config, $context);
}
$html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context);
$this->context =& $context;
$html = $this->encoder->convertFromUTF8($html, $config, $context);
return $html;
}
/**
* Filters an array of HTML snippets
* @param $config Optional HTMLPurifier_Config object for this operation.
* See HTMLPurifier::purify() for more details.
* @return Array of purified HTML
*/
function purifyArray($array_of_html, $config = null) {
$context_array = array();
foreach ($array_of_html as $key => $html) {
$array_of_html[$key] = $this->purify($html, $config);
$context_array[$key] = $this->context;
}
$this->context = $context_array;
return $array_of_html;
}
}
?>

View File

@@ -1,87 +0,0 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
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
{
/**
* Local copy of component validators.
* @note See HTMLPurifier_AttrDef_Font::$info for a similar impl.
*/
var $info;
function HTMLPurifier_AttrDef_Background($config) {
$def = $config->getCSSDefinition();
$this->info['background-color'] = $def->info['background-color'];
$this->info['background-image'] = $def->info['background-image'];
$this->info['background-repeat'] = $def->info['background-repeat'];
$this->info['background-attachment'] = $def->info['background-attachment'];
$this->info['background-position'] = $def->info['background-position'];
}
function validate($string, $config, &$context) {
// regular pre-processing
$string = $this->parseCDATA($string);
if ($string === '') return false;
// assumes URI doesn't have spaces in it
$bits = explode(' ', strtolower($string)); // bits to process
$caught = array();
$caught['color'] = false;
$caught['image'] = false;
$caught['repeat'] = false;
$caught['attachment'] = false;
$caught['position'] = false;
$i = 0; // number of catches
$none = false;
foreach ($bits as $bit) {
if ($bit === '') continue;
foreach ($caught as $key => $status) {
if ($key != 'position') {
if ($status !== false) continue;
$r = $this->info['background-' . $key]->validate($bit, $config, $context);
} else {
$r = $bit;
}
if ($r === false) continue;
if ($key == 'position') {
if ($caught[$key] === false) $caught[$key] = '';
$caught[$key] .= $r . ' ';
} else {
$caught[$key] = $r;
}
$i++;
break;
}
}
if (!$i) return false;
if ($caught['position'] !== false) {
$caught['position'] = $this->info['background-position']->
validate($caught['position'], $config, $context);
}
$ret = array();
foreach ($caught as $value) {
if ($value === false) continue;
$ret[] = $value;
}
if (empty($ret)) return false;
return implode(' ', $ret);
}
}
?>

View File

@@ -1,130 +0,0 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/CSSLength.php';
require_once 'HTMLPurifier/AttrDef/Percentage.php';
/* W3C says:
[ // adjective and number must be in correct order, even if
// you could switch them without introducing ambiguity.
// some browsers support that syntax
[
<percentage> | <length> | left | center | right
]
[
<percentage> | <length> | top | center | bottom
]?
] |
[ // this signifies that the vertical and horizontal adjectives
// can be arbitrarily ordered, however, there can only be two,
// one of each, or none at all
[
left | center | right
] ||
[
top | center | bottom
]
]
top, left = 0%
center, (none) = 50%
bottom, right = 100%
*/
/* QuirksMode says:
keyword + length/percentage must be ordered correctly, as per W3C
Internet Explorer and Opera, however, support arbitrary ordering. We
should fix it up.
Minor issue though, not strictly necessary.
*/
// control freaks may appreciate the ability to convert these to
// percentages or something, but it's not necessary
/**
* Validates the value of background-position.
*/
class HTMLPurifier_AttrDef_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 validate($string, $config, &$context) {
$string = $this->parseCDATA($string);
$bits = explode(' ', $string);
$keywords = array();
$keywords['h'] = false; // left, right
$keywords['v'] = false; // top, bottom
$keywords['c'] = false; // center
$measures = array();
$i = 0;
$lookup = array(
'top' => 'v',
'bottom' => 'v',
'left' => 'h',
'right' => 'h',
'center' => 'c'
);
foreach ($bits as $bit) {
if ($bit === '') continue;
// test for keyword
$lbit = ctype_lower($bit) ? $bit : strtolower($bit);
if (isset($lookup[$lbit])) {
$status = $lookup[$lbit];
$keywords[$status] = $lbit;
$i++;
}
// test for length
$r = $this->length->validate($bit, $config, $context);
if ($r !== false) {
$measures[] = $r;
$i++;
}
// test for percentage
$r = $this->percentage->validate($bit, $config, $context);
if ($r !== false) {
$measures[] = $r;
$i++;
}
}
if (!$i) return false; // no valid values were caught
$ret = array();
// first keyword
if ($keywords['h']) $ret[] = $keywords['h'];
elseif (count($measures)) $ret[] = array_shift($measures);
elseif ($keywords['c']) {
$ret[] = $keywords['c'];
$keywords['c'] = false; // prevent re-use: center = center center
}
if ($keywords['v']) $ret[] = $keywords['v'];
elseif (count($measures)) $ret[] = array_shift($measures);
elseif ($keywords['c']) $ret[] = $keywords['c'];
if (empty($ret)) return false;
return implode(' ', $ret);
}
}
?>

View File

@@ -8,11 +8,6 @@ require_once 'HTMLPurifier/CSSDefinition.php';
* @note We don't implement the whole CSS specification, so it might be
* difficult to reuse this component in the context of validating
* actual stylesheet declarations.
* @note If we were really serious about validating the CSS, we would
* tokenize the styles and then parse the tokens. Obviously, we
* are not doing that. Doing that could seriously harm performance,
* but would make these components a lot more viable for a CSS
* filtering solution.
*/
class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
{
@@ -25,9 +20,6 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
// we're going to break the spec and explode by semicolons.
// This is because semicolon rarely appears in escaped form
// Doing this is generally flaky but fast
// IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
// for details
$declarations = explode(';', $css);
$propvalues = array();

View File

@@ -40,7 +40,6 @@ class HTMLPurifier_AttrDef_CSSLength extends HTMLPurifier_AttrDef
// we assume all units are two characters
$unit = substr($length, $strlen - 2);
if (!ctype_lower($unit)) $unit = strtolower($unit);
$number = substr($length, 0, $strlen - 2);
if (!isset($this->units[$unit])) return false;

View File

@@ -1,58 +0,0 @@
<?php
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
* 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
{
function HTMLPurifier_AttrDef_CSSURI() {
$this->HTMLPurifier_AttrDef_URI(true); // always embedded
}
function validate($uri_string, $config, &$context) {
// parse the URI out of the string and then pass it onto
// the parent object
$uri_string = $this->parseCDATA($uri_string);
if (strpos($uri_string, 'url(') !== 0) return false;
$uri_string = substr($uri_string, 4);
$new_length = strlen($uri_string) - 1;
if ($uri_string[$new_length] != ')') return false;
$uri = trim(substr($uri_string, 0, $new_length));
if (isset($uri[0]) && ($uri[0] == "'" || $uri[0] == '"')) {
$quote = $uri[0];
$new_length = strlen($uri) - 1;
if ($uri[$new_length] !== $quote) return false;
$uri = substr($uri, 1, $new_length - 1);
}
$keys = array( '(', ')', ',', ' ', '"', "'");
$values = array('\\(', '\\)', '\\,', '\\ ', '\\"', "\\'");
$uri = str_replace($values, $keys, $uri);
$result = parent::validate($uri, $config, $context);
if ($result === false) return false;
// escape necessary characters according to CSS spec
// except for the comma, none of these should appear in the
// URI at all
$result = str_replace($keys, $values, $result);
return "url($result)";
}
}
?>

View File

@@ -49,7 +49,7 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
if ($length == 0 || $length == 1 || $length > 8 || !ctype_alnum($subtags[1])) {
return $new_string;
}
if (!ctype_lower($subtags[1])) $subtags[1] = strtolower($subtags[1]);
if (!ctype_lower($subtags[1])) $subtags[1] = strotolower($subtags[1]);
$new_string .= '-' . $subtags[1];
if ($num_subtags == 2) return $new_string;
@@ -61,7 +61,7 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
return $new_string;
}
if (!ctype_lower($subtags[$i])) {
$subtags[$i] = strtolower($subtags[$i]);
$subtags[$i] = strotolower($subtags[$i]);
}
$new_string .= '-' . $subtags[$i];
}

View File

@@ -4,7 +4,8 @@ require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates shorthand CSS property list-style.
* @warning Does not support url tokens that have internal spaces.
* @note This currently does not support list-style-image, as that functionality
* is not implemented yet elsewhere.
*/
class HTMLPurifier_AttrDef_ListStyle extends HTMLPurifier_AttrDef
{
@@ -19,7 +20,6 @@ class HTMLPurifier_AttrDef_ListStyle extends HTMLPurifier_AttrDef
$def = $config->getCSSDefinition();
$this->info['list-style-type'] = $def->info['list-style-type'];
$this->info['list-style-position'] = $def->info['list-style-position'];
$this->info['list-style-image'] = $def->info['list-style-image'];
}
function validate($string, $config, &$context) {
@@ -28,50 +28,48 @@ class HTMLPurifier_AttrDef_ListStyle extends HTMLPurifier_AttrDef
$string = $this->parseCDATA($string);
if ($string === '') return false;
// assumes URI doesn't have spaces in it
$bits = explode(' ', strtolower($string)); // bits to process
$caught = array();
$caught['type'] = false;
$caught['position'] = false;
$caught['image'] = false;
$caught_type = false;
$caught_position = false;
$caught_none = false; // as in keyword none, which is in all of them
$i = 0; // number of catches
$none = false;
$ret = '';
foreach ($bits as $bit) {
if ($i >= 3) return; // optimization bit
if ($caught_none && ($caught_type || $caught_position)) break;
if ($caught_type && $caught_position) break;
if ($bit === '') continue;
foreach ($caught as $key => $status) {
if ($status !== false) continue;
$r = $this->info['list-style-' . $key]->validate($bit, $config, $context);
if ($r === false) continue;
if ($r === 'none') {
if ($none) continue;
else $none = true;
if ($key == 'image') continue;
}
$caught[$key] = $r;
$i++;
break;
if ($bit === 'none') {
if ($caught_none) continue;
$caught_none = true;
$ret .= 'none ';
continue;
}
// if we add anymore, roll it into a loop
$r = $this->info['list-style-type']->validate($bit, $config, $context);
if ($r !== false) {
if ($caught_type) continue;
$caught_type = true;
$ret .= $r . ' ';
continue;
}
$r = $this->info['list-style-position']->validate($bit, $config, $context);
if ($r !== false) {
if ($caught_position) continue;
$caught_position = true;
$ret .= $r . ' ';
continue;
}
}
if (!$i) return false;
$ret = array();
// construct type
if ($caught['type']) $ret[] = $caught['type'];
// construct image
if ($caught['image']) $ret[] = $caught['image'];
// construct position
if ($caught['position']) $ret[] = $caught['position'];
if (empty($ret)) return false;
return implode(' ', $ret);
$ret = rtrim($ret);
return $ret ? $ret : false;
}

View File

@@ -4,13 +4,14 @@ require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Number.php';
/**
* Validates a Percentage as defined by the CSS spec.
* Validates a Percentage as defined by the HTML spec.
* @note This also allows integer pixel values.
*/
class HTMLPurifier_AttrDef_Percentage extends HTMLPurifier_AttrDef
{
/**
* Instance of HTMLPurifier_AttrDef_Number to defer number validation
* Instance of HTMLPurifier_AttrDef_Number to defer pixel validation
*/
var $number_def;

View File

@@ -24,7 +24,7 @@ HTMLPurifier_ConfigSchema::define(
'This directive has been available since 1.2.0.'
);
HTMLPurifier_ConfigSchema::define(
HTMLPurifier_ConfigSchema::Define(
'URI', 'DisableExternal', false, 'bool',
'Disables links to external websites. This is a highly effective '.
'anti-spam and anti-pagerank-leech measure, but comes at a hefty price: no'.
@@ -34,49 +34,6 @@ HTMLPurifier_ConfigSchema::define(
'This directive has been available since 1.2.0.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'DisableExternalResources', false, 'bool',
'Disables the embedding of external resources, preventing users from '.
'embedding things like images from other hosts. This prevents '.
'access tracking (good for email viewers), bandwidth leeching, '.
'cross-site request forging, goatse.cx posting, and '.
'other nasties, but also results in '.
'a loss of end-user functionality (they can\'t directly post a pic '.
'they posted from Flickr anymore). Use it if you don\'t have a '.
'robust user-content moderation team. This directive has been '.
'available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'DisableResources', false, 'bool',
'Disables embedding resources, essentially meaning no pictures. You can '.
'still link to them though. See %URI.DisableExternalResources for why '.
'this might be a good idea. This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'Munge', null, 'string/null',
'Munges all browsable (usually http, https and ftp) URI\'s into some URL '.
'redirection service. Pass this directive a URI, with %s inserted where '.
'the url-encoded original URI should be inserted (sample: '.
'<code>http://www.google.com/url?q=%s</code>). '.
'This prevents PageRank leaks, while being as transparent as possible '.
'to users (you may also want to add some client side JavaScript to '.
'override the text in the statusbar). Warning: many security experts '.
'believe that this form of protection does not deter spam-bots. '.
'You can also use this directive to redirect users to a splash page '.
'telling them they are leaving your website. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'HostBlacklist', array(), 'list',
'List of strings that are forbidden in the host of any URI. Use it to '.
'kill domain names of spam, etc. Note that it will catch anything in '.
'the domain, so <tt>moo.com</tt> will catch <tt>moo.com.example.com</tt>. '.
'This directive has been available since 1.3.0.'
);
/**
* Validates a URI as defined by RFC 3986.
* @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
@@ -86,15 +43,15 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
var $host;
var $PercentEncoder;
var $embeds_resource;
var $embeds;
/**
* @param $embeds_resource_resource Does the URI here result in an extra HTTP request?
* @param $embeds Does the URI here result in an extra HTTP request?
*/
function HTMLPurifier_AttrDef_URI($embeds_resource = false) {
function HTMLPurifier_AttrDef_URI($embeds = false) {
$this->host = new HTMLPurifier_AttrDef_Host();
$this->PercentEncoder = new HTMLPurifier_PercentEncoder();
$this->embeds_resource = (bool) $embeds_resource;
$this->embeds = (bool) $embeds;
}
function validate($uri, $config, &$context) {
@@ -139,34 +96,27 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
// no need to validate the scheme's fmt since we do that when we
// retrieve the specific scheme object from the registry
$scheme = ctype_lower($scheme) ? $scheme : strtolower($scheme);
$scheme_obj = $registry->getScheme($scheme, $config, $context);
$scheme_obj =& $registry->getScheme($scheme, $config, $context);
if (!$scheme_obj) return false; // invalid scheme, clean it out
} else {
$scheme_obj = $registry->getScheme(
$scheme_obj =& $registry->getScheme(
$config->get('URI', 'DefaultScheme'), $config, $context
);
}
// the URI we're processing embeds_resource a resource in the page, but the URI
// the URI we're processing embeds a resource in the page, but the URI
// it references cannot be located
if ($this->embeds_resource && !$scheme_obj->browsable) {
if ($this->embeds && !$scheme_obj->browsable) {
return false;
}
if ($authority !== null) {
// remove URI if it's absolute and we disabled externals or
// if it's absolute and embedded and we disabled external resources
// remove URI if it's absolute and we disallow externals
unset($our_host);
if (
$config->get('URI', 'DisableExternal') ||
(
$config->get('URI', 'DisableExternalResources') &&
$this->embeds_resource
)
) {
if ($config->get('URI', 'DisableExternal')) {
$our_host = $config->get('URI', 'Host');
if ($our_host === null) return false;
}
@@ -193,8 +143,6 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
$host = $this->host->validate($host, $config, $context);
if ($host === false) $host = null;
if ($this->checkBlacklist($host, $config, $context)) return false;
// more lenient absolute checking
if (isset($our_host)) {
$host_parts = array_reverse(explode('.', $host));
@@ -250,37 +198,10 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
if ($query !== null) $result .= "?$query";
if ($fragment !== null) $result .= "#$fragment";
// munge if necessary
$munge = $config->get('URI', 'Munge');
if (!empty($scheme_obj->browsable) && $munge !== null) {
if ($authority !== null) {
$result = str_replace('%s', rawurlencode($result), $munge);
}
}
return $result;
}
/**
* Checks a host against an array blacklist
* @param $host Host to check
* @param $config HTMLPurifier_Config instance
* @param $context HTMLPurifier_Context instance
* @return bool Is spam?
*/
function checkBlacklist($host, &$config, &$context) {
$blacklist = $config->get('URI', 'HostBlacklist');
if (!empty($blacklist)) {
foreach($blacklist as $blacklisted_host_fragment) {
if (strpos($host, $blacklisted_host_fragment) !== false) {
return true;
}
}
}
return false;
}
}
?>

View File

@@ -21,7 +21,7 @@ class HTMLPurifier_AttrTransform
* Abstract: makes changes to the attributes dependent on multiple values.
*
* @param $attr Assoc array of attributes, usually from
* HTMLPurifier_Token_Tag::$attr
* HTMLPurifier_Token_Tag::$attributes
* @param $config Mandatory HTMLPurifier_Config object.
* @param $context Mandatory HTMLPurifier_Context object
* @returns Processed attribute array.

View File

@@ -20,7 +20,7 @@ HTMLPurifier_ConfigSchema::defineAllowedValues(
class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
{
function transform($attr, $config, &$context) {
function transform($attr, $config, $context) {
if (isset($attr['dir'])) return $attr;
$attr['dir'] = $config->get('Attr', 'DefaultTextDir');
return $attr;

View File

@@ -25,7 +25,7 @@ HTMLPurifier_ConfigSchema::define(
class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
{
function transform($attr, $config, &$context) {
function transform($attr, $config, $context) {
$src = true;
if (!isset($attr['src'])) {

View File

@@ -10,7 +10,7 @@ require_once 'HTMLPurifier/AttrTransform.php';
class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform
{
function transform($attr, $config, &$context) {
function transform($attr, $config, $context) {
$lang = isset($attr['lang']) ? $attr['lang'] : false;
$xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;

View File

@@ -8,7 +8,7 @@ require_once 'HTMLPurifier/AttrTransform.php';
class HTMLPurifier_AttrTransform_TextAlign
extends HTMLPurifier_AttrTransform {
function transform($attr, $config, &$context) {
function transform($attr, $config, $context) {
if (!isset($attr['align'])) return $attr;

View File

@@ -11,9 +11,6 @@ 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.
@@ -54,19 +51,11 @@ class HTMLPurifier_CSSDefinition
$this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
array('normal', 'small-caps'), false);
$uri_or_none = new HTMLPurifier_AttrDef_Composite(
array(
new HTMLPurifier_AttrDef_Enum(array('none')),
new HTMLPurifier_AttrDef_CSSURI()
)
);
$this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
array('inside', 'outside'), false);
$this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
array('disc', 'circle', 'square', 'decimal', 'lower-roman',
'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false);
$this->info['list-style-image'] = $uri_or_none;
'upper-roman', 'lower-alpha', 'upper-alpha'), false);
$this->info['list-style'] = new HTMLPurifier_AttrDef_ListStyle($config);
@@ -74,14 +63,14 @@ class HTMLPurifier_CSSDefinition
array('capitalize', 'uppercase', 'lowercase', 'none'), false);
$this->info['color'] = new HTMLPurifier_AttrDef_Color();
$this->info['background-image'] = $uri_or_none;
$this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
);
$this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
array('scroll', 'fixed')
);
$this->info['background-position'] = new HTMLPurifier_AttrDef_BackgroundPosition();
// technically speaking, this one should get its own validator, but
// since we don't support background images, it effectively is
// equivalent to color. The only trouble is that if the author
// specifies an image and a color, they'll both end up getting dropped,
// even though we ought to implement it and just discard the image
// info. This will be fixed in a later version (see TODO) when
// better URI filtering is implemented.
$this->info['background'] =
$border_color =
$this->info['border-top-color'] =
@@ -93,8 +82,6 @@ class HTMLPurifier_CSSDefinition
new HTMLPurifier_AttrDef_Color()
));
$this->info['background'] = new HTMLPurifier_AttrDef_Background($config);
$this->info['border-color'] = new HTMLPurifier_AttrDef_Multiple($border_color);
$border_width =

View File

@@ -20,9 +20,10 @@ HTMLPurifier_ConfigSchema::define(
class HTMLPurifier_ChildDef
{
/**
* Type of child definition, usually right-most part of class name lowercase.
* Used occasionally in terms of context.
* @public
* Type of child definition, usually right-most part of class name lowercase
*
* Used occasionally in terms of context. Possible values include
* custom, required, optional and empty.
*/
var $type;
@@ -31,15 +32,12 @@ class HTMLPurifier_ChildDef
*
* This is necessary for redundant checking when changes affecting
* a child node may cause a parent node to now be disallowed.
*
* @public
*/
var $allow_empty;
/**
* Validates nodes according to definition and returns modification.
*
* @public
* @param $tokens_of_children Array of HTMLPurifier_Token
* @param $config HTMLPurifier_Config object
* @param $context HTMLPurifier_Context object
@@ -52,4 +50,391 @@ class HTMLPurifier_ChildDef
}
}
/**
* Custom validation class, accepts DTD child definitions
*
* @warning Currently this class is an all or nothing proposition, that is,
* it will only give a bool return value.
* @note This class is currently not used by any code, although it is unit
* tested.
*/
class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef
{
var $type = 'custom';
var $allow_empty = false;
/**
* Allowed child pattern as defined by the DTD
*/
var $dtd_regex;
/**
* PCRE regex derived from $dtd_regex
* @private
*/
var $_pcre_regex;
/**
* @param $dtd_regex Allowed child pattern from the DTD
*/
function HTMLPurifier_ChildDef_Custom($dtd_regex) {
$this->dtd_regex = $dtd_regex;
$this->_compileRegex();
}
/**
* Compiles the PCRE regex from a DTD regex ($dtd_regex to $_pcre_regex)
*/
function _compileRegex() {
$raw = str_replace(' ', '', $this->dtd_regex);
if ($raw{0} != '(') {
$raw = "($raw)";
}
$reg = str_replace(',', ',?', $raw);
$reg = preg_replace('/([#a-zA-Z0-9_.-]+)/', '(,?\\0)', $reg);
$this->_pcre_regex = $reg;
}
function validateChildren($tokens_of_children, $config, &$context) {
$list_of_children = '';
$nesting = 0; // depth into the nest
foreach ($tokens_of_children as $token) {
if (!empty($token->is_whitespace)) continue;
$is_child = ($nesting == 0); // direct
if ($token->type == 'start') {
$nesting++;
} elseif ($token->type == 'end') {
$nesting--;
}
if ($is_child) {
$list_of_children .= $token->name . ',';
}
}
$list_of_children = rtrim($list_of_children, ',');
$okay =
preg_match(
'/^'.$this->_pcre_regex.'$/',
$list_of_children
);
return (bool) $okay;
}
}
/**
* Definition that allows a set of elements, but disallows empty children.
*/
class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
{
/**
* Lookup table of allowed elements.
*/
var $elements = array();
/**
* @param $elements List of allowed element names (lowercase).
*/
function HTMLPurifier_ChildDef_Required($elements) {
if (is_string($elements)) {
$elements = str_replace(' ', '', $elements);
$elements = explode('|', $elements);
}
$elements = array_flip($elements);
foreach ($elements as $i => $x) $elements[$i] = true;
$this->elements = $elements;
$this->gen = new HTMLPurifier_Generator();
}
var $allow_empty = false;
var $type = 'required';
function validateChildren($tokens_of_children, $config, &$context) {
// if there are no tokens, delete parent node
if (empty($tokens_of_children)) return false;
// the new set of children
$result = array();
// current depth into the nest
$nesting = 0;
// whether or not we're deleting a node
$is_deleting = false;
// whether or not parsed character data is allowed
// this controls whether or not we silently drop a tag
// or generate escaped HTML from it
$pcdata_allowed = isset($this->elements['#PCDATA']);
// a little sanity check to make sure it's not ALL whitespace
$all_whitespace = true;
// some configuration
$escape_invalid_children = $config->get('Core', 'EscapeInvalidChildren');
foreach ($tokens_of_children as $token) {
if (!empty($token->is_whitespace)) {
$result[] = $token;
continue;
}
$all_whitespace = false; // phew, we're not talking about whitespace
$is_child = ($nesting == 0);
if ($token->type == 'start') {
$nesting++;
} elseif ($token->type == 'end') {
$nesting--;
}
if ($is_child) {
$is_deleting = false;
if (!isset($this->elements[$token->name])) {
$is_deleting = true;
if ($pcdata_allowed && $token->type == 'text') {
$result[] = $token;
} elseif ($pcdata_allowed && $escape_invalid_children) {
$result[] = new HTMLPurifier_Token_Text(
$this->gen->generateFromToken($token, $config)
);
}
continue;
}
}
if (!$is_deleting || ($pcdata_allowed && $token->type == 'text')) {
$result[] = $token;
} elseif ($pcdata_allowed && $escape_invalid_children) {
$result[] =
new HTMLPurifier_Token_Text(
$this->gen->generateFromToken( $token, $config )
);
} else {
// drop silently
}
}
if (empty($result)) return false;
if ($all_whitespace) return false;
if ($tokens_of_children == $result) return true;
return $result;
}
}
/**
* Definition that allows a set of elements, and allows no children.
* @note This is a hack to reuse code from HTMLPurifier_ChildDef_Required,
* really, one shouldn't inherit from the other. Only altered behavior
* is to overload a returned false with an array. Thus, it will never
* return false.
*/
class HTMLPurifier_ChildDef_Optional extends HTMLPurifier_ChildDef_Required
{
var $allow_empty = true;
var $type = 'optional';
function validateChildren($tokens_of_children, $config, &$context) {
$result = parent::validateChildren($tokens_of_children, $config, $context);
if ($result === false) return array();
return $result;
}
}
/**
* Definition that disallows all elements.
* @warning validateChildren() in this class is actually never called, because
* empty elements are corrected in HTMLPurifier_Strategy_MakeWellFormed
* before child definitions are parsed in earnest by
* HTMLPurifier_Strategy_FixNesting.
*/
class HTMLPurifier_ChildDef_Empty extends HTMLPurifier_ChildDef
{
var $allow_empty = true;
var $type = 'empty';
function HTMLPurifier_ChildDef_Empty() {}
function validateChildren($tokens_of_children, $config, &$context) {
return array();
}
}
/**
* Definition that uses different definitions depending on context.
*
* The del and ins tags are notable because they allow different types of
* elements depending on whether or not they're in a block or inline context.
* Chameleon allows this behavior to happen by using two different
* definitions depending on context. While this somewhat generalized,
* it is specifically intended for those two tags.
*/
class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef
{
/**
* Instance of the definition object to use when inline. Usually stricter.
*/
var $inline;
/**
* Instance of the definition object to use when block.
*/
var $block;
/**
* @param $inline List of elements to allow when inline.
* @param $block List of elements to allow when block.
*/
function HTMLPurifier_ChildDef_Chameleon($inline, $block) {
$this->inline = new HTMLPurifier_ChildDef_Optional($inline);
$this->block = new HTMLPurifier_ChildDef_Optional($block);
}
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;
}
return $result;
}
}
/**
* Definition for tables
*/
class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
{
var $allow_empty = false;
var $type = 'table';
function HTMLPurifier_ChildDef_Table() {}
function validateChildren($tokens_of_children, $config, &$context) {
if (empty($tokens_of_children)) return false;
// this ensures that the loop gets run one last time before closing
// up. It's a little bit of a hack, but it works! Just make sure you
// get rid of the token later.
$tokens_of_children[] = false;
// only one of these elements is allowed in a table
$caption = false;
$thead = false;
$tfoot = false;
// as many of these as you want
$cols = array();
$content = array();
$nesting = 0; // current depth so we can determine nodes
$is_collecting = false; // are we globbing together tokens to package
// into one of the collectors?
$collection = array(); // collected nodes
$tag_index = 0; // the first node might be whitespace,
// so this tells us where the start tag is
foreach ($tokens_of_children as $token) {
$is_child = ($nesting == 0);
if ($token === false) {
// terminating sequence started
} elseif ($token->type == 'start') {
$nesting++;
} elseif ($token->type == 'end') {
$nesting--;
}
// handle node collection
if ($is_collecting) {
if ($is_child) {
// okay, let's stash the tokens away
// first token tells us the type of the collection
switch ($collection[$tag_index]->name) {
case 'tr':
case 'tbody':
$content[] = $collection;
break;
case 'caption':
if ($caption !== false) break;
$caption = $collection;
break;
case 'thead':
case 'tfoot':
// access the appropriate variable, $thead or $tfoot
$var = $collection[$tag_index]->name;
if ($$var === false) {
$$var = $collection;
} else {
// transmutate the first and less entries into
// tbody tags, and then put into content
$collection[$tag_index]->name = 'tbody';
$collection[count($collection)-1]->name = 'tbody';
$content[] = $collection;
}
break;
case 'colgroup':
$cols[] = $collection;
break;
}
$collection = array();
$is_collecting = false;
$tag_index = 0;
} else {
// add the node to the collection
$collection[] = $token;
}
}
// terminate
if ($token === false) break;
if ($is_child) {
// determine what we're dealing with
if ($token->name == 'col') {
// the only empty tag in the possie, we can handle it
// immediately
$cols[] = array_merge($collection, array($token));
$collection = array();
$tag_index = 0;
continue;
}
switch($token->name) {
case 'caption':
case 'colgroup':
case 'thead':
case 'tfoot':
case 'tbody':
case 'tr':
$is_collecting = true;
$collection[] = $token;
continue;
default:
if ($token->type == 'text' && $token->is_whitespace) {
$collection[] = $token;
$tag_index++;
}
continue;
}
}
}
if (empty($content)) return false;
$ret = array();
if ($caption !== false) $ret = array_merge($ret, $caption);
if ($cols !== false) foreach ($cols as $token_array) $ret = array_merge($ret, $token_array);
if ($thead !== false) $ret = array_merge($ret, $thead);
if ($tfoot !== false) $ret = array_merge($ret, $tfoot);
foreach ($content as $token_array) $ret = array_merge($ret, $token_array);
if (!empty($collection) && $is_collecting == false){
// grab the trailing space
$ret = array_merge($ret, $collection);
}
array_pop($tokens_of_children); // remove phantom token
return ($ret === $tokens_of_children) ? true : $ret;
}
}
?>

View File

@@ -1,60 +0,0 @@
<?php
require_once 'HTMLPurifier/ChildDef.php';
/**
* Definition that uses different definitions depending on context.
*
* The del and ins tags are notable because they allow different types of
* elements depending on whether or not they're in a block or inline context.
* Chameleon allows this behavior to happen by using two different
* definitions depending on context. While this somewhat generalized,
* it is specifically intended for those two tags.
*/
class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef
{
/**
* Instance of the definition object to use when inline. Usually stricter.
* @public
*/
var $inline;
/**
* Instance of the definition object to use when block.
* @public
*/
var $block;
var $type = 'chameleon';
/**
* @param $inline List of elements to allow when inline.
* @param $block List of elements to allow when block.
*/
function HTMLPurifier_ChildDef_Chameleon($inline, $block) {
$this->inline = new HTMLPurifier_ChildDef_Optional($inline);
$this->block = new HTMLPurifier_ChildDef_Optional($block);
}
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;
}
return $result;
}
}
?>

View File

@@ -1,75 +0,0 @@
<?php
require_once 'HTMLPurifier/ChildDef.php';
/**
* Custom validation class, accepts DTD child definitions
*
* @warning Currently this class is an all or nothing proposition, that is,
* it will only give a bool return value.
* @note This class is currently not used by any code, although it is unit
* tested.
*/
class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef
{
var $type = 'custom';
var $allow_empty = false;
/**
* Allowed child pattern as defined by the DTD
*/
var $dtd_regex;
/**
* PCRE regex derived from $dtd_regex
* @private
*/
var $_pcre_regex;
/**
* @param $dtd_regex Allowed child pattern from the DTD
*/
function HTMLPurifier_ChildDef_Custom($dtd_regex) {
$this->dtd_regex = $dtd_regex;
$this->_compileRegex();
}
/**
* Compiles the PCRE regex from a DTD regex ($dtd_regex to $_pcre_regex)
*/
function _compileRegex() {
$raw = str_replace(' ', '', $this->dtd_regex);
if ($raw{0} != '(') {
$raw = "($raw)";
}
$reg = str_replace(',', ',?', $raw);
$reg = preg_replace('/([#a-zA-Z0-9_.-]+)/', '(,?\\0)', $reg);
$this->_pcre_regex = $reg;
}
function validateChildren($tokens_of_children, $config, &$context) {
$list_of_children = '';
$nesting = 0; // depth into the nest
foreach ($tokens_of_children as $token) {
if (!empty($token->is_whitespace)) continue;
$is_child = ($nesting == 0); // direct
if ($token->type == 'start') {
$nesting++;
} elseif ($token->type == 'end') {
$nesting--;
}
if ($is_child) {
$list_of_children .= $token->name . ',';
}
}
$list_of_children = rtrim($list_of_children, ',');
$okay =
preg_match(
'/^'.$this->_pcre_regex.'$/',
$list_of_children
);
return (bool) $okay;
}
}
?>

View File

@@ -1,22 +0,0 @@
<?php
require_once 'HTMLPurifier/ChildDef.php';
/**
* Definition that disallows all elements.
* @warning validateChildren() in this class is actually never called, because
* empty elements are corrected in HTMLPurifier_Strategy_MakeWellFormed
* before child definitions are parsed in earnest by
* HTMLPurifier_Strategy_FixNesting.
*/
class HTMLPurifier_ChildDef_Empty extends HTMLPurifier_ChildDef
{
var $allow_empty = true;
var $type = 'empty';
function HTMLPurifier_ChildDef_Empty() {}
function validateChildren($tokens_of_children, $config, &$context) {
return array();
}
}
?>

View File

@@ -1,23 +0,0 @@
<?php
require_once 'HTMLPurifier/ChildDef/Required.php';
/**
* Definition that allows a set of elements, and allows no children.
* @note This is a hack to reuse code from HTMLPurifier_ChildDef_Required,
* really, one shouldn't inherit from the other. Only altered behavior
* is to overload a returned false with an array. Thus, it will never
* return false.
*/
class HTMLPurifier_ChildDef_Optional extends HTMLPurifier_ChildDef_Required
{
var $allow_empty = true;
var $type = 'optional';
function validateChildren($tokens_of_children, $config, &$context) {
$result = parent::validateChildren($tokens_of_children, $config, $context);
if ($result === false) return array();
return $result;
}
}
?>

View File

@@ -1,104 +0,0 @@
<?php
require_once 'HTMLPurifier/ChildDef.php';
/**
* Definition that allows a set of elements, but disallows empty children.
*/
class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
{
/**
* Lookup table of allowed elements.
* @public
*/
var $elements = array();
/**
* @param $elements List of allowed element names (lowercase).
*/
function HTMLPurifier_ChildDef_Required($elements) {
if (is_string($elements)) {
$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]);
}
$this->elements = $elements;
$this->gen = new HTMLPurifier_Generator();
}
var $allow_empty = false;
var $type = 'required';
function validateChildren($tokens_of_children, $config, &$context) {
// if there are no tokens, delete parent node
if (empty($tokens_of_children)) return false;
// the new set of children
$result = array();
// current depth into the nest
$nesting = 0;
// whether or not we're deleting a node
$is_deleting = false;
// whether or not parsed character data is allowed
// this controls whether or not we silently drop a tag
// or generate escaped HTML from it
$pcdata_allowed = isset($this->elements['#PCDATA']);
// a little sanity check to make sure it's not ALL whitespace
$all_whitespace = true;
// some configuration
$escape_invalid_children = $config->get('Core', 'EscapeInvalidChildren');
foreach ($tokens_of_children as $token) {
if (!empty($token->is_whitespace)) {
$result[] = $token;
continue;
}
$all_whitespace = false; // phew, we're not talking about whitespace
$is_child = ($nesting == 0);
if ($token->type == 'start') {
$nesting++;
} elseif ($token->type == 'end') {
$nesting--;
}
if ($is_child) {
$is_deleting = false;
if (!isset($this->elements[$token->name])) {
$is_deleting = true;
if ($pcdata_allowed && $token->type == 'text') {
$result[] = $token;
} elseif ($pcdata_allowed && $escape_invalid_children) {
$result[] = new HTMLPurifier_Token_Text(
$this->gen->generateFromToken($token, $config)
);
}
continue;
}
}
if (!$is_deleting || ($pcdata_allowed && $token->type == 'text')) {
$result[] = $token;
} elseif ($pcdata_allowed && $escape_invalid_children) {
$result[] =
new HTMLPurifier_Token_Text(
$this->gen->generateFromToken( $token, $config )
);
} else {
// drop silently
}
}
if (empty($result)) return false;
if ($all_whitespace) return false;
if ($tokens_of_children == $result) return true;
return $result;
}
}
?>

View File

@@ -1,70 +0,0 @@
<?php
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 $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->init = true;
}
$result = parent::validateChildren($tokens_of_children, $config, $context);
if ($result === false) return array();
if ($result === true) $result = $tokens_of_children;
$block_wrap_start = new HTMLPurifier_Token_Start($def->info_block_wrapper);
$block_wrap_end = new HTMLPurifier_Token_End( $def->info_block_wrapper);
$is_inline = false;
$depth = 0;
$ret = array();
// assuming that there are no comment tokens
foreach ($result as $i => $token) {
$token = $result[$i];
// ifs are nested for readability
if (!$is_inline) {
if (!$depth) {
if (($token->type == 'text') ||
($def->info[$token->name]->type == 'inline')) {
$is_inline = true;
$ret[] = $block_wrap_start;
}
}
} else {
if (!$depth) {
// starting tokens have been inline text / empty
if ($token->type == 'start' || $token->type == 'empty') {
if ($def->info[$token->name]->type == 'block') {
// ended
$ret[] = $block_wrap_end;
$is_inline = false;
}
}
}
}
$ret[] = $token;
if ($token->type == 'start') $depth++;
if ($token->type == 'end') $depth--;
}
if ($is_inline) $ret[] = $block_wrap_end;
return $ret;
}
}
?>

View File

@@ -1,142 +0,0 @@
<?php
require_once 'HTMLPurifier/ChildDef.php';
/**
* Definition for tables
*/
class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
{
var $allow_empty = false;
var $type = 'table';
function HTMLPurifier_ChildDef_Table() {}
function validateChildren($tokens_of_children, $config, &$context) {
if (empty($tokens_of_children)) return false;
// this ensures that the loop gets run one last time before closing
// up. It's a little bit of a hack, but it works! Just make sure you
// get rid of the token later.
$tokens_of_children[] = false;
// only one of these elements is allowed in a table
$caption = false;
$thead = false;
$tfoot = false;
// as many of these as you want
$cols = array();
$content = array();
$nesting = 0; // current depth so we can determine nodes
$is_collecting = false; // are we globbing together tokens to package
// into one of the collectors?
$collection = array(); // collected nodes
$tag_index = 0; // the first node might be whitespace,
// so this tells us where the start tag is
foreach ($tokens_of_children as $token) {
$is_child = ($nesting == 0);
if ($token === false) {
// terminating sequence started
} elseif ($token->type == 'start') {
$nesting++;
} elseif ($token->type == 'end') {
$nesting--;
}
// handle node collection
if ($is_collecting) {
if ($is_child) {
// okay, let's stash the tokens away
// first token tells us the type of the collection
switch ($collection[$tag_index]->name) {
case 'tr':
case 'tbody':
$content[] = $collection;
break;
case 'caption':
if ($caption !== false) break;
$caption = $collection;
break;
case 'thead':
case 'tfoot':
// access the appropriate variable, $thead or $tfoot
$var = $collection[$tag_index]->name;
if ($$var === false) {
$$var = $collection;
} else {
// transmutate the first and less entries into
// tbody tags, and then put into content
$collection[$tag_index]->name = 'tbody';
$collection[count($collection)-1]->name = 'tbody';
$content[] = $collection;
}
break;
case 'colgroup':
$cols[] = $collection;
break;
}
$collection = array();
$is_collecting = false;
$tag_index = 0;
} else {
// add the node to the collection
$collection[] = $token;
}
}
// terminate
if ($token === false) break;
if ($is_child) {
// determine what we're dealing with
if ($token->name == 'col') {
// the only empty tag in the possie, we can handle it
// immediately
$cols[] = array_merge($collection, array($token));
$collection = array();
$tag_index = 0;
continue;
}
switch($token->name) {
case 'caption':
case 'colgroup':
case 'thead':
case 'tfoot':
case 'tbody':
case 'tr':
$is_collecting = true;
$collection[] = $token;
continue;
default:
if ($token->type == 'text' && $token->is_whitespace) {
$collection[] = $token;
$tag_index++;
}
continue;
}
}
}
if (empty($content)) return false;
$ret = array();
if ($caption !== false) $ret = array_merge($ret, $caption);
if ($cols !== false) foreach ($cols as $token_array) $ret = array_merge($ret, $token_array);
if ($thead !== false) $ret = array_merge($ret, $thead);
if ($tfoot !== false) $ret = array_merge($ret, $tfoot);
foreach ($content as $token_array) $ret = array_merge($ret, $token_array);
if (!empty($collection) && $is_collecting == false){
// grab the trailing space
$ret = array_merge($ret, $collection);
}
array_pop($tokens_of_children); // remove phantom token
return ($ret === $tokens_of_children) ? true : $ret;
}
}
?>

View File

@@ -44,29 +44,11 @@ class HTMLPurifier_Config
$this->def = $definition; // keep a copy around for checking
}
/**
* Convenience constructor that creates a config object based on a mixed var
* @static
* @param mixed $config Variable that defines the state of the config
* object. Can be: a HTMLPurifier_Config() object,
* an array of directives based on loadArray(),
* or a string filename of an ini file.
* @return Configured HTMLPurifier_Config object
*/
static function create($config) {
if ($config instanceof HTMLPurifier_Config) return $config;
$ret = HTMLPurifier_Config::createDefault();
if (is_string($config)) $ret->loadIni($config);
elseif (is_array($config)) $ret->loadArray($config);
return $ret;
}
/**
* Convenience constructor that creates a default configuration object.
* @static
* @return Default HTMLPurifier_Config object.
*/
static function createDefault() {
function createDefault() {
$definition =& HTMLPurifier_ConfigSchema::instance();
$config = new HTMLPurifier_Config($definition);
return $config;
@@ -77,55 +59,27 @@ class HTMLPurifier_Config
* @param $namespace String namespace
* @param $key String key
*/
function get($namespace, $key, $from_alias = false) {
function get($namespace, $key) {
if (!isset($this->def->info[$namespace][$key])) {
trigger_error('Cannot retrieve value of undefined directive',
E_USER_WARNING);
return;
}
if ($this->def->info[$namespace][$key]->class == 'alias') {
trigger_error('Cannot get value from aliased directive, use real name',
E_USER_ERROR);
return;
}
return $this->conf[$namespace][$key];
}
/**
* Retreives an array of directives to values from a given namespace
* @param $namespace String namespace
*/
function getBatch($namespace) {
if (!isset($this->def->info[$namespace])) {
trigger_error('Cannot retrieve undefined namespace',
E_USER_WARNING);
return;
}
return $this->conf[$namespace];
}
/**
* Sets a value to configuration.
* @param $namespace String namespace
* @param $key String key
* @param $value Mixed value
*/
function set($namespace, $key, $value, $from_alias = false) {
function set($namespace, $key, $value) {
if (!isset($this->def->info[$namespace][$key])) {
trigger_error('Cannot set undefined directive to value',
E_USER_WARNING);
return;
}
if ($this->def->info[$namespace][$key]->class == 'alias') {
if ($from_alias) {
trigger_error('Double-aliases not allowed, please fix '.
'ConfigSchema bug');
}
$this->set($this->def->info[$namespace][$key]->namespace,
$this->def->info[$namespace][$key]->name,
$value, true);
return;
}
$value = $this->def->validate(
$value,
$this->def->info[$namespace][$key]->type,
@@ -180,7 +134,6 @@ class HTMLPurifier_Config
*/
function loadArray($config_array) {
foreach ($config_array as $key => $value) {
$key = str_replace('_', '.', $key);
if (strpos($key, '.') !== false) {
// condensed form
list($namespace, $directive) = explode('.', $key);
@@ -195,15 +148,6 @@ class HTMLPurifier_Config
}
}
/**
* Loads configuration values from an ini file
* @param $filename Name of ini file
*/
function loadIni($filename) {
$array = parse_ini_file($filename, true);
$this->loadArray($array);
}
}
?>
?>

View File

@@ -67,9 +67,8 @@ class HTMLPurifier_ConfigSchema {
/**
* Retrieves an instance of the application-wide configuration definition.
* @static
*/
static function &instance($prototype = null) {
function &instance($prototype = null) {
static $instance;
if ($prototype !== null) {
$instance = $prototype;
@@ -82,7 +81,6 @@ class HTMLPurifier_ConfigSchema {
/**
* Defines a directive for configuration
* @static
* @warning Will fail of directive's namespace is defined
* @param $namespace Namespace the directive is in
* @param $name Key of directive
@@ -91,7 +89,7 @@ class HTMLPurifier_ConfigSchema {
* HTMLPurifier_DirectiveDef::$type for allowed values
* @param $description Description of directive for documentation
*/
static function define(
function define(
$namespace, $name, $default, $type,
$description
) {
@@ -106,11 +104,6 @@ class HTMLPurifier_ConfigSchema {
E_USER_ERROR);
return;
}
if (empty($description)) {
trigger_error('Description must be non-empty',
E_USER_ERROR);
return;
}
if (isset($def->info[$namespace][$name])) {
if (
$def->info[$namespace][$name]->type !== $type ||
@@ -151,11 +144,10 @@ class HTMLPurifier_ConfigSchema {
/**
* Defines a namespace for directives to be put into.
* @static
* @param $namespace Namespace's name
* @param $description Description of the namespace
*/
static function defineNamespace($namespace, $description) {
function defineNamespace($namespace, $description) {
$def =& HTMLPurifier_ConfigSchema::instance();
if (isset($def->info[$namespace])) {
trigger_error('Cannot redefine namespace', E_USER_ERROR);
@@ -166,11 +158,6 @@ class HTMLPurifier_ConfigSchema {
E_USER_ERROR);
return;
}
if (empty($description)) {
trigger_error('Description must be non-empty',
E_USER_ERROR);
return;
}
$def->info[$namespace] = array();
$def->info_namespace[$namespace] = new HTMLPurifier_ConfigEntity_Namespace();
$def->info_namespace[$namespace]->description = $description;
@@ -182,13 +169,12 @@ class HTMLPurifier_ConfigSchema {
*
* Directive value aliases are convenient for developers because it lets
* them set a directive to several values and get the same result.
* @static
* @param $namespace Directive's namespace
* @param $name Name of Directive
* @param $alias Name of aliased value
* @param $real Value aliased value will be converted into
*/
static function defineValueAliases($namespace, $name, $aliases) {
function defineValueAliases($namespace, $name, $aliases) {
$def =& HTMLPurifier_ConfigSchema::instance();
if (!isset($def->info[$namespace][$name])) {
trigger_error('Cannot set value alias for non-existant directive',
@@ -214,78 +200,23 @@ class HTMLPurifier_ConfigSchema {
/**
* Defines a set of allowed values for a directive.
* @static
* @param $namespace Namespace of directive
* @param $name Name of directive
* @param $allowed_values Arraylist of allowed values
*/
static function defineAllowedValues($namespace, $name, $allowed_values) {
function defineAllowedValues($namespace, $name, $allowed_values) {
$def =& HTMLPurifier_ConfigSchema::instance();
if (!isset($def->info[$namespace][$name])) {
trigger_error('Cannot define allowed values for undefined directive',
E_USER_ERROR);
return;
}
$directive =& $def->info[$namespace][$name];
$type = $directive->type;
if ($type != 'string' && $type != 'istring') {
trigger_error('Cannot define allowed values for directive whose type is not string',
E_USER_ERROR);
return;
}
if ($directive->allowed === true) {
$directive->allowed = array();
if ($def->info[$namespace][$name]->allowed === true) {
$def->info[$namespace][$name]->allowed = array();
}
foreach ($allowed_values as $value) {
$directive->allowed[$value] = true;
$def->info[$namespace][$name]->allowed[$value] = true;
}
if ($def->defaults[$namespace][$name] !== null &&
!isset($directive->allowed[$def->defaults[$namespace][$name]])) {
trigger_error('Default value must be in allowed range of variables',
E_USER_ERROR);
$directive->allowed = true; // undo undo!
return;
}
}
/**
* Defines a directive alias for backwards compatibility
* @static
* @param $namespace
* @param $name Directive that will be aliased
* @param $new_namespace
* @param $new_name Directive that the alias will be to
*/
static function defineAlias($namespace, $name, $new_namespace, $new_name) {
$def =& HTMLPurifier_ConfigSchema::instance();
if (!isset($def->info[$namespace])) {
trigger_error('Cannot define directive alias in undefined namespace',
E_USER_ERROR);
return;
}
if (!ctype_alnum($name)) {
trigger_error('Directive name must be alphanumeric',
E_USER_ERROR);
return;
}
if (isset($def->info[$namespace][$name])) {
trigger_error('Cannot define alias over directive',
E_USER_ERROR);
return;
}
if (!isset($def->info[$new_namespace][$new_name])) {
trigger_error('Cannot define alias to undefined directive',
E_USER_ERROR);
return;
}
if ($def->info[$new_namespace][$new_name]->class == 'alias') {
trigger_error('Cannot define alias to alias',
E_USER_ERROR);
return;
}
$def->info[$namespace][$name] =
new HTMLPurifier_ConfigEntity_DirectiveAlias(
$new_namespace, $new_name);
}
/**
@@ -316,26 +247,11 @@ class HTMLPurifier_ConfigSchema {
case 'bool':
if (is_int($var) && ($var === 0 || $var === 1)) {
$var = (bool) $var;
} elseif (is_string($var)) {
if ($var == 'on' || $var == 'true' || $var == '1') {
$var = true;
} elseif ($var == 'off' || $var == 'false' || $var == '0') {
$var = false;
} else {
break;
}
} elseif (!is_bool($var)) break;
return $var;
case 'list':
case 'hash':
case 'lookup':
if (is_string($var)) {
// simplistic string to array method that only works
// for simple lists of tag names or alphanumeric characters
$var = explode(',',$var);
// remove spaces
foreach ($var as $i => $j) $var[$i] = trim($j);
}
if (!is_array($var)) break;
$keys = array_keys($var);
if ($keys === array_keys($keys)) {
@@ -374,7 +290,7 @@ class HTMLPurifier_ConfigSchema {
*/
function isError($var) {
if (!is_object($var)) return false;
if (!($var instanceof HTMLPurifier_Error)) return false;
if (!is_a($var, 'HTMLPurifier_Error')) return false;
return true;
}
}
@@ -382,21 +298,13 @@ class HTMLPurifier_ConfigSchema {
/**
* Base class for configuration entity
*/
class HTMLPurifier_ConfigEntity {
var $class = false;
}
class HTMLPurifier_ConfigEntity {}
/**
* 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.
*/
@@ -411,21 +319,15 @@ class HTMLPurifier_ConfigEntity_Namespace extends HTMLPurifier_ConfigEntity {
class HTMLPurifier_ConfigEntity_Directive extends HTMLPurifier_ConfigEntity
{
var $class = 'directive';
/**
* Hash of value aliases, i.e. values that are equivalent.
*/
var $aliases = array();
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;
}
/**
* Lookup table of allowed values of the element, bool true if all allowed.
*/
var $allowed = true;
/**
* Allowed type of the directive. Values are:
@@ -442,26 +344,16 @@ class HTMLPurifier_ConfigEntity_Directive extends HTMLPurifier_ConfigEntity
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.
* Is null allowed? Has no affect for mixed type.
* @bool
*/
var $allow_null = false;
/**
* Lookup table of allowed values of the element, bool true if all allowed.
* Plaintext descriptions of the configuration entity is. Organized by
* file and line number, so multiple descriptions are allowed.
*/
var $allowed = true;
/**
* Hash of value aliases, i.e. values that are equivalent.
*/
var $aliases = array();
var $descriptions = array();
/**
* Adds a description to the array
@@ -473,26 +365,4 @@ class HTMLPurifier_ConfigEntity_Directive extends HTMLPurifier_ConfigEntity
}
/**
* 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

@@ -6,29 +6,15 @@ HTMLPurifier_ConfigSchema::define(
'Core', 'Encoding', 'utf-8', 'istring',
'If for some reason you are unable to convert all webpages to UTF-8, '.
'you can use this directive as a stop-gap compatibility change to '.
'let HTML Purifier deal with non UTF-8 input. This technique has '.
'let HTMLPurifier deal with non UTF-8 input. This technique has '.
'notable deficiencies: absolutely no characters outside of the selected '.
'character encoding will be preserved, not even the ones that have '.
'been ampersand escaped (this is due to a UTF-8 specific <em>feature</em> '.
'that automatically resolves all entities), making it pretty useless '.
'for anything except the most I18N-blind applications, although '.
'%Core.EscapeNonASCIICharacters offers fixes this trouble with '.
'another tradeoff. This directive '.
'for anything except the most I18N-blind applications. This directive '.
'only accepts ISO-8859-1 if iconv is not enabled.'
);
HTMLPurifier_ConfigSchema::define(
'Core', 'EscapeNonASCIICharacters', false, 'bool',
'This directive overcomes a deficiency in %Core.Encoding by blindly '.
'converting all non-ASCII characters into decimal numeric entities before '.
'converting it to its native encoding. This means that even '.
'characters that can be expressed in the non-UTF-8 encoding will '.
'be entity-ized, which can be a real downer for encodings like Big5. '.
'It also assumes that the ASCII repetoire is available, although '.
'this is the case for almost all encodings. Anyway, use UTF-8! This '.
'directive has been available since 1.4.0.'
);
if ( !function_exists('iconv') ) {
// only encodings with native PHP support
HTMLPurifier_ConfigSchema::defineAllowedValues(
@@ -52,25 +38,16 @@ HTMLPurifier_ConfigSchema::define(
/**
* A UTF-8 specific character encoder that handles cleaning and transforming.
* @note All functions in this class should be static.
*/
class HTMLPurifier_Encoder
{
/**
* Constructor throws fatal error if you attempt to instantiate class
*/
function HTMLPurifier_Encoder() {
trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR);
}
/**
* Cleans a UTF-8 string for well-formedness and SGML validity
*
* It will parse according to UTF-8 and return a valid UTF8 string, with
* non-SGML codepoints excluded.
*
* @static
* @note Just for reference, the non-SGML code points are 0 to 31 and
* 127 to 159, inclusive. However, we allow code points 9, 10
* and 13, which are the tab, line feed and carriage return
@@ -90,7 +67,7 @@ class HTMLPurifier_Encoder
* would need that, and I'm probably not going to implement them.
* Once again, PHP 6 should solve all our problems.
*/
static function cleanUTF8($str, $force_php = false) {
function cleanUTF8($str, $force_php = false) {
static $non_sgml_chars = array();
if (empty($non_sgml_chars)) {
@@ -248,7 +225,6 @@ class HTMLPurifier_Encoder
/**
* Translates a Unicode codepoint into its corresponding UTF-8 character.
* @static
* @note Based on Feyd's function at
* <http://forums.devnetwork.net/viewtopic.php?p=191404#191404>,
* which is in public domain.
@@ -273,7 +249,7 @@ class HTMLPurifier_Encoder
// | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes
// +----------+----------+----------+----------+
static function unichr($code) {
function unichr($code) {
if($code > 1114111 or $code < 0 or
($code >= 55296 and $code <= 57343) ) {
// bits are set outside the "valid" range as defined
@@ -312,9 +288,8 @@ class HTMLPurifier_Encoder
/**
* Converts a string to UTF-8 based on configuration.
* @static
*/
static function convertToUTF8($str, $config, &$context) {
function convertToUTF8($str, $config, &$context) {
static $iconv = null;
if ($iconv === null) $iconv = function_exists('iconv');
$encoding = $config->get('Core', 'Encoding');
@@ -324,77 +299,23 @@ class HTMLPurifier_Encoder
} elseif ($encoding === 'iso-8859-1') {
return @utf8_encode($str);
}
trigger_error('Encoding not supported', E_USER_ERROR);
}
/**
* Converts a string from UTF-8 based on configuration.
* @static
* @note Currently, this is a lossy conversion, with unexpressable
* characters being omitted.
*/
static function convertFromUTF8($str, $config, &$context) {
function convertFromUTF8($str, $config, &$context) {
static $iconv = null;
if ($iconv === null) $iconv = function_exists('iconv');
$encoding = $config->get('Core', 'Encoding');
if ($encoding === 'utf-8') return $str;
if ($config->get('Core', 'EscapeNonASCIICharacters')) {
$str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str);
}
if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
return @iconv('utf-8', $encoding . '//IGNORE', $str);
} elseif ($encoding === 'iso-8859-1') {
return @utf8_decode($str);
}
trigger_error('Encoding not supported', E_USER_ERROR);
}
/**
* Lossless (character-wise) conversion of HTML to ASCII
* @static
* @param $str UTF-8 string to be converted to ASCII
* @returns ASCII encoded string with non-ASCII character entity-ized
* @warning Adapted from MediaWiki, claiming fair use: this is a common
* algorithm. If you disagree with this license fudgery,
* implement it yourself.
* @note Uses decimal numeric entities since they are best supported.
* @note This is a DUMB function: it has no concept of keeping
* character entities that the projected character encoding
* can allow. We could possibly implement a smart version
* but that would require it to also know which Unicode
* codepoints the charset supported (not an easy task).
* @note Sort of with cleanUTF8() but it assumes that $str is
* well-formed UTF-8
*/
static function convertToASCIIDumbLossless($str) {
$bytesleft = 0;
$result = '';
$working = 0;
$len = strlen($str);
for( $i = 0; $i < $len; $i++ ) {
$bytevalue = ord( $str[$i] );
if( $bytevalue <= 0x7F ) { //0xxx xxxx
$result .= chr( $bytevalue );
$bytesleft = 0;
} elseif( $bytevalue <= 0xBF ) { //10xx xxxx
$working = $working << 6;
$working += ($bytevalue & 0x3F);
$bytesleft--;
if( $bytesleft <= 0 ) {
$result .= "&#" . $working . ";";
}
} elseif( $bytevalue <= 0xDF ) { //110x xxxx
$working = $bytevalue & 0x1F;
$bytesleft = 1;
} elseif( $bytevalue <= 0xEF ) { //1110 xxxx
$working = $bytevalue & 0x0F;
$bytesleft = 2;
} else { //1111 0xxx
$working = $bytevalue & 0x07;
$bytesleft = 3;
}
}
return $result;
}

View File

@@ -26,10 +26,9 @@ class HTMLPurifier_EntityLookup {
/**
* Retrieves sole instance of the object.
* @static
* @param Optional prototype of custom lookup table to overload with.
*/
static function instance($prototype = false) {
function instance($prototype = false) {
// no references, since PHP doesn't copy unless modified
static $instance = null;
if ($prototype) {

View File

@@ -1,39 +0,0 @@
<?php
/**
* Represents a pre or post processing filter on HTML Purifier's output
*
* Sometimes, a little ad-hoc fixing of HTML has to be done before
* it gets sent through HTML Purifier: you can use filters to acheive
* this effect. For instance, YouTube videos can be preserved using
* this manner. You could have used a decorator for this task, but
* PHP's support for them is not terribly robust, so we're going
* to just loop through the filters.
*
* Filters should be exited first in, last out. If there are three filters,
* named 1, 2 and 3, the order of execution should go 1->preFilter,
* 2->preFilter, 3->preFilter, purify, 3->postFilter, 2->postFilter,
* 1->postFilter.
*/
class HTMLPurifier_Filter
{
/**
* Name of the filter for identification purposes
*/
var $name;
/**
* Pre-processor function, handles HTML before HTML Purifier
*/
function preFilter($html, $config, &$context) {}
/**
* Post-processor function, handles HTML after HTML Purifier
*/
function postFilter($html, $config, &$context) {}
}
?>

View File

@@ -1,34 +0,0 @@
<?php
require_once 'HTMLPurifier/Filter.php';
class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
{
var $name = 'YouTube preservation';
function preFilter($html, $config, &$context) {
$pre_regex = '#<object[^>]+>.+?'.
'http://www.youtube.com/v/([A-Za-z0-9\-_]+).+?</object>#';
$pre_replace = '<span class="youtube-embed">\1</span>';
return preg_replace($pre_regex, $pre_replace, $html);
}
function postFilter($html, $config, &$context) {
$post_regex = '#<span class="youtube-embed">([A-Za-z0-9\-_]+)</span>#';
$post_replace = '<object width="425" height="350" '.
'data="http://www.youtube.com/v/\1">'.
'<param name="movie" value="http://www.youtube.com/v/\1"></param>'.
'<param name="wmode" value="transparent"></param>'.
'<!--[if IE]>'.
'<embed src="http://www.youtube.com/v/\1"'.
'type="application/x-shockwave-flash"'.
'wmode="transparent" width="425" height="350" />'.
'<![endif]-->'.
'</object>';
return preg_replace($post_regex, $post_replace, $html);
}
}
?>

View File

@@ -104,14 +104,14 @@ class HTMLPurifier_Generator
function generateFromToken($token) {
if (!isset($token->type)) return '';
if ($token->type == 'start') {
$attr = $this->generateAttributes($token->attr);
$attr = $this->generateAttributes($token->attributes);
return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
} elseif ($token->type == 'end') {
return '</' . $token->name . '>';
} elseif ($token->type == 'empty') {
$attr = $this->generateAttributes($token->attr);
$attr = $this->generateAttributes($token->attributes);
return '<' . $token->name . ($attr ? ' ' : '') . $attr .
( $this->_xhtml ? ' /': '' )
. '>';

View File

@@ -18,12 +18,6 @@ require_once 'HTMLPurifier/AttrTransform.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';
@@ -41,63 +35,6 @@ HTMLPurifier_ConfigSchema::define(
'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.
*
@@ -132,24 +69,11 @@ class HTMLPurifier_HTMLDefinition
/**
* String name of parent element HTML will be going into.
* @todo Allow this to be overloaded by user config
* @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
@@ -168,25 +92,14 @@ class HTMLPurifier_HTMLDefinition
*/
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');
// emulates the structure of the DTD
// these are condensed, however, with bad stuff taken out
// screening process was done by hand
//////////////////////////////////////////////////////////////////////
// info[] : initializes the definition objects
@@ -198,19 +111,13 @@ class HTMLPurifier_HTMLDefinition
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',
'q', 'sub', 'tt', 'sup', 'i', 'b', 'big', 'small', 'u', 's',
'strike', '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();
}
@@ -218,10 +125,6 @@ class HTMLPurifier_HTMLDefinition
//////////////////////////////////////////////////////////////////////
// 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
@@ -245,9 +148,11 @@ class HTMLPurifier_HTMLDefinition
$e_phrase_basic = 'em | strong | dfn | code | q | samp | kbd | var'.
' | cite | abbr | acronym';
$e_phrase = "$e_phrase_basic | $e_phrase_extra";
$e_inline_forms = ''; // humor the dtd
$e_misc_inline = 'ins | del';
$e_misc = "$e_misc_inline";
$e_inline = "a | $e_special | $e_fontstyle | $e_phrase";
$e_inline = "a | $e_special | $e_fontstyle | $e_phrase".
" | $e_inline_forms";
// pseudo-property we created for convenience, see later on
$e__inline = "#PCDATA | $e_inline | $e_misc_inline";
// note the casing
@@ -256,14 +161,14 @@ class HTMLPurifier_HTMLDefinition
$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_special | $e_fontstyle | $e_phrase | $e_inline_forms".
" | $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_inline_forms | $e_misc_inline");
$e_form_content = new HTMLPurifier_ChildDef_Optional('');//unused
$e_form_button_content = new HTMLPurifier_ChildDef_Optional('');//unused
@@ -271,16 +176,11 @@ class HTMLPurifier_HTMLDefinition
$this->info['del']->child =
new HTMLPurifier_ChildDef_Chameleon($e__inline, $e__flow);
$this->info['blockquote']->child=
$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 =
@@ -300,6 +200,9 @@ class HTMLPurifier_HTMLDefinition
$this->info['b']->child =
$this->info['big']->child =
$this->info['small']->child=
$this->info['u']->child =
$this->info['s']->child =
$this->info['strike']->child =
$this->info['bdo']->child =
$this->info['span']->child =
$this->info['dt']->child =
@@ -311,25 +214,15 @@ class HTMLPurifier_HTMLDefinition
$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['address']->child =
new HTMLPurifier_ChildDef_Optional("#PCDATA | p | $e_inline".
" | $e_misc_inline");
$this->info['img']->child =
$this->info['br']->child =
@@ -357,20 +250,15 @@ class HTMLPurifier_HTMLDefinition
// reuses $e_Inline and $e_Block
foreach ($e_Inline->elements as $name => $bool) {
if ($name == '#PCDATA') continue;
if (!isset($this->info[$name])) continue;
if ($name == '#PCDATA' || $name == '') continue;
$this->info[$name]->type = 'inline';
}
$e_Block = new HTMLPurifier_ChildDef_Optional($e_block);
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
@@ -460,23 +348,16 @@ class HTMLPurifier_HTMLDefinition
$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);
}
$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;
if (!$this->strict) {
$this->info['li']->attr['value'] = new HTMLPurifier_AttrDef_Integer();
$this->info['ol']->attr['start'] = new HTMLPurifier_AttrDef_Integer();
}
// URI that causes HTTP request
$this->info['img']->attr['src'] = new HTMLPurifier_AttrDef_URI(true);
//////////////////////////////////////////////////////////////////////
// info_tag_transform : transformations of tags
@@ -536,58 +417,11 @@ class HTMLPurifier_HTMLDefinition
// protect against stdclasses floating around
foreach ($this->info as $key => $obj) {
if ($obj instanceof stdClass) {
if (is_a($obj, '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) {
@@ -653,4 +487,4 @@ class HTMLPurifier_ElementDef
}
?>
?>

View File

@@ -56,6 +56,7 @@ class HTMLPurifier_Lexer
{
function HTMLPurifier_Lexer() {
$this->_encoder = new HTMLPurifier_Encoder();
$this->_entity_parser = new HTMLPurifier_EntityParser();
}
@@ -113,6 +114,8 @@ class HTMLPurifier_Lexer
return $string;
}
var $_encoder;
/**
* Lexes an HTML string into tokens.
*
@@ -135,8 +138,6 @@ class HTMLPurifier_Lexer
* default with your own implementation. A copy/reference of the prototype
* lexer will now be returned when you request a new lexer.
*
* @static
*
* @note
* Though it is possible to call this factory method from subclasses,
* such usage is not recommended.
@@ -144,14 +145,14 @@ class HTMLPurifier_Lexer
* @param $prototype Optional prototype lexer.
* @return Concrete lexer.
*/
static function create($prototype = null) {
function create($prototype = null) {
// we don't really care if it's a reference or a copy
static $lexer = null;
if ($prototype) {
$lexer = $prototype;
}
if (empty($lexer)) {
if (class_exists('DOMDocument')) { // check for DOM support
if (version_compare(PHP_VERSION, '5', '>=')) {
require_once 'HTMLPurifier/Lexer/DOMLex.php';
$lexer = new HTMLPurifier_Lexer_DOMLex();
} else {
@@ -165,12 +166,11 @@ class HTMLPurifier_Lexer
/**
* Translates CDATA sections into regular sections (through escaping).
*
* @static
* @protected
* @param $string HTML string to process.
* @returns HTML with CDATA sections escaped.
*/
static function escapeCDATA($string) {
function escapeCDATA($string) {
return preg_replace_callback(
'/<!\[CDATA\[(.+?)\]\]>/',
array('HTMLPurifier_Lexer', 'CDATACallback'),
@@ -181,14 +181,13 @@ class HTMLPurifier_Lexer
/**
* Callback function for escapeCDATA() that does the work.
*
* @static
* @warning Though this is public in order to let the callback happen,
* calling it directly is not recommended.
* @params $matches PCRE matches array, with index 0 the entire match
* and 1 the inside of the CDATA section.
* @returns Escaped internals of the CDATA section.
*/
static function CDATACallback($matches) {
function CDATACallback($matches) {
// not exactly sure why the character set is needed, but whatever
return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8');
}
@@ -213,7 +212,7 @@ class HTMLPurifier_Lexer
// clean into wellformed UTF-8 string for an SGML context: this has
// to be done after entity expansion because the entities sometimes
// represent non-SGML characters (horror, horror!)
$html = HTMLPurifier_Encoder::cleanUTF8($html);
$html = $this->_encoder->cleanUTF8($html);
return $html;
}

View File

@@ -88,11 +88,6 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
} elseif ($node->nodeType === XML_COMMENT_NODE) {
$tokens[] = $this->factory->createComment($node->data);
return;
} elseif (
// not-well tested: there may be other nodes we have to grab
$node->nodeType !== XML_ELEMENT_NODE
) {
return;
}
$attr = $node->hasAttributes() ?

View File

@@ -143,18 +143,18 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
)
);
if ($attribute_string) {
$attr = $this->parseAttributeString(
$attribute_string
, $config, $context
);
$attributes = $this->parseAttributeString(
$attribute_string
, $config, $context
);
} else {
$attr = array();
$attributes = array();
}
if ($is_self_closing) {
$array[] = new HTMLPurifier_Token_Empty($type, $attr);
$array[] = new HTMLPurifier_Token_Empty($type, $attributes);
} else {
$array[] = new HTMLPurifier_Token_Start($type, $attr);
$array[] = new HTMLPurifier_Token_Start($type, $attributes);
}
$cursor = $position_next_gt + 1;
$inside_tag = false;

View File

@@ -37,7 +37,7 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer
$string = $this->normalize($string, $config, $context);
$parser = new XML_HTMLSax3();
$parser=& new XML_HTMLSax3();
$parser->set_object($this);
$parser->set_element_handler('openHandler','closeHandler');
$parser->set_data_handler('dataHandler');

View File

@@ -1,149 +0,0 @@
<?php
require_once 'HTMLPurifier/Generator.php';
require_once 'HTMLPurifier/Token.php';
require_once 'HTMLPurifier/Encoder.php';
class HTMLPurifier_Printer
{
/**
* Instance of HTMLPurifier_Generator for HTML generation convenience funcs
*/
var $generator;
/**
* Instance of HTMLPurifier_Config, for easy access
*/
var $config;
/**
* Initialize $generator.
*/
function HTMLPurifier_Printer() {
$this->generator = new HTMLPurifier_Generator();
}
/**
* Main function that renders object or aspect of that object
* @param $config Configuration object
*/
function render($config) {}
/**
* Returns a start tag
* @param $tag Tag name
* @param $attr Attribute array
*/
function start($tag, $attr = array()) {
return $this->generator->generateFromToken(
new HTMLPurifier_Token_Start($tag, $attr ? $attr : array())
);
}
/**
* Returns an end teg
* @param $tag Tag name
*/
function end($tag) {
return $this->generator->generateFromToken(
new HTMLPurifier_Token_End($tag)
);
}
/**
* Prints a complete element with content inside
* @param $tag Tag name
* @param $contents Element contents
* @param $attr Tag attributes
* @param $escape Bool whether or not to escape contents
*/
function element($tag, $contents, $attr = array(), $escape = true) {
return $this->start($tag, $attr) .
($escape ? $this->escape($contents) : $contents) .
$this->end($tag);
}
/**
* Prints a simple key/value row in a table.
* @param $name Key
* @param $value Value
*/
function row($name, $value) {
if (is_bool($value)) $value = $value ? 'On' : 'Off';
return
$this->start('tr') . "\n" .
$this->element('th', $name) . "\n" .
$this->element('td', $value) . "\n" .
$this->end('tr')
;
}
/**
* Escapes a string for HTML output.
* @param $string String to escape
*/
function escape($string) {
$string = HTMLPurifier_Encoder::cleanUTF8($string);
$string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
return $string;
}
/**
* Takes a list of strings and turns them into a single list
* @param $array List of strings
* @param $polite Bool whether or not to add an end before the last
*/
function listify($array, $polite = false) {
if (empty($array)) return 'None';
$ret = '';
$i = count($array);
foreach ($array as $value) {
$i--;
$ret .= $value;
if ($i > 0 && !($polite && $i == 1)) $ret .= ', ';
if ($polite && $i == 1) $ret .= 'and ';
}
return $ret;
}
/**
* Retrieves the class of an object without prefixes, as well as metadata
* @param $obj Object to determine class of
* @param $prefix Further prefix to remove
*/
function getClass($obj, $sec_prefix = '') {
static $five = null;
if ($five === null) $five = version_compare(PHP_VERSION, '5', '>=');
$prefix = 'HTMLPurifier_' . $sec_prefix;
if (!$five) $prefix = strtolower($prefix);
$class = str_replace($prefix, '', get_class($obj));
$lclass = strtolower($class);
$class .= '(';
switch ($lclass) {
case 'enum':
$values = array();
foreach ($obj->valid_values as $value => $bool) {
$values[] = $value;
}
$class .= implode(', ', $values);
break;
case 'composite':
$values = array();
foreach ($obj->defs as $def) {
$values[] = $this->getClass($def, $sec_prefix);
}
$class .= implode(', ', $values);
break;
case 'multiple':
$class .= $this->getClass($obj->single, $sec_prefix) . ', ';
$class .= $obj->max;
break;
}
$class .= ')';
return $class;
}
}
?>

View File

@@ -1,40 +0,0 @@
<?php
require_once 'HTMLPurifier/Printer.php';
class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer
{
var $def;
function render($config) {
$this->def = $config->getCSSDefinition();
$ret = '';
$ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
$ret .= $this->start('table');
$ret .= $this->element('caption', 'Properties ($info)');
$ret .= $this->start('thead');
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Property', array('class' => 'heavy'));
$ret .= $this->element('th', 'Definition', array('class' => 'heavy', 'style' => 'width:auto;'));
$ret .= $this->end('tr');
$ret .= $this->end('thead');
ksort($this->def->info);
foreach ($this->def->info as $property => $obj) {
$name = $this->getClass($obj, 'AttrDef_');
$ret .= $this->row($property, $name);
}
$ret .= $this->end('table');
$ret .= $this->end('div');
return $ret;
}
}
?>

View File

@@ -1,206 +0,0 @@
<?php
require_once 'HTMLPurifier/Printer.php';
class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
{
/**
* Instance of HTMLPurifier_HTMLDefinition, for easy access
*/
var $def;
function render($config) {
$ret = '';
$this->config =& $config;
$this->def = $config->getHTMLDefinition();
$def =& $this->def;
$ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
$ret .= $this->start('table');
$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->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();
foreach ($def->info_tag_transform as $old => $new) {
$new = $this->getClass($new, 'TagTransform_');
$list[] = "<$old> with $new";
}
$ret .= $this->element('td', $this->listify($list));
$ret .= $this->end('tr');
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Pre-AttrTransform');
$ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre));
$ret .= $this->end('tr');
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Post-AttrTransform');
$ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post));
$ret .= $this->end('tr');
$ret .= $this->end('table');
$ret .= $this->renderInfo();
$ret .= $this->end('div');
return $ret;
}
/**
* Renders the Elements ($info) table
*/
function renderInfo() {
$ret = '';
$ret .= $this->start('table');
$ret .= $this->element('caption', 'Elements ($info)');
ksort($this->def->info);
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Allowed tags', array('colspan' => 2, 'class' => 'heavy'));
$ret .= $this->end('tr');
$ret .= $this->start('tr');
$ret .= $this->element('td', $this->listifyTagLookup($this->def->info), array('colspan' => 2));
$ret .= $this->end('tr');
foreach ($this->def->info as $name => $def) {
$ret .= $this->start('tr');
$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->end('tr');
if (!empty($def->excludes)) {
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Excludes');
$ret .= $this->element('td', $this->listifyTagLookup($def->excludes));
$ret .= $this->end('tr');
}
if (!empty($def->attr_transform_pre)) {
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Pre-AttrTransform');
$ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre));
$ret .= $this->end('tr');
}
if (!empty($def->attr_transform_post)) {
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Post-AttrTransform');
$ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post));
$ret .= $this->end('tr');
}
if (!empty($def->auto_close)) {
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Auto closed by');
$ret .= $this->element('td', $this->listifyTagLookup($def->auto_close));
$ret .= $this->end('tr');
}
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Allowed attributes');
$ret .= $this->element('td',$this->listifyAttr($def->attr),0,0);
$ret .= $this->end('tr');
$ret .= $this->renderChildren($def->child);
}
$ret .= $this->end('table');
return $ret;
}
/**
* Renders a row describing the allowed children of an element
* @param $def HTMLPurifier_ChildDef of pertinent element
*/
function renderChildren($def) {
$context = new HTMLPurifier_Context();
$ret = '';
$ret .= $this->start('tr');
$elements = array();
$attr = array();
if (isset($def->elements)) {
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');
}
$ret .= $this->element('th', 'Allowed children', $attr);
if ($def->type == 'chameleon') {
$ret .= $this->element('td',
'<em>Block</em>: ' .
$this->escape($this->listifyTagLookup($def->block->elements)),0,0);
$ret .= $this->end('tr');
$ret .= $this->start('tr');
$ret .= $this->element('td',
'<em>Inline</em>: ' .
$this->escape($this->listifyTagLookup($def->inline->elements)),0,0);
} else {
$ret .= $this->element('td',
'<em>'.ucfirst($def->type).'</em>: ' .
$this->escape($this->listifyTagLookup($elements)),0,0);
}
$ret .= $this->end('tr');
return $ret;
}
/**
* Listifies a tag lookup table.
* @param $array Tag lookup array in form of array('tagname' => true)
*/
function listifyTagLookup($array) {
$list = array();
foreach ($array as $name => $discard) {
if ($name !== '#PCDATA' && !isset($this->def->info[$name])) continue;
$list[] = $name;
}
return $this->listify($list);
}
/**
* Listifies a list of objects by retrieving class names and internal state
* @param $array List of objects
* @todo Also add information about internal state
*/
function listifyObjectList($array) {
$list = array();
foreach ($array as $discard => $obj) {
$list[] = $this->getClass($obj, 'AttrTransform_');
}
return $this->listify($list);
}
/**
* Listifies a hash of attributes to AttrDef classes
* @param $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef)
*/
function listifyAttr($array) {
$list = array();
foreach ($array as $name => $obj) {
if ($obj === false) continue;
$list[] = "$name&nbsp;=&nbsp;<i>" . $this->getClass($obj, 'AttrDef_') . '</i>';
}
return $this->listify($list);
}
}
?>

View File

@@ -104,11 +104,7 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
if ($count = count($stack)) {
$parent_index = $stack[$count-1];
$parent_name = $tokens[$parent_index]->name;
if ($parent_index == 0) {
$parent_def = $definition->info_parent_def;
} else {
$parent_def = $definition->info[$parent_name];
}
$parent_def = $definition->info[$parent_name];
} else {
// unknown info, it won't be used anyway
$parent_index = $parent_name = $parent_def = null;
@@ -145,25 +141,14 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
if ($excluded) {
// there is an exclusion, remove the entire node
$result = false;
$excludes = array(); // not used, but good to initialize anyway
} else {
// DEFINITION CALL
if ($i === 0) {
// special processing for the first node
$def = $definition->info_parent_def;
} else {
$def = $definition->info[$tokens[$i]->name];
}
$def = $definition->info[$tokens[$i]->name];
$child_def = $def->child;
if (!empty($def->child)) {
// have DTD child def validate children
$result = $def->child->validateChildren(
$child_tokens, $config, $context);
} else {
// weird, no child definition, get rid of everything
$result = false;
}
// have DTD child def validate children
$result = $child_def->validateChildren(
$child_tokens, $config, $context);
// determine whether or not this element has any exclusions
$excludes = $def->excludes;
@@ -243,20 +228,13 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
// Test if the token indeed is a start tag, if not, move forward
// and test again.
$size = count($tokens);
while ($i < $size and $tokens[$i]->type != 'start') {
if ($tokens[$i]->type == 'end') {
// pop a token index off the stack if we ended a node
array_pop($stack);
// pop an exclusion lookup off exclusion stack if
// we ended node and that node had exclusions
if ($i == 0 || $i == $size - 1) {
// use specialized var if it's the super-parent
$s_excludes = $definition->info_parent_def->excludes;
} else {
$s_excludes = $definition->info[$tokens[$i]->name]->excludes;
}
if ($s_excludes) {
if ($definition->info[$tokens[$i]->name]->excludes) {
array_pop($exclude_stack);
}
}

View File

@@ -30,7 +30,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$token->type == 'start' ) {
$result[] = new HTMLPurifier_Token_Empty($token->name,
$token->attr);
$token->attributes);
continue;
}
@@ -39,7 +39,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$token->type == 'empty' ) {
$result[] = new HTMLPurifier_Token_Start($token->name,
$token->attr);
$token->attributes);
$result[] = new HTMLPurifier_Token_End($token->name);
continue;

View File

@@ -5,14 +5,6 @@ require_once 'HTMLPurifier/HTMLDefinition.php';
require_once 'HTMLPurifier/Generator.php';
require_once 'HTMLPurifier/TagTransform.php';
HTMLPurifier_ConfigSchema::define(
'Core', 'RemoveInvalidImg', true, 'bool',
'This directive enables pre-emptive URI checking in <code>img</code> '.
'tags, as the attribute validation strategy is not authorized to '.
'remove elements from the document. This directive has been available '.
'since 1.3.0, revert to pre-1.3.0 behavior by setting to false.'
);
/**
* Removes all unrecognized tags from the list of tokens.
*
@@ -33,26 +25,7 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
if (!empty( $token->is_tag )) {
// DEFINITION CALL
if (isset($definition->info[$token->name])) {
// leave untouched, except for a few special cases:
// hard-coded image special case, pre-emptively drop
// if not available. Probably not abstract-able
if ( $token->name == 'img' ) {
if (!isset($token->attr['src'])) {
continue;
}
if (!isset($definition->info['img']->attr['src'])) {
continue;
}
$token->attr['src'] =
$definition->
info['img']->
attr['src']->
validate($token->attr['src'],
$config, $context);
if ($token->attr['src'] === false) continue;
}
// leave untouched
} elseif (
isset($definition->info_tag_transform[$token->name])
) {

View File

@@ -35,7 +35,7 @@ class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy
if ($token->type !== 'start' && $token->type !== 'empty') continue;
// copy out attributes for easy manipulation
$attr = $token->attr;
$attr = $token->attributes;
// do global transformations (pre)
// nothing currently utilizes this
@@ -117,7 +117,7 @@ class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy
// commit changes
// could interfere with flyweight implementation
$tokens[$key]->attr = $attr;
$tokens[$key]->attributes = $attr;
}
$context->destroy('IDAccumulator');

View File

@@ -62,16 +62,16 @@ class HTMLPurifier_TagTransform_Center extends HTMLPurifier_TagTransform
$new_tag = new HTMLPurifier_Token_End($this->transform_to);
return $new_tag;
}
$attr = $tag->attr;
$attributes = $tag->attributes;
$prepend_css = 'text-align:center;';
if (isset($attr['style'])) {
$attr['style'] = $prepend_css . $attr['style'];
if (isset($attributes['style'])) {
$attributes['style'] = $prepend_css . $attributes['style'];
} else {
$attr['style'] = $prepend_css;
$attributes['style'] = $prepend_css;
}
$new_tag = $tag->copy();
$new_tag->name = $this->transform_to;
$new_tag->attr = $attr;
$new_tag->attributes = $attributes;
return $new_tag;
}
}
@@ -115,39 +115,39 @@ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
return $new_tag;
}
$attr = $tag->attr;
$attributes = $tag->attributes;
$prepend_style = '';
// handle color transform
if (isset($attr['color'])) {
$prepend_style .= 'color:' . $attr['color'] . ';';
unset($attr['color']);
if (isset($attributes['color'])) {
$prepend_style .= 'color:' . $attributes['color'] . ';';
unset($attributes['color']);
}
// handle face transform
if (isset($attr['face'])) {
$prepend_style .= 'font-family:' . $attr['face'] . ';';
unset($attr['face']);
if (isset($attributes['face'])) {
$prepend_style .= 'font-family:' . $attributes['face'] . ';';
unset($attributes['face']);
}
// handle size transform
if (isset($attr['size'])) {
if (isset($this->_size_lookup[$attr['size']])) {
if (isset($attributes['size'])) {
if (isset($this->_size_lookup[$attributes['size']])) {
$prepend_style .= 'font-size:' .
$this->_size_lookup[$attr['size']] . ';';
$this->_size_lookup[$attributes['size']] . ';';
}
unset($attr['size']);
unset($attributes['size']);
}
if ($prepend_style) {
$attr['style'] = isset($attr['style']) ?
$prepend_style . $attr['style'] :
$attributes['style'] = isset($attributes['style']) ?
$prepend_style . $attributes['style'] :
$prepend_style;
}
$new_tag = $tag->copy();
$new_tag->name = $this->transform_to;
$new_tag->attr = $attr;
$new_tag->attributes = $attributes;
return $new_tag;

View File

@@ -50,29 +50,30 @@ class HTMLPurifier_Token_Tag extends HTMLPurifier_Token // abstract
/**
* Associative array of the tag's attributes.
*/
var $attr = array();
var $attributes = array();
/**
* Non-overloaded constructor, which lower-cases passed tag name.
*
* @param $name String name.
* @param $attr Associative array of attributes.
* @param $name String name.
* @param $attributes Associative array of attributes.
*/
function HTMLPurifier_Token_Tag($name, $attr = array()) {
function HTMLPurifier_Token_Tag($name, $attributes = array()) {
//if ($attributes === null) var_dump(debug_backtrace());
$this->name = ctype_lower($name) ? $name : strtolower($name);
foreach ($attr as $key => $value) {
foreach ($attributes as $key => $value) {
// normalization only necessary when key is not lowercase
if (!ctype_lower($key)) {
$new_key = strtolower($key);
if (!isset($attr[$new_key])) {
$attr[$new_key] = $attr[$key];
if (!isset($attributes[$new_key])) {
$attributes[$new_key] = $attributes[$key];
}
if ($new_key !== $key) {
unset($attr[$key]);
unset($attributes[$key]);
}
}
}
$this->attr = $attr;
$this->attributes = $attributes;
}
}
@@ -83,7 +84,7 @@ class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag
{
var $type = 'start';
function copy() {
return new HTMLPurifier_Token_Start($this->name, $this->attr);
return new HTMLPurifier_Token_Start($this->name, $this->attributes);
}
}
@@ -94,7 +95,7 @@ class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag
{
var $type = 'empty';
function copy() {
return new HTMLPurifier_Token_Empty($this->name, $this->attr);
return new HTMLPurifier_Token_Empty($this->name, $this->attributes);
}
}

View File

@@ -37,12 +37,12 @@ class HTMLPurifier_TokenFactory
/**
* Creates a HTMLPurifier_Token_Start.
* @param $name Tag name
* @param $attr Associative array of attributes
* @param $attribute Associative array of attributes
* @return Generated HTMLPurifier_Token_Start
*/
public function createStart($name, $attr = array()) {
public function createStart($name, $attributes = array()) {
$p = clone $this->p_start;
$p->HTMLPurifier_Token_Tag($name, $attr);
$p->HTMLPurifier_Token_Tag($name, $attributes);
return $p;
}
@@ -60,12 +60,12 @@ class HTMLPurifier_TokenFactory
/**
* Creates a HTMLPurifier_Token_Empty.
* @param $name Tag name
* @param $attr Associative array of attributes
* @param $attribute Associative array of attributes
* @return Generated HTMLPurifier_Token_Empty
*/
public function createEmpty($name, $attr = array()) {
public function createEmpty($name, $attributes = array()) {
$p = clone $this->p_empty;
$p->HTMLPurifier_Token_Tag($name, $attr);
$p->HTMLPurifier_Token_Tag($name, $attributes);
return $p;
}

View File

@@ -32,13 +32,12 @@ class HTMLPurifier_URISchemeRegistry
/**
* Retrieve sole instance of the registry.
* @static
* @param $prototype Optional prototype to overload sole instance with,
* or bool true to reset to default registry.
* @note Pass a registry object $prototype with a compatible interface and
* the function will copy it and return it all further times.
*/
static function &instance($prototype = null) {
function &instance($prototype = null) {
static $instance = null;
if ($prototype !== null) {
$instance = $prototype;

View File

@@ -1,40 +0,0 @@
<?php
require_once 'common.php';
header('Content-type: text/html; charset=UTF-8');
echo '<?xml version="1.0" encoding="UTF-8" ?>';
?><!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>HTML Purifier: All Smoketests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
#content {margin:5em;}
iframe {width:100%;height:30em;}
</style>
</head>
<body>
<h1>HTML Purifier: All Smoketests</h1>
<div id="content">
<?php
$dir = './';
$dh = opendir($dir);
while (false !== ($filename = readdir($dh))) {
if ($filename[0] == '.') continue;
if (strpos($filename, '.php') === false) continue;
if ($filename == 'common.php') continue;
if ($filename == 'all.php') continue;
?>
<iframe src="<?php echo escapeHTML($filename); ?>"></iframe>
<?php
}
?>
</div>
</body>
</html>

View File

@@ -2,8 +2,8 @@
header('Content-type: text/html; charset=UTF-8');
require_once '../library/HTMLPurifier.auto.php';
error_reporting(E_ALL | E_STRICT);
set_include_path('../library' . PATH_SEPARATOR . get_include_path());
require_once 'HTMLPurifier.php';
function escapeHTML($string) {
$string = HTMLPurifier_Encoder::cleanUTF8($string);
@@ -11,4 +11,4 @@ function escapeHTML($string) {
return $string;
}
?>
?>

View File

@@ -1,40 +0,0 @@
<?php
set_include_path('../library/' . PATH_SEPARATOR . get_include_path() );
header('Content-type: text/html; charset=UTF-8');
echo '<?xml version="1.0" encoding="UTF-8" ?>';
function printb($bool) {
echo '<strong>' . ($bool ? 'Pass' : 'Fail') . '</strong>';
}
function printEval($code) {
echo '<pre>' . htmlspecialchars($code) . '</pre>';
eval($code);
}
?><!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>HTML Purifier Function Include Smoketest</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>HTML Purifier Function Include Smoketest</h1>
<p>Tests whether or not the includes are done properly and whether or
not the library is lazy loaded.</p>
<?php printEval("require_once 'HTMLPurifier.func.php';"); ?>
<p>HTMLPurifier class doesn't exist: <?php printb(!class_exists('HTMLPurifier')); ?></li></p>
<?php printEval("HTMLPurifier('foobar');"); ?>
<p>HTMLPurifier class exists: <?php printb(class_exists('HTMLPurifier')); ?></li></p>
</body>
</html>

View File

@@ -1,44 +0,0 @@
<?php
require_once 'common.php';
echo '<?xml version="1.0" encoding="UTF-8" ?>';
?><!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>HTML Purifier Preserve YouTube Smoketest</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>HTML Purifier Preserve YouTube Smoketest</h1>
<?php
$string = '<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/JzqumbhfxRo"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/JzqumbhfxRo" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object>';
$regular_purifier = new HTMLPurifier();
$youtube_purifier = new HTMLPurifier();
require_once 'HTMLPurifier/Filter/YouTube.php';
$youtube_purifier->addFilter(new HTMLPurifier_Filter_YouTube());
?>
<h2>Unpurified</h2>
<p><a href="?break">Click here to see the unpurified version (breaks validation).</a></p>
<div><?php
if (isset($_GET['break'])) echo $string;
?></div>
<h2>Without YouTube exception</h2>
<div><?php
echo $regular_purifier->purify($string);
?></div>
<h2>With YouTube exception</h2>
<div><?php
echo $youtube_purifier->purify($string);
?></div>
</body>
</html>

View File

@@ -1,184 +0,0 @@
<?php
require_once 'common.php'; // load library
require_once 'HTMLPurifier/Printer/HTMLDefinition.php';
require_once 'HTMLPurifier/Printer/CSSDefinition.php';
$config = HTMLPurifier_Config::createDefault();
// you can do custom configuration!
if (file_exists('printDefinition.settings.php')) {
include 'printDefinition.settings.php';
}
$get = $_GET;
foreach ($_GET as $key => $value) {
if (!strncmp($key, 'Null_', 5) && !empty($value)) {
unset($get[substr($key, 5)]);
unset($get[$key]);
}
}
@$config->loadArray($get);
$printer_html_definition = new HTMLPurifier_Printer_HTMLDefinition();
$printer_css_definition = new HTMLPurifier_Printer_CSSDefinition();
echo '<?xml version="1.0" encoding="UTF-8" ?>';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>HTML Purifier Printer Smoketest</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
form table {margin:1em auto;}
form th {text-align:right;padding-right:1em;}
form .c {display:none;}
.HTMLPurifier_Printer table {border-collapse:collapse;
border:1px solid #000; width:600px;
margin:1em auto;font-family:sans-serif;font-size:75%;}
.HTMLPurifier_Printer td, .HTMLPurifier_Printer th {padding:3px;
border:1px solid #000;background:#CCC; vertical-align: baseline;}
.HTMLPurifier_Printer th {text-align:left;background:#CCF;width:20%;}
.HTMLPurifier_Printer caption {font-size:1.5em; font-weight:bold;
width:100%;}
.HTMLPurifier_Printer .heavy {background:#99C;text-align:center;}
dt {font-weight:bold;}
</style>
<script type="text/javascript">
function toggleWriteability(id_of_patient, checked) {
document.getElementById(id_of_patient).disabled = checked;
}
</script>
</head>
<body>
<h1>HTML Purifier Printer Smoketest</h1>
<p>HTML Purifier claims to have a robust yet permissive whitelist: this
page will allow you to see precisely what HTML Purifier's internal
whitelist is. You can
also twiddle with the configuration settings to see how a directive
influences the internal workings of the definition objects.</p>
<h2>Modify configuration</h2>
<p>You can specify an array by typing in a comma-separated
list of items, HTML Purifier will take care of the rest (including
transformation into a real array list or a lookup table).</p>
<form id="edit-config" name="edit-config" method="get" action="printDefinition.php">
<table>
<?php
$directives = $config->getBatch('HTML');
// can't handle hashes
foreach ($directives as $key => $value) {
$directive = "HTML.$key";
if (is_array($value)) {
$keys = array_keys($value);
if ($keys === array_keys($keys)) {
$value = implode(',', $keys);
} else {
$new_value = '';
foreach ($value as $name => $bool) {
if ($bool !== true) continue;
$new_value .= "$name,";
}
$value = rtrim($new_value, ',');
}
}
$allow_null = $config->def->info['HTML'][$key]->allow_null;
?>
<tr>
<th>
<a href="http://hp.jpsband.org/live/configdoc/plain.html#<?php echo $directive ?>">
<label for="<?php echo $directive; ?>">%<?php echo $directive; ?></label>
</a>
</th>
<?php if (is_bool($value)) { ?>
<td id="<?php echo $directive; ?>">
<label for="Yes_<?php echo $directive; ?>"><span class="c">%<?php echo $directive; ?>:</span> Yes</label>
<input type="radio" name="<?php echo $directive; ?>" id="Yes_<?php echo $directive; ?>" value="1"<?php if ($value) { ?> checked="checked"<?php } ?> /> &nbsp;
<label for="No_<?php echo $directive; ?>"><span class="c">%<?php echo $directive; ?>:</span> No</label>
<input type="radio" name="<?php echo $directive; ?>" id="No_<?php echo $directive; ?>" value="0"<?php if (!$value) { ?> checked="checked"<?php } ?> />
<?php } else { ?>
<td>
<?php if($allow_null) { ?>
<label for="Null_<?php echo $directive; ?>"><span class="c">%<?php echo $directive; ?>:</span> Null/Disabled*</label>
<input
type="checkbox"
value="1"
onclick="toggleWriteability('<?php echo $directive ?>',checked)"
name="Null_<?php echo $directive; ?>"
id="Null_<?php echo $directive; ?>"
<?php if ($value === null) { ?> checked="checked"<?php } ?>
/> or <br />
<?php } ?>
<input
type="text"
name="<?php echo $directive; ?>"
id="<?php echo $directive; ?>"
value="<?php echo escapeHTML($value); ?>"
<?php if($value === null) {echo 'disabled="disabled"';} ?>
/>
<?php } ?>
</td>
</tr>
<?php
}
?>
<tr>
<td colspan="2" style="text-align:right;">
[<a href="printDefinition.php">Reset</a>]
<input type="submit" value="Submit" />
</td>
</tr>
</table>
<p>* Some configuration directives make a distinction between an empty
variable and a null variable. A whitelist, for example, will take an
empty array as meaning <em>no</em> allowed elements, while checking
Null/Disabled will mean that user whitelisting functionality is disabled.</p>
</form>
<h2>Definitions</h2>
<dl>
<dt>Parent of Fragment</dt>
<dd>HTML that HTML Purifier does not live in a void: when it's
output, it has to be placed in another element by means of
something like <code>&lt;element&gt; &lt;?php echo $html
?&gt; &lt;/element&gt;</code>. The parent in this example
is <code>element</code>.</dd>
<dt>Strict mode</dt>
<dd>Whether or not HTML Purifier's output is Transitional or
Strict compliant. Non-strict mode still actually a little strict
and converts many deprecated elements.</dd>
<dt>#PCDATA</dt>
<dd>Literally <strong>Parsed Character Data</strong>, it is regular
text. Tags like <code>ul</code> don't allow text in them, so
#PCDATA is missing.</dd>
<dt>Tag transform</dt>
<dd>A tag transform will change one tag to another. Example: <code>font</code>
turns into a <code>span</code> tag with appropriate CSS.</dd>
<dt>Attr Transform</dt>
<dd>An attribute transform changes a group of attributes based on one
another. Currently, only <code>lang</code> and <code>xml:lang</code>
use this hook, to synchronize each other's values. Pre/Post indicates
whether or not the transform is done before/after validation.</dd>
<dt>Excludes</dt>
<dd>Tags that an element excludes are excluded for all descendants of
that element, and not just the children of them.</dd>
<dt>Name(Param1, Param2)</dt>
<dd>Represents an internal data-structure. You'll have to check out
the corresponding classes in HTML Purifier to find out more.</dd>
</dl>
<h2>HTMLDefinition</h2>
<?php echo $printer_html_definition->render($config) ?>
<h2>CSSDefinition</h2>
<?php echo $printer_css_definition->render($config) ?>
</body>
</html>

View File

@@ -1,20 +1,17 @@
<?php
// this file is encoded in UTF-8, please don't let your editor mangle it
require_once 'common.php';
echo '<?xml version="1.0" encoding="UTF-8" ?>';
?><!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>HTML Purifier UTF-8 Smoketest</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>HTMLPurifier UTF-8 Smoketest</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>HTML Purifier UTF-8 Smoketest</h1>
<h1>HTMLPurifier UTF-8 Smoketest</h1>
<?php
$purifier = new HTMLPurifier();

View File

@@ -2,17 +2,16 @@
require_once 'common.php';
echo '<?xml version="1.0" encoding="UTF-8" ?>';
?><!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>HTML Purifier Variable Width Attack Smoketest</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>HTMLPurifier Variable Width Attack Smoketest</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>HTML Purifier Variable Width Attack Smoketest</h1>
<h1>HTMLPurifier Variable Width Attack Smoketest</h1>
<p>For more information, see
<a href="http://applesoup.googlepages.com/bypass_filter.txt">Cheng Peng Su's
original advisory.</a> This particular exploit code appears only to work

View File

@@ -20,7 +20,7 @@ function formatCode($string) {
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>HTML Purifier XSS Attacks Smoketest</title>
<title>HTMLPurifier XSS Attacks Smoketest</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
.scroll {overflow:auto; width:100%;}
@@ -31,13 +31,13 @@ function formatCode($string) {
</style>
</head>
<body>
<h1>HTML Purifier XSS Attacks Smoketest</h1>
<h1>HTMLPurifier XSS Attacks Smoketest</h1>
<p>XSS attacks are from
<a href="http://ha.ckers.org/xss.html">http://ha.ckers.org/xss.html</a>.</p>
<p><strong>Caveats:</strong>
<tt>Google.com</tt> has been programatically disallowed, but as you can
see, there are ways of getting around that, so coverage in this area
is not complete. Most XSS broadcasts its presence by spawning an alert dialogue.
The last segment of tests regarding blacklisted websites is not
applicable at the moment, but when we add that functionality they'll be
relevant. Most XSS broadcasts its presence by spawning an alert dialogue.
The displayed code is not strictly correct, as linebreaks have been forced for
readability. Linewraps have been marked with <tt>»</tt>. Some tests are
omitted for your convenience. Not all control characters are displayed.</p>
@@ -48,12 +48,7 @@ omitted for your convenience. Not all control characters are displayed.</p>
if (version_compare(PHP_VERSION, '5', '<')) exit('<p>Requires PHP 5.</p>');
$xml = simplexml_load_file('xssAttacks.xml');
// programatically disallow google.com for URI evasion tests
// not complete
$config = HTMLPurifier_Config::createDefault();
$config->set('URI', 'HostBlacklist', array('google.com'));
$purifier = new HTMLPurifier($config);
$purifier = new HTMLPurifier();
?>
<table cellspacing="0" cellpadding="2">

View File

@@ -2,7 +2,7 @@
<xss>
<attack>
<name>XSS Locator</name>
<code>&apos;;alert(String.fromCharCode(88,83,83))//\&apos;;alert(String.fromCharCode(88,83,83))//&quot;;alert(String.fromCharCode(88,83,83))//\&quot;;alert(String.fromCharCode(88,83,83))//--&gt;&lt;/SCRIPT&gt;&quot;&gt;&apos;&gt;&lt;SCRIPT&gt;alert(String.fromCharCode(88,83,83))&lt;/SCRIPT&gt;=&amp;{}</code>
<code>&apos;;alert(String.fromCharCode(88,83,83))//\&apos;;alert(String.fromCharCode(88,83,83))//&quot;;alert(String.fromCharCode(88,83,83))//\&quot;;alert(String.fromCharCode(88,83,83))//&gt;&lt;/SCRIPT&gt;!--&lt;SCRIPT&gt;alert(String.fromCharCode(88,83,83))&lt;/SCRIPT&gt;=&amp;{}</code>
<desc>Inject this string, and in most cases where a script is vulnerable with no special XSS vector requirements the word &quot;XSS&quot; will pop up. You&apos;ll need to replace the &quot;&amp;&quot; with &quot;%26&quot; if you are submitting this XSS string via HTTP GET or it will be ignored and everything after it will be interpreted as another variable. Tip: If you&apos;re in a rush and need to quickly check a page, often times injecting the deprecated &quot;&lt;PLAINTEXT&gt;&quot; tag will be enough to check to see if something is vulnerable to XSS by messing up the output appreciably.</desc>
<label>Basic XSS Attacks</label>
@@ -978,6 +978,8 @@ alert(a.source)&lt;/SCRIPT&gt;</code>
-onErrorUpdate() (fires on a databound object when an error occurs while updating the associated data in the data source object)
-onExit() (fires when someone clicks on a link or presses the back button)
-onFilterChange() (fires when a visual filter completes state change)
-onFinish() (attacker could create the exploit when marquee is finished looping)

View File

@@ -70,10 +70,7 @@ class Debugger
$this->add_pre = !extension_loaded('xdebug');
}
/**
* @static
*/
static function &instance() {
function &instance() {
static $soleInstance = false;
if (!$soleInstance) $soleInstance = new Debugger();
return $soleInstance;
@@ -145,4 +142,4 @@ class Debugger
}
?>
?>

View File

@@ -1,71 +0,0 @@
<?php
require_once 'HTMLPurifier/AttrDefHarness.php';
require_once 'HTMLPurifier/AttrDef/BackgroundPosition.php';
class HTMLPurifier_AttrDef_BackgroundPositionTest extends HTMLPurifier_AttrDefHarness
{
function test() {
$this->def = new HTMLPurifier_AttrDef_BackgroundPosition();
// explicitly cited in spec
$this->assertDef('0% 0%');
$this->assertDef('100% 100%');
$this->assertDef('14% 84%');
$this->assertDef('2cm 1cm');
$this->assertDef('top');
$this->assertDef('left');
$this->assertDef('center');
$this->assertDef('right');
$this->assertDef('bottom');
$this->assertDef('left top');
$this->assertDef('center top');
$this->assertDef('right top');
$this->assertDef('left center');
$this->assertDef('right center');
$this->assertDef('left bottom');
$this->assertDef('center bottom');
$this->assertDef('right bottom');
// reordered due to internal impl details
$this->assertDef('top left', 'left top');
$this->assertDef('top center', 'center top');
$this->assertDef('top right', 'right top');
$this->assertDef('center left', 'left center');
$this->assertDef('center center', 'center'); // two centers collide
$this->assertDef('center right', 'right center');
$this->assertDef('bottom left', 'left bottom');
$this->assertDef('bottom center', 'center bottom');
$this->assertDef('bottom right', 'right bottom');
// more cases from the defined syntax
$this->assertDef('1.32in 4ex');
$this->assertDef('-14% -84.65%');
$this->assertDef('-1in -4ex');
$this->assertDef('-1pc 2.3%');
// keyword mixing
$this->assertDef('3em top');
$this->assertDef('left 50%');
// fixable keyword mixing
$this->assertDef('top 3em', '3em top');
$this->assertDef('50% left', 'left 50%');
// whitespace collapsing
$this->assertDef('3em top', '3em top');
$this->assertDef("left\n \t foo ", 'left');
// invalid uses (we're going to be strict on these)
$this->assertDef('foo bar', false);
$this->assertDef('left left', 'left');
$this->assertDef('left right top bottom center left', 'left bottom');
$this->assertDef('0fr 9%', '9%');
}
}
?>

View File

@@ -1,21 +0,0 @@
<?php
require_once 'HTMLPurifier/AttrDefHarness.php';
require_once 'HTMLPurifier/AttrDef/Background.php';
class HTMLPurifier_AttrDef_BackgroundTest extends HTMLPurifier_AttrDefHarness
{
function test() {
$this->def = new HTMLPurifier_AttrDef_Background(HTMLPurifier_Config::createDefault());
$valid = '#333 url(chess.png) repeat fixed 50% top';
$this->assertDef($valid);
$this->assertDef('url("chess.png") #333 50% top repeat fixed', $valid);
}
}
?>

View File

@@ -1,7 +1,6 @@
<?php
require_once 'HTMLPurifier/AttrDef/Border.php';
require_once 'HTMLPurifier/AttrDef/PixelsTest.php';
class HTMLPurifier_AttrDef_BorderTest extends HTMLPurifier_AttrDef_PixelsTest
{

View File

@@ -1,7 +1,6 @@
<?php
require_once 'HTMLPurifier/AttrDef/CSSLength.php';
require_once 'HTMLPurifier/AttrDefHarness.php';
class HTMLPurifier_AttrDef_CSSLengthTest extends HTMLPurifier_AttrDefHarness
{
@@ -22,8 +21,6 @@ class HTMLPurifier_AttrDef_CSSLengthTest extends HTMLPurifier_AttrDefHarness
$this->assertDef('3pt');
$this->assertDef('3pc');
$this->assertDef('3PX', '3px');
$this->assertDef('3', false);
$this->assertDef('3miles', false);

View File

@@ -1,7 +1,6 @@
<?php
require_once 'HTMLPurifier/AttrDef/CSS.php';
require_once 'HTMLPurifier/AttrDefHarness.php';
class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
{
@@ -25,7 +24,7 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
$this->assertDef('text-transform:capitalize;');
$this->assertDef('background-color:rgb(0,0,255);');
$this->assertDef('background-color:transparent;');
$this->assertDef('background:#333 url(chess.png) repeat fixed 50% top;');
$this->assertDef('background:#FF9;');
$this->assertDef('color:#F00;');
$this->assertDef('border-top-color:#F00;');
$this->assertDef('border-color:#F00 #FF0;');
@@ -72,13 +71,6 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
$this->assertDef('vertical-align:12px;');
$this->assertDef('vertical-align:50%;');
$this->assertDef('table-layout:fixed;');
$this->assertDef('list-style-image:url(nice.jpg);');
$this->assertDef('list-style:disc url(nice.jpg) inside;');
$this->assertDef('background-image:url(foo.jpg);');
$this->assertDef('background-image:none;');
$this->assertDef('background-repeat:repeat-y;');
$this->assertDef('background-attachment:fixed;');
$this->assertDef('background-position:left 90%;');
// duplicates
$this->assertDef('text-align:right;text-align:left;',

View File

@@ -1,37 +0,0 @@
<?php
require_once 'HTMLPurifier/AttrDef/CSSURI.php';
require_once 'HTMLPurifier/AttrDefHarness.php';
class HTMLPurifier_AttrDef_CSSURITest extends HTMLPurifier_AttrDefHarness
{
function test() {
$this->def = new HTMLPurifier_AttrDef_CSSURI();
$this->assertDef('', false);
// we could be nice but we won't be
$this->assertDef('http://www.example.com/', false);
// no quotes are used, since that's the most widely supported
// syntax
$this->assertDef('url(', false);
$this->assertDef('url()', true);
$result = "url(http://www.example.com/)";
$this->assertDef('url(http://www.example.com/)', $result);
$this->assertDef('url("http://www.example.com/")', $result);
$this->assertDef("url('http://www.example.com/')", $result);
$this->assertDef(
' url( "http://www.example.com/" ) ', $result);
// escaping
$this->assertDef("url(http://www.example.com/foo,bar\))",
"url(http://www.example.com/foo\,bar\))");
}
}
?>

View File

@@ -1,7 +1,6 @@
<?php
require_once 'HTMLPurifier/AttrDef/Color.php';
require_once 'HTMLPurifier/AttrDefHarness.php';
class HTMLPurifier_AttrDef_ColorTest extends HTMLPurifier_AttrDefHarness
{

View File

@@ -1,7 +1,6 @@
<?php
require_once 'HTMLPurifier/AttrDef/Composite.php';
require_once 'HTMLPurifier/AttrDefHarness.php';
class HTMLPurifier_AttrDef_Composite_Testable extends
HTMLPurifier_AttrDef_Composite
@@ -29,10 +28,10 @@ class HTMLPurifier_AttrDef_CompositeTest extends HTMLPurifier_AttrDefHarness
// first test: value properly validates on first definition
// so second def is never called
$def1 = new HTMLPurifier_AttrDefMock($this);
$def2 = new HTMLPurifier_AttrDefMock($this);
$def1 =& new HTMLPurifier_AttrDefMock($this);
$def2 =& new HTMLPurifier_AttrDefMock($this);
$defs = array(&$def1, &$def2);
$def = new HTMLPurifier_AttrDef_Composite_Testable($defs);
$def =& new HTMLPurifier_AttrDef_Composite_Testable($defs);
$input = 'FOOBAR';
$output = 'foobar';
$def1_params = array($input, $config, $context);
@@ -48,10 +47,10 @@ class HTMLPurifier_AttrDef_CompositeTest extends HTMLPurifier_AttrDefHarness
// second test, first def fails, second def works
$def1 = new HTMLPurifier_AttrDefMock($this);
$def2 = new HTMLPurifier_AttrDefMock($this);
$def1 =& new HTMLPurifier_AttrDefMock($this);
$def2 =& new HTMLPurifier_AttrDefMock($this);
$defs = array(&$def1, &$def2);
$def = new HTMLPurifier_AttrDef_Composite_Testable($defs);
$def =& new HTMLPurifier_AttrDef_Composite_Testable($defs);
$input = 'BOOMA';
$output = 'booma';
$def_params = array($input, $config, $context);
@@ -68,10 +67,10 @@ class HTMLPurifier_AttrDef_CompositeTest extends HTMLPurifier_AttrDefHarness
// third test, all fail, so composite faiils
$def1 = new HTMLPurifier_AttrDefMock($this);
$def2 = new HTMLPurifier_AttrDefMock($this);
$def1 =& new HTMLPurifier_AttrDefMock($this);
$def2 =& new HTMLPurifier_AttrDefMock($this);
$defs = array(&$def1, &$def2);
$def = new HTMLPurifier_AttrDef_Composite_Testable($defs);
$def =& new HTMLPurifier_AttrDef_Composite_Testable($defs);
$input = 'BOOMA';
$output = false;
$def_params = array($input, $config, $context);

Some files were not shown because too many files have changed in this diff Show More