mirror of
https://github.com/ezyang/htmlpurifier.git
synced 2025-08-04 21:28:06 +02:00
Compare commits
7 Commits
v1.4.1-str
...
v1.1.2
Author | SHA1 | Date | |
---|---|---|---|
|
48ce521572 | ||
|
728e6c5b44 | ||
|
8104145580 | ||
|
6ef8abd04f | ||
|
bc5871f389 | ||
|
30d75c999d | ||
|
64d8ca9831 |
6
CREDITS
6
CREDITS
@@ -2,6 +2,6 @@
|
|||||||
CREDITS
|
CREDITS
|
||||||
|
|
||||||
Almost everything written by Edward Z. Yang (Ambush Commander). Lots of thanks
|
Almost everything written by Edward Z. Yang (Ambush Commander). Lots of thanks
|
||||||
to the DevNetwork Community for their help (see docs/ref-devnetwork.html for
|
to the DevNetwork Community for their help (see docs/devnetwork.html for more
|
||||||
more details), Feyd especially (namely IPv6 and optimization). Thanks to RSnake
|
details), Feyd especially (namely IPv6 and optimization). Thanks to RSnake for
|
||||||
for letting me package his fantastic XSS cheatsheet for a smoketest.
|
letting me package his fantastic XSS cheatsheet for a smoketest.
|
||||||
|
2
Doxyfile
2
Doxyfile
@@ -4,7 +4,7 @@
|
|||||||
# Project related configuration options
|
# Project related configuration options
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
PROJECT_NAME = HTML Purifier
|
PROJECT_NAME = HTML Purifier
|
||||||
PROJECT_NUMBER = 1.4.1
|
PROJECT_NUMBER = 1.1.2
|
||||||
OUTPUT_DIRECTORY = "C:/Documents and Settings/Edward/My Documents/My Webs/htmlpurifier/docs/doxygen"
|
OUTPUT_DIRECTORY = "C:/Documents and Settings/Edward/My Documents/My Webs/htmlpurifier/docs/doxygen"
|
||||||
CREATE_SUBDIRS = NO
|
CREATE_SUBDIRS = NO
|
||||||
OUTPUT_LANGUAGE = English
|
OUTPUT_LANGUAGE = English
|
||||||
|
5
INSTALL
5
INSTALL
@@ -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
|
down to the bottom of this INSTALL document to see the code, but you really
|
||||||
should make sure a few things are properly done.
|
should make sure a few things are properly done.
|
||||||
|
|
||||||
Todo: Convert to using the array syntax for configuration.
|
|
||||||
|
|
||||||
|
|
||||||
1. Compatibility
|
1. Compatibility
|
||||||
@@ -26,7 +25,7 @@ not having either of these extensions.
|
|||||||
|
|
||||||
Simply use:
|
Simply use:
|
||||||
|
|
||||||
require_once '/path/to/library/HTMLPurifier.auto.php';
|
require_once '/path/to/library/HTMLPurifier.auto.php';
|
||||||
|
|
||||||
...and you're good to go. Since HTML Purifier's codebase is fairly
|
...and you're good to go. Since HTML Purifier's codebase is fairly
|
||||||
large, I recommend only including HTML Purifier when you need it.
|
large, I recommend only including HTML Purifier when you need it.
|
||||||
@@ -34,7 +33,7 @@ large, I recommend only including HTML Purifier when you need it.
|
|||||||
If you don't like your include_path to be fiddled around with, simply set
|
If you don't like your include_path to be fiddled around with, simply set
|
||||||
HTML Purifier's library/ directory to the include path yourself and then:
|
HTML Purifier's library/ directory to the include path yourself and then:
|
||||||
|
|
||||||
require_once 'HTMLPurifier.php';
|
require_once 'HTMLPurifier.php';
|
||||||
|
|
||||||
Only the contents in the library/ folder are necessary, so you can remove
|
Only the contents in the library/ folder are necessary, so you can remove
|
||||||
everything else when using HTML Purifier in a production environment.
|
everything else when using HTML Purifier in a production environment.
|
||||||
|
110
NEWS
110
NEWS
@@ -2,122 +2,12 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
|
|||||||
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||
|
|
||||||
= KEY ====================
|
= KEY ====================
|
||||||
# Breaks back-compat
|
|
||||||
! Feature
|
! Feature
|
||||||
- Bugfix
|
- Bugfix
|
||||||
+ Sub-comment
|
+ Sub-comment
|
||||||
. Internal change
|
. 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
|
|
||||||
+ %Attr.IDPrefix - %Attr.IDBlacklist alternative that munges all user IDs
|
|
||||||
so that they don't collide with your IDs
|
|
||||||
+ %Attr.IDPrefixLocal - Same as above, but for when there are multiple
|
|
||||||
instances of user content on the page
|
|
||||||
+ Profuse documentation on how to use these available in docs/enduser-id.txt
|
|
||||||
! Added MODx plugin <http://modxcms.com/forums/index.php/topic,6604.0.html>
|
|
||||||
! Added percent encoding normalization
|
|
||||||
! XSS attacks smoketest given facelift
|
|
||||||
! Configuration documentation now has table of contents
|
|
||||||
! Added %URI.DisableExternal, which prevents links to external websites. You
|
|
||||||
can also use %URI.Host to permit absolute linking to subdomains
|
|
||||||
! Non-accessible resources (ex. mailto) blocked from embedded URIs (img src)
|
|
||||||
- Type variable in HTMLDefinition was not being set properly, fixed
|
|
||||||
- Documentation updated
|
|
||||||
+ TODO added request Phalanger
|
|
||||||
+ TODO added request Native compression
|
|
||||||
+ TODO added request Remove redundant tags
|
|
||||||
+ TODO added possible plaintext formatter for HTML Purifier documentation
|
|
||||||
+ Updated ConfigDoc TODO
|
|
||||||
+ Improved inline comments in AttrDef/Class.php, AttrDef/CSS.php
|
|
||||||
and AttrDef/Host.php
|
|
||||||
+ Revamped documentation into HTML, along with misc updates
|
|
||||||
- HTMLPurifier_Context doesn't throw a variable reference error if you attempt
|
|
||||||
to retrieve a non-existent variable
|
|
||||||
. Switched to purify()-wide Context object registry
|
|
||||||
. Refactored unit tests to minimize duplication
|
|
||||||
. XSS attack sheet updated
|
|
||||||
. configdoc.xml now has xml:space attached to default value nodes
|
|
||||||
. Allow configuration directives to permit null values
|
|
||||||
. Cleaned up test-cases to remove unnecessary swallowErrors()
|
|
||||||
|
|
||||||
1.1.2, released 2006-09-30
|
1.1.2, released 2006-09-30
|
||||||
! Add HTMLPurifier.auto.php stub file that configures include_path
|
! Add HTMLPurifier.auto.php stub file that configures include_path
|
||||||
- Documentation updated
|
- Documentation updated
|
||||||
|
25
README
25
README
@@ -1,22 +1,13 @@
|
|||||||
|
|
||||||
README
|
README
|
||||||
All about HTML Purifier
|
All about HTMLPurifier
|
||||||
|
|
||||||
HTML Purifier is an HTML filtering solution that uses a unique combination
|
HTMLPurifier is an HTML filtering solution. It uses a unique combination of
|
||||||
of robust whitelists and agressive parsing to ensure that not only are
|
robust whitelists and agressive parsing to ensure that not only are XSS
|
||||||
XSS attacks thwarted, but the resulting HTML is standards compliant.
|
attacks thwarted, but the resulting HTML is standards compliant.
|
||||||
|
|
||||||
HTML Purifier is oriented towards richly formatted documents from
|
See INSTALL on how to use the library. See docs/ for more developer-oriented
|
||||||
untrusted sources that require CSS and a full tag-set. This library can
|
documentation as well as some code examples. Users of TinyMCE or FCKeditor
|
||||||
be configured to accept a more restrictive set of tags, but it won't be
|
may be especially interested in WYSIWYG.
|
||||||
as efficient as more bare-bones parsers. It will, however, do the job
|
|
||||||
right, which may be more important.
|
|
||||||
|
|
||||||
Places to go:
|
HTMLPurifier can be found on the web at: http://hp.jpsband.org/
|
||||||
|
|
||||||
* 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/
|
|
||||||
|
39
SLOW
Normal file
39
SLOW
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
SLOW
|
||||||
|
also known as the HELP ME LIBRARY IS TOO SLOW MY PAGE TAKE TOO LONG LOAD page
|
||||||
|
|
||||||
|
HTMLPurifier 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 HTMLPurifier 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
|
||||||
|
expects a certain to be available but it doesn't.
|
||||||
|
|
||||||
|
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 tell me if you decide to do that! ;-)
|
96
TODO
96
TODO
@@ -1,91 +1,53 @@
|
|||||||
|
|
||||||
TODO List
|
TODO List
|
||||||
|
|
||||||
= KEY ====================
|
Ongoing
|
||||||
# Flagship
|
- Lots of profiling, make it faster!
|
||||||
- Regular
|
- Plugins for major CMSes (very tricky issue)
|
||||||
? At-risk
|
|
||||||
==========================
|
|
||||||
|
|
||||||
1.5 release
|
1.2 release
|
||||||
# Implement all non-essential attribute transforms, configurable
|
- Make URI validation routines tighter (especially mailto)
|
||||||
# URI validation routines tighter (see docs/dev-code-quality.html) (COMPLEX)
|
- More extensive URI filtering schemes
|
||||||
# Advanced 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
|
- Distinguish between different types of URIs, for instance, a mailto URI
|
||||||
- Requires I18N facilities to be created first (COMPLEX)
|
in IMG SRC is nonsensical
|
||||||
? Configuration profiles: sets of directives that get set with one func call
|
- Error logging for filtering/cleanup procedures
|
||||||
- XSS-attempt detection
|
|
||||||
|
|
||||||
1.6 release
|
1.3 release
|
||||||
# Add pre-packaged "levels" of cleaning (custom behavior already done)
|
- Add various "levels" of cleaning
|
||||||
- More fine-grained control over escaping behavior
|
- Related: Allow strict (X)HTML
|
||||||
- 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
|
1.4 release
|
||||||
# Additional support for poorly written HTML
|
- Additional support for poorly written HTML
|
||||||
- Microsoft Word HTML cleaning (i.e. MsoNormal, but research essential!)
|
- Implement all non-essential attribute transforms
|
||||||
- Friendly strict handling of <address> (block -> <br>)
|
- Microsoft Word HTML cleaning (i.e. MsoNormal)
|
||||||
- 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
|
2.0 release
|
||||||
# Legit token based CSS parsing (will require revamping almost every
|
- Formatters for plaintext
|
||||||
AttrDef class)
|
|
||||||
# Formatters for plaintext (COMPLEX)
|
|
||||||
- Auto-paragraphing (be sure to leverage fact that we know when things
|
- Auto-paragraphing (be sure to leverage fact that we know when things
|
||||||
shouldn't be paragraphed, such as lists and tables).
|
shouldn't be paragraphed, such as lists and tables).
|
||||||
- Linkify URLs
|
- Linkify URLs
|
||||||
- Smileys
|
- Smileys
|
||||||
- Linkification for HTML Purifier docs: notably configuration and classes
|
|
||||||
|
|
||||||
3.0 release
|
3.0 release
|
||||||
- Extended HTML capabilities based on namespacing and tag transforms (COMPLEX)
|
- Extended HTML capabilities based on namespacing and tag transforms
|
||||||
- Hooks for adding custom processors to custom namespaced tags and
|
- Hooks for adding custom processors to custom namespaced tags and
|
||||||
attributes, offer default implementation
|
attributes, offer default implementation
|
||||||
- Lots of documentation and samples
|
- Lots of documentation and samples
|
||||||
- Allow tags to be "armored", an internal flag that protects them
|
|
||||||
from validation and passes them out unharmed
|
Unknown release (on a scratch-an-itch basis)
|
||||||
- XHTML 1.1 support
|
- 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)).
|
||||||
- Fixes for Firefox's inability to handle COL alignment props (Bug 915)
|
- Fixes for Firefox's inability to handle COL alignment props (Bug 915)
|
||||||
- Automatically add non-breaking spaces to empty table cells when
|
- Automatically add non-breaking spaces to empty table cells when
|
||||||
empty-cells:show is applied to have compatibility with Internet Explorer
|
empty-cells:show is applied to have compatibility with Internet Explorer
|
||||||
- Convert RTL/LTR override characters to <bdo> tags, or vice versa on demand.
|
- Non-lossy dumb alternate character encoding transformations, achieved by
|
||||||
Also, enable disabling of directionality
|
numerically encoding all non-ASCII characters
|
||||||
|
- Semi-lossy dumb alternate character encoding transformations, achieved by
|
||||||
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
|
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
|
Wontfix
|
||||||
- Non-lossy smart alternate character encoding transformations (unless
|
- Non-lossy smart alternate character encoding transformations
|
||||||
patch provided)
|
|
||||||
- Pretty-printing HTML, users can use Tidy on the output on entire page
|
- Pretty-printing HTML, users can use Tidy on the output on entire page
|
||||||
|
3
WYSIWYG
3
WYSIWYG
@@ -18,5 +18,4 @@ HTML Purifier is perfect for filtering pure-HTML input from WYSIWYG editors.
|
|||||||
Enough said.
|
Enough said.
|
||||||
|
|
||||||
There is a proof-of-concept integration of HTML Purifier with the Mantis
|
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
|
bugtracker at http://hp.jpsband.org/mantis/
|
||||||
this integration was acheived at http://hp.jpsband.org/mantis_notes.txt
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.4 KiB |
@@ -12,8 +12,10 @@ TODO:
|
|||||||
- multipage documentation
|
- multipage documentation
|
||||||
- determine how to multilingualize
|
- determine how to multilingualize
|
||||||
- factor out code into classes
|
- factor out code into classes
|
||||||
|
- generate a table of contents
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Check and configure environment
|
// Check and configure environment
|
||||||
|
|
||||||
@@ -80,6 +82,9 @@ $dom_root->appendChild($dom_document->createElement('title', 'HTML Purifier'));
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
TODO for XML format:
|
TODO for XML format:
|
||||||
|
- namespace descriptions
|
||||||
|
- enumerated values
|
||||||
|
- default values
|
||||||
- create a definition (DTD or other) once interface stabilizes
|
- create a definition (DTD or other) once interface stabilizes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -99,8 +104,6 @@ foreach($schema->info as $namespace_name => $namespace_info) {
|
|||||||
|
|
||||||
foreach ($namespace_info as $name => $info) {
|
foreach ($namespace_info as $name => $info) {
|
||||||
|
|
||||||
if ($info->class == 'alias') continue;
|
|
||||||
|
|
||||||
$dom_directive = $dom_document->createElement('directive');
|
$dom_directive = $dom_document->createElement('directive');
|
||||||
$dom_namespace->appendChild($dom_directive);
|
$dom_namespace->appendChild($dom_directive);
|
||||||
|
|
||||||
@@ -112,12 +115,9 @@ foreach($schema->info as $namespace_name => $namespace_info) {
|
|||||||
$dom_constraints = $dom_document->createElement('constraints');
|
$dom_constraints = $dom_document->createElement('constraints');
|
||||||
$dom_directive->appendChild($dom_constraints);
|
$dom_directive->appendChild($dom_constraints);
|
||||||
|
|
||||||
$dom_type = $dom_document->createElement('type', $info->type);
|
$dom_constraints->appendChild(
|
||||||
if ($info->allow_null) {
|
$dom_document->createElement('type', $info->type)
|
||||||
$dom_type->setAttribute('allow-null', 'yes');
|
);
|
||||||
}
|
|
||||||
$dom_constraints->appendChild($dom_type);
|
|
||||||
|
|
||||||
if ($info->allowed !== true) {
|
if ($info->allowed !== true) {
|
||||||
$dom_allowed = $dom_document->createElement('allowed');
|
$dom_allowed = $dom_document->createElement('allowed');
|
||||||
$dom_constraints->appendChild($dom_allowed);
|
$dom_constraints->appendChild($dom_allowed);
|
||||||
@@ -133,20 +133,14 @@ foreach($schema->info as $namespace_name => $namespace_info) {
|
|||||||
$default = $raw_default ? 'true' : 'false';
|
$default = $raw_default ? 'true' : 'false';
|
||||||
} elseif (is_string($raw_default)) {
|
} elseif (is_string($raw_default)) {
|
||||||
$default = "\"$raw_default\"";
|
$default = "\"$raw_default\"";
|
||||||
} elseif (is_null($raw_default)) {
|
|
||||||
$default = 'null';
|
|
||||||
} else {
|
} else {
|
||||||
$default = print_r(
|
$default = print_r(
|
||||||
$schema->defaults[$namespace_name][$name], true
|
$schema->defaults[$namespace_name][$name], true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
$dom_constraints->appendChild(
|
||||||
$dom_default = $dom_document->createElement('default', $default);
|
$dom_document->createElement('default', $default)
|
||||||
|
);
|
||||||
// remove this once we get a DTD
|
|
||||||
$dom_default->setAttribute('xml:space', 'preserve');
|
|
||||||
|
|
||||||
$dom_constraints->appendChild($dom_default);
|
|
||||||
|
|
||||||
$dom_descriptions = $dom_document->createElement('descriptions');
|
$dom_descriptions = $dom_document->createElement('descriptions');
|
||||||
$dom_directive->appendChild($dom_descriptions);
|
$dom_directive->appendChild($dom_descriptions);
|
||||||
|
@@ -1,6 +1,3 @@
|
|||||||
|
|
||||||
body {margin:1em 4em;}
|
|
||||||
|
|
||||||
table {border-collapse:collapse;}
|
table {border-collapse:collapse;}
|
||||||
table td, table th {padding:0.2em;}
|
table td, table th {padding:0.2em;}
|
||||||
|
|
||||||
@@ -8,17 +5,3 @@ table.constraints {margin:0 0 1em;}
|
|||||||
table.constraints th {text-align:left;padding-left:0.4em;}
|
table.constraints th {text-align:left;padding-left:0.4em;}
|
||||||
table.constraints td {padding-right:0.4em;}
|
table.constraints td {padding-right:0.4em;}
|
||||||
table.constraints td pre {margin:0;}
|
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; }
|
|
||||||
|
@@ -18,47 +18,28 @@
|
|||||||
<xsl:template match="/">
|
<xsl:template match="/">
|
||||||
<html lang="en" xml:lang="en">
|
<html lang="en" xml:lang="en">
|
||||||
<head>
|
<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" />
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
|
||||||
<link rel="stylesheet" type="text/css" href="styles/plain.css" />
|
<link rel="stylesheet" type="text/css" href="styles/plain.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="library"><xsl:value-of select="/configdoc/title" /></div>
|
|
||||||
<h1>Configuration Documentation</h1>
|
|
||||||
<h2>Table of Contents</h2>
|
|
||||||
<ul id="toc">
|
|
||||||
<xsl:apply-templates mode="toc" />
|
|
||||||
</ul>
|
|
||||||
<xsl:apply-templates />
|
<xsl:apply-templates />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template match="title" mode="toc" />
|
<xsl:template match="title">
|
||||||
<xsl:template match="namespace" mode="toc">
|
<h1><xsl:value-of select="/configdoc/title" /> Configuration Documentation</h1>
|
||||||
<xsl:if test="count(directive)>0">
|
|
||||||
<li>
|
|
||||||
<a href="#{@id}"><xsl:value-of select="name" /></a>
|
|
||||||
<ul>
|
|
||||||
<xsl:apply-templates select="directive" mode="toc" />
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</xsl:if>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
<xsl:template match="directive" mode="toc">
|
|
||||||
<li><a href="#{@id}"><xsl:value-of select="name" /></a></li>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="title" />
|
|
||||||
|
|
||||||
<xsl:template match="namespace">
|
<xsl:template match="namespace">
|
||||||
<xsl:apply-templates />
|
<xsl:apply-templates />
|
||||||
<xsl:if test="count(directive)=0">
|
<xsl:if test="count(child::directive)=0">
|
||||||
<p>No configuration directives defined for this namespace.</p>
|
<p>No configuration directives defined for this namespace.</p>
|
||||||
</xsl:if>
|
</xsl:if>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
<xsl:template match="namespace/name">
|
<xsl:template match="namespace/name">
|
||||||
<h2 id="{../@id}"><xsl:value-of select="." /></h2>
|
<h2 id="{../@id}"><xsl:value-of select="text()" /></h2>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
<xsl:template match="namespace/description">
|
<xsl:template match="namespace/description">
|
||||||
<div class="description">
|
<div class="description">
|
||||||
@@ -70,7 +51,7 @@
|
|||||||
<xsl:apply-templates />
|
<xsl:apply-templates />
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
<xsl:template match="directive/name">
|
<xsl:template match="directive/name">
|
||||||
<h3 id="{../@id}"><xsl:value-of select="../@id" /></h3>
|
<h3 id="{../@id}"><xsl:value-of select="text()" /></h3>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
<xsl:template match="directive/constraints">
|
<xsl:template match="directive/constraints">
|
||||||
<table class="constraints">
|
<table class="constraints">
|
||||||
@@ -100,9 +81,6 @@
|
|||||||
<xsl:variable name="type" select="text()" />
|
<xsl:variable name="type" select="text()" />
|
||||||
<xsl:attribute name="class">type type-<xsl:value-of select="$type" /></xsl:attribute>
|
<xsl:attribute name="class">type type-<xsl:value-of select="$type" /></xsl:attribute>
|
||||||
<xsl:value-of select="$typeLookup/types/type[@id=$type]/text()" />
|
<xsl:value-of select="$typeLookup/types/type[@id=$type]/text()" />
|
||||||
<xsl:if test="@allow-null='yes'">
|
|
||||||
(or null)
|
|
||||||
</xsl:if>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
39
docs/code-quality.txt
Normal file
39
docs/code-quality.txt
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
Code Quality Issues
|
||||||
|
|
||||||
|
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,
|
||||||
|
while I can't list mistakes in here, I can list prototype-like segments
|
||||||
|
of code that should be aggressively refactored after the beta is released.
|
||||||
|
This does not list optimization issues, that needs to be done after intense
|
||||||
|
profiling.
|
||||||
|
|
||||||
|
Here we go:
|
||||||
|
|
||||||
|
AttrDef
|
||||||
|
Class - doesn't support Unicode characters (fringe); uses regular
|
||||||
|
expressions
|
||||||
|
Lang - code duplication; premature optimization; doesn't consult official
|
||||||
|
lists (fringe)
|
||||||
|
Length - easily mistaken for CSSLength
|
||||||
|
URI - multiple regular expressions; needs host validation routines factored
|
||||||
|
out for mailto scheme; missing validation for query; fragment and path,
|
||||||
|
no percent-encode fixing
|
||||||
|
CSS - parser doesn't accept advanced CSS (fringe)
|
||||||
|
Number - constructor interface is inconsistent with Integer
|
||||||
|
AttrTransform - doesn't accept AttrContext
|
||||||
|
Config - "load configuration" hooks missing, rich set* accessors missing
|
||||||
|
ConfigSchema - redefinition is a mess
|
||||||
|
Strategy
|
||||||
|
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).
|
||||||
|
RemoveForeignElements - should be run in parallel with MakeWellFormed
|
||||||
|
URIScheme - needs to have callable generic checks
|
||||||
|
ftp - missing typecode check
|
||||||
|
mailto - doesn't validate emails
|
||||||
|
news - doesn't validate opaque path
|
||||||
|
nntp - doesn't constrain path
|
||||||
|
EOL
|
||||||
|
|
23
docs/colors.txt
Normal file
23
docs/colors.txt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
Colors
|
||||||
|
Hammering some sense into those content-makers
|
||||||
|
|
||||||
|
Your website probably has a color-scheme. Green on white, purple on yellow,
|
||||||
|
whatever. When you give users the ability to style their content, you may
|
||||||
|
want them to keep in line with your styling. If you're website is all
|
||||||
|
about light colors, you don't want a user to come in and vandalize your
|
||||||
|
page with a deep maroon.
|
||||||
|
|
||||||
|
This is an extremely silly feature proposal, but I'm writing it down anyway.
|
||||||
|
|
||||||
|
What if the user could constrain the colors specified in inline styles? You
|
||||||
|
are only allowed to use these shades of dark green for text and these shades
|
||||||
|
of light yellow for the background. At the very least, you could ensure
|
||||||
|
that we did not have pale yellow on white text.
|
||||||
|
|
||||||
|
Implementation issues:
|
||||||
|
1. Requires the color attribute definition to know, currently, what the text
|
||||||
|
and background colors are. This becomes difficult when classes are thrown
|
||||||
|
into the mix.
|
||||||
|
2. The user still has to define the permissible colors, how does one do
|
||||||
|
something like that?
|
46
docs/config-ideas.txt
Normal file
46
docs/config-ideas.txt
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
%Attr.ClassBlacklist,
|
||||||
|
%Attr.ClassWhitelist,
|
||||||
|
%Attr.ClassListMode - determines what classes are allowed. When
|
||||||
|
%Attr.ClassListMode is set to Blacklist, only allow those not in
|
||||||
|
%Attr.ClassBlacklist. When it's Whitelist, only allow those in
|
||||||
|
%Attr.ClassWhitelist.
|
||||||
|
|
||||||
|
%Attr.LangAlphaOnly - designate whether or not to allow numerals in language
|
||||||
|
code subtags
|
||||||
|
* RFC 1766, the current standard referenced by XML, does not permit
|
||||||
|
numbers, but,
|
||||||
|
* RFC 3066, the superseding best practice standard since January 2001,
|
||||||
|
permits them.
|
||||||
|
We allow numbers by default, but you generally never see them
|
||||||
|
at all, which makes this a little more sane.
|
||||||
|
|
||||||
|
%Attr.MaxWidth,
|
||||||
|
%Attr.MaxHeight - caps for width and height related checks.
|
||||||
|
(a hack in Pixels for an image crashing attack could be replaced by this)
|
||||||
|
|
||||||
|
%URI.Munge - will munge all URIs to a different URI, which should redirect
|
||||||
|
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
|
||||||
|
|
||||||
|
%URI.AddRelNofollow - will add rel="nofollow" to all links, preventing the
|
||||||
|
spread of ill-gotten pagerank
|
||||||
|
|
||||||
|
%URI.Host - host of website, for external link checks
|
||||||
|
|
||||||
|
%URI.RelativeToAbsolute - transforms all relative URIs to absolute form
|
||||||
|
|
||||||
|
%URI.DisableExternal - disable external links
|
@@ -10,19 +10,22 @@ Directives are divided into namespaces, indicating the major portion of
|
|||||||
functionality they cover (although there may be overlaps. Please consult
|
functionality they cover (although there may be overlaps. Please consult
|
||||||
the documentation in ConfigDef for more information on these namespaces.
|
the documentation in ConfigDef for more information on these namespaces.
|
||||||
|
|
||||||
Since configuration is dependant on context, internal classes require a
|
Since configuration is dependent on context, most of the internal classes
|
||||||
configuration object to be passed as a parameter. (They also require a
|
require a configuration object to be passed as a parameter. However, a few
|
||||||
Context object).
|
make this optional: they will supply a default configuration object if none
|
||||||
|
are passed. These classes are: HTMLPurifier::*, Generator::generateFromTokens
|
||||||
|
and Lexer::tokenizeHTML. However, whenever a valid configuration object
|
||||||
|
is defined, that object should be used.
|
||||||
|
|
||||||
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.
|
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()
|
1. Client calls Config->getHTMLDefinition()
|
||||||
2. Config calls HTMLDefinition->createNew(this)
|
2. Config calls HTMLDefinition->createNew(this)
|
||||||
3. HTMLDefinition constructs itself with base configuration
|
3. HTMLDefinition constructs itself with base configuration
|
||||||
4. HTMLDefinition calls Config->get('HTML')
|
4. HTMLDefinition calls Config->get('HTMLDefinition')
|
||||||
5. Config returns array of directives
|
5. Config returns array of directives that later construction
|
||||||
6. HTMLDefinition performs operations and changes specified by directives
|
6. HTMLDefinition performs operations and changes specified by directives
|
||||||
7. HTMLPurifier returns constructed definition
|
7. HTMLPurifier returns constructed definition
|
||||||
8. Config caches definition so it doesn't have to be generated again
|
8. Config caches definition so it doesn't have to be generated again
|
||||||
@@ -33,7 +36,3 @@ custom copy, which OVERRIDES all directives. Only the base, vanilla copy
|
|||||||
is the Singleton, the object actually interfaced with is a operated-upon
|
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
|
clone of that object. Also, if an update to the directives would update
|
||||||
the definition, you'd have to force reconstruction.
|
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.
|
|
@@ -1,52 +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="Discusses code quality issues and places that need to be refactored in HTML Purifier." />
|
|
||||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
|
||||||
|
|
||||||
<title>Code Quality Issues - HTML Purifier</title>
|
|
||||||
|
|
||||||
</head><body>
|
|
||||||
|
|
||||||
<h1>Code Quality Issues</h1>
|
|
||||||
|
|
||||||
<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,
|
|
||||||
while I can't list mistakes in here, I can list prototype-like segments
|
|
||||||
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
|
|
||||||
Lang - code duplication; premature optimization
|
|
||||||
Length - easily mistaken for CSSLength
|
|
||||||
URI - multiple regular expressions; missing validation for parts (?)
|
|
||||||
CSS - parser doesn't accept advanced CSS (fringe)
|
|
||||||
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
|
|
||||||
MakeWellFormed - insufficient automatic closing definitions (check HTML
|
|
||||||
spec for optional end tags, also, closing based on type (block/inline)
|
|
||||||
might be efficient).
|
|
||||||
RemoveForeignElements - should be run in parallel with MakeWellFormed
|
|
||||||
URIScheme - needs to have callable generic checks
|
|
||||||
mailto - doesn't validate emails, doesn't validate querystring
|
|
||||||
news - doesn't validate opaque path
|
|
||||||
nntp - doesn't constrain path
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<div id="version">$Id$</div>
|
|
||||||
|
|
||||||
</body></html>
|
|
@@ -1,82 +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="Defines class naming conventions in HTML Purifier." />
|
|
||||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
|
||||||
|
|
||||||
<title>Naming Conventions - HTML Purifier</title>
|
|
||||||
|
|
||||||
</head><body>
|
|
||||||
|
|
||||||
<h1>Naming Conventions</h1>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
|
|
||||||
<dt>All classes occupy the HTMLPurifier pseudo-namespace.</dt>
|
|
||||||
<dd>This means that all classes are prefixed with HTMLPurifier_. As such, all
|
|
||||||
names under HTMLPurifier_ are reserved. I recommend that you use the name
|
|
||||||
HTMLPurifierX_YourName_ClassName, especially if you want to take advantage
|
|
||||||
of HTMLPurifier_ConfigDef.</dd>
|
|
||||||
|
|
||||||
<dt>All classes correspond to their path if library/ was in the include path</dt>
|
|
||||||
<dd>HTMLPurifier_AttrDef is located at HTMLPurifier/AttrDef.php; replace
|
|
||||||
underscores with slashes and append .php and you'll have the location of
|
|
||||||
the class.</dd>
|
|
||||||
|
|
||||||
<dt>Harness and Test are reserved class names for unit tests</dt>
|
|
||||||
<dd>The suffix <code>Test</code> indicates that the class is a subclass of UnitTestCase
|
|
||||||
(of the Simpletest library) and is testable. "Harness" indicates a subclass
|
|
||||||
of UnitTestCase that is not meant to be run but to be extended into
|
|
||||||
concrete test cases and contains custom test methods (i.e. assert*())</dd>
|
|
||||||
|
|
||||||
<dt>Class names do not necessarily represent inheritance hierarchies</dt>
|
|
||||||
<dd>While we try to reflect inheritance in naming to some extent, it is not
|
|
||||||
guaranteed (for instance, none of the classes inherit from HTMLPurifier,
|
|
||||||
the base class). However, all class files have the require_once
|
|
||||||
declarations to whichever classes they are tightly coupled to.</dd>
|
|
||||||
|
|
||||||
<dt>Strategy has a meaning different from the Gang of Four pattern</dt>
|
|
||||||
<dd>In Design Patterns, the Gang of Four describes a Strategy object as
|
|
||||||
encapsulating an algorithm so that they can be switched at run-time. While
|
|
||||||
our strategies are indeed algorithms, they are not meant to be substituted:
|
|
||||||
all must be present in order for proper functioning.</dd>
|
|
||||||
|
|
||||||
<dt>Abbreviations are avoided</dt>
|
|
||||||
<dd>We try to avoid abbreviations as much as possible, but in some cases,
|
|
||||||
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>Def to Definition</li>
|
|
||||||
<li><code>$ret</code> is the value to be returned in a function</li>
|
|
||||||
</ul>
|
|
||||||
</dd>
|
|
||||||
|
|
||||||
<dt>Ambiguity concerning the definition of Def/Definition</dt>
|
|
||||||
<dd>While a definition normally defines the structure/acceptable values of
|
|
||||||
an entity, most of the definitions in this application also attempt
|
|
||||||
to validate and fix the value. I am unsure of a better name, as
|
|
||||||
"Validator" would exclude fixing the value, "Fixer" doesn't invoke
|
|
||||||
the proper image of "fixing" something, and "ValidatorFixer" is too long!
|
|
||||||
Some other suggestions were "Handler", "Reference", "Check", "Fix",
|
|
||||||
"Repair" and "Heal".</dd>
|
|
||||||
|
|
||||||
<dt>Transform not Transformer</dt>
|
|
||||||
<dd>Transform is both a noun and a verb, and thus we define a "Transform" as
|
|
||||||
something that "transforms," leaving "Transformer" (which sounds like an
|
|
||||||
electrical device/robot toy).</dd>
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
<div id="version">$Id$</div>
|
|
||||||
|
|
||||||
</body></html>
|
|
@@ -1,33 +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="Discusses possible methods of optimizing HTML Purifier." />
|
|
||||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
|
||||||
|
|
||||||
<title>Optimization - HTML Purifier</title>
|
|
||||||
|
|
||||||
</head><body>
|
|
||||||
|
|
||||||
<h1>Optimization</h1>
|
|
||||||
|
|
||||||
<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
|
|
||||||
that itch, put it here!</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>Make Tokens Flyweights (may prove problematic, probably not worth it)</li>
|
|
||||||
<li>Rewrite regexps into PHP code</li>
|
|
||||||
<li>Serialize the Definition object</li>
|
|
||||||
<li>Batch regexp validation (do as many per function call as possible)</li>
|
|
||||||
<li>Parallelize strategies</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div id="version">$Id$</div>
|
|
||||||
|
|
||||||
</body></html>
|
|
@@ -1,45 +1,31 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
|
<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="Credits and links to DevNetwork forum topics on HTML Purifier." />
|
<title>DevNetwork Forums</title>
|
||||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
|
||||||
|
</head>
|
||||||
<title>DevNetwork Credits - HTML Purifier</title>
|
<body>
|
||||||
|
|
||||||
</head>
|
<p>Many thanks to the DevNetwork community for answering questions,
|
||||||
<body>
|
theorizing about design, and offering encouragement during
|
||||||
|
the development of this library in these forum threads:</p>
|
||||||
<h1>DevNetwork Credits</h1>
|
|
||||||
|
<ul>
|
||||||
<div id="filing">Filed under Reference</div>
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=52905">HTMLPurifier PHP Library hompeage</a></li>
|
||||||
<div id="index">Return to the <a href="index.html">index</a>.</div>
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53056">How much of CSS to implement?</a></li>
|
||||||
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53083">Parsing URL only according to URI : Security Risk?</a></li>
|
||||||
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53096">Gimme a name : URI and friends</a></li>
|
||||||
<p>Many thanks to the DevNetwork community for answering questions,
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53415">How to document configuration directives</a></li>
|
||||||
theorizing about design, and offering encouragement during
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53479">IPv6</a></li>
|
||||||
the development of this library in these forum threads:</p>
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53539">http and ftp versus news and mailto</a></li>
|
||||||
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53579">HTMLPurifier - Take your best shot</a></li>
|
||||||
<ul>
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53664">Need help optimizing a block of code</a>
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=52905">HTMLPurifier PHP Library hompeage</a></li>
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53861">Non-SGML characters</a>
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53056">How much of CSS to implement?</a></li>
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=54283">Wordpress makes me cry</a>
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53083">Parsing URL only according to URI : Security Risk?</a></li>
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=54478">Parameter Object vs. Parameter Array vs. Parameter Functions</a>
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53096">Gimme a name : URI and friends</a></li>
|
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=54521">Convert encoding where output cannot represent characters</a>
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53415">How to document configuration directives</a></li>
|
</ul>
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53479">IPv6</a></li>
|
</body>
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53539">http and ftp versus news and mailto</a></li>
|
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53579">HTMLPurifier - Take your best shot</a></li>
|
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53664">Need help optimizing a block of code</a></li>
|
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53861">Non-SGML characters</a></li>
|
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=54283">Wordpress makes me cry</a></li>
|
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=54478">Parameter Object vs. Parameter Array vs. Parameter Functions</a></li>
|
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=54521">Convert encoding where output cannot represent characters</a></li>
|
|
||||||
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=56411">Reporting errors in a document without line numbers</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>...as well as any I may have forgotten.</p>
|
|
||||||
|
|
||||||
<div id="version">$Id$</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
@@ -1,147 +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 various methods for allowing IDs in documents safely in HTML Purifier." />
|
|
||||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
|
||||||
|
|
||||||
<title>IDs - HTML Purifier</title>
|
|
||||||
|
|
||||||
</head><body>
|
|
||||||
|
|
||||||
<h1 class="subtitled">IDs</h1>
|
|
||||||
<div class="subtitle">What they are, why you should(n't) wear them, and how to deal with it</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>Prior to HTML Purifier 1.2.0, this library blithely accepted user input that
|
|
||||||
looked like this:</p>
|
|
||||||
|
|
||||||
<pre><a id="fragment">Anchor</a></pre>
|
|
||||||
|
|
||||||
<p>...presenting an attractive vector for those that would destroy standards
|
|
||||||
compliance: simply set the ID to one that is already used elsewhere in the
|
|
||||||
document and voila: validation breaks. There was a half-hearted attempt to
|
|
||||||
prevent this by allowing users to blacklist IDs, but I suspect that no one
|
|
||||||
really bothered, and thus, with the release of 1.2.0, IDs are now <em>removed</em>
|
|
||||||
by default.</p>
|
|
||||||
|
|
||||||
<p>IDs, however, are quite useful functionality to have, so if users start
|
|
||||||
complaining about broken anchors you'll probably want to turn them back on
|
|
||||||
with %HTML.EnableAttrID. But before you go mucking around with the config
|
|
||||||
object, it's probably worth to take some precautions to keep your page
|
|
||||||
validating. Why?</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>Standards-compliant pages are good</li>
|
|
||||||
<li>Duplicated IDs interfere with anchors. If there are two id="foobar"s in a
|
|
||||||
document, which spot does a browser presented with the fragment #foobar go
|
|
||||||
to? Most browsers opt for the first appearing ID, making it impossible
|
|
||||||
to references the second section. Similarly, duplicated IDs can hijack
|
|
||||||
client-side scripting that relies on the IDs of elements.</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<p>You have (currently) four ways of dealing with the problem.</p>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2 class="subtitled">Blacklisting IDs</h2>
|
|
||||||
<div class="subsubtitle">Good for pages with single content source and stable templates</div>
|
|
||||||
|
|
||||||
<p>Keeping in terms with the
|
|
||||||
<acronym title="Keep It Simple, Stupid">KISS</acronym> principle, let us
|
|
||||||
deal with the most obvious solution: preventing users from using any IDs that
|
|
||||||
appear elsewhere on the document. The method is simple:</p>
|
|
||||||
|
|
||||||
<pre>$config->set('HTML', 'EnableAttrID', true);
|
|
||||||
$config->set('Attr', 'IDBlacklist' array(
|
|
||||||
'list', 'of', 'attributes', 'that', 'are', 'forbidden'
|
|
||||||
));</pre>
|
|
||||||
|
|
||||||
<p>That being said, there are some notable drawbacks. First of all, you have to
|
|
||||||
know precisely which IDs are being used by the HTML surrounding the user code.
|
|
||||||
This is easier said than done: quite often the page designer and the system
|
|
||||||
coder work separately, so the designer has to constantly be talking with the
|
|
||||||
coder whenever he decides to add a new anchor. Miss one and you open yourself
|
|
||||||
to possible standards-compliance issues.</p>
|
|
||||||
|
|
||||||
<p>Furthermore, this position becomes untenable when a single web page must hold
|
|
||||||
multiple portions of user-submitted content. Since there's obviously no way
|
|
||||||
to find out before-hand what IDs users will use, the blacklist is helpless.
|
|
||||||
And even since HTML Purifier validates each segment seperately, perhaps doing
|
|
||||||
so at different times, it would be extremely difficult to dynamically update
|
|
||||||
the blacklist inbetween runs.</p>
|
|
||||||
|
|
||||||
<p>Finally, simply destroying the ID is extremely un-userfriendly behavior: after
|
|
||||||
all, they might have simply specified a duplicate ID by accident.</p>
|
|
||||||
|
|
||||||
<p>Thus, we get to our second method.</p>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2 class="subtitled">Namespacing IDs</h2>
|
|
||||||
<div class="subsubtitle">Lazy developer's way, but needs user education</div>
|
|
||||||
|
|
||||||
<p>This method, too, is quite simple: add a prefix to all user IDs. With this
|
|
||||||
code:</p>
|
|
||||||
|
|
||||||
<pre>$config->set('HTML', 'EnableAttrID', true);
|
|
||||||
$config->set('Attr', 'IDPrefix', 'user_');</pre>
|
|
||||||
|
|
||||||
<p>...this:</p>
|
|
||||||
|
|
||||||
<pre><a id="foobar">Anchor!</a></pre>
|
|
||||||
|
|
||||||
<p>...turns into:</p>
|
|
||||||
|
|
||||||
<pre><a id="user_foobar">Anchor!</a></pre>
|
|
||||||
|
|
||||||
<p>As long as you don't have any IDs that start with user_, collisions are
|
|
||||||
guaranteed not to happen. The drawback is obvious: if a user submits
|
|
||||||
id="foobar", they probably expect to be able to reference their page with
|
|
||||||
#foobar. You'll have to tell them, "No, that doesn't work, you have to add
|
|
||||||
user_ to the beginning."</p>
|
|
||||||
|
|
||||||
<p>And yes, things get hairier. Even with a nice prefix, we still have done
|
|
||||||
nothing about multiple HTML Purifier outputs on one page. Thus, we have
|
|
||||||
a second configuration value to piggy-back off of: %Attr.IDPrefixLocal:</p>
|
|
||||||
|
|
||||||
<pre>$config->set('Attr', 'IDPrefixLocal', 'comment' . $id . '_');</pre>
|
|
||||||
|
|
||||||
<p>This new attributes does nothing but append on to regular IDPrefix, but is
|
|
||||||
special in that it is volatile: it's value is determined at run-time and
|
|
||||||
cannot possibly be cordoned into, say, a .ini config file. As for what to
|
|
||||||
put into the directive, is up to you, but I would recommend the ID number
|
|
||||||
the text has been assigned in the database. Whatever you pick, however, it
|
|
||||||
has to be unique and stable for the text you are validating. Note, however,
|
|
||||||
that we require that %Attr.IDPrefix be set before you use this directive.</p>
|
|
||||||
|
|
||||||
<p>And also remember: the user has to know what this prefix is too!</p>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2>Abstinence</h2>
|
|
||||||
|
|
||||||
<p>You may not want to bother. That's okay too, just don't enable IDs.</p>
|
|
||||||
|
|
||||||
<p>Personally, I would take this road whenever user-submitted content would be
|
|
||||||
possibly be shown together on one page. Why a blog comment would need to use
|
|
||||||
anchors is beyond me.</p>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2>Denial</h2>
|
|
||||||
|
|
||||||
<p>To revert back to pre-1.2.0 behavior, simply:</p>
|
|
||||||
|
|
||||||
<pre>$config->set('HTML', 'EnableAttrID', true);</pre>
|
|
||||||
|
|
||||||
<p>Don't come crying to me when your page mysteriously stops validating, though.</p>
|
|
||||||
|
|
||||||
<div id="version">$Id$</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -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><?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();
|
|
||||||
}
|
|
||||||
?></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><?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;
|
|
||||||
?></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>
|
|
@@ -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 é and æ. 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 > Page Info: Encoding</dd>
|
|
||||||
<dt>Internet Explorer</dt>
|
|
||||||
<dd>View > 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, "Didn't we already find out our
|
|
||||||
encoding?" 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><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></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 "claimed"
|
|
||||||
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 "UTF-8"</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 = "utf-8"</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><?xml version="1.0" encoding="UTF-8"?></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 "Why are there
|
|
||||||
so many options?" 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, θ can be
|
|
||||||
written <code>&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: 激光,
|
|
||||||
這兩個字是甚麼意思.
|
|
||||||
The entity-ized version would look like this:</p>
|
|
||||||
|
|
||||||
<pre>&#28608;&#20809;, &#36889;&#20841;&#20491;&#23383;&#26159;&#29978;&#40636;&#24847;&#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, "intelligible" character
|
|
||||||
entities like <code>&theta;</code> will leave users who are
|
|
||||||
uninterested in learning HTML scratching their heads. On the other
|
|
||||||
hand, if they see θ 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
|
|
||||||
"smart" 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>
|
|
@@ -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 "Fido and the Bones of Spring".
|
|
||||||
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>
|
|
||||||
<object width="425" height="350">
|
|
||||||
<param name="movie" value="http://www.youtube.com/v/AyPzM5WK8ys" />
|
|
||||||
<param name="wmode" value="transparent" />
|
|
||||||
<embed src="http://www.youtube.com/v/AyPzM5WK8ys"
|
|
||||||
type="application/x-shockwave-flash"
|
|
||||||
wmode="transparent" width="425" height="350" />
|
|
||||||
</object>
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>There are two things to note about this code:</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li><code><embed></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><span
|
|
||||||
class="embed-youtube">AyPzM5WK8ys</span></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><?php
|
|
||||||
// assuming $purifier is an instance of HTMLPurifier
|
|
||||||
require_once 'HTMLPurifier/Filter/YouTube.php';
|
|
||||||
$purifier->addFilter(new HTMLPurifier_Filter_YouTube());
|
|
||||||
?></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>
|
|
@@ -1,14 +1,15 @@
|
|||||||
<?php exit;
|
<?php
|
||||||
|
|
||||||
// This file demonstrates basic usage of HTMLPurifier.
|
// 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();
|
$purifier = new HTMLPurifier();
|
||||||
$html = '<b>Simple and short';
|
$html = '<b>Simple and short';
|
||||||
|
|
||||||
$pure_html = $purifier->purify($html);
|
$pure_html = $purifier->purify($html);
|
||||||
|
|
||||||
echo $pure_html;
|
|
||||||
|
|
||||||
?>
|
?>
|
@@ -1,66 +1,34 @@
|
|||||||
<?php
|
<?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';
|
?><!DOCTYPE html
|
||||||
header("Content-type:$content;charset=UTF-8");
|
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||||
|
|
||||||
// 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"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
<?php
|
<html>
|
||||||
} 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">
|
|
||||||
<head>
|
<head>
|
||||||
<title>HTML Purifier Live Demo</title>
|
<title>HTMLPurifier Live Demo</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>HTML Purifier Live Demo</h1>
|
<h1>HTMLPurifier Live Demo</h1>
|
||||||
<?php
|
<?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) {
|
$html = get_magic_quotes_gpc() ? stripslashes($_POST['html']) : $_POST['html'];
|
||||||
?>
|
|
||||||
<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'];
|
|
||||||
|
|
||||||
$config = HTMLPurifier_Config::createDefault();
|
$config = HTMLPurifier_Config::createDefault();
|
||||||
$config->set('Core', 'TidyFormat', !empty($_REQUEST['tidy']));
|
$config->set('Core', 'TidyFormat', !empty($_POST['tidy']));
|
||||||
$config->set('HTML', 'Strict', !empty($_REQUEST['strict']));
|
|
||||||
$purifier = new HTMLPurifier($config);
|
$purifier = new HTMLPurifier($config);
|
||||||
$pure_html = $purifier->purify($html);
|
$pure_html = $purifier->purify($html);
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<p>Here is your purified HTML:</p>
|
<p>Here is your purified HTML:</p>
|
||||||
<div style="border:5px solid #CCC;margin:0 10%;padding:1em;">
|
<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
|
<?php
|
||||||
|
|
||||||
echo $pure_html;
|
echo $pure_html;
|
||||||
@@ -75,34 +43,23 @@ echo htmlspecialchars($pure_html, ENT_COMPAT, 'UTF-8');
|
|||||||
|
|
||||||
?></pre>
|
?></pre>
|
||||||
<?php
|
<?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 {
|
} 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>
|
will filter it.</p>
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<form id="filter" action="demo.php<?php
|
<form name="filter" action="demo.php<?php
|
||||||
echo '?' . getFormMethod();
|
if (isset($_GET['profile']) || isset($_GET['XDEBUG_PROFILE'])) {
|
||||||
if (isset($_REQUEST['profile']) || isset($_REQUEST['XDEBUG_PROFILE'])) {
|
echo '?XDEBUG_PROFILE=1';
|
||||||
echo '&XDEBUG_PROFILE=1';
|
} ?>" method="post">
|
||||||
} ?>" method="<?php echo getFormMethod(); ?>">
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>HTML Purifier Input (<?php echo getFormMethod(); ?>)</legend>
|
<legend>HTML</legend>
|
||||||
<textarea name="html" cols="60" rows="15"><?php
|
<textarea name="html" cols="60" rows="15"><?php
|
||||||
|
|
||||||
if (isset($html)) {
|
if (isset($html)) {
|
||||||
@@ -110,27 +67,13 @@ if (isset($html)) {
|
|||||||
HTMLPurifier_Encoder::cleanUTF8($html), ENT_COMPAT, 'UTF-8');
|
HTMLPurifier_Encoder::cleanUTF8($html), ENT_COMPAT, 'UTF-8');
|
||||||
}
|
}
|
||||||
?></textarea>
|
?></textarea>
|
||||||
<?php if (getFormMethod() == 'get') { ?>
|
<div>Nicely format output with Tidy? <input type="checkbox" value="1"
|
||||||
<p><strong>Warning:</strong> GET request method can only hold
|
name="tidy"<?php if (!empty($_POST['tidy'])) echo ' checked="checked"'; ?> /></div>
|
||||||
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>
|
<div>
|
||||||
<input type="submit" value="Submit" name="submit" class="button" />
|
<input type="submit" value="Submit" name="submit" class="button" />
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
<p>Return to <a href="http://hp.jpsband.org/">HTML Purifier's home page</a>.
|
<p>Return to <a href="http://hp.jpsband.org/">HTMLPurifier's home page</a>.</p>
|
||||||
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>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@@ -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
|
definitely too large for HTML that would be allowed in blog comments. Going
|
||||||
from Transitional to Strict requires changes to the definition.
|
from Transitional to Strict requires changes to the definition.
|
||||||
|
|
||||||
Allowing users to specify their own whitelists is one step (implemented, btw),
|
However, allowing users to specify their own whitelists was an idea I
|
||||||
but I have doubts on only doing this. Simply put, the typical programmer is too
|
rejected from the start. Simply put, the typical programmer is too lazy
|
||||||
lazy to actually go through the trouble of investigating which tags, attributes
|
to actually go through the trouble of investigating which tags, attributes
|
||||||
and properties to allow. HTMLDefinition makes a big part of what HTMLPurifier
|
and properties to allow. HTMLDefinition makes a big part of what HTMLPurifier
|
||||||
is.
|
is.
|
||||||
|
|
||||||
The idea, then, is to setup fundamentally different set of definitions, which
|
The idea, then, is to setup fundamentally different set of definitions, which
|
||||||
can further be customized using simpler configuration options. Alternatively,
|
can further be customized using simpler configuration options.
|
||||||
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).
|
|
||||||
|
|
||||||
Here are some fuzzy levels you could set:
|
Here are some fuzzy levels you could set:
|
||||||
|
|
||||||
@@ -31,7 +28,7 @@ Here are some fuzzy levels you could set:
|
|||||||
to be useful)
|
to be useful)
|
||||||
3. Pages - As permissive as possible without allowing XSS. No protection
|
3. Pages - As permissive as possible without allowing XSS. No protection
|
||||||
against bad design sense, unfortunantely. Suitable for wiki and page
|
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
|
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>
|
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
|
and friends to be implemented, which is a lot of work for not a lot of
|
||||||
@@ -129,5 +126,5 @@ Colored - background-color (background), border-color (border), color
|
|||||||
Dramatic - border, list-style-position (list-style), margin, padding,
|
Dramatic - border, list-style-position (list-style), margin, padding,
|
||||||
text-align, text-indent, text-transform, vertical-align, line-height
|
text-align, text-indent, text-transform, vertical-align, line-height
|
||||||
|
|
||||||
Dramatic elements substantially change the look of text in ways that should
|
Dramatic elements substnatially change the look of text in ways that should
|
||||||
probably have been reserved to other areas.
|
probably have been reserved to other areas.
|
158
docs/index.html
158
docs/index.html
@@ -1,158 +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="Index to all HTML Purifier documentation." />
|
|
||||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
|
||||||
|
|
||||||
<title>Documentation - HTML Purifier</title>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<h1>Documentation</h1>
|
|
||||||
|
|
||||||
<p><strong><a href="http://hp.jpsband.org/">HTML Purifier</a></strong> has documentation for all types of people.
|
|
||||||
Here is an index of all of them.</p>
|
|
||||||
|
|
||||||
<h2>End-user</h2>
|
|
||||||
<p>End-user documentation that contains articles, tutorials and useful
|
|
||||||
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>
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
<h2>Development</h2>
|
|
||||||
<p>Developer documentation detailing code issues, roadmaps and project
|
|
||||||
conventions.</p>
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
|
|
||||||
<dt><a href="dev-code-quality.html">Code Quality Issues</a></dt>
|
|
||||||
<dd>Discusses code quality issues and places that need to be refactored.</dd>
|
|
||||||
|
|
||||||
<dt><a href="dev-progress.html">Implementation Progress</a></dt>
|
|
||||||
<dd>Tables detailing HTML element and CSS property implementation coverage.</dd>
|
|
||||||
|
|
||||||
<dt><a href="dev-naming.html">Naming Conventions</a></dt>
|
|
||||||
<dd>Defines class naming conventions.</dd>
|
|
||||||
|
|
||||||
<dt><a href="dev-optimization.html">Optimization</a></dt>
|
|
||||||
<dd>Discusses possible methods of optimizing HTML Purifier.</dd>
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
<h2>Proposals</h2>
|
|
||||||
<p>Proposed features, as well as the associated rambling to get a clear
|
|
||||||
objective in place before attempted implementation.</p>
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt><a href="proposal-colors.html">Colors</a></dt>
|
|
||||||
<dd>Proposal to allow for color constraints.</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
<h2>Reference</h2>
|
|
||||||
<p>Miscellaneous essays, research pieces and other reference type material
|
|
||||||
that may not directly discuss HTML Purifier.</p>
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt><a href="ref-devnetwork.html">DevNetwork Credits</a></dt>
|
|
||||||
<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>
|
|
56
docs/naming.txt
Normal file
56
docs/naming.txt
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
Naming
|
||||||
|
|
||||||
|
The classes in this library follow a few naming conventions, which may
|
||||||
|
help you find the correct functionality more quickly. Here they are:
|
||||||
|
|
||||||
|
All classes occupy the HTMLPurifier pseudo-namespace.
|
||||||
|
This means that all classes are prefixed with HTMLPurifier_. As such, all
|
||||||
|
names under HTMLPurifier_ are reserved. I recommend that you use the name
|
||||||
|
HTMLPurifierX_YourName_ClassName, especially if you want to take advantage
|
||||||
|
of HTMLPurifier_ConfigDef.
|
||||||
|
|
||||||
|
All classes correspond to their path if library/ was in the include path
|
||||||
|
HTMLPurifier_AttrDef is located at HTMLPurifier/AttrDef.php; replace
|
||||||
|
underscores with slashes and append .php and you'll have the location of
|
||||||
|
the class.
|
||||||
|
|
||||||
|
Harness and Test are reserved class names for unit tests
|
||||||
|
The suffix "Test" indicates that the class is a subclass of UnitTestCase
|
||||||
|
(of the Simpletest library) and is testable. "Harness" indicates a subclass
|
||||||
|
of UnitTestCase that is not meant to be run but to be extended into
|
||||||
|
concrete test cases and contains custom test methods (i.e. assert*())
|
||||||
|
|
||||||
|
Class names do not necessarily represent inheritance hierarchies
|
||||||
|
While we try to reflect inheritance in naming to some extent, it is not
|
||||||
|
guaranteed (for instance, none of the classes inherit from HTMLPurifier,
|
||||||
|
the base class). However, all class files have the require_once
|
||||||
|
declarations to whichever classes they are tightly coupled to.
|
||||||
|
|
||||||
|
Strategy has a meaning different from the Gang of Four pattern
|
||||||
|
In Design Patterns, the Gang of Four describes a Strategy object as
|
||||||
|
encapsulating an algorithm so that they can be switched at run-time. While
|
||||||
|
our strategies are indeed algorithms, they are not meant to be substituted:
|
||||||
|
all must be present in order for proper functioning.
|
||||||
|
|
||||||
|
Abbreviations are avoided
|
||||||
|
We try to avoid abbreviations as much as possible, but in some cases,
|
||||||
|
abbreviated version is more readable than the full version. Here, we
|
||||||
|
list common abbreviations:
|
||||||
|
Attr(s) -> Attribute(s)
|
||||||
|
Def -> Definition
|
||||||
|
|
||||||
|
Ambiguity concerning the definition of Def/Definition
|
||||||
|
While a definition normally defines the structure/acceptable values of
|
||||||
|
an entity, most of the definitions in this application also attempt
|
||||||
|
to validate and fix the value. I am unsure of a better name, as
|
||||||
|
"Validator" would exclude fixing the value, "Fixer" doesn't invoke
|
||||||
|
the proper image of "fixing" something, and "ValidatorFixer" is too long!
|
||||||
|
Some other suggestions were "Handler", "Reference", "Check", "Fix",
|
||||||
|
"Repair" and "Heal".
|
||||||
|
|
||||||
|
Transform not Transformer
|
||||||
|
Transform is both a noun and a verb, and thus we define a "Transform" as
|
||||||
|
something that "transforms," leaving "Transformer" (which sounds like an
|
||||||
|
electrical device/robot toy).
|
||||||
|
|
12
docs/optimization.txt
Normal file
12
docs/optimization.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
Optimization
|
||||||
|
|
||||||
|
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
|
||||||
|
that itch, put it here!
|
||||||
|
|
||||||
|
- Make Tokens Flyweights (may prove problematic, probably not worth it)
|
||||||
|
- Rewrite regexps into PHP code
|
||||||
|
- Serialize the Definition object
|
||||||
|
- Batch regexp validation (do as many per function call as possible)
|
||||||
|
- Parallelize strategies
|
@@ -1,302 +1,292 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
|
<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="Tables detailing HTML element and CSS property implementation coverage in HTML Purifier." />
|
<title>HTMLPurifier Progress</title>
|
||||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
|
||||||
|
<style type="text/css">
|
||||||
<title>Implementation Progress - HTML Purifier</title>
|
|
||||||
|
td {padding-right:1em;border-bottom:1px solid #000;padding-left:0.5em;}
|
||||||
<style type="text/css">
|
th {text-align:left;padding-top:1.4em;font-size:13pt;
|
||||||
|
border-bottom:2px solid #000;background:#FFF;}
|
||||||
td {padding-right:1em;border-bottom:1px solid #000;padding-left:0.5em;}
|
thead th {text-align:left;padding:0.1em;background-color:#EEE;}
|
||||||
th {text-align:left;padding-top:1.4em;font-size:13pt;
|
|
||||||
border-bottom:2px solid #000;background:#FFF;}
|
.impl-yes {background:#9D9;}
|
||||||
thead th {text-align:left;padding:0.1em;background-color:#EEE;}
|
.impl-partial {background:#FFA;}
|
||||||
|
.impl-no {background:#CCC;}
|
||||||
.impl-yes {background:#9D9;}
|
|
||||||
.impl-partial {background:#FFA;}
|
.danger {color:#600;}
|
||||||
.impl-no {background:#CCC;}
|
.css1 {color:#060;}
|
||||||
|
.required {font-weight:bold;}
|
||||||
.danger {color:#600;}
|
.feature {color:#999;}
|
||||||
.css1 {color:#060;}
|
|
||||||
.required {font-weight:bold;}
|
</style>
|
||||||
.feature {color:#999;}
|
|
||||||
|
</head><body>
|
||||||
</style>
|
|
||||||
|
<h1>HTMLPurifier Progress</h1>
|
||||||
</head><body>
|
|
||||||
|
<h2>Key</h2>
|
||||||
<h1>Implementation Progress</h1>
|
|
||||||
|
<table cellspacing="0"><tbody>
|
||||||
<div id="filing">Filed under Development</div>
|
<tr><td class="impl-yes">Implemented</td></tr>
|
||||||
<div id="index">Return to the <a href="index.html">index</a>.</div>
|
<tr><td class="impl-partial">Partially implemented</td></tr>
|
||||||
<div id="home"><a href="http://hp.jpsband.org/">HTML Purifier</a> End-User Documentation</div>
|
<tr><td class="impl-no">Will not implement</td></tr>
|
||||||
|
<tr><td class="danger">Dangerous attribute/property</td></tr>
|
||||||
<h2>Key</h2>
|
<tr><td class="css1">Present in CSS1</td></tr>
|
||||||
|
<tr><td class="feature">Feature, requires extra work</td></tr>
|
||||||
<table cellspacing="0"><tbody>
|
</tbody></table>
|
||||||
<tr><td class="impl-yes">Implemented</td></tr>
|
|
||||||
<tr><td class="impl-partial">Partially implemented</td></tr>
|
<h3>CSS</h3>
|
||||||
<tr><td class="impl-no">Will not implement</td></tr>
|
|
||||||
<tr><td class="danger">Dangerous attribute/property</td></tr>
|
<table cellspacing="0">
|
||||||
<tr><td class="css1">Present in CSS1</td></tr>
|
|
||||||
<tr><td class="feature">Feature, requires extra work</td></tr>
|
<thead>
|
||||||
</tbody></table>
|
<tr><th>Name</th><th>Notes</th></tr>
|
||||||
|
</thead>
|
||||||
<h2>CSS</h2>
|
|
||||||
|
<!--
|
||||||
<table cellspacing="0">
|
<tr><td>-</td><td>-</td></tr>
|
||||||
|
-->
|
||||||
<thead>
|
|
||||||
<tr><th>Name</th><th>Notes</th></tr>
|
<tbody>
|
||||||
</thead>
|
<tr><th colspan="2">Standard</th></tr>
|
||||||
|
<tr class="css1 impl-yes"><td>background-color</td><td>COMPOSITE(<color>, transparent)</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><td>-</td><td>-</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>
|
||||||
<tbody>
|
<tr class="css1 impl-yes"><td>border-width</td><td>MULTIPLE</td></tr>
|
||||||
<tr><th colspan="2">Standard</th></tr>
|
<tr class="css1 impl-yes"><td>border-*</td><td>SHORTHAND</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>background-color</td><td>COMPOSITE(<color>, transparent)</td></tr>
|
<tr class="impl-yes"><td>border-*-color</td><td>COMPOSITE(<color>, transparent)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>background</td><td>SHORTHAND, currently alias for background-color</td></tr>
|
<tr class="impl-yes"><td>border-*-style</td><td>ENUM(none, hidden, dotted, dashed,
|
||||||
<tr class="css1 impl-yes"><td>border</td><td>SHORTHAND, MULTIPLE</td></tr>
|
solid, double, groove, ridge, inset, outset)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>border-color</td><td>MULTIPLE</td></tr>
|
<tr class="css1 impl-yes"><td>border-*-width</td><td>COMPOSITE(<length>, thin, medium, thick)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>border-style</td><td>MULTIPLE</td></tr>
|
<tr class="css1 impl-yes"><td>clear</td><td>ENUM(none, left, right, both)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>border-width</td><td>MULTIPLE</td></tr>
|
<tr class="css1 impl-yes"><td>color</td><td><color></td></tr>
|
||||||
<tr class="css1 impl-yes"><td>border-*</td><td>SHORTHAND</td></tr>
|
<tr class="css1 impl-yes"><td>float</td><td>ENUM(left, right, none), May require layout
|
||||||
<tr class="impl-yes"><td>border-*-color</td><td>COMPOSITE(<color>, transparent)</td></tr>
|
precautions with clear</td></tr>
|
||||||
<tr class="impl-yes"><td>border-*-style</td><td>ENUM(none, hidden, dotted, dashed,
|
<tr class="css1 impl-yes"><td>font</td><td>SHORTHAND</td></tr>
|
||||||
solid, double, groove, ridge, inset, outset)</td></tr>
|
<tr class="css1 impl-yes"><td>font-family</td><td>CSS validator may complain if fallback font
|
||||||
<tr class="css1 impl-yes"><td>border-*-width</td><td>COMPOSITE(<length>, thin, medium, thick)</td></tr>
|
family not specified</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>clear</td><td>ENUM(none, left, right, both)</td></tr>
|
<tr class="css1 impl-yes"><td>font-size</td><td>COMPOSITE(<absolute-size>,
|
||||||
<tr class="css1 impl-yes"><td>color</td><td><color></td></tr>
|
<relative-size>, <length>, <percentage>)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>float</td><td>ENUM(left, right, none), May require layout
|
<tr class="css1 impl-yes"><td>font-style</td><td>ENUM(normal, italic, oblique)</td></tr>
|
||||||
precautions with clear</td></tr>
|
<tr class="css1 impl-yes"><td>font-variant</td><td>ENUM(normal, small-caps)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>font</td><td>SHORTHAND</td></tr>
|
<tr class="css1 impl-yes"><td>font-weight</td><td>ENUM(normal, bold, bolder, lighter,
|
||||||
<tr class="css1 impl-yes"><td>font-family</td><td>CSS validator may complain if fallback font
|
100, 200, 300, 400, 500, 600, 700, 800, 900), maybe special code for
|
||||||
family not specified</td></tr>
|
in-between integers</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>font-size</td><td>COMPOSITE(<absolute-size>,
|
<tr class="css1 impl-yes"><td>letter-spacing</td><td>COMPOSITE(<length>, normal)</td></tr>
|
||||||
<relative-size>, <length>, <percentage>)</td></tr>
|
<tr class="css1 impl-yes"><td>line-height</td><td>COMPOSITE(<number>,
|
||||||
<tr class="css1 impl-yes"><td>font-style</td><td>ENUM(normal, italic, oblique)</td></tr>
|
<length>, <percentage>, normal)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>font-variant</td><td>ENUM(normal, small-caps)</td></tr>
|
<tr class="css1 impl-yes"><td>list-style-position</td><td>ENUM(inside, outside),
|
||||||
<tr class="css1 impl-yes"><td>font-weight</td><td>ENUM(normal, bold, bolder, lighter,
|
Strange behavior in browsers</td></tr>
|
||||||
100, 200, 300, 400, 500, 600, 700, 800, 900), maybe special code for
|
<tr class="css1 impl-yes"><td>list-style-type</td><td>ENUM(...),
|
||||||
in-between integers</td></tr>
|
Well-supported values are: disc, circle, square,
|
||||||
<tr class="css1 impl-yes"><td>letter-spacing</td><td>COMPOSITE(<length>, normal)</td></tr>
|
decimal, lower-roman, upper-roman, lower-alpha and upper-alpha. See also
|
||||||
<tr class="css1 impl-yes"><td>line-height</td><td>COMPOSITE(<number>,
|
CSS 3. Mostly IE lack of support.</td></tr>
|
||||||
<length>, <percentage>, normal)</td></tr>
|
<tr class="css1 impl-yes"><td>list-style</td><td>SHORTHAND</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>list-style-position</td><td>ENUM(inside, outside),
|
<tr class="css1 impl-yes"><td>margin</td><td>MULTIPLE</td></tr>
|
||||||
Strange behavior in browsers</td></tr>
|
<tr class="css1 impl-yes"><td>margin-*</td><td>COMPOSITE(<length>,
|
||||||
<tr class="css1 impl-yes"><td>list-style-type</td><td>ENUM(...),
|
<percentage>, auto)</td></tr>
|
||||||
Well-supported values are: disc, circle, square,
|
<tr class="css1 impl-yes"><td>padding</td><td>MULTIPLE</td></tr>
|
||||||
decimal, lower-roman, upper-roman, lower-alpha and upper-alpha. See also
|
<tr class="css1 impl-yes"><td>padding-*</td><td>COMPOSITE(<length>(positive),
|
||||||
CSS 3. Mostly IE lack of support.</td></tr>
|
<percentage>(positive))</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>list-style</td><td>SHORTHAND</td></tr>
|
<tr class="css1 impl-yes"><td>text-align</td><td>ENUM(left, right,
|
||||||
<tr class="css1 impl-yes"><td>margin</td><td>MULTIPLE</td></tr>
|
center, justify)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>margin-*</td><td>COMPOSITE(<length>,
|
<tr class="css1 impl-yes"><td>text-decoration</td><td>No blink (argh my eyes), not
|
||||||
<percentage>, auto)</td></tr>
|
enum, can be combined (composite sorta): underline, overline,
|
||||||
<tr class="css1 impl-yes"><td>padding</td><td>MULTIPLE</td></tr>
|
line-through</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>padding-*</td><td>COMPOSITE(<length>(positive),
|
<tr class="css1 impl-yes"><td>text-indent</td><td>COMPOSITE(<length>,
|
||||||
<percentage>(positive))</td></tr>
|
<percentage>)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>text-align</td><td>ENUM(left, right,
|
<tr class="css1 impl-yes"><td>text-transform</td><td>ENUM(capitalize, uppercase,
|
||||||
center, justify)</td></tr>
|
lowercase, none)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>text-decoration</td><td>No blink (argh my eyes), not
|
<tr class="css1 impl-yes"><td>width</td><td>COMPOSITE(<length>,
|
||||||
enum, can be combined (composite sorta): underline, overline,
|
<percentage>, auto), Interesting</td></tr>
|
||||||
line-through</td></tr>
|
<tr class="css1 impl-yes"><td>word-spacing</td><td>COMPOSITE(<length>, auto),
|
||||||
<tr class="css1 impl-yes"><td>text-indent</td><td>COMPOSITE(<length>,
|
IE 5 no support</td></tr>
|
||||||
<percentage>)</td></tr>
|
</tbody>
|
||||||
<tr class="css1 impl-yes"><td>text-transform</td><td>ENUM(capitalize, uppercase,
|
|
||||||
lowercase, none)</td></tr>
|
<tbody>
|
||||||
<tr class="css1 impl-yes"><td>width</td><td>COMPOSITE(<length>,
|
<tr><th colspan="2">Table</th></tr>
|
||||||
<percentage>, auto), Interesting</td></tr>
|
<tr class="impl-yes"><td>border-collapse</td><td>ENUM(collapse, seperate)</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>word-spacing</td><td>COMPOSITE(<length>, auto),
|
<tr class="impl-yes"><td>caption-side</td><td>ENUM(top, bottom)</td></tr>
|
||||||
IE 5 no support</td></tr>
|
<tr class="feature"><td>empty-cells</td><td>ENUM(show, hide), No IE support makes this useless,
|
||||||
</tbody>
|
possible fix with &nbsp;? Unknown release milestone.</td></tr>
|
||||||
|
<tr class="impl-yes"><td>table-layout</td><td>ENUM(auto, fixed)</td></tr>
|
||||||
<tbody>
|
<tr class="impl-yes css1"><td>vertical-align</td><td>COMPOSITE(ENUM(baseline, sub,
|
||||||
<tr><th colspan="2">Table</th></tr>
|
super, top, text-top, middle, bottom, text-bottom), <percentage>,
|
||||||
<tr class="impl-yes"><td>border-collapse</td><td>ENUM(collapse, seperate)</td></tr>
|
<length>) Also applies to others with explicit height</td></tr>
|
||||||
<tr class="impl-yes"><td>caption-side</td><td>ENUM(top, bottom)</td></tr>
|
</tbody>
|
||||||
<tr class="feature"><td>empty-cells</td><td>ENUM(show, hide), No IE support makes this useless,
|
|
||||||
possible fix with &nbsp;? Unknown release milestone.</td></tr>
|
<tbody>
|
||||||
<tr class="impl-yes"><td>table-layout</td><td>ENUM(auto, fixed)</td></tr>
|
<tr><th colspan="2">Absolute positioning, unknown release milestone</th></tr>
|
||||||
<tr class="impl-yes css1"><td>vertical-align</td><td>COMPOSITE(ENUM(baseline, sub,
|
<tr class="danger"><td>bottom</td><td rowspan="4">Dangerous, must be non-negative</td></tr>
|
||||||
super, top, text-top, middle, bottom, text-bottom), <percentage>,
|
<tr class="danger"><td>left</td></tr>
|
||||||
<length>) Also applies to others with explicit height</td></tr>
|
<tr class="danger"><td>right</td></tr>
|
||||||
</tbody>
|
<tr class="danger"><td>top</td></tr>
|
||||||
|
<tr><td>clip</td><td>-</td></tr>
|
||||||
<tbody>
|
<tr class="danger"><td>position</td><td>ENUM(static, relative, absolute, fixed), permit
|
||||||
<tr><th colspan="2">Absolute positioning, unknown release milestone</th></tr>
|
relative not absolute?</td></tr>
|
||||||
<tr class="danger impl-no"><td>bottom</td><td rowspan="4">Dangerous, must be non-negative to even be considered,
|
<tr class="danger"><td>z-index</td><td>Dangerous</td></tr>
|
||||||
but it's still possible to arbitrarily position by running over.</td></tr>
|
</tbody>
|
||||||
<tr class="danger impl-no"><td>left</td></tr>
|
|
||||||
<tr class="danger impl-no"><td>right</td></tr>
|
<tbody>
|
||||||
<tr class="danger impl-no"><td>top</td></tr>
|
<tr><th colspan="2">Unknown</th></tr>
|
||||||
<tr class="impl-no"><td>clip</td><td>-</td></tr>
|
<tr class="danger css1"><td>background-image</td><td>Dangerous, target milestone 1.2</td></tr>
|
||||||
<tr class="danger impl-no"><td>position</td><td>ENUM(static, relative, absolute, fixed)
|
<tr class="css1"><td>background-attachment</td><td>ENUM(scroll, fixed),
|
||||||
relative not absolute?</td></tr>
|
Depends on background-image</td></tr>
|
||||||
<tr class="danger impl-no"><td>z-index</td><td>Dangerous</td></tr>
|
<tr class="css1"><td>background-position</td><td>Depends on background-image</td></tr>
|
||||||
</tbody>
|
<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;
|
||||||
<tbody>
|
will not implement list-item, run-in (Opera only) or table (no IE);
|
||||||
<tr><th colspan="2">Unknown</th></tr>
|
inline-block has incomplete IE6 support and requires -moz-inline-box
|
||||||
<tr class="danger css1 impl-yes"><td>background-image</td><td>Dangerous, target milestone 1.3</td></tr>
|
for Mozilla. Unknown target milestone.</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>background-attachment</td><td>ENUM(scroll, fixed),
|
<tr><td class="css1">height</td><td>Interesting, why use it? Unknown target milestone.</td></tr>
|
||||||
Depends on background-image</td></tr>
|
<tr class="danger css1"><td>list-style-image</td><td>Dangerous? Target milestone 1.2</td></tr>
|
||||||
<tr class="css1 impl-yes"><td>background-position</td><td>Depends on background-image</td></tr>
|
<tr class="impl-no"><td>max-height</td><td rowspan="4">No IE 5/6</td></tr>
|
||||||
<tr class="danger impl-no"><td>cursor</td><td>Dangerous but fluffy</td></tr>
|
<tr class="impl-no"><td>min-height</td></tr>
|
||||||
<tr class="danger css1"><td>display</td><td>ENUM(...), Dangerous but interesting;
|
<tr class="impl-no"><td>max-width</td></tr>
|
||||||
will not implement list-item, run-in (Opera only) or table (no IE);
|
<tr class="impl-no"><td>min-width</td></tr>
|
||||||
inline-block has incomplete IE6 support and requires -moz-inline-box
|
<tr class="impl-no"><td>orphans</td><td>No IE support</td></tr>
|
||||||
for Mozilla. Unknown target milestone.</td></tr>
|
<tr class="impl-no"><td>widows</td><td>No IE support</td></tr>
|
||||||
<tr class="css1"><td>height</td><td>Interesting, why use it? Unknown target milestone.</td></tr>
|
<tr><td>overflow</td><td>ENUM, IE 5/6 almost (remove visible if set). Unknown target milestone.</td></tr>
|
||||||
<tr class="danger css1 impl-yes"><td>list-style-image</td><td>Dangerous?</td></tr>
|
<tr><td>page-break-after</td><td>ENUM(auto, always, avoid, left, right),
|
||||||
<tr class="impl-no"><td>max-height</td><td rowspan="4">No IE 5/6</td></tr>
|
IE 5.5/6 and Opera. Unknown target milestone.</td></tr>
|
||||||
<tr class="impl-no"><td>min-height</td></tr>
|
<tr><td>page-break-before</td><td>ENUM(auto, always, avoid, left, right),
|
||||||
<tr class="impl-no"><td>max-width</td></tr>
|
Mostly supported. Unknown target milestone.</td></tr>
|
||||||
<tr class="impl-no"><td>min-width</td></tr>
|
<tr><td>page-break-inside</td><td>ENUM(avoid, auto), Opera only. Unknown target milestone.</td></tr>
|
||||||
<tr class="impl-no"><td>orphans</td><td>No IE support</td></tr>
|
<tr class="impl-no"><td>quotes</td><td>May be dropped from CSS2, fairly useless for inline context</td></tr>
|
||||||
<tr class="impl-no"><td>widows</td><td>No IE support</td></tr>
|
<tr class="impl-no"><td>visibility</td><td>ENUM(visible, hidden, collapse),
|
||||||
<tr><td>overflow</td><td>ENUM, IE 5/6 almost (remove visible if set). Unknown target milestone.</td></tr>
|
Dangerous</td></tr>
|
||||||
<tr><td>page-break-after</td><td>ENUM(auto, always, avoid, left, right),
|
<tr class="css1 feature"><td>white-space</td><td>ENUM(normal, pre, nowrap, pre-wrap,
|
||||||
IE 5.5/6 and Opera. Unknown target milestone.</td></tr>
|
pre-line), Spotty implementation:
|
||||||
<tr><td>page-break-before</td><td>ENUM(auto, always, avoid, left, right),
|
pre (no IE 5/6), nowrap (no IE 5),
|
||||||
Mostly supported. Unknown target milestone.</td></tr>
|
pre-wrap (only Opera), pre-line (no support). Fixable? Unknown target milestone.</td></tr>
|
||||||
<tr><td>page-break-inside</td><td>ENUM(avoid, auto), Opera only. Unknown target milestone.</td></tr>
|
</tbody>
|
||||||
<tr class="impl-no"><td>quotes</td><td>May be dropped from CSS2, fairly useless for inline context</td></tr>
|
|
||||||
<tr class="impl-no"><td>visibility</td><td>ENUM(visible, hidden, collapse),
|
<tbody class="impl-no">
|
||||||
Dangerous</td></tr>
|
<tr><th colspan="2">Aural</th></tr>
|
||||||
<tr class="css1 feature"><td>white-space</td><td>ENUM(normal, pre, nowrap, pre-wrap,
|
<tr><td>azimuth</td><td>-</td></tr>
|
||||||
pre-line), Spotty implementation:
|
<tr><td>cue</td><td>-</td></tr>
|
||||||
pre (no IE 5/6), nowrap (no IE 5),
|
<tr><td>cue-after</td><td>-</td></tr>
|
||||||
pre-wrap (only Opera), pre-line (no support). Fixable? Unknown target milestone.</td></tr>
|
<tr><td>cue-before</td><td>-</td></tr>
|
||||||
</tbody>
|
<tr><td>elevation</td><td>-</td></tr>
|
||||||
|
<tr><td>pause-after</td><td>-</td></tr>
|
||||||
<tbody class="impl-no">
|
<tr><td>pause-before</td><td>-</td></tr>
|
||||||
<tr><th colspan="2">Aural</th></tr>
|
<tr><td>pause</td><td>-</td></tr>
|
||||||
<tr><td>azimuth</td><td>-</td></tr>
|
<tr><td>pitch-range</td><td>-</td></tr>
|
||||||
<tr><td>cue</td><td>-</td></tr>
|
<tr><td>pitch</td><td>-</td></tr>
|
||||||
<tr><td>cue-after</td><td>-</td></tr>
|
<tr><td>play-during</td><td>-</td></tr>
|
||||||
<tr><td>cue-before</td><td>-</td></tr>
|
<tr><td>richness</td><td>-</td></tr>
|
||||||
<tr><td>elevation</td><td>-</td></tr>
|
<tr><td>speak-header</td><td>Table related</td></tr>
|
||||||
<tr><td>pause-after</td><td>-</td></tr>
|
<tr><td>speak-numeral</td><td>-</td></tr>
|
||||||
<tr><td>pause-before</td><td>-</td></tr>
|
<tr><td>speak-punctuation</td><td>-</td></tr>
|
||||||
<tr><td>pause</td><td>-</td></tr>
|
<tr><td>speak</td><td>-</td></tr>
|
||||||
<tr><td>pitch-range</td><td>-</td></tr>
|
<tr><td>speech-rate</td><td>-</td></tr>
|
||||||
<tr><td>pitch</td><td>-</td></tr>
|
<tr><td>stress</td><td>-</td></tr>
|
||||||
<tr><td>play-during</td><td>-</td></tr>
|
<tr><td>voice-family</td><td>-</td></tr>
|
||||||
<tr><td>richness</td><td>-</td></tr>
|
<tr><td>volume</td><td>-</td></tr>
|
||||||
<tr><td>speak-header</td><td>Table related</td></tr>
|
</tbody>
|
||||||
<tr><td>speak-numeral</td><td>-</td></tr>
|
|
||||||
<tr><td>speak-punctuation</td><td>-</td></tr>
|
<tbody class="impl-no">
|
||||||
<tr><td>speak</td><td>-</td></tr>
|
<tr><th colspan="2">Will not implement</th></tr>
|
||||||
<tr><td>speech-rate</td><td>-</td></tr>
|
<tr><td>content</td><td>Not applicable for inline styles</td></tr>
|
||||||
<tr><td>stress</td><td>-</td></tr>
|
<tr><td>counter-increment</td><td>Needs content, Opera only</td></tr>
|
||||||
<tr><td>voice-family</td><td>-</td></tr>
|
<tr><td>counter-reset</td><td>Needs content, Opera only</td></tr>
|
||||||
<tr><td>volume</td><td>-</td></tr>
|
<tr><td>direction</td><td>No support</td></tr>
|
||||||
</tbody>
|
<tr><td>outline-color</td><td rowspan="4">IE Mac and Opera on outside,
|
||||||
|
Mozilla on inside and needs -moz-outline, no IE support.</td></tr>
|
||||||
<tbody class="impl-no">
|
<tr><td>outline-style</td></tr>
|
||||||
<tr><th colspan="2">Will not implement</th></tr>
|
<tr><td>outline-width</td></tr>
|
||||||
<tr><td>content</td><td>Not applicable for inline styles</td></tr>
|
<tr><td>outline</td></tr>
|
||||||
<tr><td>counter-increment</td><td>Needs content, Opera only</td></tr>
|
<tr><td>unicode-bidi</td><td>No support</td></tr>
|
||||||
<tr><td>counter-reset</td><td>Needs content, Opera only</td></tr>
|
</tbody>
|
||||||
<tr><td>direction</td><td>No support</td></tr>
|
|
||||||
<tr><td>outline-color</td><td rowspan="4">IE Mac and Opera on outside,
|
</table>
|
||||||
Mozilla on inside and needs -moz-outline, no IE support.</td></tr>
|
|
||||||
<tr><td>outline-style</td></tr>
|
<h2>Interesting Attributes</h2>
|
||||||
<tr><td>outline-width</td></tr>
|
|
||||||
<tr><td>outline</td></tr>
|
<table cellspacing="0">
|
||||||
<tr><td>unicode-bidi</td><td>No support</td></tr>
|
|
||||||
</tbody>
|
<thead>
|
||||||
|
<tr><th>Attribute</th><th>Tags</th><th>Notes</th></tr>
|
||||||
</table>
|
</thead>
|
||||||
|
|
||||||
<h2>Interesting Attributes</h2>
|
<!--
|
||||||
|
<tr><th></th></tr>
|
||||||
<table cellspacing="0">
|
<tbody>
|
||||||
|
<tr><td>-</td><td>-</td><td>-</td></tr>
|
||||||
<thead>
|
</tbody>
|
||||||
<tr><th>Attribute</th><th>Tags</th><th>Notes</th></tr>
|
-->
|
||||||
</thead>
|
|
||||||
|
<tbody>
|
||||||
<!--
|
<tr><th colspan="3">CSS</th></tr>
|
||||||
<tr><th></th></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><td>-</td><td>-</td><td>-</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>
|
||||||
<tbody>
|
<tr class="impl-no"><td>tabindex</td><td>A</td><td>May interfere with main interface</td></tr>
|
||||||
<tr><th colspan="3">CSS</th></tr>
|
<tr><td>target</td><td>A</td><td>Config enabled, only useful for frame layouts</td></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>
|
</tbody>
|
||||||
</tbody>
|
|
||||||
|
<tbody>
|
||||||
<tbody>
|
<tr><th colspan="3">Miscellaneous</th></tr>
|
||||||
<tr><th colspan="3">Questionable</th></tr>
|
<tr><td>datetime</td><td>DEL, INS</td><td>No visible effect, ISO format</td></tr>
|
||||||
<tr class="impl-no"><td>accesskey</td><td>A</td><td>May interfere with main interface</td></tr>
|
<tr><td>rel</td><td>A</td><td>Largely user-defined: nofollow, tag (see microformats)</td></tr>
|
||||||
<tr class="impl-no"><td>tabindex</td><td>A</td><td>May interfere with main interface</td></tr>
|
<tr><td>rev</td><td>A</td><td>Largely user-defined: vote-*</td></tr>
|
||||||
<tr><td>target</td><td>A</td><td>Config enabled, only useful for frame layouts, disallowed in strict</td></tr>
|
<tr class="feature"><td>axis</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
|
||||||
</tbody>
|
<tr class="feature"><td>char</td><td>COL, COLGROUP, TBODY, TD, TFOOT, TH, THEAD, TR</td><td>W3C only: No browser implementation</td></tr>
|
||||||
|
<tr class="feature"><td>headers</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
|
||||||
<tbody>
|
<tr class="feature"><td>scope</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
|
||||||
<tr><th colspan="3">Miscellaneous</th></tr>
|
</tbody>
|
||||||
<tr><td>datetime</td><td>DEL, INS</td><td>No visible effect, ISO format</td></tr>
|
|
||||||
<tr><td>rel</td><td>A</td><td>Largely user-defined: nofollow, tag (see microformats)</td></tr>
|
<tbody class="impl-yes">
|
||||||
<tr><td>rev</td><td>A</td><td>Largely user-defined: vote-*</td></tr>
|
<tr><th colspan="3">URI</th></tr>
|
||||||
<tr class="feature"><td>axis</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
|
<tr><td rowspan="2">cite</td><td>BLOCKQUOTE, Q</td><td>For attribution</td></tr>
|
||||||
<tr class="feature"><td>char</td><td>COL, COLGROUP, TBODY, TD, TFOOT, TH, THEAD, TR</td><td>W3C only: No browser implementation</td></tr>
|
<tr><td>DEL, INS</td><td>Link to explanation why it changed</td></tr>
|
||||||
<tr class="feature"><td>headers</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
|
<tr><td>href</td><td>A</td><td>-</td></tr>
|
||||||
<tr class="feature"><td>scope</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
|
<tr><td>longdesc</td><td>IMG</td><td>-</td></tr>
|
||||||
</tbody>
|
<tr class="required"><td>src</td><td>IMG</td><td>Required</td></tr>
|
||||||
|
</tbody>
|
||||||
<tbody class="impl-yes">
|
|
||||||
<tr><th colspan="3">URI</th></tr>
|
<tbody>
|
||||||
<tr><td rowspan="2">cite</td><td>BLOCKQUOTE, Q</td><td>For attribution</td></tr>
|
<tr><th colspan="3">Transform, target milestone 1.4</th></tr>
|
||||||
<tr><td>DEL, INS</td><td>Link to explanation why it changed</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>href</td><td>A</td><td>-</td></tr>
|
<tr><td>IMG</td><td rowspan="2">Margin-left and margin-right = auto or parent div</td></tr>
|
||||||
<tr><td>longdesc</td><td>IMG</td><td>-</td></tr>
|
<tr><td>TABLE</td></tr>
|
||||||
<tr class="required"><td>src</td><td>IMG</td><td>Required</td></tr>
|
<tr><td>HR</td><td>Equivalent style 'text-align' (IE tested)</td></tr>
|
||||||
</tbody>
|
<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>
|
||||||
<tbody>
|
<tr><td rowspan="3">bgcolor</td><td>TABLE</td><td>Equivalent style 'background-color' (IE tested)</td></tr>
|
||||||
<tr><th colspan="3">Transform, target milestone 1.4</th></tr>
|
<tr><td>TR</td><td>Equivalent style 'background-color' (IE tested)</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>TD, TH</td><td>Equivalent style 'background-color'</td></tr>
|
||||||
<tr><td>IMG</td><td rowspan="2">Margin-left and margin-right = auto or parent div</td></tr>
|
<tr><td>border</td><td>IMG</td><td>Equivalent style 'border-width', only applies when link present</td></tr>
|
||||||
<tr><td>TABLE</td></tr>
|
<tr><td>clear</td><td>BR</td><td>Near-equiv style 'clear', transform 'all' into 'both'</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 class="impl-no"><td>compact</td><td>DL, OL, UL</td><td>Boolean, needs custom CSS class; rarely used anyway</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>dir</td><td>BDO</td><td>Required, insert ltr (or configuration value) if none</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>height</td><td>TD, TH</td><td>Near-equiv style 'height', needs px suffix if original was in pixels</td></tr>
|
||||||
<tr><td rowspan="3">bgcolor</td><td>TABLE</td><td>Equivalent style 'background-color'</td></tr>
|
<tr><td>hspace</td><td>IMG</td><td>Near-equiv styles 'margin-top' and 'margin-bottom', needs px suffix</td></tr>
|
||||||
<tr><td>TR</td><td>Equivalent style 'background-color'</td></tr>
|
<tr class="impl-yes"><td>lang</td><td>*</td><td>Copy value to xml:lang</td></tr>
|
||||||
<tr><td>TD, TH</td><td>Equivalent style 'background-color'</td></tr>
|
<tr><td rowspan="2">name</td><td>IMG</td><td>Turn into ID</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>A</td><td>Turn into ID? (not deprecated, though in which specs?)</td></tr>
|
||||||
<tr><td>clear</td><td>BR</td><td>Near-equiv style 'clear', transform 'all' into 'both'</td></tr>
|
<tr><td>noshade</td><td>HR</td><td>Boolean, style 'border-style:solid;'</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><td>nowrap</td><td>TD, TH</td><td>Boolean, style 'white-space:nowrap;' (not compat with IE5)</td></tr>
|
||||||
<tr class="required impl-yes"><td>dir</td><td>BDO</td><td>Required, insert ltr (or configuration value) if none</td></tr>
|
<tr><td>size</td><td>HR</td><td>Near-equiv 'width', needs px suffix if original was pixels</td></tr>
|
||||||
<tr><td>height</td><td>TD, TH</td><td>Near-equiv style 'height', needs px suffix if original was in 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><td>hspace</td><td>IMG</td><td>Near-equiv styles 'margin-top' and 'margin-bottom', needs px suffix</td></tr>
|
<tr><td>start</td><td>OL</td><td>Poorly supported 'counter-reset', transform may not be desirable</td></tr>
|
||||||
<tr class="impl-yes"><td>lang</td><td>*</td><td>Copy value to xml:lang</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 rowspan="2">name</td><td>IMG</td><td>Turn into ID</td></tr>
|
<tr><td>OL</td></tr>
|
||||||
<tr><td>A</td><td>Turn into ID? (not deprecated, though in which specs?)</td></tr>
|
<tr><td>UL</td></tr>
|
||||||
<tr><td>noshade</td><td>HR</td><td>Boolean, style 'border-style:solid;'</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>nowrap</td><td>TD, TH</td><td>Boolean, style 'white-space:nowrap;' (not compat with IE5)</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>size</td><td>HR</td><td>Near-equiv 'width', needs px suffix if original was pixels</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 class="required impl-yes"><td>src</td><td>IMG</td><td>Required, insert blank or default img if not set</td></tr>
|
<tr><td>TD, TH</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>
|
</tbody>
|
||||||
<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>
|
</table>
|
||||||
<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>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>
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div id="version">$Id$</div>
|
|
||||||
|
|
||||||
</body></html>
|
</body></html>
|
@@ -1,48 +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="Proposal to allow for color constraints in HTML Purifier." />
|
|
||||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
|
||||||
|
|
||||||
<title>Proposal: Colors - HTML Purifier</title>
|
|
||||||
|
|
||||||
</head><body>
|
|
||||||
|
|
||||||
<h1 class="subtitled">Colors</h1>
|
|
||||||
<div class="subtitle">Hammering some sense into those color-blind newbies</div>
|
|
||||||
|
|
||||||
<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>,
|
|
||||||
<span style="color:#A0F; background:#FF0;">purple on yellow</span>,
|
|
||||||
whatever. When you give users the ability to style their content, you may
|
|
||||||
want them to keep in line with your styling. If you're website is all
|
|
||||||
about light colors, you don't want a user to come in and vandalize your
|
|
||||||
page with a deep maroon.</p>
|
|
||||||
|
|
||||||
<p>This is an extremely silly feature proposal, but I'm writing it down anyway.</p>
|
|
||||||
|
|
||||||
<p>What if the user could constrain the colors specified in inline styles? You
|
|
||||||
are only allowed to use these shades of dark green for text and these shades
|
|
||||||
of light yellow for the background. At the very least, you could ensure
|
|
||||||
that we did not have pale yellow on white text.</p>
|
|
||||||
|
|
||||||
<h2>Implementation issues</h2>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>Requires the color attribute definition to know, currently, what the text
|
|
||||||
and background colors are. This becomes difficult when classes are thrown
|
|
||||||
into the mix.</li>
|
|
||||||
<li>The user still has to define the permissible colors, how does one do
|
|
||||||
something like that?</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<div id="version">$Id$</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -1,98 +0,0 @@
|
|||||||
We are going to model our I18N/L10N off of MediaWiki's system. Their's is
|
|
||||||
obviously quite complicated, so we're going to simplify it a bit for our needs.
|
|
||||||
|
|
||||||
== Structure ==
|
|
||||||
|
|
||||||
First, you have a Language object. This object contains all the localisable
|
|
||||||
message strings, as well as other important language-specific settings and
|
|
||||||
custom behavior (uppercasing, lowercasing, printing dates, formatting
|
|
||||||
numbers, etc.)
|
|
||||||
|
|
||||||
The object is constructed from two sources: subclassed versions of itself
|
|
||||||
(classes) and Message files (messages).
|
|
||||||
|
|
||||||
== General use ==
|
|
||||||
|
|
||||||
You load a language object by calling the Language::factory() function.
|
|
||||||
This function the class file for the object (taking in account fallback
|
|
||||||
languages by using the fallback langauge's object but overloading the
|
|
||||||
language key) and returns that object. Nothing else happens.
|
|
||||||
|
|
||||||
When a message/etc is requested, a lazy load initializor is called. Now the
|
|
||||||
real work starts. We're first going to take the scenario that the language
|
|
||||||
is not cached. The system loads the Messages file by:
|
|
||||||
|
|
||||||
require( $filename );
|
|
||||||
$cache = compact( self::$mLocalisationKeys );
|
|
||||||
|
|
||||||
...where self::$mLocalisationKeys is the name of variables that could be used
|
|
||||||
in the localization file. This lets you use things like:
|
|
||||||
|
|
||||||
$fallback = false;
|
|
||||||
$rtl = false;
|
|
||||||
|
|
||||||
...and easily siphon them into arrays.
|
|
||||||
|
|
||||||
Then, we load the $fallback language (if not set, English) to fill in the gaps in
|
|
||||||
the messages. There is specialized behavior for certain keys, as they can be
|
|
||||||
mergeable maps, lists or alias lists (not sure what the last one is).
|
|
||||||
|
|
||||||
== Caching ==
|
|
||||||
|
|
||||||
MediaWiki has lots of caching mechanisms built in, which make the code somewhat
|
|
||||||
more difficult to understand. Before doing any loading, MediaWiki will check
|
|
||||||
the following places to see if we can be lazy:
|
|
||||||
|
|
||||||
1. $mLocalisationCache[$code] - just a variable where it may have been stashed
|
|
||||||
2. serialized/$code.ser - compiled serialized language file
|
|
||||||
3. Memcached version of file (with expiration checking)
|
|
||||||
|
|
||||||
Expiration checking consists of by ensuring all dependencies have filemtime
|
|
||||||
that match the ones bundled with the cached copy. Similar checking could be
|
|
||||||
implemented for serialized versions, as it seems that they are not updated
|
|
||||||
until manually recompiled.
|
|
||||||
|
|
||||||
== Behavior ==
|
|
||||||
|
|
||||||
Things that are localizable:
|
|
||||||
|
|
||||||
- Weekdays (and abbrev)
|
|
||||||
- Months (and abbrev)
|
|
||||||
- Bookstores
|
|
||||||
- Skin names
|
|
||||||
- Date preferences / Custom date format
|
|
||||||
- Default date format
|
|
||||||
- Default user option overrides
|
|
||||||
-+ Language names
|
|
||||||
- Timezones
|
|
||||||
-+ Character encoding conversion via iconv
|
|
||||||
- UpperLowerCase first (needs casemaps for some)
|
|
||||||
- UpperLowerCase
|
|
||||||
- Uppercase words
|
|
||||||
- Uppercase word breaks
|
|
||||||
- Case folding
|
|
||||||
- Strip punctuation for MySQL search
|
|
||||||
- Get first character
|
|
||||||
-+ Alternate encoding
|
|
||||||
-+ Recoding for edit (and then recode input)
|
|
||||||
-+ RTL
|
|
||||||
-+ Direction mark character depending on RTL
|
|
||||||
-? Arrow depending on RTL
|
|
||||||
- Languages where italics cannot be used
|
|
||||||
-+ Number formatting (commafy, transform digits, transform separators)
|
|
||||||
- Truncate (multibyte)
|
|
||||||
- Grammar conversions for inflected languages
|
|
||||||
- Plural transformations
|
|
||||||
- Formatting expiry times
|
|
||||||
- Segmenting for diffs (Chinese)
|
|
||||||
- Convert to variants of language
|
|
||||||
- Language specific user preference options
|
|
||||||
- Link trails [[foo]]bar
|
|
||||||
-+ Language code (RFC 3066)
|
|
||||||
|
|
||||||
Neat functionality:
|
|
||||||
|
|
||||||
- I18N sprintfDate
|
|
||||||
- Roman numeral formatting
|
|
||||||
|
|
||||||
Items marked with a + likely need to be addressed by HTML Purifier
|
|
@@ -1,44 +0,0 @@
|
|||||||
|
|
||||||
Configuration Ideas
|
|
||||||
|
|
||||||
Here are some theoretical configuration ideas that we could implement some
|
|
||||||
time. Note the naming convention: %Namespace.Directive
|
|
||||||
|
|
||||||
%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
|
|
||||||
|
|
||||||
%Attr.ClassBlacklist,
|
|
||||||
%Attr.ClassWhitelist,
|
|
||||||
%Attr.ClassPolicy - determines what classes are allowed. When
|
|
||||||
%Attr.ClassPolicy is set to Blacklist, only allow those not in
|
|
||||||
%Attr.ClassBlacklist. When it's Whitelist, only allow those in
|
|
||||||
%Attr.ClassWhitelist.
|
|
||||||
|
|
||||||
%Attr.MaxWidth,
|
|
||||||
%Attr.MaxHeight - caps for width and height related checks.
|
|
||||||
(the hack in Pixels for an image crashing attack could be replaced by this)
|
|
||||||
|
|
||||||
%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.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
|
|
||||||
or allow all in then do specific blacklists with whitelist intervening.
|
|
||||||
'DenyAll' or 'AllowAll' (default)
|
|
||||||
|
|
||||||
%URI.DisableIPHosts - URIs that have IP addresses for hosts are disallowed.
|
|
||||||
Be sure to also grab unusual encodings (dword, hex and octal), which may
|
|
||||||
be currently be caught by regular DNS
|
|
||||||
%URI.DisableIDN - Disallow raw internationalized domain names. Punycode
|
|
||||||
will still be permitted.
|
|
||||||
|
|
||||||
%URI.ConvertUnusualIPHosts - transform dword/hex/octal IP addresses to the
|
|
||||||
regular form
|
|
||||||
%URI.ConvertAbsoluteDNS - Remove extra dots after host names that trigger
|
|
||||||
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).
|
|
||||||
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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)
|
|
@@ -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/>
|
|
@@ -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:
|
to be effective. Things to remember:
|
||||||
|
|
||||||
1. Character Encoding: UTF-8.
|
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
|
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
|
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
|
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.
|
The doctype is a good place to start.
|
||||||
|
|
||||||
3. IDs
|
3. IDs
|
||||||
This segment is obsoleted by enduser-id.html
|
|
||||||
They need to be unique, but without some knowledge of the
|
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
|
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
|
needs to be set: we may want to consider disallowing IDs by default to
|
@@ -2,8 +2,8 @@
|
|||||||
Is HTML Purifier Strict or Transitional?
|
Is HTML Purifier Strict or Transitional?
|
||||||
A little bit of helpful guidance
|
A little bit of helpful guidance
|
||||||
|
|
||||||
Despite the fact that HTML Purifier professes to support both transitional and
|
Despite the fact that HTML Purifier professes only to support transitional
|
||||||
strict HTML, it rejects a lot of attributes and elements that are actually, indeed,
|
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
|
valid. You can investigate progress.html to find out precisely what we
|
||||||
are doing to these *deprecated* attributes.
|
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
|
restrictions on certain things. The start and value attributes in ol and
|
||||||
li (respectively) perhaps are the most contested. There's is currently no
|
li (respectively) perhaps are the most contested. There's is currently no
|
||||||
widely supported browser method short of JavaScript that can replace these
|
widely supported browser method short of JavaScript that can replace these
|
||||||
two deprecated elements. It behooves us to allow these deprecated
|
two deprecated elements. HTML Purifier does not currently support them, but
|
||||||
attributes when the output is transitional.
|
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
|
Fortunantely, that's the only real bugger case. The others have near-perfect
|
||||||
CSS equivalents, and were presentational anyway. However, the other question
|
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.
|
doesn't solve that problem.
|
||||||
|
|
||||||
It's an icky question, and we'll have to deal with it as more and more
|
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
|
transforms get implemented.
|
||||||
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.
|
|
||||||
|
|
@@ -1,44 +0,0 @@
|
|||||||
html {font-size:1em; font-family:serif; }
|
|
||||||
body {margin-left:4em; margin-right:4em; }
|
|
||||||
|
|
||||||
dt {font-weight:bold; }
|
|
||||||
pre {margin-left:2em; }
|
|
||||||
pre, code, tt {font-family:monospace; font-size:1em; }
|
|
||||||
|
|
||||||
h1 {text-align:center; font-family:Garamond, serif;
|
|
||||||
font-variant:small-caps;}
|
|
||||||
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; }
|
|
||||||
|
|
||||||
/* For witty quips */
|
|
||||||
.subtitled {margin-bottom:0em;}
|
|
||||||
.subtitle , .subsubtitle {font-size:.8em; margin-bottom:1em;
|
|
||||||
font-style:italic; margin-top:-.2em;text-align:center;}
|
|
||||||
.subsubtitle {text-align:left;margin-left:2em;}
|
|
||||||
|
|
||||||
/* Used for special "See also" links. */
|
|
||||||
.reference {font-style:italic;margin-left:2em;}
|
|
||||||
|
|
||||||
/* 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; }
|
|
||||||
.table thead th {margin:0; background:#888; color:#FFF; }
|
|
||||||
.table thead th:first-child {-moz-border-radius-topleft:1em;}
|
|
||||||
.table tbody td {border-bottom:1px solid #CCC; padding-right:0.6em;padding-left:0.6em;}
|
|
||||||
|
|
||||||
/* Category of the file */
|
|
||||||
#filing {font-weight:bold; font-size:smaller; }
|
|
||||||
|
|
||||||
/* 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;}
|
|
@@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -22,7 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
HTML Purifier 1.4.1 - Standards Compliant HTML Filtering
|
HTML Purifier - Standards Compliant HTML Filtering
|
||||||
Copyright (C) 2006 Edward Z. Yang
|
Copyright (C) 2006 Edward Z. Yang
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
@@ -44,7 +44,6 @@
|
|||||||
// they get included
|
// they get included
|
||||||
require_once 'HTMLPurifier/ConfigSchema.php';
|
require_once 'HTMLPurifier/ConfigSchema.php';
|
||||||
require_once 'HTMLPurifier/Config.php';
|
require_once 'HTMLPurifier/Config.php';
|
||||||
require_once 'HTMLPurifier/Context.php';
|
|
||||||
|
|
||||||
require_once 'HTMLPurifier/Lexer.php';
|
require_once 'HTMLPurifier/Lexer.php';
|
||||||
require_once 'HTMLPurifier/Generator.php';
|
require_once 'HTMLPurifier/Generator.php';
|
||||||
@@ -64,107 +63,51 @@ require_once 'HTMLPurifier/Encoder.php';
|
|||||||
class HTMLPurifier
|
class HTMLPurifier
|
||||||
{
|
{
|
||||||
|
|
||||||
var $version = '1.4.1';
|
|
||||||
|
|
||||||
var $config;
|
var $config;
|
||||||
var $filters;
|
|
||||||
|
|
||||||
var $lexer, $strategy, $generator;
|
var $lexer, $strategy, $generator;
|
||||||
|
|
||||||
/**
|
|
||||||
* Final HTMLPurifier_Context of last run purification. Might be an array.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
var $context;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the purifier.
|
* Initializes the purifier.
|
||||||
* @param $config Optional HTMLPurifier_Config object for all instances of
|
* @param $config Optional HTMLPurifier_Config object for all instances of
|
||||||
* the purifier, if omitted, a default configuration is
|
* the purifier, if omitted, a default configuration is
|
||||||
* supplied (which can be overridden on a per-use basis).
|
* 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) {
|
function HTMLPurifier($config = null) {
|
||||||
|
|
||||||
$this->config = HTMLPurifier_Config::create($config);
|
$this->config = $config ? $config : HTMLPurifier_Config::createDefault();
|
||||||
|
|
||||||
$this->lexer = HTMLPurifier_Lexer::create();
|
$this->lexer = HTMLPurifier_Lexer::create();
|
||||||
$this->strategy = new HTMLPurifier_Strategy_Core();
|
$this->strategy = new HTMLPurifier_Strategy_Core();
|
||||||
$this->generator = new HTMLPurifier_Generator();
|
$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.
|
* Filters an HTML snippet/document to be XSS-free and standards-compliant.
|
||||||
*
|
*
|
||||||
* @param $html String of HTML to purify
|
* @param $html String of HTML to purify
|
||||||
* @param $config HTMLPurifier_Config object for this operation, if omitted,
|
* @param $config HTMLPurifier_Config object for this operation, if omitted,
|
||||||
* defaults to the config object specified during this
|
* defaults to the config object specified during this
|
||||||
* object's construction. The parameter can also be any type
|
* object's construction.
|
||||||
* that HTMLPurifier_Config::create() supports.
|
|
||||||
* @return Purified HTML
|
* @return Purified HTML
|
||||||
*/
|
*/
|
||||||
function purify($html, $config = null) {
|
function purify($html, $config = null) {
|
||||||
|
$config = $config ? $config : $this->config;
|
||||||
$config = $config ? HTMLPurifier_Config::create($config) : $this->config;
|
$html = $this->encoder->convertToUTF8($html, $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
|
|
||||||
$html =
|
$html =
|
||||||
$this->generator->generateFromTokens(
|
$this->generator->generateFromTokens(
|
||||||
// list of tokens
|
|
||||||
$this->strategy->execute(
|
$this->strategy->execute(
|
||||||
// list of un-purified tokens
|
$this->lexer->tokenizeHTML($html, $config),
|
||||||
$this->lexer->tokenizeHTML(
|
$config
|
||||||
// un-purified HTML
|
|
||||||
$html, $config, $context
|
|
||||||
),
|
|
||||||
$config, $context
|
|
||||||
),
|
),
|
||||||
$config, $context
|
$config
|
||||||
);
|
);
|
||||||
|
$html = $this->encoder->convertFromUTF8($html, $config);
|
||||||
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;
|
|
||||||
return $html;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
26
library/HTMLPurifier/AttrContext.php
Normal file
26
library/HTMLPurifier/AttrContext.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal data-structure used in attribute validation to accumulate state.
|
||||||
|
*
|
||||||
|
* This is a data-structure that holds objects that accumulate state, like
|
||||||
|
* HTMLPurifier_IDAccumulator. It's better than using globals!
|
||||||
|
*
|
||||||
|
* @note Many functions that accept this object have it as a mandatory
|
||||||
|
* parameter, even when there is no use for it. Though this is
|
||||||
|
* for the same reasons as why HTMLPurifier_Config is a mandatory
|
||||||
|
* parameter, it is also because you cannot assign a default value
|
||||||
|
* to a parameter passed by reference (passing by reference is essential
|
||||||
|
* for context to work in PHP 4).
|
||||||
|
*/
|
||||||
|
|
||||||
|
class HTMLPurifier_AttrContext
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Contains an HTMLPurifier_IDAccumulator, which keeps track of used IDs.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
var $id_accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
require_once 'HTMLPurifier/AttrContext.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all validating attribute definitions.
|
* Base class for all validating attribute definitions.
|
||||||
*
|
*
|
||||||
@@ -20,7 +22,10 @@ class HTMLPurifier_AttrDef
|
|||||||
var $minimized = false;
|
var $minimized = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates and cleans passed string according to a definition.
|
* Abstract function defined for functions that validate and clean strings.
|
||||||
|
*
|
||||||
|
* This function forms the basis for all the subclasses: they must
|
||||||
|
* define this method.
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
* @param $string String to be validated and cleaned.
|
* @param $string String to be validated and cleaned.
|
||||||
|
@@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -8,11 +8,6 @@ require_once 'HTMLPurifier/CSSDefinition.php';
|
|||||||
* @note We don't implement the whole CSS specification, so it might be
|
* @note We don't implement the whole CSS specification, so it might be
|
||||||
* difficult to reuse this component in the context of validating
|
* difficult to reuse this component in the context of validating
|
||||||
* actual stylesheet declarations.
|
* 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
|
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.
|
// we're going to break the spec and explode by semicolons.
|
||||||
// This is because semicolon rarely appears in escaped form
|
// 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);
|
$declarations = explode(';', $css);
|
||||||
$propvalues = array();
|
$propvalues = array();
|
||||||
@@ -51,7 +43,6 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
|
|||||||
$propvalues[$property] = $result;
|
$propvalues[$property] = $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// procedure does not write the new CSS simultaneously, so it's
|
|
||||||
// slightly inefficient, but it's the only way of getting rid of
|
// slightly inefficient, but it's the only way of getting rid of
|
||||||
// duplicates. Perhaps config to optimize it, but not now.
|
// duplicates. Perhaps config to optimize it, but not now.
|
||||||
|
|
||||||
|
@@ -40,7 +40,6 @@ class HTMLPurifier_AttrDef_CSSLength extends HTMLPurifier_AttrDef
|
|||||||
|
|
||||||
// we assume all units are two characters
|
// we assume all units are two characters
|
||||||
$unit = substr($length, $strlen - 2);
|
$unit = substr($length, $strlen - 2);
|
||||||
if (!ctype_lower($unit)) $unit = strtolower($unit);
|
|
||||||
$number = substr($length, 0, $strlen - 2);
|
$number = substr($length, 0, $strlen - 2);
|
||||||
|
|
||||||
if (!isset($this->units[$unit])) return false;
|
if (!isset($this->units[$unit])) return false;
|
||||||
|
@@ -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)";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -24,14 +24,13 @@ class HTMLPurifier_AttrDef_Class extends HTMLPurifier_AttrDef
|
|||||||
// and plus it would complicate optimization efforts (you never
|
// and plus it would complicate optimization efforts (you never
|
||||||
// see that anyway).
|
// see that anyway).
|
||||||
$matches = array();
|
$matches = array();
|
||||||
$pattern = '/(?:(?<=\s)|\A)'. // look behind for space or string start
|
$pattern = '/(?:(?<=\s)|\A)'.
|
||||||
'((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'.
|
'((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'.
|
||||||
'(?:(?=\s)|\z)/'; // look ahead for space or string end
|
'(?:(?=\s)|\z)/';
|
||||||
preg_match_all($pattern, $string, $matches);
|
preg_match_all($pattern, $string, $matches);
|
||||||
|
|
||||||
if (empty($matches[1])) return false;
|
if (empty($matches[1])) return false;
|
||||||
|
|
||||||
// reconstruct class string
|
|
||||||
$new_string = '';
|
$new_string = '';
|
||||||
foreach ($matches[1] as $class_names) {
|
foreach ($matches[1] as $class_names) {
|
||||||
$new_string .= $class_names . ' ';
|
$new_string .= $class_names . ' ';
|
||||||
|
@@ -1,17 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
require_once 'HTMLPurifier/AttrDef.php';
|
|
||||||
|
|
||||||
class HTMLPurifier_AttrDef_Email extends HTMLPurifier_AttrDef
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unpacks a mailbox into its display-name and address
|
|
||||||
*/
|
|
||||||
function unpack($string) {
|
|
||||||
// needs to be implemented
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
require_once 'HTMLPurifier/AttrDef/Email.php';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Primitive email validation class based on the regexp found at
|
|
||||||
* http://www.regular-expressions.info/email.html
|
|
||||||
*/
|
|
||||||
class HTMLPurifier_AttrDef_Email_SimpleCheck extends HTMLPurifier_AttrDef_Email
|
|
||||||
{
|
|
||||||
|
|
||||||
function validate($string, $config, &$context) {
|
|
||||||
// no support for named mailboxes i.e. "Bob <bob@example.com>"
|
|
||||||
// that needs more percent encoding to be done
|
|
||||||
if ($string == '') return false;
|
|
||||||
$string = trim($string);
|
|
||||||
$result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string);
|
|
||||||
return $result ? $string : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -5,7 +5,7 @@ require_once 'HTMLPurifier/AttrDef/IPv4.php';
|
|||||||
require_once 'HTMLPurifier/AttrDef/IPv6.php';
|
require_once 'HTMLPurifier/AttrDef/IPv6.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates a host according to the IPv4, IPv6 and DNS (future) specifications.
|
* Validates a host according to the IPv4, IPv6 and DNS specifications.
|
||||||
*/
|
*/
|
||||||
class HTMLPurifier_AttrDef_Host extends HTMLPurifier_AttrDef
|
class HTMLPurifier_AttrDef_Host extends HTMLPurifier_AttrDef
|
||||||
{
|
{
|
||||||
@@ -35,8 +35,6 @@ class HTMLPurifier_AttrDef_Host extends HTMLPurifier_AttrDef
|
|||||||
if ($valid === false) return false;
|
if ($valid === false) return false;
|
||||||
return '['. $valid . ']';
|
return '['. $valid . ']';
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to do checks on unusual encodings too
|
|
||||||
$ipv4 = $this->ipv4->validate($string, $config, $context);
|
$ipv4 = $this->ipv4->validate($string, $config, $context);
|
||||||
if ($ipv4 !== false) return $ipv4;
|
if ($ipv4 !== false) return $ipv4;
|
||||||
|
|
||||||
|
@@ -3,30 +3,6 @@
|
|||||||
require_once 'HTMLPurifier/AttrDef.php';
|
require_once 'HTMLPurifier/AttrDef.php';
|
||||||
require_once 'HTMLPurifier/IDAccumulator.php';
|
require_once 'HTMLPurifier/IDAccumulator.php';
|
||||||
|
|
||||||
HTMLPurifier_ConfigSchema::define(
|
|
||||||
'Attr', 'IDPrefix', '', 'string',
|
|
||||||
'String to prefix to IDs. If you have no idea what IDs your pages '.
|
|
||||||
'may use, you may opt to simply add a prefix to all user-submitted ID '.
|
|
||||||
'attributes so that they are still usable, but will not conflict with '.
|
|
||||||
'core page IDs. Example: setting the directive to \'user_\' will result in '.
|
|
||||||
'a user submitted \'foo\' to become \'user_foo\' Be sure to set '.
|
|
||||||
'%HTML.EnableAttrID to true before using '.
|
|
||||||
'this. This directive was available since 1.2.0.'
|
|
||||||
);
|
|
||||||
|
|
||||||
HTMLPurifier_ConfigSchema::define(
|
|
||||||
'Attr', 'IDPrefixLocal', '', 'string',
|
|
||||||
'Temporary prefix for IDs used in conjunction with %Attr.IDPrefix. If '.
|
|
||||||
'you need to allow multiple sets of '.
|
|
||||||
'user content on web page, you may need to have a seperate prefix that '.
|
|
||||||
'changes with each iteration. This way, seperately submitted user content '.
|
|
||||||
'displayed on the same page doesn\'t clobber each other. Ideal values '.
|
|
||||||
'are unique identifiers for the content it represents (i.e. the id of '.
|
|
||||||
'the row in the database). Be sure to add a seperator (like an underscore) '.
|
|
||||||
'at the end. Warning: this directive will not work unless %Attr.IDPrefix '.
|
|
||||||
'is set to a non-empty value! This directive was available since 1.2.0.'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the HTML attribute ID.
|
* Validates the HTML attribute ID.
|
||||||
* @warning Even though this is the id processor, it
|
* @warning Even though this is the id processor, it
|
||||||
@@ -44,19 +20,7 @@ class HTMLPurifier_AttrDef_ID extends HTMLPurifier_AttrDef
|
|||||||
$id = trim($id); // trim it first
|
$id = trim($id); // trim it first
|
||||||
|
|
||||||
if ($id === '') return false;
|
if ($id === '') return false;
|
||||||
|
if (isset($context->id_accumulator->ids[$id])) return false;
|
||||||
$prefix = $config->get('Attr', 'IDPrefix');
|
|
||||||
if ($prefix !== '') {
|
|
||||||
$prefix .= $config->get('Attr', 'IDPrefixLocal');
|
|
||||||
// prevent re-appending the prefix
|
|
||||||
if (strpos($id, $prefix) !== 0) $id = $prefix . $id;
|
|
||||||
} elseif ($config->get('Attr', 'IDPrefixLocal') !== '') {
|
|
||||||
trigger_error('%Attr.IDPrefixLocal cannot be used unless '.
|
|
||||||
'%Attr.IDPrefix is set', E_USER_WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
$id_accumulator =& $context->get('IDAccumulator');
|
|
||||||
if (isset($id_accumulator->ids[$id])) return false;
|
|
||||||
|
|
||||||
// we purposely avoid using regex, hopefully this is faster
|
// we purposely avoid using regex, hopefully this is faster
|
||||||
|
|
||||||
@@ -71,7 +35,7 @@ class HTMLPurifier_AttrDef_ID extends HTMLPurifier_AttrDef
|
|||||||
$result = ($trim === '');
|
$result = ($trim === '');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($result) $id_accumulator->add($id);
|
if ($result) $context->id_accumulator->add($id);
|
||||||
|
|
||||||
// if no change was made to the ID, return the result
|
// if no change was made to the ID, return the result
|
||||||
// else, return the new id if stripping whitespace made it
|
// else, return the new id if stripping whitespace made it
|
||||||
|
@@ -49,7 +49,7 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
|
|||||||
if ($length == 0 || $length == 1 || $length > 8 || !ctype_alnum($subtags[1])) {
|
if ($length == 0 || $length == 1 || $length > 8 || !ctype_alnum($subtags[1])) {
|
||||||
return $new_string;
|
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];
|
$new_string .= '-' . $subtags[1];
|
||||||
if ($num_subtags == 2) return $new_string;
|
if ($num_subtags == 2) return $new_string;
|
||||||
@@ -61,7 +61,7 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
|
|||||||
return $new_string;
|
return $new_string;
|
||||||
}
|
}
|
||||||
if (!ctype_lower($subtags[$i])) {
|
if (!ctype_lower($subtags[$i])) {
|
||||||
$subtags[$i] = strtolower($subtags[$i]);
|
$subtags[$i] = strotolower($subtags[$i]);
|
||||||
}
|
}
|
||||||
$new_string .= '-' . $subtags[$i];
|
$new_string .= '-' . $subtags[$i];
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,8 @@ require_once 'HTMLPurifier/AttrDef.php';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates shorthand CSS property list-style.
|
* 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
|
class HTMLPurifier_AttrDef_ListStyle extends HTMLPurifier_AttrDef
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,6 @@ class HTMLPurifier_AttrDef_ListStyle extends HTMLPurifier_AttrDef
|
|||||||
$def = $config->getCSSDefinition();
|
$def = $config->getCSSDefinition();
|
||||||
$this->info['list-style-type'] = $def->info['list-style-type'];
|
$this->info['list-style-type'] = $def->info['list-style-type'];
|
||||||
$this->info['list-style-position'] = $def->info['list-style-position'];
|
$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) {
|
function validate($string, $config, &$context) {
|
||||||
@@ -28,50 +28,48 @@ class HTMLPurifier_AttrDef_ListStyle extends HTMLPurifier_AttrDef
|
|||||||
$string = $this->parseCDATA($string);
|
$string = $this->parseCDATA($string);
|
||||||
if ($string === '') return false;
|
if ($string === '') return false;
|
||||||
|
|
||||||
// assumes URI doesn't have spaces in it
|
|
||||||
$bits = explode(' ', strtolower($string)); // bits to process
|
$bits = explode(' ', strtolower($string)); // bits to process
|
||||||
|
|
||||||
$caught = array();
|
$caught_type = false;
|
||||||
$caught['type'] = false;
|
$caught_position = false;
|
||||||
$caught['position'] = false;
|
$caught_none = false; // as in keyword none, which is in all of them
|
||||||
$caught['image'] = false;
|
|
||||||
|
|
||||||
$i = 0; // number of catches
|
$ret = '';
|
||||||
$none = false;
|
|
||||||
|
|
||||||
foreach ($bits as $bit) {
|
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;
|
if ($bit === '') continue;
|
||||||
foreach ($caught as $key => $status) {
|
|
||||||
if ($status !== false) continue;
|
if ($bit === 'none') {
|
||||||
$r = $this->info['list-style-' . $key]->validate($bit, $config, $context);
|
if ($caught_none) continue;
|
||||||
if ($r === false) continue;
|
$caught_none = true;
|
||||||
if ($r === 'none') {
|
$ret .= 'none ';
|
||||||
if ($none) continue;
|
continue;
|
||||||
else $none = true;
|
}
|
||||||
if ($key == 'image') continue;
|
|
||||||
}
|
// if we add anymore, roll it into a loop
|
||||||
$caught[$key] = $r;
|
|
||||||
$i++;
|
$r = $this->info['list-style-type']->validate($bit, $config, $context);
|
||||||
break;
|
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 = rtrim($ret);
|
||||||
|
return $ret ? $ret : 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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,13 +4,14 @@ require_once 'HTMLPurifier/AttrDef.php';
|
|||||||
require_once 'HTMLPurifier/AttrDef/Number.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
|
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;
|
var $number_def;
|
||||||
|
|
||||||
|
@@ -4,7 +4,6 @@ require_once 'HTMLPurifier/AttrDef.php';
|
|||||||
require_once 'HTMLPurifier/URIScheme.php';
|
require_once 'HTMLPurifier/URIScheme.php';
|
||||||
require_once 'HTMLPurifier/URISchemeRegistry.php';
|
require_once 'HTMLPurifier/URISchemeRegistry.php';
|
||||||
require_once 'HTMLPurifier/AttrDef/Host.php';
|
require_once 'HTMLPurifier/AttrDef/Host.php';
|
||||||
require_once 'HTMLPurifier/PercentEncoder.php';
|
|
||||||
|
|
||||||
HTMLPurifier_ConfigSchema::define(
|
HTMLPurifier_ConfigSchema::define(
|
||||||
'URI', 'DefaultScheme', 'http', 'string',
|
'URI', 'DefaultScheme', 'http', 'string',
|
||||||
@@ -12,71 +11,6 @@ HTMLPurifier_ConfigSchema::define(
|
|||||||
'select the proper object validator when no scheme information is present.'
|
'select the proper object validator when no scheme information is present.'
|
||||||
);
|
);
|
||||||
|
|
||||||
HTMLPurifier_ConfigSchema::define(
|
|
||||||
'URI', 'Host', null, 'string/null',
|
|
||||||
'Defines the domain name of the server, so we can determine whether or '.
|
|
||||||
'an absolute URI is from your website or not. Not strictly necessary, '.
|
|
||||||
'as users should be using relative URIs to reference resources on your '.
|
|
||||||
'website. It will, however, let you use absolute URIs to link to '.
|
|
||||||
'subdomains of the domain you post here: i.e. example.com will allow '.
|
|
||||||
'sub.example.com. However, higher up domains will still be excluded: '.
|
|
||||||
'if you set %URI.Host to sub.example.com, example.com will be blocked. '.
|
|
||||||
'This directive has been available since 1.2.0.'
|
|
||||||
);
|
|
||||||
|
|
||||||
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'.
|
|
||||||
'links or images outside of your domain will be allowed. Non-linkified '.
|
|
||||||
'URIs will still be preserved. If you want to be able to link to '.
|
|
||||||
'subdomains or use absolute URIs, specify %URI.Host for your website. '.
|
|
||||||
'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.
|
* Validates a URI as defined by RFC 3986.
|
||||||
* @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
|
* @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
|
||||||
@@ -85,16 +19,9 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
|
|||||||
{
|
{
|
||||||
|
|
||||||
var $host;
|
var $host;
|
||||||
var $PercentEncoder;
|
|
||||||
var $embeds_resource;
|
|
||||||
|
|
||||||
/**
|
function HTMLPurifier_AttrDef_URI() {
|
||||||
* @param $embeds_resource_resource Does the URI here result in an extra HTTP request?
|
|
||||||
*/
|
|
||||||
function HTMLPurifier_AttrDef_URI($embeds_resource = false) {
|
|
||||||
$this->host = new HTMLPurifier_AttrDef_Host();
|
$this->host = new HTMLPurifier_AttrDef_Host();
|
||||||
$this->PercentEncoder = new HTMLPurifier_PercentEncoder();
|
|
||||||
$this->embeds_resource = (bool) $embeds_resource;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function validate($uri, $config, &$context) {
|
function validate($uri, $config, &$context) {
|
||||||
@@ -105,9 +32,6 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
|
|||||||
// parse as CDATA
|
// parse as CDATA
|
||||||
$uri = $this->parseCDATA($uri);
|
$uri = $this->parseCDATA($uri);
|
||||||
|
|
||||||
// fix up percent-encoding
|
|
||||||
$uri = $this->PercentEncoder->normalize($uri);
|
|
||||||
|
|
||||||
// while it would be nice to use parse_url(), that's specifically
|
// while it would be nice to use parse_url(), that's specifically
|
||||||
// for HTTP and thus won't work for our generic URI parsing
|
// for HTTP and thus won't work for our generic URI parsing
|
||||||
|
|
||||||
@@ -139,38 +63,18 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
|
|||||||
// no need to validate the scheme's fmt since we do that when we
|
// no need to validate the scheme's fmt since we do that when we
|
||||||
// retrieve the specific scheme object from the registry
|
// retrieve the specific scheme object from the registry
|
||||||
$scheme = ctype_lower($scheme) ? $scheme : strtolower($scheme);
|
$scheme = ctype_lower($scheme) ? $scheme : strtolower($scheme);
|
||||||
$scheme_obj = $registry->getScheme($scheme, $config, $context);
|
$scheme_obj =& $registry->getScheme($scheme, $config);
|
||||||
if (!$scheme_obj) return false; // invalid scheme, clean it out
|
if (!$scheme_obj) return false; // invalid scheme, clean it out
|
||||||
} else {
|
} else {
|
||||||
$scheme_obj = $registry->getScheme(
|
$scheme_obj =& $registry->getScheme(
|
||||||
$config->get('URI', 'DefaultScheme'), $config, $context
|
$config->get('URI', 'DefaultScheme'), $config
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// the URI we're processing embeds_resource a resource in the page, but the URI
|
|
||||||
// it references cannot be located
|
|
||||||
if ($this->embeds_resource && !$scheme_obj->browsable) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ($authority !== null) {
|
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
|
|
||||||
unset($our_host);
|
|
||||||
if (
|
|
||||||
$config->get('URI', 'DisableExternal') ||
|
|
||||||
(
|
|
||||||
$config->get('URI', 'DisableExternalResources') &&
|
|
||||||
$this->embeds_resource
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
$our_host = $config->get('URI', 'Host');
|
|
||||||
if ($our_host === null) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$HEXDIG = '[A-Fa-f0-9]';
|
$HEXDIG = '[A-Fa-f0-9]';
|
||||||
$unreserved = 'A-Za-z0-9-._~'; // make sure you wrap with []
|
$unreserved = 'A-Za-z0-9-._~'; // make sure you wrap with []
|
||||||
$sub_delims = '!$&\'()'; // needs []
|
$sub_delims = '!$&\'()'; // needs []
|
||||||
@@ -193,19 +97,6 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
|
|||||||
$host = $this->host->validate($host, $config, $context);
|
$host = $this->host->validate($host, $config, $context);
|
||||||
if ($host === false) $host = null;
|
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));
|
|
||||||
// could be cached
|
|
||||||
$our_host_parts = array_reverse(explode('.', $our_host));
|
|
||||||
foreach ($our_host_parts as $i => $discard) {
|
|
||||||
if (!isset($host_parts[$i])) return false;
|
|
||||||
if ($host_parts[$i] != $our_host_parts[$i]) return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// userinfo and host are validated within the regexp
|
// userinfo and host are validated within the regexp
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -229,7 +120,7 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
|
|||||||
// note that $fragment is omitted
|
// note that $fragment is omitted
|
||||||
list($userinfo, $host, $port, $path, $query) =
|
list($userinfo, $host, $port, $path, $query) =
|
||||||
$scheme_obj->validateComponents(
|
$scheme_obj->validateComponents(
|
||||||
$userinfo, $host, $port, $path, $query, $config, $context
|
$userinfo, $host, $port, $path, $query, $config
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@@ -250,37 +141,10 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
|
|||||||
if ($query !== null) $result .= "?$query";
|
if ($query !== null) $result .= "?$query";
|
||||||
if ($fragment !== null) $result .= "#$fragment";
|
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;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
@@ -21,12 +21,11 @@ class HTMLPurifier_AttrTransform
|
|||||||
* Abstract: makes changes to the attributes dependent on multiple values.
|
* Abstract: makes changes to the attributes dependent on multiple values.
|
||||||
*
|
*
|
||||||
* @param $attr Assoc array of attributes, usually from
|
* @param $attr Assoc array of attributes, usually from
|
||||||
* HTMLPurifier_Token_Tag::$attr
|
* HTMLPurifier_Token_Tag::$attributes
|
||||||
* @param $config Mandatory HTMLPurifier_Config object.
|
* @param $config Mandatory HTMLPurifier_Config object.
|
||||||
* @param $context Mandatory HTMLPurifier_Context object
|
|
||||||
* @returns Processed attribute array.
|
* @returns Processed attribute array.
|
||||||
*/
|
*/
|
||||||
function transform($attr, $config, &$context) {
|
function transform($attr, $config) {
|
||||||
trigger_error('Cannot call abstract function', E_USER_ERROR);
|
trigger_error('Cannot call abstract function', E_USER_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ HTMLPurifier_ConfigSchema::defineAllowedValues(
|
|||||||
class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
|
class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
|
||||||
{
|
{
|
||||||
|
|
||||||
function transform($attr, $config, &$context) {
|
function transform($attr, $config) {
|
||||||
if (isset($attr['dir'])) return $attr;
|
if (isset($attr['dir'])) return $attr;
|
||||||
$attr['dir'] = $config->get('Attr', 'DefaultTextDir');
|
$attr['dir'] = $config->get('Attr', 'DefaultTextDir');
|
||||||
return $attr;
|
return $attr;
|
||||||
|
@@ -25,7 +25,7 @@ HTMLPurifier_ConfigSchema::define(
|
|||||||
class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
|
class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
|
||||||
{
|
{
|
||||||
|
|
||||||
function transform($attr, $config, &$context) {
|
function transform($attr, $config) {
|
||||||
|
|
||||||
$src = true;
|
$src = true;
|
||||||
if (!isset($attr['src'])) {
|
if (!isset($attr['src'])) {
|
||||||
|
@@ -10,7 +10,7 @@ require_once 'HTMLPurifier/AttrTransform.php';
|
|||||||
class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform
|
class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform
|
||||||
{
|
{
|
||||||
|
|
||||||
function transform($attr, $config, &$context) {
|
function transform($attr, $config) {
|
||||||
|
|
||||||
$lang = isset($attr['lang']) ? $attr['lang'] : false;
|
$lang = isset($attr['lang']) ? $attr['lang'] : false;
|
||||||
$xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;
|
$xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;
|
||||||
|
@@ -8,7 +8,7 @@ require_once 'HTMLPurifier/AttrTransform.php';
|
|||||||
class HTMLPurifier_AttrTransform_TextAlign
|
class HTMLPurifier_AttrTransform_TextAlign
|
||||||
extends HTMLPurifier_AttrTransform {
|
extends HTMLPurifier_AttrTransform {
|
||||||
|
|
||||||
function transform($attr, $config, &$context) {
|
function transform($attr, $config) {
|
||||||
|
|
||||||
if (!isset($attr['align'])) return $attr;
|
if (!isset($attr['align'])) return $attr;
|
||||||
|
|
||||||
|
@@ -11,9 +11,6 @@ require_once 'HTMLPurifier/AttrDef/FontFamily.php';
|
|||||||
require_once 'HTMLPurifier/AttrDef/Font.php';
|
require_once 'HTMLPurifier/AttrDef/Font.php';
|
||||||
require_once 'HTMLPurifier/AttrDef/Border.php';
|
require_once 'HTMLPurifier/AttrDef/Border.php';
|
||||||
require_once 'HTMLPurifier/AttrDef/ListStyle.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.
|
* Defines allowed CSS attributes and what their values are.
|
||||||
@@ -54,19 +51,11 @@ class HTMLPurifier_CSSDefinition
|
|||||||
$this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
|
$this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
|
||||||
array('normal', 'small-caps'), false);
|
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(
|
$this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
|
||||||
array('inside', 'outside'), false);
|
array('inside', 'outside'), false);
|
||||||
$this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
|
$this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
|
||||||
array('disc', 'circle', 'square', 'decimal', 'lower-roman',
|
array('disc', 'circle', 'square', 'decimal', 'lower-roman',
|
||||||
'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false);
|
'upper-roman', 'lower-alpha', 'upper-alpha'), false);
|
||||||
$this->info['list-style-image'] = $uri_or_none;
|
|
||||||
|
|
||||||
$this->info['list-style'] = new HTMLPurifier_AttrDef_ListStyle($config);
|
$this->info['list-style'] = new HTMLPurifier_AttrDef_ListStyle($config);
|
||||||
|
|
||||||
@@ -74,14 +63,14 @@ class HTMLPurifier_CSSDefinition
|
|||||||
array('capitalize', 'uppercase', 'lowercase', 'none'), false);
|
array('capitalize', 'uppercase', 'lowercase', 'none'), false);
|
||||||
$this->info['color'] = new HTMLPurifier_AttrDef_Color();
|
$this->info['color'] = new HTMLPurifier_AttrDef_Color();
|
||||||
|
|
||||||
$this->info['background-image'] = $uri_or_none;
|
// technically speaking, this one should get its own validator, but
|
||||||
$this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
|
// since we don't support background images, it effectively is
|
||||||
array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
|
// equivalent to color. The only trouble is that if the author
|
||||||
);
|
// specifies an image and a color, they'll both end up getting dropped,
|
||||||
$this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
|
// even though we ought to implement it and just discard the image
|
||||||
array('scroll', 'fixed')
|
// info. This will be fixed in a later version (see TODO) when
|
||||||
);
|
// better URI filtering is implemented.
|
||||||
$this->info['background-position'] = new HTMLPurifier_AttrDef_BackgroundPosition();
|
$this->info['background'] =
|
||||||
|
|
||||||
$border_color =
|
$border_color =
|
||||||
$this->info['border-top-color'] =
|
$this->info['border-top-color'] =
|
||||||
@@ -93,8 +82,6 @@ class HTMLPurifier_CSSDefinition
|
|||||||
new HTMLPurifier_AttrDef_Color()
|
new HTMLPurifier_AttrDef_Color()
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->info['background'] = new HTMLPurifier_AttrDef_Background($config);
|
|
||||||
|
|
||||||
$this->info['border-color'] = new HTMLPurifier_AttrDef_Multiple($border_color);
|
$this->info['border-color'] = new HTMLPurifier_AttrDef_Multiple($border_color);
|
||||||
|
|
||||||
$border_width =
|
$border_width =
|
||||||
|
@@ -20,9 +20,10 @@ HTMLPurifier_ConfigSchema::define(
|
|||||||
class HTMLPurifier_ChildDef
|
class HTMLPurifier_ChildDef
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Type of child definition, usually right-most part of class name lowercase.
|
* Type of child definition, usually right-most part of class name lowercase
|
||||||
* Used occasionally in terms of context.
|
*
|
||||||
* @public
|
* Used occasionally in terms of context. Possible values include
|
||||||
|
* custom, required, optional and empty.
|
||||||
*/
|
*/
|
||||||
var $type;
|
var $type;
|
||||||
|
|
||||||
@@ -31,25 +32,409 @@ class HTMLPurifier_ChildDef
|
|||||||
*
|
*
|
||||||
* This is necessary for redundant checking when changes affecting
|
* This is necessary for redundant checking when changes affecting
|
||||||
* a child node may cause a parent node to now be disallowed.
|
* a child node may cause a parent node to now be disallowed.
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
var $allow_empty;
|
var $allow_empty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates nodes according to definition and returns modification.
|
* Validates nodes according to definition and returns modification.
|
||||||
*
|
*
|
||||||
* @public
|
* @warning $context is NOT HTMLPurifier_AttrContext
|
||||||
* @param $tokens_of_children Array of HTMLPurifier_Token
|
* @param $tokens_of_children Array of HTMLPurifier_Token
|
||||||
* @param $config HTMLPurifier_Config object
|
* @param $config HTMLPurifier_Config object
|
||||||
* @param $context HTMLPurifier_Context object
|
* @param $context String context indicating inline, block or unknown
|
||||||
* @return bool true to leave nodes as is
|
* @return bool true to leave nodes as is
|
||||||
* @return bool false to remove parent node
|
* @return bool false to remove parent node
|
||||||
* @return array of replacement child tokens
|
* @return array of replacement child tokens
|
||||||
*/
|
*/
|
||||||
function validateChildren($tokens_of_children, $config, &$context) {
|
function validateChildren($tokens_of_children, $config, $context) {
|
||||||
trigger_error('Call to abstract function', E_USER_ERROR);
|
trigger_error('Call to abstract function', E_USER_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
switch ($context) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -44,29 +44,11 @@ class HTMLPurifier_Config
|
|||||||
$this->def = $definition; // keep a copy around for checking
|
$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.
|
* Convenience constructor that creates a default configuration object.
|
||||||
* @static
|
|
||||||
* @return Default HTMLPurifier_Config object.
|
* @return Default HTMLPurifier_Config object.
|
||||||
*/
|
*/
|
||||||
static function createDefault() {
|
function createDefault() {
|
||||||
$definition =& HTMLPurifier_ConfigSchema::instance();
|
$definition =& HTMLPurifier_ConfigSchema::instance();
|
||||||
$config = new HTMLPurifier_Config($definition);
|
$config = new HTMLPurifier_Config($definition);
|
||||||
return $config;
|
return $config;
|
||||||
@@ -77,60 +59,29 @@ class HTMLPurifier_Config
|
|||||||
* @param $namespace String namespace
|
* @param $namespace String namespace
|
||||||
* @param $key String key
|
* @param $key String key
|
||||||
*/
|
*/
|
||||||
function get($namespace, $key, $from_alias = false) {
|
function get($namespace, $key) {
|
||||||
if (!isset($this->def->info[$namespace][$key])) {
|
if (!isset($this->conf[$namespace][$key])) {
|
||||||
trigger_error('Cannot retrieve value of undefined directive',
|
trigger_error('Cannot retrieve value of undefined directive',
|
||||||
E_USER_WARNING);
|
E_USER_WARNING);
|
||||||
return;
|
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];
|
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.
|
* Sets a value to configuration.
|
||||||
* @param $namespace String namespace
|
* @param $namespace String namespace
|
||||||
* @param $key String key
|
* @param $key String key
|
||||||
* @param $value Mixed value
|
* @param $value Mixed value
|
||||||
*/
|
*/
|
||||||
function set($namespace, $key, $value, $from_alias = false) {
|
function set($namespace, $key, $value) {
|
||||||
if (!isset($this->def->info[$namespace][$key])) {
|
if (!isset($this->conf[$namespace][$key])) {
|
||||||
trigger_error('Cannot set undefined directive to value',
|
trigger_error('Cannot set undefined directive to value',
|
||||||
E_USER_WARNING);
|
E_USER_WARNING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($this->def->info[$namespace][$key]->class == 'alias') {
|
$value = $this->def->validate($value,
|
||||||
if ($from_alias) {
|
$this->def->info[$namespace][$key]->type);
|
||||||
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,
|
|
||||||
$this->def->info[$namespace][$key]->allow_null
|
|
||||||
);
|
|
||||||
if (is_string($value)) {
|
if (is_string($value)) {
|
||||||
// resolve value alias if defined
|
// resolve value alias if defined
|
||||||
if (isset($this->def->info[$namespace][$key]->aliases[$value])) {
|
if (isset($this->def->info[$namespace][$key]->aliases[$value])) {
|
||||||
@@ -144,7 +95,7 @@ class HTMLPurifier_Config
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->def->isError($value)) {
|
if ($value === null) {
|
||||||
trigger_error('Value is of invalid type', E_USER_WARNING);
|
trigger_error('Value is of invalid type', E_USER_WARNING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -173,37 +124,6 @@ class HTMLPurifier_Config
|
|||||||
return $this->css_definition;
|
return $this->css_definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads configuration values from an array with the following structure:
|
|
||||||
* Namespace.Directive => Value
|
|
||||||
* @param $config_array Configuration associative array
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
$this->set($namespace, $directive, $value);
|
|
||||||
} else {
|
|
||||||
$namespace = $key;
|
|
||||||
$namespace_values = $value;
|
|
||||||
foreach ($namespace_values as $directive => $value) {
|
|
||||||
$this->set($namespace, $directive, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
@@ -1,7 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once 'HTMLPurifier/Error.php';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration definition, defines directives and their defaults.
|
* Configuration definition, defines directives and their defaults.
|
||||||
* @todo The ability to define things multiple times is confusing and should
|
* @todo The ability to define things multiple times is confusing and should
|
||||||
@@ -67,9 +65,8 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves an instance of the application-wide configuration definition.
|
* Retrieves an instance of the application-wide configuration definition.
|
||||||
* @static
|
|
||||||
*/
|
*/
|
||||||
static function &instance($prototype = null) {
|
function &instance($prototype = null) {
|
||||||
static $instance;
|
static $instance;
|
||||||
if ($prototype !== null) {
|
if ($prototype !== null) {
|
||||||
$instance = $prototype;
|
$instance = $prototype;
|
||||||
@@ -82,7 +79,6 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a directive for configuration
|
* Defines a directive for configuration
|
||||||
* @static
|
|
||||||
* @warning Will fail of directive's namespace is defined
|
* @warning Will fail of directive's namespace is defined
|
||||||
* @param $namespace Namespace the directive is in
|
* @param $namespace Namespace the directive is in
|
||||||
* @param $name Key of directive
|
* @param $name Key of directive
|
||||||
@@ -91,7 +87,7 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
* HTMLPurifier_DirectiveDef::$type for allowed values
|
* HTMLPurifier_DirectiveDef::$type for allowed values
|
||||||
* @param $description Description of directive for documentation
|
* @param $description Description of directive for documentation
|
||||||
*/
|
*/
|
||||||
static function define(
|
function define(
|
||||||
$namespace, $name, $default, $type,
|
$namespace, $name, $default, $type,
|
||||||
$description
|
$description
|
||||||
) {
|
) {
|
||||||
@@ -106,11 +102,6 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
E_USER_ERROR);
|
E_USER_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (empty($description)) {
|
|
||||||
trigger_error('Description must be non-empty',
|
|
||||||
E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isset($def->info[$namespace][$name])) {
|
if (isset($def->info[$namespace][$name])) {
|
||||||
if (
|
if (
|
||||||
$def->info[$namespace][$name]->type !== $type ||
|
$def->info[$namespace][$name]->type !== $type ||
|
||||||
@@ -120,19 +111,12 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// process modifiers
|
|
||||||
$type_values = explode('/', $type, 2);
|
|
||||||
$type = $type_values[0];
|
|
||||||
$modifier = isset($type_values[1]) ? $type_values[1] : false;
|
|
||||||
$allow_null = ($modifier === 'null');
|
|
||||||
|
|
||||||
if (!isset($def->types[$type])) {
|
if (!isset($def->types[$type])) {
|
||||||
trigger_error('Invalid type for configuration directive',
|
trigger_error('Invalid type for configuration directive',
|
||||||
E_USER_ERROR);
|
E_USER_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$default = $def->validate($default, $type, $allow_null);
|
if ($def->validate($default, $type) === null) {
|
||||||
if ($def->isError($default)) {
|
|
||||||
trigger_error('Default value does not match directive type',
|
trigger_error('Default value does not match directive type',
|
||||||
E_USER_ERROR);
|
E_USER_ERROR);
|
||||||
return;
|
return;
|
||||||
@@ -140,7 +124,6 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
$def->info[$namespace][$name] =
|
$def->info[$namespace][$name] =
|
||||||
new HTMLPurifier_ConfigEntity_Directive();
|
new HTMLPurifier_ConfigEntity_Directive();
|
||||||
$def->info[$namespace][$name]->type = $type;
|
$def->info[$namespace][$name]->type = $type;
|
||||||
$def->info[$namespace][$name]->allow_null = $allow_null;
|
|
||||||
$def->defaults[$namespace][$name] = $default;
|
$def->defaults[$namespace][$name] = $default;
|
||||||
}
|
}
|
||||||
$backtrace = debug_backtrace();
|
$backtrace = debug_backtrace();
|
||||||
@@ -151,11 +134,10 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a namespace for directives to be put into.
|
* Defines a namespace for directives to be put into.
|
||||||
* @static
|
|
||||||
* @param $namespace Namespace's name
|
* @param $namespace Namespace's name
|
||||||
* @param $description Description of the namespace
|
* @param $description Description of the namespace
|
||||||
*/
|
*/
|
||||||
static function defineNamespace($namespace, $description) {
|
function defineNamespace($namespace, $description) {
|
||||||
$def =& HTMLPurifier_ConfigSchema::instance();
|
$def =& HTMLPurifier_ConfigSchema::instance();
|
||||||
if (isset($def->info[$namespace])) {
|
if (isset($def->info[$namespace])) {
|
||||||
trigger_error('Cannot redefine namespace', E_USER_ERROR);
|
trigger_error('Cannot redefine namespace', E_USER_ERROR);
|
||||||
@@ -166,11 +148,6 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
E_USER_ERROR);
|
E_USER_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (empty($description)) {
|
|
||||||
trigger_error('Description must be non-empty',
|
|
||||||
E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$def->info[$namespace] = array();
|
$def->info[$namespace] = array();
|
||||||
$def->info_namespace[$namespace] = new HTMLPurifier_ConfigEntity_Namespace();
|
$def->info_namespace[$namespace] = new HTMLPurifier_ConfigEntity_Namespace();
|
||||||
$def->info_namespace[$namespace]->description = $description;
|
$def->info_namespace[$namespace]->description = $description;
|
||||||
@@ -182,13 +159,12 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
*
|
*
|
||||||
* Directive value aliases are convenient for developers because it lets
|
* Directive value aliases are convenient for developers because it lets
|
||||||
* them set a directive to several values and get the same result.
|
* them set a directive to several values and get the same result.
|
||||||
* @static
|
|
||||||
* @param $namespace Directive's namespace
|
* @param $namespace Directive's namespace
|
||||||
* @param $name Name of Directive
|
* @param $name Name of Directive
|
||||||
* @param $alias Name of aliased value
|
* @param $alias Name of aliased value
|
||||||
* @param $real Value aliased value will be converted into
|
* @param $real Value aliased value will be converted into
|
||||||
*/
|
*/
|
||||||
static function defineValueAliases($namespace, $name, $aliases) {
|
function defineValueAliases($namespace, $name, $aliases) {
|
||||||
$def =& HTMLPurifier_ConfigSchema::instance();
|
$def =& HTMLPurifier_ConfigSchema::instance();
|
||||||
if (!isset($def->info[$namespace][$name])) {
|
if (!isset($def->info[$namespace][$name])) {
|
||||||
trigger_error('Cannot set value alias for non-existant directive',
|
trigger_error('Cannot set value alias for non-existant directive',
|
||||||
@@ -214,129 +190,58 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a set of allowed values for a directive.
|
* Defines a set of allowed values for a directive.
|
||||||
* @static
|
|
||||||
* @param $namespace Namespace of directive
|
* @param $namespace Namespace of directive
|
||||||
* @param $name Name of directive
|
* @param $name Name of directive
|
||||||
* @param $allowed_values Arraylist of allowed values
|
* @param $allowed_values Arraylist of allowed values
|
||||||
*/
|
*/
|
||||||
static function defineAllowedValues($namespace, $name, $allowed_values) {
|
function defineAllowedValues($namespace, $name, $allowed_values) {
|
||||||
$def =& HTMLPurifier_ConfigSchema::instance();
|
$def =& HTMLPurifier_ConfigSchema::instance();
|
||||||
if (!isset($def->info[$namespace][$name])) {
|
if (!isset($def->info[$namespace][$name])) {
|
||||||
trigger_error('Cannot define allowed values for undefined directive',
|
trigger_error('Cannot define allowed values for undefined directive',
|
||||||
E_USER_ERROR);
|
E_USER_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$directive =& $def->info[$namespace][$name];
|
if ($def->info[$namespace][$name]->allowed === true) {
|
||||||
$type = $directive->type;
|
$def->info[$namespace][$name]->allowed = array();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
foreach ($allowed_values as $value) {
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate a variable according to type. Return null if invalid.
|
* Validate a variable according to type. Return null if invalid.
|
||||||
*/
|
*/
|
||||||
function validate($var, $type, $allow_null = false) {
|
function validate($var, $type) {
|
||||||
if (!isset($this->types[$type])) {
|
if (!isset($this->types[$type])) {
|
||||||
trigger_error('Invalid type', E_USER_ERROR);
|
trigger_error('Invalid type', E_USER_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($allow_null && $var === null) return null;
|
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'mixed':
|
case 'mixed':
|
||||||
return $var;
|
return $var;
|
||||||
case 'istring':
|
case 'istring':
|
||||||
case 'string':
|
case 'string':
|
||||||
if (!is_string($var)) break;
|
if (!is_string($var)) return;
|
||||||
if ($type === 'istring') $var = strtolower($var);
|
if ($type === 'istring') $var = strtolower($var);
|
||||||
return $var;
|
return $var;
|
||||||
case 'int':
|
case 'int':
|
||||||
if (is_string($var) && ctype_digit($var)) $var = (int) $var;
|
if (is_string($var) && ctype_digit($var)) $var = (int) $var;
|
||||||
elseif (!is_int($var)) break;
|
elseif (!is_int($var)) return;
|
||||||
return $var;
|
return $var;
|
||||||
case 'float':
|
case 'float':
|
||||||
if (is_string($var) && is_numeric($var)) $var = (float) $var;
|
if (is_string($var) && is_numeric($var)) $var = (float) $var;
|
||||||
elseif (!is_float($var)) break;
|
elseif (!is_float($var)) return;
|
||||||
return $var;
|
return $var;
|
||||||
case 'bool':
|
case 'bool':
|
||||||
if (is_int($var) && ($var === 0 || $var === 1)) {
|
if (is_int($var) && ($var === 0 || $var === 1)) {
|
||||||
$var = (bool) $var;
|
$var = (bool) $var;
|
||||||
} elseif (is_string($var)) {
|
} elseif (!is_bool($var)) return;
|
||||||
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;
|
return $var;
|
||||||
case 'list':
|
case 'list':
|
||||||
case 'hash':
|
case 'hash':
|
||||||
case 'lookup':
|
case 'lookup':
|
||||||
if (is_string($var)) {
|
if (!is_array($var)) return;
|
||||||
// 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);
|
$keys = array_keys($var);
|
||||||
if ($keys === array_keys($keys)) {
|
if ($keys === array_keys($keys)) {
|
||||||
if ($type == 'list') return $var;
|
if ($type == 'list') return $var;
|
||||||
@@ -346,7 +251,7 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
$new[$key] = true;
|
$new[$key] = true;
|
||||||
}
|
}
|
||||||
return $new;
|
return $new;
|
||||||
} else break;
|
} else return;
|
||||||
}
|
}
|
||||||
if ($type === 'lookup') {
|
if ($type === 'lookup') {
|
||||||
foreach ($var as $key => $value) {
|
foreach ($var as $key => $value) {
|
||||||
@@ -355,13 +260,8 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
}
|
}
|
||||||
return $var;
|
return $var;
|
||||||
}
|
}
|
||||||
$error = new HTMLPurifier_Error();
|
|
||||||
return $error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes an absolute path and munges it into a more manageable relative path
|
|
||||||
*/
|
|
||||||
function mungeFilename($filename) {
|
function mungeFilename($filename) {
|
||||||
$offset = strrpos($filename, 'HTMLPurifier');
|
$offset = strrpos($filename, 'HTMLPurifier');
|
||||||
$filename = substr($filename, $offset);
|
$filename = substr($filename, $offset);
|
||||||
@@ -369,34 +269,18 @@ class HTMLPurifier_ConfigSchema {
|
|||||||
return $filename;
|
return $filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if var is an HTMLPurifier_Error object
|
|
||||||
*/
|
|
||||||
function isError($var) {
|
|
||||||
if (!is_object($var)) return false;
|
|
||||||
if (!($var instanceof HTMLPurifier_Error)) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for configuration entity
|
* Base class for configuration entity
|
||||||
*/
|
*/
|
||||||
class HTMLPurifier_ConfigEntity {
|
class HTMLPurifier_ConfigEntity {}
|
||||||
var $class = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structure object describing of a namespace
|
* Structure object describing of a namespace
|
||||||
*/
|
*/
|
||||||
class HTMLPurifier_ConfigEntity_Namespace extends HTMLPurifier_ConfigEntity {
|
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.
|
* String description of what kinds of directives go in this namespace.
|
||||||
*/
|
*/
|
||||||
@@ -411,21 +295,15 @@ class HTMLPurifier_ConfigEntity_Namespace extends HTMLPurifier_ConfigEntity {
|
|||||||
class HTMLPurifier_ConfigEntity_Directive 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,
|
* Lookup table of allowed values of the element, bool true if all allowed.
|
||||||
$descriptions = null,
|
*/
|
||||||
$allow_null = null,
|
var $allowed = true;
|
||||||
$allowed = null,
|
|
||||||
$aliases = null
|
|
||||||
) {
|
|
||||||
if ( $type !== null) $this->type = $type;
|
|
||||||
if ($descriptions !== null) $this->descriptions = $descriptions;
|
|
||||||
if ( $allow_null !== null) $this->allow_null = $allow_null;
|
|
||||||
if ( $allowed !== null) $this->allowed = $allowed;
|
|
||||||
if ( $aliases !== null) $this->aliases = $aliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allowed type of the directive. Values are:
|
* Allowed type of the directive. Values are:
|
||||||
@@ -440,29 +318,12 @@ class HTMLPurifier_ConfigEntity_Directive extends HTMLPurifier_ConfigEntity
|
|||||||
* - mixed (anything goes)
|
* - mixed (anything goes)
|
||||||
*/
|
*/
|
||||||
var $type = 'mixed';
|
var $type = 'mixed';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plaintext descriptions of the configuration entity is. Organized by
|
* Plaintext descriptions of the configuration entity is. Organized by
|
||||||
* file and line number, so multiple descriptions are allowed.
|
* file and line number, so multiple descriptions are allowed.
|
||||||
*/
|
*/
|
||||||
var $descriptions = array();
|
var $descriptions = array();
|
||||||
|
|
||||||
/**
|
|
||||||
* Is null allowed? Has no effect for mixed type.
|
|
||||||
* @bool
|
|
||||||
*/
|
|
||||||
var $allow_null = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup table of allowed values of the element, bool true if all allowed.
|
|
||||||
*/
|
|
||||||
var $allowed = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash of value aliases, i.e. values that are equivalent.
|
|
||||||
*/
|
|
||||||
var $aliases = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a description to the array
|
* Adds a description to the array
|
||||||
*/
|
*/
|
||||||
@@ -473,26 +334,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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -1,76 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registry object that contains information about the current context.
|
|
||||||
*/
|
|
||||||
class HTMLPurifier_Context
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private array that stores the references.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
var $_storage = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a variable into the context.
|
|
||||||
* @param $name String name
|
|
||||||
* @param $ref Variable to be registered
|
|
||||||
*/
|
|
||||||
function register($name, &$ref) {
|
|
||||||
if (isset($this->_storage[$name])) {
|
|
||||||
trigger_error('Name collision, cannot re-register',
|
|
||||||
E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->_storage[$name] =& $ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a variable reference from the context.
|
|
||||||
* @param $name String name
|
|
||||||
*/
|
|
||||||
function &get($name) {
|
|
||||||
if (!isset($this->_storage[$name])) {
|
|
||||||
trigger_error('Attempted to retrieve non-existent variable',
|
|
||||||
E_USER_ERROR);
|
|
||||||
$var = null; // so we can return by reference
|
|
||||||
return $var;
|
|
||||||
}
|
|
||||||
return $this->_storage[$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destorys a variable in the context.
|
|
||||||
* @param $name String name
|
|
||||||
*/
|
|
||||||
function destroy($name) {
|
|
||||||
if (!isset($this->_storage[$name])) {
|
|
||||||
trigger_error('Attempted to destroy non-existent variable',
|
|
||||||
E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unset($this->_storage[$name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether or not the variable exists.
|
|
||||||
* @param $name String name
|
|
||||||
*/
|
|
||||||
function exists($name) {
|
|
||||||
return isset($this->_storage[$name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a series of variables from an associative array
|
|
||||||
* @param $context_array Assoc array of variables to load
|
|
||||||
*/
|
|
||||||
function loadArray(&$context_array) {
|
|
||||||
foreach ($context_array as $key => $discard) {
|
|
||||||
$this->register($key, $context_array[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -6,29 +6,15 @@ HTMLPurifier_ConfigSchema::define(
|
|||||||
'Core', 'Encoding', 'utf-8', 'istring',
|
'Core', 'Encoding', 'utf-8', 'istring',
|
||||||
'If for some reason you are unable to convert all webpages to UTF-8, '.
|
'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 '.
|
'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 '.
|
'notable deficiencies: absolutely no characters outside of the selected '.
|
||||||
'character encoding will be preserved, not even the ones that have '.
|
'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> '.
|
'been ampersand escaped (this is due to a UTF-8 specific <em>feature</em> '.
|
||||||
'that automatically resolves all entities), making it pretty useless '.
|
'that automatically resolves all entities), making it pretty useless '.
|
||||||
'for anything except the most I18N-blind applications, although '.
|
'for anything except the most I18N-blind applications. This directive '.
|
||||||
'%Core.EscapeNonASCIICharacters offers fixes this trouble with '.
|
|
||||||
'another tradeoff. This directive '.
|
|
||||||
'only accepts ISO-8859-1 if iconv is not enabled.'
|
'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') ) {
|
if ( !function_exists('iconv') ) {
|
||||||
// only encodings with native PHP support
|
// only encodings with native PHP support
|
||||||
HTMLPurifier_ConfigSchema::defineAllowedValues(
|
HTMLPurifier_ConfigSchema::defineAllowedValues(
|
||||||
@@ -52,25 +38,16 @@ HTMLPurifier_ConfigSchema::define(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A UTF-8 specific character encoder that handles cleaning and transforming.
|
* A UTF-8 specific character encoder that handles cleaning and transforming.
|
||||||
* @note All functions in this class should be static.
|
|
||||||
*/
|
*/
|
||||||
class HTMLPurifier_Encoder
|
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
|
* 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
|
* It will parse according to UTF-8 and return a valid UTF8 string, with
|
||||||
* non-SGML codepoints excluded.
|
* non-SGML codepoints excluded.
|
||||||
*
|
*
|
||||||
* @static
|
|
||||||
* @note Just for reference, the non-SGML code points are 0 to 31 and
|
* @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
|
* 127 to 159, inclusive. However, we allow code points 9, 10
|
||||||
* and 13, which are the tab, line feed and carriage return
|
* 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.
|
* would need that, and I'm probably not going to implement them.
|
||||||
* Once again, PHP 6 should solve all our problems.
|
* 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();
|
static $non_sgml_chars = array();
|
||||||
if (empty($non_sgml_chars)) {
|
if (empty($non_sgml_chars)) {
|
||||||
@@ -248,32 +225,8 @@ class HTMLPurifier_Encoder
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates a Unicode codepoint into its corresponding UTF-8 character.
|
* 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.
|
|
||||||
* @note While we're going to do code point parsing anyway, a good
|
|
||||||
* optimization would be to refuse to translate code points that
|
|
||||||
* are non-SGML characters. However, this could lead to duplication.
|
|
||||||
* @note This is very similar to the unichr function in
|
|
||||||
* maintenance/generate-entity-file.php (although this is superior,
|
|
||||||
* due to its sanity checks).
|
|
||||||
*/
|
*/
|
||||||
|
function unichr($code) {
|
||||||
// +----------+----------+----------+----------+
|
|
||||||
// | 33222222 | 22221111 | 111111 | |
|
|
||||||
// | 10987654 | 32109876 | 54321098 | 76543210 | bit
|
|
||||||
// +----------+----------+----------+----------+
|
|
||||||
// | | | | 0xxxxxxx | 1 byte 0x00000000..0x0000007F
|
|
||||||
// | | | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF
|
|
||||||
// | | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF
|
|
||||||
// | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF
|
|
||||||
// +----------+----------+----------+----------+
|
|
||||||
// | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF)
|
|
||||||
// | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes
|
|
||||||
// +----------+----------+----------+----------+
|
|
||||||
|
|
||||||
static function unichr($code) {
|
|
||||||
if($code > 1114111 or $code < 0 or
|
if($code > 1114111 or $code < 0 or
|
||||||
($code >= 55296 and $code <= 57343) ) {
|
($code >= 55296 and $code <= 57343) ) {
|
||||||
// bits are set outside the "valid" range as defined
|
// bits are set outside the "valid" range as defined
|
||||||
@@ -312,9 +265,8 @@ class HTMLPurifier_Encoder
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a string to UTF-8 based on configuration.
|
* Converts a string to UTF-8 based on configuration.
|
||||||
* @static
|
|
||||||
*/
|
*/
|
||||||
static function convertToUTF8($str, $config, &$context) {
|
function convertToUTF8($str, $config) {
|
||||||
static $iconv = null;
|
static $iconv = null;
|
||||||
if ($iconv === null) $iconv = function_exists('iconv');
|
if ($iconv === null) $iconv = function_exists('iconv');
|
||||||
$encoding = $config->get('Core', 'Encoding');
|
$encoding = $config->get('Core', 'Encoding');
|
||||||
@@ -324,77 +276,23 @@ class HTMLPurifier_Encoder
|
|||||||
} elseif ($encoding === 'iso-8859-1') {
|
} elseif ($encoding === 'iso-8859-1') {
|
||||||
return @utf8_encode($str);
|
return @utf8_encode($str);
|
||||||
}
|
}
|
||||||
trigger_error('Encoding not supported', E_USER_ERROR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a string from UTF-8 based on configuration.
|
* Converts a string from UTF-8 based on configuration.
|
||||||
* @static
|
|
||||||
* @note Currently, this is a lossy conversion, with unexpressable
|
* @note Currently, this is a lossy conversion, with unexpressable
|
||||||
* characters being omitted.
|
* characters being omitted.
|
||||||
*/
|
*/
|
||||||
static function convertFromUTF8($str, $config, &$context) {
|
function convertFromUTF8($str, $config) {
|
||||||
static $iconv = null;
|
static $iconv = null;
|
||||||
if ($iconv === null) $iconv = function_exists('iconv');
|
if ($iconv === null) $iconv = function_exists('iconv');
|
||||||
$encoding = $config->get('Core', 'Encoding');
|
$encoding = $config->get('Core', 'Encoding');
|
||||||
if ($encoding === 'utf-8') return $str;
|
if ($encoding === 'utf-8') return $str;
|
||||||
if ($config->get('Core', 'EscapeNonASCIICharacters')) {
|
|
||||||
$str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str);
|
|
||||||
}
|
|
||||||
if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
|
if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
|
||||||
return @iconv('utf-8', $encoding . '//IGNORE', $str);
|
return @iconv('utf-8', $encoding . '//IGNORE', $str);
|
||||||
} elseif ($encoding === 'iso-8859-1') {
|
} elseif ($encoding === 'iso-8859-1') {
|
||||||
return @utf8_decode($str);
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -19,17 +19,16 @@ class HTMLPurifier_EntityLookup {
|
|||||||
*/
|
*/
|
||||||
function setup($file = false) {
|
function setup($file = false) {
|
||||||
if (!$file) {
|
if (!$file) {
|
||||||
$file = dirname(__FILE__) . '/EntityLookup/entities.ser';
|
$file = dirname(__FILE__) . '/EntityLookup/data.txt';
|
||||||
}
|
}
|
||||||
$this->table = unserialize(file_get_contents($file));
|
$this->table = unserialize(file_get_contents($file));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves sole instance of the object.
|
* Retrieves sole instance of the object.
|
||||||
* @static
|
|
||||||
* @param Optional prototype of custom lookup table to overload with.
|
* @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
|
// no references, since PHP doesn't copy unless modified
|
||||||
static $instance = null;
|
static $instance = null;
|
||||||
if ($prototype) {
|
if ($prototype) {
|
||||||
|
@@ -3,10 +3,6 @@
|
|||||||
require_once 'HTMLPurifier/EntityLookup.php';
|
require_once 'HTMLPurifier/EntityLookup.php';
|
||||||
require_once 'HTMLPurifier/Encoder.php';
|
require_once 'HTMLPurifier/Encoder.php';
|
||||||
|
|
||||||
// if want to implement error collecting here, we'll need to use some sort
|
|
||||||
// of global data (probably trigger_error) because it's impossible to pass
|
|
||||||
// $config or $context to the callback functions.
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles referencing and derefencing character entities
|
* Handles referencing and derefencing character entities
|
||||||
*/
|
*/
|
||||||
@@ -76,12 +72,37 @@ class HTMLPurifier_EntityParser
|
|||||||
*
|
*
|
||||||
* @warning Though this is public in order to let the callback happen,
|
* @warning Though this is public in order to let the callback happen,
|
||||||
* calling it directly is not recommended.
|
* calling it directly is not recommended.
|
||||||
|
* @note Based on Feyd's function at
|
||||||
|
* <http://forums.devnetwork.net/viewtopic.php?p=191404#191404>,
|
||||||
|
* which is in public domain.
|
||||||
|
* @note While we're going to do code point parsing anyway, a good
|
||||||
|
* optimization would be to refuse to translate code points that
|
||||||
|
* are non-SGML characters. However, this could lead to duplication.
|
||||||
|
* @note This function is heavily intimate with the inner workings of
|
||||||
|
* UTF-8 and would also be well suited in the Encoder class (or at
|
||||||
|
* least deferring some processing to it). This is also very
|
||||||
|
* similar to the unichr function in
|
||||||
|
* maintenance/generate-entity-file.php (although this is superior,
|
||||||
|
* due to its sanity checks).
|
||||||
* @param $matches PCRE matches array, with 0 the entire match, and
|
* @param $matches PCRE matches array, with 0 the entire match, and
|
||||||
* either index 1, 2 or 3 set with a hex value, dec value,
|
* either index 1, 2 or 3 set with a hex value, dec value,
|
||||||
* or string (respectively).
|
* or string (respectively).
|
||||||
* @returns Replacement string.
|
* @returns Replacement string.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// +----------+----------+----------+----------+
|
||||||
|
// | 33222222 | 22221111 | 111111 | |
|
||||||
|
// | 10987654 | 32109876 | 54321098 | 76543210 | bit
|
||||||
|
// +----------+----------+----------+----------+
|
||||||
|
// | | | | 0xxxxxxx | 1 byte 0x00000000..0x0000007F
|
||||||
|
// | | | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF
|
||||||
|
// | | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF
|
||||||
|
// | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF
|
||||||
|
// +----------+----------+----------+----------+
|
||||||
|
// | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF)
|
||||||
|
// | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes
|
||||||
|
// +----------+----------+----------+----------+
|
||||||
|
|
||||||
function nonSpecialEntityCallback($matches) {
|
function nonSpecialEntityCallback($matches) {
|
||||||
// replaces all but big five
|
// replaces all but big five
|
||||||
$entity = $matches[0];
|
$entity = $matches[0];
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return object from functions that signifies error when null doesn't cut it
|
|
||||||
*/
|
|
||||||
class HTMLPurifier_Error {}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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) {}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
// pretty-printing with indentation would be pretty cool
|
||||||
|
|
||||||
require_once 'HTMLPurifier/Lexer.php';
|
require_once 'HTMLPurifier/Lexer.php';
|
||||||
|
|
||||||
HTMLPurifier_ConfigSchema::define(
|
HTMLPurifier_ConfigSchema::define(
|
||||||
@@ -50,7 +52,6 @@ class HTMLPurifier_Generator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Bool cache of %Core.XHTML
|
* Bool cache of %Core.XHTML
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
var $_xhtml = true;
|
var $_xhtml = true;
|
||||||
|
|
||||||
@@ -59,8 +60,9 @@ class HTMLPurifier_Generator
|
|||||||
* @param $tokens Array of HTMLPurifier_Token
|
* @param $tokens Array of HTMLPurifier_Token
|
||||||
* @param $config HTMLPurifier_Config object
|
* @param $config HTMLPurifier_Config object
|
||||||
* @return Generated HTML
|
* @return Generated HTML
|
||||||
|
* @note Only unit tests may omit configuration: internals MUST pass config
|
||||||
*/
|
*/
|
||||||
function generateFromTokens($tokens, $config, &$context) {
|
function generateFromTokens($tokens, $config = null) {
|
||||||
$html = '';
|
$html = '';
|
||||||
if (!$config) $config = HTMLPurifier_Config::createDefault();
|
if (!$config) $config = HTMLPurifier_Config::createDefault();
|
||||||
$this->_clean_utf8 = $config->get('Core', 'CleanUTF8DuringGeneration');
|
$this->_clean_utf8 = $config->get('Core', 'CleanUTF8DuringGeneration');
|
||||||
@@ -104,14 +106,14 @@ class HTMLPurifier_Generator
|
|||||||
function generateFromToken($token) {
|
function generateFromToken($token) {
|
||||||
if (!isset($token->type)) return '';
|
if (!isset($token->type)) return '';
|
||||||
if ($token->type == 'start') {
|
if ($token->type == 'start') {
|
||||||
$attr = $this->generateAttributes($token->attr);
|
$attr = $this->generateAttributes($token->attributes);
|
||||||
return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
|
return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
|
||||||
|
|
||||||
} elseif ($token->type == 'end') {
|
} elseif ($token->type == 'end') {
|
||||||
return '</' . $token->name . '>';
|
return '</' . $token->name . '>';
|
||||||
|
|
||||||
} elseif ($token->type == 'empty') {
|
} elseif ($token->type == 'empty') {
|
||||||
$attr = $this->generateAttributes($token->attr);
|
$attr = $this->generateAttributes($token->attributes);
|
||||||
return '<' . $token->name . ($attr ? ' ' : '') . $attr .
|
return '<' . $token->name . ($attr ? ' ' : '') . $attr .
|
||||||
( $this->_xhtml ? ' /': '' )
|
( $this->_xhtml ? ' /': '' )
|
||||||
. '>';
|
. '>';
|
||||||
|
@@ -18,86 +18,10 @@ require_once 'HTMLPurifier/AttrTransform.php';
|
|||||||
require_once 'HTMLPurifier/AttrTransform/BdoDir.php';
|
require_once 'HTMLPurifier/AttrTransform/BdoDir.php';
|
||||||
require_once 'HTMLPurifier/AttrTransform/ImgRequired.php';
|
require_once 'HTMLPurifier/AttrTransform/ImgRequired.php';
|
||||||
require_once 'HTMLPurifier/ChildDef.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/Generator.php';
|
||||||
require_once 'HTMLPurifier/Token.php';
|
require_once 'HTMLPurifier/Token.php';
|
||||||
require_once 'HTMLPurifier/TagTransform.php';
|
require_once 'HTMLPurifier/TagTransform.php';
|
||||||
|
|
||||||
HTMLPurifier_ConfigSchema::define(
|
|
||||||
'HTML', 'EnableAttrID', false, 'bool',
|
|
||||||
'Allows the ID attribute in HTML. This is disabled by default '.
|
|
||||||
'due to the fact that without proper configuration user input can '.
|
|
||||||
'easily break the validation of a webpage by specifying an ID that is '.
|
|
||||||
'already on the surrounding HTML. If you don\'t mind throwing caution to '.
|
|
||||||
'the wind, enable this directive, but I strongly recommend you also '.
|
|
||||||
'consider blacklisting IDs you use (%Attr.IDBlacklist) or prefixing all '.
|
|
||||||
'user supplied IDs (%Attr.IDPrefix). This directive has been available '.
|
|
||||||
'since 1.2.0, and when set to true reverts to the behavior of pre-1.2.0 '.
|
|
||||||
'versions.'
|
|
||||||
);
|
|
||||||
|
|
||||||
HTMLPurifier_ConfigSchema::define(
|
|
||||||
'HTML', 'Strict', false, 'bool',
|
|
||||||
'Determines whether or not to use Transitional (loose) or Strict rulesets. '.
|
|
||||||
'This directive has been available since 1.3.0.'
|
|
||||||
);
|
|
||||||
|
|
||||||
HTMLPurifier_ConfigSchema::define(
|
|
||||||
'HTML', 'BlockWrapper', 'p', 'string',
|
|
||||||
'String name of element to wrap inline elements that are inside a block '.
|
|
||||||
'context. This only occurs in the children of blockquote in strict mode. '.
|
|
||||||
'Example: by default value, <code><blockquote>Foo</blockquote></code> '.
|
|
||||||
'would become <code><blockquote><p>Foo</p></blockquote></code>. The '.
|
|
||||||
'<code><p></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.
|
* Defines the purified HTML type with large amounts of objects.
|
||||||
*
|
*
|
||||||
@@ -132,24 +56,11 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* String name of parent element HTML will be going into.
|
* String name of parent element HTML will be going into.
|
||||||
|
* @todo Allow this to be overloaded by user config
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
var $info_parent = 'div';
|
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
|
* Associative array of deprecated tag name to HTMLPurifier_TagTransform
|
||||||
* @public
|
* @public
|
||||||
@@ -168,25 +79,14 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
*/
|
*/
|
||||||
var $info_attr_transform_post = array();
|
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.
|
* Initializes the definition, the meat of the class.
|
||||||
*/
|
*/
|
||||||
function setup($config) {
|
function setup($config) {
|
||||||
|
|
||||||
// some cached config values
|
// emulates the structure of the DTD
|
||||||
$this->strict = $config->get('HTML', 'Strict');
|
// these are condensed, however, with bad stuff taken out
|
||||||
|
// screening process was done by hand
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// info[] : initializes the definition objects
|
// info[] : initializes the definition objects
|
||||||
@@ -198,19 +98,13 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
array(
|
array(
|
||||||
'ins', 'del', 'blockquote', 'dd', 'li', 'div', 'em', 'strong',
|
'ins', 'del', 'blockquote', 'dd', 'li', 'div', 'em', 'strong',
|
||||||
'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym',
|
'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym',
|
||||||
'q', 'sub', 'tt', 'sup', 'i', 'b', 'big', 'small',
|
'q', 'sub', 'tt', 'sup', 'i', 'b', 'big', 'small', 'u', 's',
|
||||||
'bdo', 'span', 'dt', 'p', 'h1', 'h2', 'h3', 'h4',
|
'strike', 'bdo', 'span', 'dt', 'p', 'h1', 'h2', 'h3', 'h4',
|
||||||
'h5', 'h6', 'ol', 'ul', 'dl', 'address', 'img', 'br', 'hr',
|
'h5', 'h6', 'ol', 'ul', 'dl', 'address', 'img', 'br', 'hr',
|
||||||
'pre', 'a', 'table', 'caption', 'thead', 'tfoot', 'tbody',
|
'pre', 'a', 'table', 'caption', 'thead', 'tfoot', 'tbody',
|
||||||
'colgroup', 'col', 'td', 'th', 'tr'
|
'colgroup', 'col', 'td', 'th', 'tr'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!$this->strict) {
|
|
||||||
$allowed_tags[] = 'u';
|
|
||||||
$allowed_tags[] = 's';
|
|
||||||
$allowed_tags[] = 'strike';
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($allowed_tags as $tag) {
|
foreach ($allowed_tags as $tag) {
|
||||||
$this->info[$tag] = new HTMLPurifier_ElementDef();
|
$this->info[$tag] = new HTMLPurifier_ElementDef();
|
||||||
}
|
}
|
||||||
@@ -218,10 +112,6 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// info[]->child : defines allowed children for elements
|
// 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
|
// entities: prefixed with e_ and _ replaces . from DTD
|
||||||
// double underlines are entities we made up
|
// double underlines are entities we made up
|
||||||
|
|
||||||
@@ -245,9 +135,11 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
$e_phrase_basic = 'em | strong | dfn | code | q | samp | kbd | var'.
|
$e_phrase_basic = 'em | strong | dfn | code | q | samp | kbd | var'.
|
||||||
' | cite | abbr | acronym';
|
' | cite | abbr | acronym';
|
||||||
$e_phrase = "$e_phrase_basic | $e_phrase_extra";
|
$e_phrase = "$e_phrase_basic | $e_phrase_extra";
|
||||||
|
$e_inline_forms = ''; // humor the dtd
|
||||||
$e_misc_inline = 'ins | del';
|
$e_misc_inline = 'ins | del';
|
||||||
$e_misc = "$e_misc_inline";
|
$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
|
// pseudo-property we created for convenience, see later on
|
||||||
$e__inline = "#PCDATA | $e_inline | $e_misc_inline";
|
$e__inline = "#PCDATA | $e_inline | $e_misc_inline";
|
||||||
// note the casing
|
// note the casing
|
||||||
@@ -256,14 +148,14 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
$e_lists = 'ul | ol | dl';
|
$e_lists = 'ul | ol | dl';
|
||||||
$e_blocktext = 'pre | hr | blockquote | address';
|
$e_blocktext = 'pre | hr | blockquote | address';
|
||||||
$e_block = "p | $e_heading | div | $e_lists | $e_blocktext | table";
|
$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 = "#PCDATA | $e_block | $e_inline | $e_misc";
|
||||||
$e_Flow = new HTMLPurifier_ChildDef_Optional($e__flow);
|
$e_Flow = new HTMLPurifier_ChildDef_Optional($e__flow);
|
||||||
$e_a_content = new HTMLPurifier_ChildDef_Optional("#PCDATA".
|
$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_pre_content = new HTMLPurifier_ChildDef_Optional("#PCDATA | a".
|
||||||
" | $e_special_basic | $e_fontstyle_basic | $e_phrase_basic".
|
" | $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_content = new HTMLPurifier_ChildDef_Optional('');//unused
|
||||||
$e_form_button_content = new HTMLPurifier_ChildDef_Optional('');//unused
|
$e_form_button_content = new HTMLPurifier_ChildDef_Optional('');//unused
|
||||||
|
|
||||||
@@ -271,16 +163,11 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
$this->info['del']->child =
|
$this->info['del']->child =
|
||||||
new HTMLPurifier_ChildDef_Chameleon($e__inline, $e__flow);
|
new HTMLPurifier_ChildDef_Chameleon($e__inline, $e__flow);
|
||||||
|
|
||||||
|
$this->info['blockquote']->child=
|
||||||
$this->info['dd']->child =
|
$this->info['dd']->child =
|
||||||
$this->info['li']->child =
|
$this->info['li']->child =
|
||||||
$this->info['div']->child = $e_Flow;
|
$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['caption']->child =
|
||||||
$this->info['em']->child =
|
$this->info['em']->child =
|
||||||
$this->info['strong']->child =
|
$this->info['strong']->child =
|
||||||
@@ -300,6 +187,9 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
$this->info['b']->child =
|
$this->info['b']->child =
|
||||||
$this->info['big']->child =
|
$this->info['big']->child =
|
||||||
$this->info['small']->child=
|
$this->info['small']->child=
|
||||||
|
$this->info['u']->child =
|
||||||
|
$this->info['s']->child =
|
||||||
|
$this->info['strike']->child =
|
||||||
$this->info['bdo']->child =
|
$this->info['bdo']->child =
|
||||||
$this->info['span']->child =
|
$this->info['span']->child =
|
||||||
$this->info['dt']->child =
|
$this->info['dt']->child =
|
||||||
@@ -311,25 +201,15 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
$this->info['h5']->child =
|
$this->info['h5']->child =
|
||||||
$this->info['h6']->child = $e_Inline;
|
$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
|
// the only three required definitions, besides custom table code
|
||||||
$this->info['ol']->child =
|
$this->info['ol']->child =
|
||||||
$this->info['ul']->child = new HTMLPurifier_ChildDef_Required('li');
|
$this->info['ul']->child = new HTMLPurifier_ChildDef_Required('li');
|
||||||
|
|
||||||
$this->info['dl']->child = new HTMLPurifier_ChildDef_Required('dt|dd');
|
$this->info['dl']->child = new HTMLPurifier_ChildDef_Required('dt|dd');
|
||||||
|
|
||||||
if ($this->strict) {
|
$this->info['address']->child =
|
||||||
$this->info['address']->child = $e_Inline;
|
new HTMLPurifier_ChildDef_Optional("#PCDATA | p | $e_inline".
|
||||||
} else {
|
" | $e_misc_inline");
|
||||||
$this->info['address']->child =
|
|
||||||
new HTMLPurifier_ChildDef_Optional("#PCDATA | p | $e_inline".
|
|
||||||
" | $e_misc_inline");
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info['img']->child =
|
$this->info['img']->child =
|
||||||
$this->info['br']->child =
|
$this->info['br']->child =
|
||||||
@@ -356,21 +236,16 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
// info[]->type : defines the type of the element (block or inline)
|
// info[]->type : defines the type of the element (block or inline)
|
||||||
|
|
||||||
// reuses $e_Inline and $e_Block
|
// reuses $e_Inline and $e_Block
|
||||||
foreach ($e_Inline->elements as $name => $bool) {
|
|
||||||
if ($name == '#PCDATA') continue;
|
foreach ($e_Inline->elements as $name) {
|
||||||
if (!isset($this->info[$name])) continue;
|
|
||||||
$this->info[$name]->type = 'inline';
|
$this->info[$name]->type = 'inline';
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($e_Block->elements as $name => $bool) {
|
$e_Block = new HTMLPurifier_ChildDef_Optional($e_block);
|
||||||
if (!isset($this->info[$name])) continue;
|
foreach ($e_Block->elements as $name) {
|
||||||
$this->info[$name]->type = 'block';
|
$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
|
// info[]->excludes : defines elements that aren't allowed in here
|
||||||
|
|
||||||
@@ -396,6 +271,7 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
// which manually override these in their local definitions
|
// which manually override these in their local definitions
|
||||||
$this->info_global_attr = array(
|
$this->info_global_attr = array(
|
||||||
// core attrs
|
// core attrs
|
||||||
|
'id' => new HTMLPurifier_AttrDef_ID(),
|
||||||
'class' => new HTMLPurifier_AttrDef_Class(),
|
'class' => new HTMLPurifier_AttrDef_Class(),
|
||||||
'title' => $e_Text,
|
'title' => $e_Text,
|
||||||
'style' => new HTMLPurifier_AttrDef_CSS(),
|
'style' => new HTMLPurifier_AttrDef_CSS(),
|
||||||
@@ -405,10 +281,6 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
'xml:lang' => new HTMLPurifier_AttrDef_Lang(),
|
'xml:lang' => new HTMLPurifier_AttrDef_Lang(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($config->get('HTML', 'EnableAttrID')) {
|
|
||||||
$this->info_global_attr['id'] = new HTMLPurifier_AttrDef_ID();
|
|
||||||
}
|
|
||||||
|
|
||||||
// required attribute stipulation handled in attribute transformation
|
// required attribute stipulation handled in attribute transformation
|
||||||
$this->info['bdo']->attr = array(); // nothing else
|
$this->info['bdo']->attr = array(); // nothing else
|
||||||
|
|
||||||
@@ -460,23 +332,14 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
$this->info['td']->attr['colspan'] =
|
$this->info['td']->attr['colspan'] =
|
||||||
$this->info['th']->attr['colspan'] = $e__NumberSpan;
|
$this->info['th']->attr['colspan'] = $e__NumberSpan;
|
||||||
|
|
||||||
if (!$config->get('Attr', 'DisableURI')) {
|
$e_URI = new HTMLPurifier_AttrDef_URI();
|
||||||
$e_URI = new HTMLPurifier_AttrDef_URI();
|
$this->info['a']->attr['href'] =
|
||||||
$this->info['a']->attr['href'] =
|
$this->info['img']->attr['longdesc'] =
|
||||||
$this->info['img']->attr['longdesc'] =
|
$this->info['img']->attr['src'] =
|
||||||
$this->info['del']->attr['cite'] =
|
$this->info['del']->attr['cite'] =
|
||||||
$this->info['ins']->attr['cite'] =
|
$this->info['ins']->attr['cite'] =
|
||||||
$this->info['blockquote']->attr['cite'] =
|
$this->info['blockquote']->attr['cite'] =
|
||||||
$this->info['q']->attr['cite'] = $e_URI;
|
$this->info['q']->attr['cite'] = $e_URI;
|
||||||
|
|
||||||
// URI that causes HTTP request
|
|
||||||
$this->info['img']->attr['src'] = new HTMLPurifier_AttrDef_URI(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->strict) {
|
|
||||||
$this->info['li']->attr['value'] = new HTMLPurifier_AttrDef_Integer();
|
|
||||||
$this->info['ol']->attr['start'] = new HTMLPurifier_AttrDef_Integer();
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// info_tag_transform : transformations of tags
|
// info_tag_transform : transformations of tags
|
||||||
@@ -534,60 +397,6 @@ class HTMLPurifier_HTMLDefinition
|
|||||||
|
|
||||||
$this->info_attr_transform_post[] = new HTMLPurifier_AttrTransform_Lang();
|
$this->info_attr_transform_post[] = new HTMLPurifier_AttrTransform_Lang();
|
||||||
|
|
||||||
// protect against stdclasses floating around
|
|
||||||
foreach ($this->info as $key => $obj) {
|
|
||||||
if ($obj instanceof stdClass) {
|
|
||||||
unset($this->info[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// info_block_wrapper : wraps inline elements in block context
|
|
||||||
|
|
||||||
$block_wrapper = $config->get('HTML', 'BlockWrapper');
|
|
||||||
if (isset($e_Block->elements[$block_wrapper])) {
|
|
||||||
$this->info_block_wrapper = $block_wrapper;
|
|
||||||
} else {
|
|
||||||
trigger_error('Cannot use non-block element as block wrapper.',
|
|
||||||
E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// info_parent : parent element of the HTML fragment
|
|
||||||
|
|
||||||
$parent = $config->get('HTML', 'Parent');
|
|
||||||
if (isset($this->info[$parent])) {
|
|
||||||
$this->info_parent = $parent;
|
|
||||||
} else {
|
|
||||||
trigger_error('Cannot use unrecognized element as parent.',
|
|
||||||
E_USER_ERROR);
|
|
||||||
}
|
|
||||||
$this->info_parent_def = $this->info[$this->info_parent];
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// %HTML.Allowed(Elements|Attributes) : cut non-allowed elements
|
|
||||||
|
|
||||||
$allowed_elements = $config->get('HTML', 'AllowedElements');
|
|
||||||
if (is_array($allowed_elements)) {
|
|
||||||
foreach ($this->info as $name => $d) {
|
|
||||||
if(!isset($allowed_elements[$name])) unset($this->info[$name]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$allowed_attributes = $config->get('HTML', 'AllowedAttributes');
|
|
||||||
if (is_array($allowed_attributes)) {
|
|
||||||
foreach ($this->info_global_attr as $attr_key => $info) {
|
|
||||||
if (!isset($allowed_attributes["*.$attr_key"])) {
|
|
||||||
unset($this->info_global_attr[$attr_key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($this->info as $tag => $info) {
|
|
||||||
foreach ($info->attr as $attr => $attr_info) {
|
|
||||||
if (!isset($allowed_attributes["$tag.$attr"])) {
|
|
||||||
unset($this->info[$tag]->attr[$attr]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAttrForTableElements($attr, $def) {
|
function setAttrForTableElements($attr, $def) {
|
||||||
@@ -653,4 +462,4 @@ class HTMLPurifier_ElementDef
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
@@ -3,9 +3,6 @@
|
|||||||
/**
|
/**
|
||||||
* Component of HTMLPurifier_AttrContext that accumulates IDs to prevent dupes
|
* Component of HTMLPurifier_AttrContext that accumulates IDs to prevent dupes
|
||||||
* @note In Slashdot-speak, dupe means duplicate.
|
* @note In Slashdot-speak, dupe means duplicate.
|
||||||
* @note This class does not accept $config or $context, thus, it is the
|
|
||||||
* burden of the callee to register the appropriate errors or
|
|
||||||
* configuration.
|
|
||||||
*/
|
*/
|
||||||
class HTMLPurifier_IDAccumulator
|
class HTMLPurifier_IDAccumulator
|
||||||
{
|
{
|
||||||
|
@@ -56,6 +56,7 @@ class HTMLPurifier_Lexer
|
|||||||
{
|
{
|
||||||
|
|
||||||
function HTMLPurifier_Lexer() {
|
function HTMLPurifier_Lexer() {
|
||||||
|
$this->_encoder = new HTMLPurifier_Encoder();
|
||||||
$this->_entity_parser = new HTMLPurifier_EntityParser();
|
$this->_entity_parser = new HTMLPurifier_EntityParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,13 +114,15 @@ class HTMLPurifier_Lexer
|
|||||||
return $string;
|
return $string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var $_encoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lexes an HTML string into tokens.
|
* Lexes an HTML string into tokens.
|
||||||
*
|
*
|
||||||
* @param $string String HTML.
|
* @param $string String HTML.
|
||||||
* @return HTMLPurifier_Token array representation of HTML.
|
* @return HTMLPurifier_Token array representation of HTML.
|
||||||
*/
|
*/
|
||||||
function tokenizeHTML($string, $config, &$context) {
|
function tokenizeHTML($string, $config = null) {
|
||||||
trigger_error('Call to abstract class', E_USER_ERROR);
|
trigger_error('Call to abstract class', E_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,8 +138,6 @@ class HTMLPurifier_Lexer
|
|||||||
* default with your own implementation. A copy/reference of the prototype
|
* default with your own implementation. A copy/reference of the prototype
|
||||||
* lexer will now be returned when you request a new lexer.
|
* lexer will now be returned when you request a new lexer.
|
||||||
*
|
*
|
||||||
* @static
|
|
||||||
*
|
|
||||||
* @note
|
* @note
|
||||||
* Though it is possible to call this factory method from subclasses,
|
* Though it is possible to call this factory method from subclasses,
|
||||||
* such usage is not recommended.
|
* such usage is not recommended.
|
||||||
@@ -144,14 +145,14 @@ class HTMLPurifier_Lexer
|
|||||||
* @param $prototype Optional prototype lexer.
|
* @param $prototype Optional prototype lexer.
|
||||||
* @return Concrete 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
|
// we don't really care if it's a reference or a copy
|
||||||
static $lexer = null;
|
static $lexer = null;
|
||||||
if ($prototype) {
|
if ($prototype) {
|
||||||
$lexer = $prototype;
|
$lexer = $prototype;
|
||||||
}
|
}
|
||||||
if (empty($lexer)) {
|
if (empty($lexer)) {
|
||||||
if (class_exists('DOMDocument')) { // check for DOM support
|
if (version_compare(PHP_VERSION, '5', '>=')) {
|
||||||
require_once 'HTMLPurifier/Lexer/DOMLex.php';
|
require_once 'HTMLPurifier/Lexer/DOMLex.php';
|
||||||
$lexer = new HTMLPurifier_Lexer_DOMLex();
|
$lexer = new HTMLPurifier_Lexer_DOMLex();
|
||||||
} else {
|
} else {
|
||||||
@@ -165,12 +166,11 @@ class HTMLPurifier_Lexer
|
|||||||
/**
|
/**
|
||||||
* Translates CDATA sections into regular sections (through escaping).
|
* Translates CDATA sections into regular sections (through escaping).
|
||||||
*
|
*
|
||||||
* @static
|
|
||||||
* @protected
|
* @protected
|
||||||
* @param $string HTML string to process.
|
* @param $string HTML string to process.
|
||||||
* @returns HTML with CDATA sections escaped.
|
* @returns HTML with CDATA sections escaped.
|
||||||
*/
|
*/
|
||||||
static function escapeCDATA($string) {
|
function escapeCDATA($string) {
|
||||||
return preg_replace_callback(
|
return preg_replace_callback(
|
||||||
'/<!\[CDATA\[(.+?)\]\]>/',
|
'/<!\[CDATA\[(.+?)\]\]>/',
|
||||||
array('HTMLPurifier_Lexer', 'CDATACallback'),
|
array('HTMLPurifier_Lexer', 'CDATACallback'),
|
||||||
@@ -181,14 +181,13 @@ class HTMLPurifier_Lexer
|
|||||||
/**
|
/**
|
||||||
* Callback function for escapeCDATA() that does the work.
|
* Callback function for escapeCDATA() that does the work.
|
||||||
*
|
*
|
||||||
* @static
|
|
||||||
* @warning Though this is public in order to let the callback happen,
|
* @warning Though this is public in order to let the callback happen,
|
||||||
* calling it directly is not recommended.
|
* calling it directly is not recommended.
|
||||||
* @params $matches PCRE matches array, with index 0 the entire match
|
* @params $matches PCRE matches array, with index 0 the entire match
|
||||||
* and 1 the inside of the CDATA section.
|
* and 1 the inside of the CDATA section.
|
||||||
* @returns Escaped internals 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
|
// not exactly sure why the character set is needed, but whatever
|
||||||
return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8');
|
return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8');
|
||||||
}
|
}
|
||||||
@@ -197,7 +196,7 @@ class HTMLPurifier_Lexer
|
|||||||
* Takes a piece of HTML and normalizes it by converting entities, fixing
|
* Takes a piece of HTML and normalizes it by converting entities, fixing
|
||||||
* encoding, extracting bits, and other good stuff.
|
* encoding, extracting bits, and other good stuff.
|
||||||
*/
|
*/
|
||||||
function normalize($html, $config, &$context) {
|
function normalize($html, $config) {
|
||||||
|
|
||||||
// extract body from document if applicable
|
// extract body from document if applicable
|
||||||
if ($config->get('Core', 'AcceptFullDocuments')) {
|
if ($config->get('Core', 'AcceptFullDocuments')) {
|
||||||
@@ -213,7 +212,7 @@ class HTMLPurifier_Lexer
|
|||||||
// clean into wellformed UTF-8 string for an SGML context: this has
|
// clean into wellformed UTF-8 string for an SGML context: this has
|
||||||
// to be done after entity expansion because the entities sometimes
|
// to be done after entity expansion because the entities sometimes
|
||||||
// represent non-SGML characters (horror, horror!)
|
// represent non-SGML characters (horror, horror!)
|
||||||
$html = HTMLPurifier_Encoder::cleanUTF8($html);
|
$html = $this->_encoder->cleanUTF8($html);
|
||||||
|
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
@@ -38,9 +38,10 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
|||||||
$this->factory = new HTMLPurifier_TokenFactory();
|
$this->factory = new HTMLPurifier_TokenFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tokenizeHTML($string, $config, &$context) {
|
public function tokenizeHTML($string, $config = null) {
|
||||||
|
if (!$config) $config = HTMLPurifier_Config::createDefault();
|
||||||
|
|
||||||
$string = $this->normalize($string, $config, $context);
|
$string = $this->normalize($string, $config);
|
||||||
|
|
||||||
// preprocess string, essential for UTF-8
|
// preprocess string, essential for UTF-8
|
||||||
$string =
|
$string =
|
||||||
@@ -88,11 +89,6 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
|||||||
} elseif ($node->nodeType === XML_COMMENT_NODE) {
|
} elseif ($node->nodeType === XML_COMMENT_NODE) {
|
||||||
$tokens[] = $this->factory->createComment($node->data);
|
$tokens[] = $this->factory->createComment($node->data);
|
||||||
return;
|
return;
|
||||||
} elseif (
|
|
||||||
// not-well tested: there may be other nodes we have to grab
|
|
||||||
$node->nodeType !== XML_ELEMENT_NODE
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$attr = $node->hasAttributes() ?
|
$attr = $node->hasAttributes() ?
|
||||||
|
@@ -24,9 +24,11 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
|||||||
*/
|
*/
|
||||||
var $_whitespace = "\x20\x09\x0D\x0A";
|
var $_whitespace = "\x20\x09\x0D\x0A";
|
||||||
|
|
||||||
function tokenizeHTML($html, $config, &$context) {
|
function tokenizeHTML($html, $config = null) {
|
||||||
|
|
||||||
$html = $this->normalize($html, $config, $context);
|
if (!$config) $config = HTMLPurifier_Config::createDefault();
|
||||||
|
|
||||||
|
$html = $this->normalize($html, $config);
|
||||||
|
|
||||||
$cursor = 0; // our location in the text
|
$cursor = 0; // our location in the text
|
||||||
$inside_tag = false; // whether or not we're parsing the inside of a tag
|
$inside_tag = false; // whether or not we're parsing the inside of a tag
|
||||||
@@ -143,18 +145,17 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
if ($attribute_string) {
|
if ($attribute_string) {
|
||||||
$attr = $this->parseAttributeString(
|
$attributes = $this->parseAttributeString(
|
||||||
$attribute_string
|
$attribute_string
|
||||||
, $config, $context
|
);
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$attr = array();
|
$attributes = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($is_self_closing) {
|
if ($is_self_closing) {
|
||||||
$array[] = new HTMLPurifier_Token_Empty($type, $attr);
|
$array[] = new HTMLPurifier_Token_Empty($type, $attributes);
|
||||||
} else {
|
} else {
|
||||||
$array[] = new HTMLPurifier_Token_Start($type, $attr);
|
$array[] = new HTMLPurifier_Token_Start($type, $attributes);
|
||||||
}
|
}
|
||||||
$cursor = $position_next_gt + 1;
|
$cursor = $position_next_gt + 1;
|
||||||
$inside_tag = false;
|
$inside_tag = false;
|
||||||
@@ -180,7 +181,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
|||||||
* @param $string Inside of tag excluding name.
|
* @param $string Inside of tag excluding name.
|
||||||
* @returns Assoc array of attributes.
|
* @returns Assoc array of attributes.
|
||||||
*/
|
*/
|
||||||
function parseAttributeString($string, $config, &$context) {
|
function parseAttributeString($string) {
|
||||||
$string = (string) $string; // quick typecast
|
$string = (string) $string; // quick typecast
|
||||||
|
|
||||||
if ($string == '') return array(); // no attributes
|
if ($string == '') return array(); // no attributes
|
||||||
|
@@ -31,13 +31,14 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer
|
|||||||
*/
|
*/
|
||||||
var $tokens = array();
|
var $tokens = array();
|
||||||
|
|
||||||
function tokenizeHTML($string, $config, &$context) {
|
function tokenizeHTML($string, $config = null) {
|
||||||
|
|
||||||
$this->tokens = array();
|
$this->tokens = array();
|
||||||
|
|
||||||
$string = $this->normalize($string, $config, $context);
|
if (!$config) $config = HTMLPurifier_Config::createDefault();
|
||||||
|
$string = $this->normalize($string, $config);
|
||||||
|
|
||||||
$parser = new XML_HTMLSax3();
|
$parser=& new XML_HTMLSax3();
|
||||||
$parser->set_object($this);
|
$parser->set_object($this);
|
||||||
$parser->set_element_handler('openHandler','closeHandler');
|
$parser->set_element_handler('openHandler','closeHandler');
|
||||||
$parser->set_data_handler('dataHandler');
|
$parser->set_data_handler('dataHandler');
|
||||||
|
@@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that handles operations involving percent-encoding in URIs.
|
|
||||||
*/
|
|
||||||
class HTMLPurifier_PercentEncoder
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fix up percent-encoding by decoding unreserved characters and normalizing
|
|
||||||
* @param $string String to normalize
|
|
||||||
*/
|
|
||||||
function normalize($string) {
|
|
||||||
if ($string == '') return '';
|
|
||||||
$parts = explode('%', $string);
|
|
||||||
$ret = array_shift($parts);
|
|
||||||
foreach ($parts as $part) {
|
|
||||||
$length = strlen($part);
|
|
||||||
if ($length < 2) {
|
|
||||||
$ret .= '%25' . $part;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$encoding = substr($part, 0, 2);
|
|
||||||
$text = substr($part, 2);
|
|
||||||
if (!ctype_xdigit($encoding)) {
|
|
||||||
$ret .= '%25' . $part;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$int = hexdec($encoding);
|
|
||||||
if (
|
|
||||||
($int >= 48 && $int <= 57) || // digits
|
|
||||||
($int >= 65 && $int <= 90) || // uppercase letters
|
|
||||||
($int >= 97 && $int <= 122) || // lowercase letters
|
|
||||||
$int == 126 || $int == 45 || $int == 46 || $int == 95 // ~-._
|
|
||||||
) {
|
|
||||||
$ret .= chr($int) . $text;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$encoding = strtoupper($encoding);
|
|
||||||
$ret .= '%' . $encoding . $text;
|
|
||||||
}
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -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 = <i>" . $this->getClass($obj, 'AttrDef_') . '</i>';
|
|
||||||
}
|
|
||||||
return $this->listify($list);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
@@ -24,7 +24,7 @@ class HTMLPurifier_Strategy
|
|||||||
* @param $config Configuration options
|
* @param $config Configuration options
|
||||||
* @returns Processed array of token objects.
|
* @returns Processed array of token objects.
|
||||||
*/
|
*/
|
||||||
function execute($tokens, $config, &$context) {
|
function execute($tokens, $config = null) {
|
||||||
trigger_error('Cannot call abstract function', E_USER_ERROR);
|
trigger_error('Cannot call abstract function', E_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,9 +18,9 @@ class HTMLPurifier_Strategy_Composite extends HTMLPurifier_Strategy
|
|||||||
trigger_error('Attempt to instantiate abstract object', E_USER_ERROR);
|
trigger_error('Attempt to instantiate abstract object', E_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
function execute($tokens, $config, &$context) {
|
function execute($tokens, $config) {
|
||||||
foreach ($this->strategies as $strategy) {
|
foreach ($this->strategies as $strategy) {
|
||||||
$tokens = $strategy->execute($tokens, $config, $context);
|
$tokens = $strategy->execute($tokens, $config);
|
||||||
}
|
}
|
||||||
return $tokens;
|
return $tokens;
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,8 @@ require_once 'HTMLPurifier/HTMLDefinition.php';
|
|||||||
class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
||||||
{
|
{
|
||||||
|
|
||||||
function execute($tokens, $config, &$context) {
|
function execute($tokens, $config) {
|
||||||
|
|
||||||
//####################################################################//
|
//####################################################################//
|
||||||
// Pre-processing
|
// Pre-processing
|
||||||
|
|
||||||
@@ -48,10 +49,6 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
|||||||
array_unshift($tokens, new HTMLPurifier_Token_Start($parent_name));
|
array_unshift($tokens, new HTMLPurifier_Token_Start($parent_name));
|
||||||
$tokens[] = new HTMLPurifier_Token_End($parent_name);
|
$tokens[] = new HTMLPurifier_Token_End($parent_name);
|
||||||
|
|
||||||
// setup the context variables
|
|
||||||
$parent_type = 'unknown'; // reference var that we alter
|
|
||||||
$context->register('ParentType', $parent_type);
|
|
||||||
|
|
||||||
//####################################################################//
|
//####################################################################//
|
||||||
// Loop initialization
|
// Loop initialization
|
||||||
|
|
||||||
@@ -104,11 +101,7 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
|||||||
if ($count = count($stack)) {
|
if ($count = count($stack)) {
|
||||||
$parent_index = $stack[$count-1];
|
$parent_index = $stack[$count-1];
|
||||||
$parent_name = $tokens[$parent_index]->name;
|
$parent_name = $tokens[$parent_index]->name;
|
||||||
if ($parent_index == 0) {
|
$parent_def = $definition->info[$parent_name];
|
||||||
$parent_def = $definition->info_parent_def;
|
|
||||||
} else {
|
|
||||||
$parent_def = $definition->info[$parent_name];
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// unknown info, it won't be used anyway
|
// unknown info, it won't be used anyway
|
||||||
$parent_index = $parent_name = $parent_def = null;
|
$parent_index = $parent_name = $parent_def = null;
|
||||||
@@ -116,10 +109,10 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
|||||||
|
|
||||||
// calculate context
|
// calculate context
|
||||||
if (isset($parent_def)) {
|
if (isset($parent_def)) {
|
||||||
$parent_type = $parent_def->type;
|
$context = $parent_def->type;
|
||||||
} else {
|
} else {
|
||||||
// generally found in specialized elements like UL
|
// generally found in specialized elements like UL
|
||||||
$parent_type = 'unknown';
|
$context = 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
//################################################################//
|
//################################################################//
|
||||||
@@ -145,25 +138,14 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
|||||||
if ($excluded) {
|
if ($excluded) {
|
||||||
// there is an exclusion, remove the entire node
|
// there is an exclusion, remove the entire node
|
||||||
$result = false;
|
$result = false;
|
||||||
$excludes = array(); // not used, but good to initialize anyway
|
|
||||||
} else {
|
} else {
|
||||||
// DEFINITION CALL
|
// DEFINITION CALL
|
||||||
if ($i === 0) {
|
$def = $definition->info[$tokens[$i]->name];
|
||||||
// special processing for the first node
|
$child_def = $def->child;
|
||||||
$def = $definition->info_parent_def;
|
|
||||||
} else {
|
|
||||||
$def = $definition->info[$tokens[$i]->name];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($def->child)) {
|
// have DTD child def validate children
|
||||||
// have DTD child def validate children
|
$result = $child_def->validateChildren(
|
||||||
$result = $def->child->validateChildren(
|
$child_tokens, $config,$context);
|
||||||
$child_tokens, $config, $context);
|
|
||||||
} else {
|
|
||||||
// weird, no child definition, get rid of everything
|
|
||||||
$result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine whether or not this element has any exclusions
|
// determine whether or not this element has any exclusions
|
||||||
$excludes = $def->excludes;
|
$excludes = $def->excludes;
|
||||||
@@ -243,20 +225,13 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
|||||||
|
|
||||||
// Test if the token indeed is a start tag, if not, move forward
|
// Test if the token indeed is a start tag, if not, move forward
|
||||||
// and test again.
|
// and test again.
|
||||||
$size = count($tokens);
|
|
||||||
while ($i < $size and $tokens[$i]->type != 'start') {
|
while ($i < $size and $tokens[$i]->type != 'start') {
|
||||||
if ($tokens[$i]->type == 'end') {
|
if ($tokens[$i]->type == 'end') {
|
||||||
// pop a token index off the stack if we ended a node
|
// pop a token index off the stack if we ended a node
|
||||||
array_pop($stack);
|
array_pop($stack);
|
||||||
// pop an exclusion lookup off exclusion stack if
|
// pop an exclusion lookup off exclusion stack if
|
||||||
// we ended node and that node had exclusions
|
// we ended node and that node had exclusions
|
||||||
if ($i == 0 || $i == $size - 1) {
|
if ($definition->info[$tokens[$i]->name]->excludes) {
|
||||||
// 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) {
|
|
||||||
array_pop($exclude_stack);
|
array_pop($exclude_stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,9 +247,6 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
|||||||
array_shift($tokens);
|
array_shift($tokens);
|
||||||
array_pop($tokens);
|
array_pop($tokens);
|
||||||
|
|
||||||
// remove context variables
|
|
||||||
$context->destroy('ParentType');
|
|
||||||
|
|
||||||
//####################################################################//
|
//####################################################################//
|
||||||
// Return
|
// Return
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ require_once 'HTMLPurifier/Generator.php';
|
|||||||
class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
||||||
{
|
{
|
||||||
|
|
||||||
function execute($tokens, $config, &$context) {
|
function execute($tokens, $config) {
|
||||||
$definition = $config->getHTMLDefinition();
|
$definition = $config->getHTMLDefinition();
|
||||||
$generator = new HTMLPurifier_Generator();
|
$generator = new HTMLPurifier_Generator();
|
||||||
$result = array();
|
$result = array();
|
||||||
@@ -30,7 +30,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
$token->type == 'start' ) {
|
$token->type == 'start' ) {
|
||||||
|
|
||||||
$result[] = new HTMLPurifier_Token_Empty($token->name,
|
$result[] = new HTMLPurifier_Token_Empty($token->name,
|
||||||
$token->attr);
|
$token->attributes);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
$token->type == 'empty' ) {
|
$token->type == 'empty' ) {
|
||||||
|
|
||||||
$result[] = new HTMLPurifier_Token_Start($token->name,
|
$result[] = new HTMLPurifier_Token_Start($token->name,
|
||||||
$token->attr);
|
$token->attributes);
|
||||||
$result[] = new HTMLPurifier_Token_End($token->name);
|
$result[] = new HTMLPurifier_Token_End($token->name);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -86,7 +86,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
if (empty($current_nesting)) {
|
if (empty($current_nesting)) {
|
||||||
if ($escape_invalid_tags) {
|
if ($escape_invalid_tags) {
|
||||||
$result[] = new HTMLPurifier_Token_Text(
|
$result[] = new HTMLPurifier_Token_Text(
|
||||||
$generator->generateFromToken($token, $config, $context)
|
$generator->generateFromToken($token, $config)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -123,7 +123,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
if ($skipped_tags === false) {
|
if ($skipped_tags === false) {
|
||||||
if ($escape_invalid_tags) {
|
if ($escape_invalid_tags) {
|
||||||
$result[] = new HTMLPurifier_Token_Text(
|
$result[] = new HTMLPurifier_Token_Text(
|
||||||
$generator->generateFromToken($token, $config, $context)
|
$generator->generateFromToken($token, $config)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@@ -5,14 +5,6 @@ require_once 'HTMLPurifier/HTMLDefinition.php';
|
|||||||
require_once 'HTMLPurifier/Generator.php';
|
require_once 'HTMLPurifier/Generator.php';
|
||||||
require_once 'HTMLPurifier/TagTransform.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.
|
* Removes all unrecognized tags from the list of tokens.
|
||||||
*
|
*
|
||||||
@@ -24,7 +16,7 @@ HTMLPurifier_ConfigSchema::define(
|
|||||||
class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
|
class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
|
||||||
{
|
{
|
||||||
|
|
||||||
function execute($tokens, $config, &$context) {
|
function execute($tokens, $config) {
|
||||||
$definition = $config->getHTMLDefinition();
|
$definition = $config->getHTMLDefinition();
|
||||||
$generator = new HTMLPurifier_Generator();
|
$generator = new HTMLPurifier_Generator();
|
||||||
$result = array();
|
$result = array();
|
||||||
@@ -33,26 +25,7 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
|
|||||||
if (!empty( $token->is_tag )) {
|
if (!empty( $token->is_tag )) {
|
||||||
// DEFINITION CALL
|
// DEFINITION CALL
|
||||||
if (isset($definition->info[$token->name])) {
|
if (isset($definition->info[$token->name])) {
|
||||||
// leave untouched, except for a few special cases:
|
// leave untouched
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} elseif (
|
} elseif (
|
||||||
isset($definition->info_tag_transform[$token->name])
|
isset($definition->info_tag_transform[$token->name])
|
||||||
) {
|
) {
|
||||||
@@ -60,11 +33,11 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
|
|||||||
// DEFINITION CALL
|
// DEFINITION CALL
|
||||||
$token = $definition->
|
$token = $definition->
|
||||||
info_tag_transform[$token->name]->
|
info_tag_transform[$token->name]->
|
||||||
transform($token, $config, $context);
|
transform($token);
|
||||||
} elseif ($escape_invalid_tags) {
|
} elseif ($escape_invalid_tags) {
|
||||||
// invalid tag, generate HTML and insert in
|
// invalid tag, generate HTML and insert in
|
||||||
$token = new HTMLPurifier_Token_Text(
|
$token = new HTMLPurifier_Token_Text(
|
||||||
$generator->generateFromToken($token, $config, $context)
|
$generator->generateFromToken($token, $config)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user