1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-08-05 13:47:24 +02:00

Compare commits

..

32 Commits

Author SHA1 Message Date
Edward Z. Yang
587d642826 Release 3.1.0.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1728 48356398-32a2-884e-a903-53898d9a118a
2008-05-18 05:46:06 +00:00
Edward Z. Yang
0bef016271 [3.1.0] Get testing working again for all versions
- Standalone testing setup properly with autoload
- Bootstrap autoloader deals more robustly with classes that don't exist, preventing class_exists($class, true) from barfing.
- Cleanup $_reporter to $reporter

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1727 48356398-32a2-884e-a903-53898d9a118a
2008-05-16 01:49:33 +00:00
Edward Z. Yang
ef6a1c9274 Allow for users to load Language class files themselves. Messages are still HTML Purifier dependent; we need to figure out a way around that.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1725 48356398-32a2-884e-a903-53898d9a118a
2008-05-15 23:22:34 +00:00
Edward Z. Yang
86b1da9b6f [3.1.0] Fixed bug with fallback languages in LanguageFactory
- Also, reverted bogus Generator changes

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1723 48356398-32a2-884e-a903-53898d9a118a
2008-05-15 23:04:46 +00:00
Edward Z. Yang
00ea2062d4 [3.1.0] Fix buggy LanguageFactory. This revision is incomplete.
- Some bogus commits to Generator were made, and will be reverted next revision.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1722 48356398-32a2-884e-a903-53898d9a118a
2008-05-15 17:47:47 +00:00
Edward Z. Yang
cb5d5d0648 [3.1.0] Revamp URI handling of percent encoding and validation.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1709 48356398-32a2-884e-a903-53898d9a118a
2008-05-14 02:19:00 +00:00
Edward Z. Yang
77ce3e8b4a [3.1.0] Extend scanner to catch $this->config; chmod new directories from Serializer. I'm not exactly sure what the implications of the bugfix are, but hopefully it won't blow up.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1708 48356398-32a2-884e-a903-53898d9a118a
2008-05-13 03:17:38 +00:00
Edward Z. Yang
e0c0d8eab6 [3.1.0] Allow arbitrary whitespace in %HTML.Allowed
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1707 48356398-32a2-884e-a903-53898d9a118a
2008-05-13 02:02:27 +00:00
Edward Z. Yang
ce46fb618c [3.1.0] Add missing tests and errors for forbidden attributes
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1706 48356398-32a2-884e-a903-53898d9a118a
2008-05-13 01:41:25 +00:00
Edward Z. Yang
9f37764614 Update TODO with items from Denis.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1702 48356398-32a2-884e-a903-53898d9a118a
2008-05-06 03:08:09 +00:00
Edward Z. Yang
aaf6ba421c Sync with SimpleTest codebase
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1701 48356398-32a2-884e-a903-53898d9a118a
2008-04-28 19:52:13 +00:00
Edward Z. Yang
4b862f64e6 [3.1.0] Fix ScriptRequired bug with trusted installs
- Generator now takes $config and $context during instantiation
- Double quotes outside of attributes are not escaped


git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1700 48356398-32a2-884e-a903-53898d9a118a
2008-04-28 01:35:07 +00:00
Edward Z. Yang
be2cfb7918 Fix latest batch of SimpleTest changes. Also, commit forgotten NEWS.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1699 48356398-32a2-884e-a903-53898d9a118a
2008-04-26 19:50:27 +00:00
Edward Z. Yang
2f29c27a59 [3.1.0] Fix broken PH5P in latest versions of DOM with bandaid; punt to DirectLex.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1698 48356398-32a2-884e-a903-53898d9a118a
2008-04-26 19:47:22 +00:00
Edward Z. Yang
144bd6f07a [3.1.0] Fix bug with 3.1.0-dev version number (the dash caused problems, so we switched to commas)
- Refactored out null definition cache during HTMLDefinition tests


git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1697 48356398-32a2-884e-a903-53898d9a118a
2008-04-26 19:28:14 +00:00
Edward Z. Yang
a95f600e76 Fix another fatal error from SimpleTest refactoring.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1696 48356398-32a2-884e-a903-53898d9a118a
2008-04-26 03:18:07 +00:00
Edward Z. Yang
84aa2ca390 [3.1.0] Implement tag@attr for Allowed and Forbidden
- Fix (or null) bug in configdoc

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1695 48356398-32a2-884e-a903-53898d9a118a
2008-04-26 03:14:01 +00:00
Edward Z. Yang
1f8619cda5 [3.1.0] Fix and revamp configForm.php smoketest
- Fix bool/null ConfigForm field

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1694 48356398-32a2-884e-a903-53898d9a118a
2008-04-26 01:13:58 +00:00
Edward Z. Yang
04b1ec33cb [3.1.0] version -> VERSION
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1693 48356398-32a2-884e-a903-53898d9a118a
2008-04-25 05:47:36 +00:00
Edward Z. Yang
6d9643a92e [3.1.0] Add const version to HTMLPurifier, also bump version to 3.1.0-dev; this apparently is a good idea!
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1692 48356398-32a2-884e-a903-53898d9a118a
2008-04-25 05:26:10 +00:00
Edward Z. Yang
438d973073 Renumber as 3.1.0, however, NOT releasing (WHATSNEW isn't updated)
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1691 48356398-32a2-884e-a903-53898d9a118a
2008-04-25 03:54:38 +00:00
Edward Z. Yang
f295465ad4 [3.1.0] Allow index to be false for config from for creation
- Static-ify Printer_ConfigForm's get* functions

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1690 48356398-32a2-884e-a903-53898d9a118a
2008-04-25 02:43:31 +00:00
Edward Z. Yang
eaabccdd9b [3.1.0] More PHP4->PHP5 conversions, notably reference removal of most methods that return objects
- Removed HTMLPurifier_Error
- Documentation updates
- Removed more copy() methods in favor of clone
- HTMLPurifier::getInstance() to HTMLPurifier::instance()
- Fix InterchangeBuilder to use HTMLPURIFIER_PREFIX

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1689 48356398-32a2-884e-a903-53898d9a118a
2008-04-23 02:40:17 +00:00
Edward Z. Yang
893cdd0301 All 3.1.0 TODOs are done!
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1687 48356398-32a2-884e-a903-53898d9a118a
2008-04-23 00:17:52 +00:00
Edward Z. Yang
1ba77fedd4 [3.1.0] Implement DenyElementDecorator for imagecrash-protection against CSS width/height
- Misc doc changes
- Add missing inheritance for AttrDef_CSS decorators


git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1684 48356398-32a2-884e-a903-53898d9a118a
2008-04-22 22:28:54 +00:00
Edward Z. Yang
fae720115a Update TODO
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1683 48356398-32a2-884e-a903-53898d9a118a
2008-04-22 20:57:11 +00:00
Edward Z. Yang
c0f2e69c9f [3.1.0] Update French documentation.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1682 48356398-32a2-884e-a903-53898d9a118a
2008-04-22 20:43:47 +00:00
Edward Z. Yang
ca6b20ff2b [phorum-3.0.0.1] Improve installation documentation.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1681 48356398-32a2-884e-a903-53898d9a118a
2008-04-22 18:30:01 +00:00
Edward Z. Yang
c4aa3ee40c [3.1.0] Encoder optimization, as suggested by Diego
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1680 48356398-32a2-884e-a903-53898d9a118a
2008-04-22 18:14:40 +00:00
Edward Z. Yang
e9c7873057 [3.1.0] Fix validation error with missing li.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1679 48356398-32a2-884e-a903-53898d9a118a
2008-04-22 17:35:39 +00:00
Edward Z. Yang
d45f42e6a8 Update docs.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1678 48356398-32a2-884e-a903-53898d9a118a
2008-04-22 17:32:42 +00:00
Edward Z. Yang
f46aef698e Post rc skirmishes.
- Update docs
- Update source code comments in generated files
- release1-update.php now flushes after it finishes
- Make InterchangeBuilder alphabetize

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1676 48356398-32a2-884e-a903-53898d9a118a
2008-04-22 16:20:45 +00:00
117 changed files with 1639 additions and 835 deletions

View File

@@ -31,7 +31,7 @@ PROJECT_NAME = HTMLPurifier
# This could be handy for archiving the generated documentation or # This could be handy for archiving the generated documentation or
# if some version control system is used. # if some version control system is used.
PROJECT_NUMBER = 3.1.0rc1 PROJECT_NUMBER = 3.1.0
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put. # base path where the generated documentation will be put.

View File

@@ -17,7 +17,7 @@ ce document pour quelques choses.
1. Compatibilité 1. Compatibilité
HTML Purifier fonctionne dans PHP 5. PHP 5.0.0 est le dernier HTML Purifier fonctionne dans PHP 5. PHP 5.0.5 est le dernier
version que je le testais. Il ne dépend de les autre librairies. version que je le testais. Il ne dépend de les autre librairies.
Les extensions optionnel est iconv (en général déjà installer) et Les extensions optionnel est iconv (en général déjà installer) et
@@ -34,19 +34,15 @@ Utilisez:
...quand vous devez utiliser HTML Purifier (ne inclure pas quand vous ...quand vous devez utiliser HTML Purifier (ne inclure pas quand vous
ne devez pas, parce que HTML Purifier est trés grand.) ne devez pas, parce que HTML Purifier est trés grand.)
Si vous n'aime pas que HTML Purifier change vos include_path, on peut HTML Purifier utilise 'autoload'. Si vous avez définu la fonction
change vos include_path, et: __autoload, vous doivez ajoute cet programme:
require_once 'HTMLPurifier.php'; spl_autoload_register('__autoload')
Seuleument les contents dans library/ est essentiel; vous peut enlever Plus d'information est dans le document 'INSTALL'.
les autre fichiers quand vous est dans une atmosphère professionnel.
[En cours de construction] 3. Installation vite
6. Installation vite
Si votre site web est en UTF-8 et XHTML Transitional, utilisez: Si votre site web est en UTF-8 et XHTML Transitional, utilisez:

54
NEWS
View File

@@ -9,6 +9,60 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
. Internal change . Internal change
========================== ==========================
3.1.0, released 2008-05-18
# Unnecessary references to objects (vestiges of PHP4) removed from method
signatures. The following methods do not need references when assigning from
them and will result in E_STRICT errors if you try:
+ HTMLPurifier_Config->get*Definition() [* = HTML, CSS]
+ HTMLPurifier_ConfigSchema::instance()
+ HTMLPurifier_DefinitionCacheFactory::instance()
+ HTMLPurifier_DefinitionCacheFactory->create()
+ HTMLPurifier_DoctypeRegistry->register()
+ HTMLPurifier_DoctypeRegistry->get()
+ HTMLPurifier_HTMLModule->addElement()
+ HTMLPurifier_HTMLModule->addBlankElement()
+ HTMLPurifier_LanguageFactory::instance()
# Printer_ConfigForm's get*() functions were static-ified
# %HTML.ForbiddenAttributes requires attribute declarations to be in the
form of tag@attr, NOT tag.attr (which will throw an error and won't do
anything). This is for forwards compatibility with XML; you'd do best
to migrate an %HTML.AllowedAttributes directives to this syntax too.
! Allow index to be false for config from form creation
! Added HTMLPurifier::VERSION constant
! Commas, not dashes, used for serializer IDs. This change is forwards-compatible
and allows for version numbers like "3.1.0-dev".
! %HTML.Allowed deals gracefully with whitespace anywhere, anytime!
! HTML Purifier's URI handling is a lot more robust, with much stricter
validation checks and better percent encoding handling.
! Bootstrap autoloader deals more robustly with classes that don't exist,
preventing class_exists($class, true) from barfing.
- InterchangeBuilder now alphabetizes its lists
- Validation error in configdoc output fixed
- Iconv and other encoding errors muted even with custom error handlers that
do not honor error_reporting
- Add protection against imagecrash attack with CSS height/width
- HTMLPurifier::instance() created for consistency, is equivalent to getInstance()
- Fixed and revamped broken ConfigForm smoketest
- Bug with bool/null fields in Printer_ConfigForm fixed
- Bug with global forbidden attributes fixed
- Improved error messages for allowed and forbidden HTML elements and attributes
- Missing (or null) in configdoc documentation restored
- If DOM throws and exception during parsing with PH5P (occurs in newer versions
of DOM), HTML Purifier punts to DirectLex
- Fatal error with unserialization of ScriptRequired
- Created directories are now chmod'ed properly
- Fixed bug with fallback languages in LanguageFactory
- Standalone testing setup properly with autoload
. Out-of-date documentation revised
. UTF-8 encoding check optimization as suggested by Diego
. HTMLPurifier_Error removed in favor of exceptions
. More copy() function removed; should use clone instead
. More extensive unit tests for HTMLDefinition
. assertPurification moved to central harness
. HTMLPurifier_Generator accepts $config and $context parameters during
instantiation, not runtime
. Double-quotes outside of attribute values are now unescaped
3.1.0rc1, released 2008-04-22 3.1.0rc1, released 2008-04-22
# Autoload support added. Internal require_once's removed in favor of an # Autoload support added. Internal require_once's removed in favor of an
explicit require list or autoloading. To use HTML Purifier, explicit require list or autoloading. To use HTML Purifier,

118
TODO
View File

@@ -7,48 +7,32 @@ TODO List
? Maybe I'll Do It ? Maybe I'll Do It
========================== ==========================
If no interest is expressed for a feature that may required a considerable If no interest is expressed for a feature that may require a considerable
amount of effort to implement, it may get endlessly delayed. Do not be amount of effort to implement, it may get endlessly delayed. Do not be
afraid to cast your vote for the next feature to be implemented! afraid to cast your vote for the next feature to be implemented!
- Implement validation for query and for fragment
UPCOMING RELEASE
----------------
IMPORTANT
- Release candidate, because of the major changes
DOCUMENTATION
- Update French translation of README
IMPORTANT FEATURES
- Factor out command line parser into its own class, and unit test it
NICE FEATURES
- Factor demo.php into a set of Printer classes, and then create a stub
file for users here (inside the actual HTML Purifier library)
- Support exporting configuration, so users can easily tweak settings
in the demo, and then copy-paste into their own setup
BUGS
- Style attribute height/width limiting for images
- Easy way to blacklist elements and attributes
- Investigate iconv error emitting
- Investigate UTF-8 optimization <http://htmlpurifier.org/phorum/read.php?3,1496>
- Figure out what to do about target="" and name="", since they show up so often
FUTURE VERSIONS FUTURE VERSIONS
--------------- ---------------
3.2 release [Error'ed] 3.2 release [It's All About Trust] (floating)
# Error logging for filtering/cleanup procedures # Implement untrusted, dangerous elements/attributes
- XSS-attempt detection - Objects and Forms are especially wanted
# Implement IDREF support (harder than it seems, since you cannot have
IDREFs to non-existent IDs)
# Frameset XHTML 1.0 and HTML 4.01 doctypes
- Research and implement a "safe" version of the Object module
3.3 release [Do What I Mean, Not What I Say] 3.3 release [Error'ed]
# Error logging for filtering/cleanup procedures
- XSS-attempt detection--certain errors are flagged XSS-like
3.4 release [Do What I Mean, Not What I Say]
# Additional support for poorly written HTML # Additional support for poorly written HTML
- Microsoft Word HTML cleaning (i.e. MsoNormal, but research essential!) - Microsoft Word HTML cleaning (i.e. MsoNormal, but research essential!)
- Friendly strict handling of <address> (block -> <br>) - Friendly strict handling of <address> (block -> <br>)
- Remove redundant tags, ex. <u><u>Underlined</u></u>. Implementation notes: ? Remove redundant tags, ex. <u><u>Underlined</u></u>. Implementation notes:
1. Analyzing which tags to remove duplicants 1. Analyzing which tags to remove duplicants
2. Ensure attributes are merged into the parent tag 2. Ensure attributes are merged into the parent tag
3. Extend the tag exclusion system to specify whether or not the 3. Extend the tag exclusion system to specify whether or not the
@@ -58,27 +42,21 @@ FUTURE VERSIONS
- Remove empty inline tags<i></i> - Remove empty inline tags<i></i>
- Append something to duplicate IDs so they're still usable (impl. note: the - Append something to duplicate IDs so they're still usable (impl. note: the
dupe detector would also need to detect the suffix as well) dupe detector would also need to detect the suffix as well)
- Externalize inline CSS to promote clean HTML - Externalize inline CSS to promote clean HTML, proposed by Sander Tekelenburg
3.4 release [It's All About Trust] (floating)
# Implement untrusted, dangerous elements/attributes
- Objects and Forms are especially wanted
# Implement IDREF support (harder than it seems, since you cannot have
IDREFs to non-existent IDs)
# Frameset XHTML 1.0 and HTML 4.01 doctypes
4.0 release [Beyond HTML] 4.0 release [Beyond HTML]
# Legit token based CSS parsing (will require revamping almost every # Legit token based CSS parsing (will require revamping almost every
AttrDef class). Probably will use CSSTidy class AttrDef class). Probably will use CSSTidy class?
# More control over allowed CSS properties (maybe modularize it in the # More control over allowed CSS properties using a modularization
same fashion!)
# HTML 5 support # HTML 5 support
# IRI support
- Standardize token armor for all areas of processing - Standardize token armor for all areas of processing
- Convert RTL/LTR override characters to <bdo> tags, or vice versa on demand. - Convert RTL/LTR override characters to <bdo> tags, or vice versa on demand.
Also, enable disabling of directionality Also, enable disabling of directionality
- Table of Contents generation (XHTML Compiler might be reusable)
5.0 release [To XML and Beyond] 5.0 release [To XML and Beyond]
- AllowedAttributes and ForbiddenAttributes step on the toes of XML by
using periods; this needs to be changed.
- Extended HTML capabilities based on namespacing and tag transforms (COMPLEX) - Extended HTML capabilities based on namespacing and tag transforms (COMPLEX)
- 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
@@ -86,45 +64,63 @@ FUTURE VERSIONS
Ongoing Ongoing
- More refactoring to take advantage of PHP5's facilities - More refactoring to take advantage of PHP5's facilities
- Lots of profiling, make it faster! - Refactor unit tests into lots of test methods
- Plugins for major CMSes (COMPLEX) - Plugins for major CMSes (COMPLEX)
- phpBB - phpBB
- Drupal needs loving!
- Phorum need loving!
- more! (look for ones that use WYSIWYGs) - more! (look for ones that use WYSIWYGs)
- Complete basic smoketests - Also, maybe a FAQ for extension writers with HTML Purifier
AutoFormat AutoFormat
- Smileys - Smileys
- Syntax highlighting with <pre> and possibly <?php - Syntax highlighting (with GeSHi) with <pre> and possibly <?php
- Look at http://drupal.org/project/Modules/category/63 for ideas - Look at http://drupal.org/project/Modules/category/63 for ideas
Unknown release (on a scratch-an-itch basis) Optimizations
# CHMOD install script for PEAR installs - Reduce size of internal data-structures (esp. HTMLDefinition)
? Have 'lang' attribute be checked against official lists, achieved by - Research memory usage of objects versus arrays
encoding all characters that have string entity equivalents - Combine multiple strategies into a single, single-pass strategy
- Abstract ChildDef_BlockQuote to work with all elements that only - Get PH5P working with the latest versions of DOM, which have much more
allow blocks in them, required or optional stringent error checking procedures. Maybe convert straight to tokens.
- Reorganize Unit Tests - Get rid of set_include_path(). Save this for another major release.
Neat feature related
! Factor demo.php into a set of Printer classes, and then create a stub
file for users here (inside the actual HTML Purifier library)
! Support exporting configuration, so users can easily tweak settings
in the demo, and then copy-paste into their own setup
- Advanced URI filtering schemes (see docs/proposal-new-directives.txt) - Advanced URI filtering schemes (see docs/proposal-new-directives.txt)
- Implement lenient <ruby> child validation - Allow scoped="scoped" attribute in <style> tags; may be troublesome
because regular CSS has no way of uniquely identifying nodes, so we'd
have to generate IDs
- Explain how to use HTML Purifier in non-PHP languages / create - Explain how to use HTML Purifier in non-PHP languages / create
a simple command line stub (or complicated?) a simple command line stub (or complicated?)
- 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
- Table of Contents generation (XHTML Compiler might be reusable). May also
be out-of-band information.
- Full set of color keywords. Also, a way to add onto them without
finalizing the configuration object.
- Write a var_export and memcached DefinitionCache - Denis
Maintenance related (slightly boring)
# CHMOD install script for PEAR installs
! Factor out command line parser into its own class, and unit test it
! Nested configuration namespaces
- Distinguish between default settings and explicitly set settings, so - Distinguish between default settings and explicitly set settings, so
configurations can be merged configurations can be merged
- Nested configuration namespaces
- Allow scoped="scoped" attribute in <style> tags; may be troublesome
because regular CSS has no way of uniquely identifying nodes, so we'd
have to generate IDs
- Time PHPT tests - Time PHPT tests
Requested ChildDef related (very boring)
- Abstract ChildDef_BlockQuote to work with all elements that only
allow blocks in them, required or optional
- Implement lenient <ruby> child validation
Wontfix Wontfix
- Non-lossy smart alternate character encoding transformations (unless - Non-lossy smart alternate character encoding transformations (unless
patch provided) 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
- Native content compression, whitespace stripping (don't rely on Tidy, make - Native content compression, whitespace stripping: use gzip if this is
sure we don't remove from <pre> or related tags): use gzip if this is
really important really important

View File

@@ -1 +1 @@
3.1.0rc1 3.1.0

View File

@@ -1,8 +1,10 @@
Release 3.1.0rc1 is a release candidate aimed primarily at ironing out bugs HTML Purifier 3.1.0 is the second release series for HTML Purifier on PHP 5
in HTML Purifier's shiny new __autoload system. There are lots of new features, as well as a security update related to URIs. It shifts over to using
however; some notable ones include support for the !important CSS modifier, autoload, and also includes support for the !important CSS modifier,
display and visibility CSS properties with %CSS.AllowTricky, marquee display and visibility CSS properties with %CSS.AllowTricky, marquee with
with %HTML.Proprietary (had you scared for a moment, hmm?), a kses() wrapper, %HTML.Proprietary (had you scared for a moment, hmm?), a kses() wrapper,
%CSS.AllowedProperties, %HTML.ForbiddenAttributes and %HTML.ForbiddenElements %CSS.AllowedProperties, %HTML.ForbiddenAttributes and
and a totally revamped ConfigDoc system. And of course, the usual slew of %HTML.ForbiddenElements and a totally revamped ConfigDoc system. Since the
bugfixes. release candidate, there have also been a number of stability fixes such as
improved URI escaping, a change in serializer ID format, and a relaxed
format for %HTML.Allowed. And as always, numerous bugfixes.

View File

@@ -15,7 +15,7 @@ TODO:
- add blurbs to ToC - add blurbs to ToC
*/ */
if (version_compare(PHP_VERSION, '5.2.0', '<')) exit('PHP 5.2.0 or greater required.'); if (version_compare(PHP_VERSION, '5.2', '<')) exit('PHP 5.2+ required.');
error_reporting(E_ALL | E_STRICT); error_reporting(E_ALL | E_STRICT);
chdir(dirname(__FILE__)); chdir(dirname(__FILE__));
@@ -38,6 +38,7 @@ $configdoc_xml = 'configdoc.xml';
$xml_builder = new HTMLPurifier_ConfigSchema_Builder_Xml(); $xml_builder = new HTMLPurifier_ConfigSchema_Builder_Xml();
$xml_builder->openURI($configdoc_xml); $xml_builder->openURI($configdoc_xml);
$xml_builder->build($interchange); $xml_builder->build($interchange);
unset($xml_builder); // free handle
$xslt = new ConfigDoc_HTMLXSLTProcessor(); $xslt = new ConfigDoc_HTMLXSLTProcessor();
$xslt->importStylesheet(dirname(__FILE__) . "/styles/$style.xsl"); $xslt->importStylesheet(dirname(__FILE__) . "/styles/$style.xsl");

View File

@@ -227,7 +227,7 @@
</tr> </tr>
</xsl:template> </xsl:template>
<xsl:template match="constraints/external/project"> <xsl:template match="constraints/external/project">
<xsl:value-of select="." /> <li><xsl:value-of select="." /></li>
</xsl:template> </xsl:template>
</xsl:stylesheet> </xsl:stylesheet>

View File

@@ -5,7 +5,7 @@
<line>131</line> <line>131</line>
</file> </file>
<file name="HTMLPurifier/Lexer.php"> <file name="HTMLPurifier/Lexer.php">
<line>93</line> <line>85</line>
</file> </file>
<file name="HTMLPurifier/Lexer/DirectLex.php"> <file name="HTMLPurifier/Lexer/DirectLex.php">
<line>50</line> <line>50</line>
@@ -18,22 +18,22 @@
</directive> </directive>
<directive id="CSS.Proprietary"> <directive id="CSS.Proprietary">
<file name="HTMLPurifier/CSSDefinition.php"> <file name="HTMLPurifier/CSSDefinition.php">
<line>201</line> <line>202</line>
</file> </file>
</directive> </directive>
<directive id="CSS.AllowTricky"> <directive id="CSS.AllowTricky">
<file name="HTMLPurifier/CSSDefinition.php"> <file name="HTMLPurifier/CSSDefinition.php">
<line>205</line> <line>206</line>
</file> </file>
</directive> </directive>
<directive id="CSS.AllowImportant"> <directive id="CSS.AllowImportant">
<file name="HTMLPurifier/CSSDefinition.php"> <file name="HTMLPurifier/CSSDefinition.php">
<line>209</line> <line>210</line>
</file> </file>
</directive> </directive>
<directive id="CSS.AllowedProperties"> <directive id="CSS.AllowedProperties">
<file name="HTMLPurifier/CSSDefinition.php"> <file name="HTMLPurifier/CSSDefinition.php">
<line>261</line> <line>262</line>
</file> </file>
</directive> </directive>
<directive id="Cache.DefinitionImpl"> <directive id="Cache.DefinitionImpl">
@@ -63,19 +63,19 @@
</directive> </directive>
<directive id="Core.Encoding"> <directive id="Core.Encoding">
<file name="HTMLPurifier/Encoder.php"> <file name="HTMLPurifier/Encoder.php">
<line>267</line> <line>281</line>
<line>285</line> <line>305</line>
</file> </file>
</directive> </directive>
<directive id="Test.ForceNoIconv"> <directive id="Test.ForceNoIconv">
<file name="HTMLPurifier/Encoder.php"> <file name="HTMLPurifier/Encoder.php">
<line>269</line> <line>283</line>
<line>290</line> <line>310</line>
</file> </file>
</directive> </directive>
<directive id="Core.EscapeNonASCIICharacters"> <directive id="Core.EscapeNonASCIICharacters">
<file name="HTMLPurifier/Encoder.php"> <file name="HTMLPurifier/Encoder.php">
<line>287</line> <line>307</line>
</file> </file>
</directive> </directive>
<directive id="Core.MaintainLineNumbers"> <directive id="Core.MaintainLineNumbers">
@@ -83,7 +83,7 @@
<line>81</line> <line>81</line>
</file> </file>
<file name="HTMLPurifier/Lexer.php"> <file name="HTMLPurifier/Lexer.php">
<line>90</line> <line>82</line>
</file> </file>
<file name="HTMLPurifier/Lexer/DirectLex.php"> <file name="HTMLPurifier/Lexer/DirectLex.php">
<line>45</line> <line>45</line>
@@ -91,17 +91,17 @@
</directive> </directive>
<directive id="Output.CommentScriptContents"> <directive id="Output.CommentScriptContents">
<file name="HTMLPurifier/Generator.php"> <file name="HTMLPurifier/Generator.php">
<line>40</line> <line>41</line>
</file> </file>
</directive> </directive>
<directive id="Output.TidyFormat"> <directive id="Output.TidyFormat">
<file name="HTMLPurifier/Generator.php"> <file name="HTMLPurifier/Generator.php">
<line>61</line> <line>70</line>
</file> </file>
</directive> </directive>
<directive id="Output.Newline"> <directive id="Output.Newline">
<file name="HTMLPurifier/Generator.php"> <file name="HTMLPurifier/Generator.php">
<line>86</line> <line>84</line>
</file> </file>
</directive> </directive>
<directive id="HTML.BlockWrapper"> <directive id="HTML.BlockWrapper">
@@ -131,12 +131,12 @@
</directive> </directive>
<directive id="HTML.ForbiddenElements"> <directive id="HTML.ForbiddenElements">
<file name="HTMLPurifier/HTMLDefinition.php"> <file name="HTMLPurifier/HTMLDefinition.php">
<line>303</line> <line>328</line>
</file> </file>
</directive> </directive>
<directive id="HTML.ForbiddenAttributes"> <directive id="HTML.ForbiddenAttributes">
<file name="HTMLPurifier/HTMLDefinition.php"> <file name="HTMLPurifier/HTMLDefinition.php">
<line>304</line> <line>329</line>
</file> </file>
</directive> </directive>
<directive id="HTML.Trusted"> <directive id="HTML.Trusted">
@@ -144,7 +144,7 @@
<line>198</line> <line>198</line>
</file> </file>
<file name="HTMLPurifier/Lexer.php"> <file name="HTMLPurifier/Lexer.php">
<line>245</line> <line>238</line>
</file> </file>
<file name="HTMLPurifier/Lexer/DirectLex.php"> <file name="HTMLPurifier/Lexer/DirectLex.php">
<line>34</line> <line>34</line>
@@ -172,17 +172,17 @@
</directive> </directive>
<directive id="Core.Language"> <directive id="Core.Language">
<file name="HTMLPurifier/LanguageFactory.php"> <file name="HTMLPurifier/LanguageFactory.php">
<line>85</line> <line>88</line>
</file> </file>
</directive> </directive>
<directive id="Core.LexerImpl"> <directive id="Core.LexerImpl">
<file name="HTMLPurifier/Lexer.php"> <file name="HTMLPurifier/Lexer.php">
<line>78</line> <line>70</line>
</file> </file>
</directive> </directive>
<directive id="Core.ConvertDocumentToFragment"> <directive id="Core.ConvertDocumentToFragment">
<file name="HTMLPurifier/Lexer.php"> <file name="HTMLPurifier/Lexer.php">
<line>237</line> <line>230</line>
</file> </file>
</directive> </directive>
<directive id="URI.Host"> <directive id="URI.Host">
@@ -215,12 +215,12 @@
</directive> </directive>
<directive id="URI.Disable"> <directive id="URI.Disable">
<file name="HTMLPurifier/AttrDef/URI.php"> <file name="HTMLPurifier/AttrDef/URI.php">
<line>24</line> <line>23</line>
</file> </file>
</directive> </directive>
<directive id="URI.Munge"> <directive id="URI.Munge">
<file name="HTMLPurifier/AttrDef/URI.php"> <file name="HTMLPurifier/AttrDef/URI.php">
<line>78</line> <line>68</line>
</file> </file>
</directive> </directive>
<directive id="Core.ColorKeywords"> <directive id="Core.ColorKeywords">

View File

@@ -367,7 +367,7 @@ Test.Example</pre>
For example, <code>HTMLPurifier_ConfigSchema_Builder_ConfigSchema</code> For example, <code>HTMLPurifier_ConfigSchema_Builder_ConfigSchema</code>
generates a runtime <code>HTMLPurifier_ConfigSchema</code> object, generates a runtime <code>HTMLPurifier_ConfigSchema</code> object,
which <code>HTMLPurifier_Config</code> uses to validate its incoming which <code>HTMLPurifier_Config</code> uses to validate its incoming
data. There is also a planned documentation builder. data. There is also an XML serializer, which is used to build documentation.
</p> </p>
<div id="version">$Id$</div> <div id="version">$Id$</div>

View File

@@ -158,7 +158,7 @@
<pre>$config = HTMLPurifier_Config::createDefault(); <pre>$config = HTMLPurifier_Config::createDefault();
$config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial'); $config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML', 'DefinitionRev', 1); $config->set('HTML', 'DefinitionRev', 1);
$def =& $config->getHTMLDefinition(true);</pre> $def = $config->getHTMLDefinition(true);</pre>
<p> <p>
Assuming that HTML Purifier has already been properly loaded (hint: Assuming that HTML Purifier has already been properly loaded (hint:
@@ -214,7 +214,7 @@ $def =& $config->getHTMLDefinition(true);</pre>
$config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial'); $config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML', 'DefinitionRev', 1); $config->set('HTML', 'DefinitionRev', 1);
<strong>$config->set('Core', 'DefinitionCache', null); // remove this later!</strong> <strong>$config->set('Core', 'DefinitionCache', null); // remove this later!</strong>
$def =& $config->getHTMLDefinition(true);</pre> $def = $config->getHTMLDefinition(true);</pre>
<p> <p>
A few things should be mentioned about the caching mechanism before A few things should be mentioned about the caching mechanism before
@@ -270,7 +270,7 @@ $def =& $config->getHTMLDefinition(true);</pre>
$config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial'); $config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML', 'DefinitionRev', 1); $config->set('HTML', 'DefinitionRev', 1);
$config->set('Core', 'DefinitionCache', null); // remove this later! $config->set('Core', 'DefinitionCache', null); // remove this later!
$def =& $config->getHTMLDefinition(true); $def = $config->getHTMLDefinition(true);
<strong>$def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');</strong></pre> <strong>$def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');</strong></pre>
<p> <p>
@@ -388,7 +388,7 @@ $def =& $config->getHTMLDefinition(true);
$config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial'); $config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML', 'DefinitionRev', 1); $config->set('HTML', 'DefinitionRev', 1);
$config->set('Core', 'DefinitionCache', null); // remove this later! $config->set('Core', 'DefinitionCache', null); // remove this later!
$def =& $config->getHTMLDefinition(true); $def = $config->getHTMLDefinition(true);
<strong>$def->addAttribute('a', 'target', new HTMLPurifier_AttrDef_Enum( <strong>$def->addAttribute('a', 'target', new HTMLPurifier_AttrDef_Enum(
array('_blank','_self','_target','_top') array('_blank','_self','_target','_top')
));</strong></pre> ));</strong></pre>
@@ -735,11 +735,11 @@ $def =& $config->getHTMLDefinition(true);
$config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial'); $config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML', 'DefinitionRev', 1); $config->set('HTML', 'DefinitionRev', 1);
$config->set('Core', 'DefinitionCache', null); // remove this later! $config->set('Core', 'DefinitionCache', null); // remove this later!
$def =& $config->getHTMLDefinition(true); $def = $config->getHTMLDefinition(true);
$def->addAttribute('a', 'target', new HTMLPurifier_AttrDef_Enum( $def->addAttribute('a', 'target', new HTMLPurifier_AttrDef_Enum(
array('_blank','_self','_target','_top') array('_blank','_self','_target','_top')
)); ));
<strong>$form =& $def->addElement( <strong>$form = $def->addElement(
'form', // name 'form', // name
'Block', // content set 'Block', // content set
'Flow', // allowed children 'Flow', // allowed children

View File

@@ -35,7 +35,7 @@
{ {
public $name = '<strong>NameOfFilter</strong>'; public $name = '<strong>NameOfFilter</strong>';
public function prepare($config) {} public function prepare($config) {}
public function filter(&$uri, $config, &$context) {} public function filter(&$uri, $config, $context) {}
}</pre> }</pre>
<p> <p>
@@ -60,8 +60,8 @@
public function HTMLPurifier_URI($scheme, $userinfo, $host, $port, $path, $query, $fragment); public function HTMLPurifier_URI($scheme, $userinfo, $host, $port, $path, $query, $fragment);
public function toString(); public function toString();
public function copy(); public function copy();
public function getSchemeObj($config, &$context); public function getSchemeObj($config, $context);
public function validate($config, &$context); public function validate($config, $context);
}</pre> }</pre>
<p> <p>
@@ -139,7 +139,7 @@
<pre>class HTMLPurifier_URIFilter_ConvertIDNToPunycode extends HTMLPurifier_URIFilter <pre>class HTMLPurifier_URIFilter_ConvertIDNToPunycode extends HTMLPurifier_URIFilter
{ {
public $name = 'ConvertIDNToPunycode'; public $name = 'ConvertIDNToPunycode';
public function filter(&$uri, $config, &$context) { public function filter(&$uri, $config, $context) {
if (is_null($uri->host)) return true; if (is_null($uri->host)) return true;
if ($uri->host == utf8_decode($uri->host)) { if ($uri->host == utf8_decode($uri->host)) {
// is ASCII, abort // is ASCII, abort
@@ -163,7 +163,7 @@
to use it. Fortunately, this part's simple: to use it. Fortunately, this part's simple:
</p> </p>
<pre>$uri =& $config->getDefinition('URI'); <pre>$uri = $config->getDefinition('URI');
$uri->addFilter(new HTMLPurifier_URIFilter_<strong>NameOfFilter</strong>());</pre> $uri->addFilter(new HTMLPurifier_URIFilter_<strong>NameOfFilter</strong>());</pre>
<p> <p>
@@ -177,7 +177,7 @@ $uri->addFilter(new HTMLPurifier_URIFilter_<strong>NameOfFilter</strong>());</pr
'URI', '<strong>NameOfFilter</strong>', false, 'bool', 'URI', '<strong>NameOfFilter</strong>', false, 'bool',
'<strong>What your filter does.</strong>' '<strong>What your filter does.</strong>'
); );
$uri =& $config->getDefinition('URI', true); $uri = $config->getDefinition('URI', true);
$uri->registerFilter(new HTMLPurifier_URIFilter_<strong>NameOfFilter</strong>()); $uri->registerFilter(new HTMLPurifier_URIFilter_<strong>NameOfFilter</strong>());
</pre> </pre>

View File

@@ -75,9 +75,7 @@ passes through HTML Purifier <em>unharmed</em>.
<p>And the corresponding usage:</p> <p>And the corresponding usage:</p>
<pre>&lt;?php <pre>&lt;?php
// assuming $purifier is an instance of HTMLPurifier $config->set('Filter', 'YouTube', true);
require_once 'HTMLPurifier/Filter/YouTube.php';
$purifier-&gt;addFilter(new HTMLPurifier_Filter_YouTube());
?&gt;</pre> ?&gt;</pre>
<p>There is a bit going in the two code snippets, so let's explain.</p> <p>There is a bit going in the two code snippets, so let's explain.</p>

View File

@@ -7,7 +7,7 @@
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
* FILE, changes will be overwritten the next time the script is run. * FILE, changes will be overwritten the next time the script is run.
* *
* @version 3.0.0 * @version 3.1.0
* *
* @warning * @warning
* You must *not* include any other HTML Purifier files before this file, * You must *not* include any other HTML Purifier files before this file,
@@ -41,7 +41,6 @@ require 'HTMLPurifier/ElementDef.php';
require 'HTMLPurifier/Encoder.php'; require 'HTMLPurifier/Encoder.php';
require 'HTMLPurifier/EntityLookup.php'; require 'HTMLPurifier/EntityLookup.php';
require 'HTMLPurifier/EntityParser.php'; require 'HTMLPurifier/EntityParser.php';
require 'HTMLPurifier/Error.php';
require 'HTMLPurifier/ErrorCollector.php'; require 'HTMLPurifier/ErrorCollector.php';
require 'HTMLPurifier/Exception.php'; require 'HTMLPurifier/Exception.php';
require 'HTMLPurifier/Filter.php'; require 'HTMLPurifier/Filter.php';
@@ -82,6 +81,7 @@ require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require 'HTMLPurifier/AttrDef/CSS/Border.php'; require 'HTMLPurifier/AttrDef/CSS/Border.php';
require 'HTMLPurifier/AttrDef/CSS/Color.php'; require 'HTMLPurifier/AttrDef/CSS/Color.php';
require 'HTMLPurifier/AttrDef/CSS/Composite.php'; require 'HTMLPurifier/AttrDef/CSS/Composite.php';
require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
require 'HTMLPurifier/AttrDef/CSS/Filter.php'; require 'HTMLPurifier/AttrDef/CSS/Filter.php';
require 'HTMLPurifier/AttrDef/CSS/Font.php'; require 'HTMLPurifier/AttrDef/CSS/Font.php';
require 'HTMLPurifier/AttrDef/CSS/FontFamily.php'; require 'HTMLPurifier/AttrDef/CSS/FontFamily.php';
@@ -116,6 +116,7 @@ require 'HTMLPurifier/AttrTransform/ImgSpace.php';
require 'HTMLPurifier/AttrTransform/Lang.php'; require 'HTMLPurifier/AttrTransform/Lang.php';
require 'HTMLPurifier/AttrTransform/Length.php'; require 'HTMLPurifier/AttrTransform/Length.php';
require 'HTMLPurifier/AttrTransform/Name.php'; require 'HTMLPurifier/AttrTransform/Name.php';
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
require 'HTMLPurifier/ChildDef/Chameleon.php'; require 'HTMLPurifier/ChildDef/Chameleon.php';
require 'HTMLPurifier/ChildDef/Custom.php'; require 'HTMLPurifier/ChildDef/Custom.php';
require 'HTMLPurifier/ChildDef/Empty.php'; require 'HTMLPurifier/ChildDef/Empty.php';

View File

@@ -15,13 +15,11 @@
* -# Generating HTML from the purified tokens. * -# Generating HTML from the purified tokens.
* *
* However, most users will only need to interface with the HTMLPurifier * However, most users will only need to interface with the HTMLPurifier
* class, so this massive amount of infrastructure is usually concealed. * and HTMLPurifier_Config.
* If you plan on working with the internals, be sure to include
* HTMLPurifier_ConfigSchema and HTMLPurifier_Config.
*/ */
/* /*
HTML Purifier 3.1.0rc1 - Standards Compliant HTML Filtering HTML Purifier 3.1.0 - Standards Compliant HTML Filtering
Copyright (C) 2006-2008 Edward Z. Yang Copyright (C) 2006-2008 Edward Z. Yang
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
@@ -57,7 +55,10 @@ class HTMLPurifier
{ {
/** Version of HTML Purifier */ /** Version of HTML Purifier */
public $version = '3.1.0rc1'; public $version = '3.1.0';
/** Constant with version of HTML Purifier */
const VERSION = '3.1.0';
/** Global configuration object */ /** Global configuration object */
public $config; public $config;
@@ -89,7 +90,6 @@ class HTMLPurifier
$this->config = HTMLPurifier_Config::create($config); $this->config = HTMLPurifier_Config::create($config);
$this->strategy = new HTMLPurifier_Strategy_Core(); $this->strategy = new HTMLPurifier_Strategy_Core();
$this->generator = new HTMLPurifier_Generator();
} }
@@ -123,8 +123,8 @@ class HTMLPurifier
$context = new HTMLPurifier_Context(); $context = new HTMLPurifier_Context();
// our friendly neighborhood generator, all primed with configuration too! // setup HTML generator
$this->generator->generateFromTokens(array(), $config, $context); $this->generator = new HTMLPurifier_Generator($config, $context);
$context->register('Generator', $this->generator); $context->register('Generator', $this->generator);
// set up global context variables // set up global context variables
@@ -177,8 +177,7 @@ class HTMLPurifier
$html, $config, $context $html, $config, $context
), ),
$config, $context $config, $context
), )
$config, $context
); );
for ($i = $filter_size - 1; $i >= 0; $i--) { for ($i = $filter_size - 1; $i >= 0; $i--) {
@@ -209,9 +208,10 @@ class HTMLPurifier
/** /**
* Singleton for enforcing just one HTML Purifier in your system * Singleton for enforcing just one HTML Purifier in your system
* @param $prototype Optional prototype HTMLPurifier instance to * @param $prototype Optional prototype HTMLPurifier instance to
* overload singleton with. * overload singleton with, or HTMLPurifier_Config
* instance to configure the generated version with.
*/ */
public static function getInstance($prototype = null) { public static function instance($prototype = null) {
if (!self::$instance || $prototype) { if (!self::$instance || $prototype) {
if ($prototype instanceof HTMLPurifier) { if ($prototype instanceof HTMLPurifier) {
self::$instance = $prototype; self::$instance = $prototype;
@@ -224,4 +224,11 @@ class HTMLPurifier
return self::$instance; return self::$instance;
} }
/**
* @note Backwards compatibility, see instance()
*/
public static function getInstance($prototype = null) {
return HTMLPurifier::instance($prototype);
}
} }

View File

@@ -35,7 +35,6 @@ require_once $__dir . '/HTMLPurifier/ElementDef.php';
require_once $__dir . '/HTMLPurifier/Encoder.php'; require_once $__dir . '/HTMLPurifier/Encoder.php';
require_once $__dir . '/HTMLPurifier/EntityLookup.php'; require_once $__dir . '/HTMLPurifier/EntityLookup.php';
require_once $__dir . '/HTMLPurifier/EntityParser.php'; require_once $__dir . '/HTMLPurifier/EntityParser.php';
require_once $__dir . '/HTMLPurifier/Error.php';
require_once $__dir . '/HTMLPurifier/ErrorCollector.php'; require_once $__dir . '/HTMLPurifier/ErrorCollector.php';
require_once $__dir . '/HTMLPurifier/Exception.php'; require_once $__dir . '/HTMLPurifier/Exception.php';
require_once $__dir . '/HTMLPurifier/Filter.php'; require_once $__dir . '/HTMLPurifier/Filter.php';
@@ -76,6 +75,7 @@ require_once $__dir . '/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Border.php'; require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Border.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Color.php'; require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Color.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Composite.php'; require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Composite.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Filter.php'; require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Filter.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Font.php'; require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Font.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/FontFamily.php'; require_once $__dir . '/HTMLPurifier/AttrDef/CSS/FontFamily.php';
@@ -110,6 +110,7 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php'; require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php'; require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php'; require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php'; require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php';

View File

@@ -70,9 +70,10 @@ abstract class HTMLPurifier_AttrDef
* @return Created AttrDef object corresponding to $string * @return Created AttrDef object corresponding to $string
*/ */
public function make($string) { public function make($string) {
// default implementation, return flyweight of this object // default implementation, return a flyweight of this object.
// if overloaded, it is *necessary* for you to clone the // If $string has an effect on the returned object (i.e. you
// object (usually by instantiating a new copy) and return that // need to overload this method), it is best
// to clone or instantiate new copies. (Instantiation is safer.)
return $this; return $this;
} }

View File

@@ -0,0 +1,26 @@
<?php
/**
* Decorator which enables CSS properties to be disabled for specific elements.
*/
class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef
{
protected $def, $element;
/**
* @param $def Definition to wrap
* @param $element Element to deny
*/
public function __construct($def, $element) {
$this->def = $def;
$this->element = $element;
}
/**
* Checks if CurrentToken is set and equal to $this->element
*/
public function validate($string, $config, $context) {
$token = $context->get('CurrentToken', true);
if ($token && $token->name == $this->element) return false;
return $this->def->validate($string, $config, $context);
}
}

View File

@@ -3,7 +3,7 @@
/** /**
* Decorator which enables !important to be used in CSS values. * Decorator which enables !important to be used in CSS values.
*/ */
class HTMLPurifier_AttrDef_CSS_ImportantDecorator class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef
{ {
protected $def, $allow; protected $def, $allow;

View File

@@ -7,7 +7,7 @@
class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
{ {
protected $parser, $percentEncoder; protected $parser;
protected $embedsResource; protected $embedsResource;
/** /**
@@ -15,7 +15,6 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
*/ */
public function __construct($embeds_resource = false) { public function __construct($embeds_resource = false) {
$this->parser = new HTMLPurifier_URIParser(); $this->parser = new HTMLPurifier_URIParser();
$this->percentEncoder = new HTMLPurifier_PercentEncoder();
$this->embedsResource = (bool) $embeds_resource; $this->embedsResource = (bool) $embeds_resource;
} }
@@ -23,9 +22,7 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
if ($config->get('URI', 'Disable')) return false; if ($config->get('URI', 'Disable')) return false;
// initial operations
$uri = $this->parseCDATA($uri); $uri = $this->parseCDATA($uri);
$uri = $this->percentEncoder->normalize($uri);
// parse the URI // parse the URI
$uri = $this->parser->parse($uri); $uri = $this->parser->parse($uri);
@@ -42,7 +39,7 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
if (!$result) break; if (!$result) break;
// chained filtering // chained filtering
$uri_def =& $config->getDefinition('URI'); $uri_def = $config->getDefinition('URI');
$result = $uri_def->filter($uri, $config, $context); $result = $uri_def->filter($uri, $config, $context);
if (!$result) break; if (!$result) break;
@@ -61,13 +58,6 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
$context->destroy('EmbeddedURI'); $context->destroy('EmbeddedURI');
if (!$ok) return false; if (!$ok) return false;
// munge scheme off if necessary (this must be last)
if (!is_null($uri->scheme) && is_null($uri->host)) {
if ($uri_def->defaultScheme == $uri->scheme) {
$uri->scheme = null;
}
}
// back to string // back to string
$result = $uri->toString(); $result = $uri->toString();

View File

@@ -36,11 +36,23 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
$ipv4 = $this->ipv4->validate($string, $config, $context); $ipv4 = $this->ipv4->validate($string, $config, $context);
if ($ipv4 !== false) return $ipv4; if ($ipv4 !== false) return $ipv4;
// validate a domain name here, do filtering, etc etc etc // A regular domain name.
// We could use this, but it would break I18N domain names // This breaks I18N domain names, but we don't have proper IRI support,
//$match = preg_match('/^[a-z0-9][\w\-\.]*[a-z0-9]$/i', $string); // so force users to insert Punycode. If there's complaining we'll
//if (!$match) return false; // try to fix things into an international friendly form.
// The productions describing this are:
$a = '[a-z]'; // alpha
$an = '[a-z0-9]'; // alphanum
$and = '[a-z0-9-]'; // alphanum | "-"
// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
$domainlabel = "$an($and*$an)?";
// toplabel = alpha | alpha *( alphanum | "-" ) alphanum
$toplabel = "$a($and*$an)?";
// hostname = *( domainlabel "." ) toplabel [ "." ]
$match = preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string);
if (!$match) return false;
return $string; return $string;
} }

View File

@@ -0,0 +1,14 @@
<?php
/**
* Implements required attribute stipulation for <script>
*/
class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform
{
public function transform($attr, $config, $context) {
if (!isset($attr['type'])) {
$attr['type'] = 'text/javascript';
}
return $attr;
}
}

View File

@@ -37,7 +37,7 @@ class HTMLPurifier_Bootstrap
public static function autoload($class) { public static function autoload($class) {
$file = HTMLPurifier_Bootstrap::getPath($class); $file = HTMLPurifier_Bootstrap::getPath($class);
if (!$file) return false; if (!$file) return false;
require $file; require HTMLPURIFIER_PREFIX . '/' . $file;
return true; return true;
} }
@@ -48,11 +48,13 @@ class HTMLPurifier_Bootstrap
if (strncmp('HTMLPurifier', $class, 12) !== 0) return false; if (strncmp('HTMLPurifier', $class, 12) !== 0) return false;
// Custom implementations // Custom implementations
if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) { if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
$code = str_replace('_', '-', substr($class, 22)); $code = str_replace('_', '-', substr($class, 22));
return 'HTMLPurifier/Language/classes/' . $code . '.php'; $file = 'HTMLPurifier/Language/classes/' . $code . '.php';
} else {
$file = str_replace('_', '/', $class) . '.php';
} }
// Standard implementation if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) return false;
return str_replace('_', '/', $class) . '.php'; return $file;
} }
/** /**

View File

@@ -151,11 +151,12 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
$this->info['width'] = $this->info['width'] =
$this->info['height'] = $this->info['height'] =
new HTMLPurifier_AttrDef_CSS_DenyElementDecorator(
new HTMLPurifier_AttrDef_CSS_Composite(array( new HTMLPurifier_AttrDef_CSS_Composite(array(
new HTMLPurifier_AttrDef_CSS_Length(true), new HTMLPurifier_AttrDef_CSS_Length(true),
new HTMLPurifier_AttrDef_CSS_Percentage(true), new HTMLPurifier_AttrDef_CSS_Percentage(true),
new HTMLPurifier_AttrDef_Enum(array('auto')) new HTMLPurifier_AttrDef_Enum(array('auto'))
)); )), 'img');
$this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration(); $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();

View File

@@ -3,7 +3,7 @@
/** /**
* Defines allowed child nodes and validates tokens against it. * Defines allowed child nodes and validates tokens against it.
*/ */
class HTMLPurifier_ChildDef abstract 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.
@@ -34,9 +34,7 @@ class HTMLPurifier_ChildDef
* @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
*/ */
public function validateChildren($tokens_of_children, $config, $context) { abstract public function validateChildren($tokens_of_children, $config, $context);
trigger_error('Call to abstract function', E_USER_ERROR);
}
} }

View File

@@ -20,7 +20,7 @@ class HTMLPurifier_Config
/** /**
* HTML Purifier's version * HTML Purifier's version
*/ */
public $version = '3.1.0rc1'; public $version = '3.1.0';
/** /**
* Bool indicator whether or not to automatically finalize * Bool indicator whether or not to automatically finalize
@@ -72,7 +72,7 @@ class HTMLPurifier_Config
* @param $definition HTMLPurifier_ConfigSchema that defines what directives * @param $definition HTMLPurifier_ConfigSchema that defines what directives
* are allowed. * are allowed.
*/ */
public function __construct(&$definition) { public function __construct($definition) {
$this->conf = $definition->defaults; // set up, copy in defaults $this->conf = $definition->defaults; // set up, copy in defaults
$this->def = $definition; // keep a copy around for checking $this->def = $definition; // keep a copy around for checking
$this->parser = new HTMLPurifier_VarParser_Flexible(); $this->parser = new HTMLPurifier_VarParser_Flexible();
@@ -107,7 +107,7 @@ class HTMLPurifier_Config
* @return Default HTMLPurifier_Config object. * @return Default HTMLPurifier_Config object.
*/ */
public static function createDefault() { public static function createDefault() {
$definition =& HTMLPurifier_ConfigSchema::instance(); $definition = HTMLPurifier_ConfigSchema::instance();
$config = new HTMLPurifier_Config($definition); $config = new HTMLPurifier_Config($definition);
return $config; return $config;
} }
@@ -254,21 +254,21 @@ class HTMLPurifier_Config
} }
/** /**
* Retrieves reference to the HTML definition. * Retrieves object reference to the HTML definition.
* @param $raw Return a copy that has not been setup yet. Must be * @param $raw Return a copy that has not been setup yet. Must be
* called before it's been setup, otherwise won't work. * called before it's been setup, otherwise won't work.
*/ */
public function &getHTMLDefinition($raw = false) { public function getHTMLDefinition($raw = false) {
$def =& $this->getDefinition('HTML', $raw); return $this->getDefinition('HTML', $raw);
return $def; // prevent PHP 4.4.0 from complaining
} }
/** /**
* Retrieves reference to the CSS definition * Retrieves object reference to the CSS definition
* @param $raw Return a copy that has not been setup yet. Must be
* called before it's been setup, otherwise won't work.
*/ */
public function &getCSSDefinition($raw = false) { public function getCSSDefinition($raw = false) {
$def =& $this->getDefinition('CSS', $raw); return $this->getDefinition('CSS', $raw);
return $def;
} }
/** /**
@@ -276,7 +276,7 @@ class HTMLPurifier_Config
* @param $type Type of definition: HTML, CSS, etc * @param $type Type of definition: HTML, CSS, etc
* @param $raw Whether or not definition should be returned raw * @param $raw Whether or not definition should be returned raw
*/ */
public function &getDefinition($type, $raw = false) { public function getDefinition($type, $raw = false) {
if (!$this->finalized && $this->autoFinalize) $this->finalize(); if (!$this->finalized && $this->autoFinalize) $this->finalize();
$factory = HTMLPurifier_DefinitionCacheFactory::instance(); $factory = HTMLPurifier_DefinitionCacheFactory::instance();
$cache = $factory->create($type, $this); $cache = $factory->create($type, $this);
@@ -310,17 +310,13 @@ class HTMLPurifier_Config
} elseif ($type == 'URI') { } elseif ($type == 'URI') {
$this->definitions[$type] = new HTMLPurifier_URIDefinition(); $this->definitions[$type] = new HTMLPurifier_URIDefinition();
} else { } else {
trigger_error("Definition of $type type not supported"); throw new HTMLPurifier_Exception("Definition of $type type not supported");
$false = false;
return $false;
} }
// quick abort if raw // quick abort if raw
if ($raw) { if ($raw) {
if (is_null($this->get($type, 'DefinitionID'))) { if (is_null($this->get($type, 'DefinitionID'))) {
// fatally error out if definition ID not set // fatally error out if definition ID not set
trigger_error("Cannot retrieve raw version without specifying %$type.DefinitionID", E_USER_ERROR); throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID");
$false = new HTMLPurifier_Error();
return $false;
} }
return $this->definitions[$type]; return $this->definitions[$type];
} }
@@ -407,7 +403,7 @@ class HTMLPurifier_Config
* @param $mq_fix Boolean whether or not to enable magic quotes fix * @param $mq_fix Boolean whether or not to enable magic quotes fix
* @param $schema Instance of HTMLPurifier_ConfigSchema to use, if not global copy * @param $schema Instance of HTMLPurifier_ConfigSchema to use, if not global copy
*/ */
public static function loadArrayFromForm($array, $index, $allowed = true, $mq_fix = true, $schema = null) { public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {
$ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema); $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);
$config = HTMLPurifier_Config::create($ret, $schema); $config = HTMLPurifier_Config::create($ret, $schema);
return $config; return $config;
@@ -417,7 +413,7 @@ class HTMLPurifier_Config
* Merges in configuration values from $_GET/$_POST to object. NOT STATIC. * Merges in configuration values from $_GET/$_POST to object. NOT STATIC.
* @note Same parameters as loadArrayFromForm * @note Same parameters as loadArrayFromForm
*/ */
public function mergeArrayFromForm($array, $index, $allowed = true, $mq_fix = true) { public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) {
$ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def); $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);
$this->loadArray($ret); $this->loadArray($ret);
} }
@@ -426,8 +422,8 @@ class HTMLPurifier_Config
* Prepares an array from a form into something usable for the more * Prepares an array from a form into something usable for the more
* strict parts of HTMLPurifier_Config * strict parts of HTMLPurifier_Config
*/ */
public static function prepareArrayFromForm($array, $index, $allowed = true, $mq_fix = true, $schema = null) { public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {
$array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array(); if ($index !== false) $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
$mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc(); $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();
$allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema); $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);

View File

@@ -3,7 +3,7 @@
/** /**
* Base class for configuration entity * Base class for configuration entity
*/ */
class HTMLPurifier_ConfigDef { abstract class HTMLPurifier_ConfigDef {
public $class = false; public $class = false;
} }

View File

@@ -40,7 +40,7 @@ class HTMLPurifier_ConfigSchema {
/** /**
* Retrieves an instance of the application-wide configuration definition. * Retrieves an instance of the application-wide configuration definition.
*/ */
public static function &instance($prototype = null) { public static function instance($prototype = null) {
if ($prototype !== null) { if ($prototype !== null) {
HTMLPurifier_ConfigSchema::$singleton = $prototype; HTMLPurifier_ConfigSchema::$singleton = $prototype;
} elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) { } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) {
@@ -104,9 +104,8 @@ class HTMLPurifier_ConfigSchema {
* @param $allowed Lookup array of allowed values * @param $allowed Lookup array of allowed values
*/ */
public function addAllowedValues($namespace, $name, $allowed) { public function addAllowedValues($namespace, $name, $allowed) {
$directive =& $this->info[$namespace][$name]; $type = $this->info[$namespace][$name]->type;
$type = $directive->type; $this->info[$namespace][$name]->allowed = $allowed;
$directive->allowed = $allowed;
} }
/** /**
@@ -130,21 +129,21 @@ class HTMLPurifier_ConfigSchema {
$type = $type_values[0]; $type = $type_values[0];
$modifier = isset($type_values[1]) ? $type_values[1] : false; $modifier = isset($type_values[1]) ? $type_values[1] : false;
$allow_null = ($modifier === 'null'); $allow_null = ($modifier === 'null');
$def =& HTMLPurifier_ConfigSchema::instance(); $def = HTMLPurifier_ConfigSchema::instance();
$def->add($namespace, $name, $default, $type, $allow_null); $def->add($namespace, $name, $default, $type, $allow_null);
} }
/** @see HTMLPurifier_ConfigSchema->addNamespace() */ /** @see HTMLPurifier_ConfigSchema->addNamespace() */
public static function defineNamespace($namespace, $description) { public static function defineNamespace($namespace, $description) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__); HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def =& HTMLPurifier_ConfigSchema::instance(); $def = HTMLPurifier_ConfigSchema::instance();
$def->addNamespace($namespace); $def->addNamespace($namespace);
} }
/** @see HTMLPurifier_ConfigSchema->addValueAliases() */ /** @see HTMLPurifier_ConfigSchema->addValueAliases() */
public static function defineValueAliases($namespace, $name, $aliases) { public static function defineValueAliases($namespace, $name, $aliases) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__); HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def =& HTMLPurifier_ConfigSchema::instance(); $def = HTMLPurifier_ConfigSchema::instance();
$def->addValueAliases($namespace, $name, $aliases); $def->addValueAliases($namespace, $name, $aliases);
} }
@@ -155,14 +154,14 @@ class HTMLPurifier_ConfigSchema {
foreach ($allowed_values as $value) { foreach ($allowed_values as $value) {
$allowed[$value] = true; $allowed[$value] = true;
} }
$def =& HTMLPurifier_ConfigSchema::instance(); $def = HTMLPurifier_ConfigSchema::instance();
$def->addAllowedValues($namespace, $name, $allowed); $def->addAllowedValues($namespace, $name, $allowed);
} }
/** @see HTMLPurifier_ConfigSchema->addAlias() */ /** @see HTMLPurifier_ConfigSchema->addAlias() */
public static function defineAlias($namespace, $name, $new_namespace, $new_name) { public static function defineAlias($namespace, $name, $new_namespace, $new_name) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__); HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def =& HTMLPurifier_ConfigSchema::instance(); $def = HTMLPurifier_ConfigSchema::instance();
$def->addAlias($namespace, $name, $new_namespace, $new_name); $def->addAlias($namespace, $name, $new_namespace, $new_name);
} }

View File

@@ -71,8 +71,10 @@ class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
$this->startElement('constraints'); $this->startElement('constraints');
if ($directive->version) $this->writeElement('version', $directive->version); if ($directive->version) $this->writeElement('version', $directive->version);
$this->writeElement('type', $directive->type); $this->startElement('type');
if ($directive->typeAllowsNull) $this->writeAttribute('allow-null', 'yes'); if ($directive->typeAllowsNull) $this->writeAttribute('allow-null', 'yes');
$this->text($directive->type);
$this->endElement(); // type
if ($directive->allowed) { if ($directive->allowed) {
$this->startElement('allowed'); $this->startElement('allowed');
foreach ($directive->allowed as $value => $x) $this->writeElement('value', $value); foreach ($directive->allowed as $value => $x) $this->writeElement('value', $value);

View File

@@ -17,21 +17,27 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder
$builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder(); $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
$interchange = new HTMLPurifier_ConfigSchema_Interchange(); $interchange = new HTMLPurifier_ConfigSchema_Interchange();
if (!$dir) $dir = realpath(dirname(__FILE__) . '/schema/'); if (!$dir) $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema/';
$info = parse_ini_file($dir . 'info.ini'); $info = parse_ini_file($dir . 'info.ini');
$interchange->name = $info['name']; $interchange->name = $info['name'];
$files = array();
$dh = opendir($dir); $dh = opendir($dir);
while (false !== ($file = readdir($dh))) { while (false !== ($file = readdir($dh))) {
if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') { if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') {
continue; continue;
} }
$files[] = $file;
}
closedir($dh);
sort($files);
foreach ($files as $file) {
$builder->build( $builder->build(
$interchange, $interchange,
new HTMLPurifier_StringHash( $parser->parseFile($dir . $file) ) new HTMLPurifier_StringHash( $parser->parseFile($dir . $file) )
); );
} }
closedir($dh);
return $interchange; return $interchange;
} }

File diff suppressed because one or more lines are too long

View File

@@ -21,3 +21,17 @@ $styles = $purifier->context->get('StyleBlocks');
foreach ($styles as $style) { foreach ($styles as $style) {
echo '<style type="text/css">' . $style . "</style>\n"; echo '<style type="text/css">' . $style . "</style>\n";
}]]></pre> }]]></pre>
<p>
<strong>Warning:</strong> It is possible for a user to mount an
imagecrash attack using this CSS. Counter-measures are difficult;
it is not simply enough to limit the range of CSS lengths (using
relative lengths with many nesting levels allows for large values
to be attained without actually specifying them in the stylesheet),
and the flexible nature of selectors makes it difficult to selectively
disable lengths on image tags (HTML Purifier, however, does disable
CSS width and height in inline styling). There are probably two effective
counter measures: an explicit width and height set to auto in all
images in your document (unlikely) or the disabling of width and
height (somewhat reasonable). Whether or not these measures should be
used is left to the reader.
</p>

View File

@@ -4,7 +4,17 @@ VERSION: 3.1.0
DEFAULT: array() DEFAULT: array()
--DESCRIPTION-- --DESCRIPTION--
<p> <p>
This directive complements %HTML.ForbiddenElements and is the inverse of While this directive is similar to %HTML.AllowedAttributes, for
%HTML.AllowedAttributes. Please see the former for a discussion of why you forwards-compatibility with XML, this attribute has a different syntax. Instead of
<code>tag.attr</code>, use <code>tag@attr</code>. To disallow <code>href</code>
attributes in <code>a</code> tags, set this directive to
<code>a@href</code>. You can also disallow an attribute globally with
<code>attr</code> or <code>*@attr</code> (either syntax is fine; the latter
is provided for consistency with %HTML.AllowedAttributes).
</p>
<p>
<strong>Warning:</strong> This directive complements %HTML.ForbiddenElements,
accordingly, check
out that directive for a discussion of why you
should think twice before using this directive. should think twice before using this directive.
</p> </p>

View File

@@ -4,6 +4,8 @@
* Registry object that contains information about the current context. * Registry object that contains information about the current context.
* @warning Is a bit buggy when variables are set to null: it thinks * @warning Is a bit buggy when variables are set to null: it thinks
* they don't exist! So use false instead, please. * they don't exist! So use false instead, please.
* @note Since the variables Context deals with may not be objects,
* references are very important here! Do not remove!
*/ */
class HTMLPurifier_Context class HTMLPurifier_Context
{ {
@@ -16,7 +18,7 @@ class HTMLPurifier_Context
/** /**
* Registers a variable into the context. * Registers a variable into the context.
* @param $name String name * @param $name String name
* @param $ref Variable to be registered * @param $ref Reference to variable to be registered
*/ */
public function register($name, &$ref) { public function register($name, &$ref) {
if (isset($this->_storage[$name])) { if (isset($this->_storage[$name])) {

View File

@@ -26,8 +26,8 @@ abstract class HTMLPurifier_DefinitionCache
* @param Instance of HTMLPurifier_Config * @param Instance of HTMLPurifier_Config
*/ */
public function generateKey($config) { public function generateKey($config) {
return $config->version . '-' . // possibly replace with function calls return $config->version . ',' . // possibly replace with function calls
$config->getBatchSerial($this->type) . '-' . $config->getBatchSerial($this->type) . ',' .
$config->get($this->type, 'DefinitionRev'); $config->get($this->type, 'DefinitionRev');
} }
@@ -38,8 +38,8 @@ abstract class HTMLPurifier_DefinitionCache
* @param $config Instance of HTMLPurifier_Config to test against * @param $config Instance of HTMLPurifier_Config to test against
*/ */
public function isOld($key, $config) { public function isOld($key, $config) {
if (substr_count($key, '-') < 2) return true; if (substr_count($key, ',') < 2) return true;
list($version, $hash, $revision) = explode('-', $key, 3); list($version, $hash, $revision) = explode(',', $key, 3);
$compare = version_compare($version, $config->version); $compare = version_compare($version, $config->version);
// version mismatch, is always old // version mismatch, is always old
if ($compare != 0) return true; if ($compare != 0) return true;

View File

@@ -100,18 +100,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends
* @return Number of bytes written if success, or false if failure. * @return Number of bytes written if success, or false if failure.
*/ */
private function _write($file, $data) { private function _write($file, $data) {
static $file_put_contents; return file_put_contents($file, $data);
if ($file_put_contents === null) {
$file_put_contents = function_exists('file_put_contents');
}
if ($file_put_contents) {
return file_put_contents($file, $data);
}
$fh = fopen($file, 'w');
if (!$fh) return false;
$status = fwrite($fh, $data);
fclose($fh);
return $status;
} }
/** /**
@@ -130,7 +119,9 @@ class HTMLPurifier_DefinitionCache_Serializer extends
} elseif (!$this->_testPermissions($base)) { } elseif (!$this->_testPermissions($base)) {
return false; return false;
} }
$old = umask(0022); // disable group and world writes
mkdir($directory); mkdir($directory);
umask($old);
} elseif (!$this->_testPermissions($directory)) { } elseif (!$this->_testPermissions($directory)) {
return false; return false;
} }

View File

@@ -20,7 +20,7 @@ class HTMLPurifier_DefinitionCacheFactory
/** /**
* Retrieves an instance of global definition cache factory. * Retrieves an instance of global definition cache factory.
*/ */
public static function &instance($prototype = null) { public static function instance($prototype = null) {
static $instance; static $instance;
if ($prototype !== null) { if ($prototype !== null) {
$instance = $prototype; $instance = $prototype;
@@ -45,11 +45,10 @@ class HTMLPurifier_DefinitionCacheFactory
* @param $name Name of definitions handled by cache * @param $name Name of definitions handled by cache
* @param $config Instance of HTMLPurifier_Config * @param $config Instance of HTMLPurifier_Config
*/ */
public function &create($type, $config) { public function create($type, $config) {
$method = $config->get('Cache', 'DefinitionImpl'); $method = $config->get('Cache', 'DefinitionImpl');
if ($method === null) { if ($method === null) {
$null = new HTMLPurifier_DefinitionCache_Null($type); return new HTMLPurifier_DefinitionCache_Null($type);
return $null;
} }
if (!empty($this->caches[$method][$type])) { if (!empty($this->caches[$method][$type])) {
return $this->caches[$method][$type]; return $this->caches[$method][$type];

View File

@@ -21,9 +21,9 @@ class HTMLPurifier_DoctypeRegistry
* @param $modules Modules doctype will load * @param $modules Modules doctype will load
* @param $modules_for_modes Modules doctype will load for certain modes * @param $modules_for_modes Modules doctype will load for certain modes
* @param $aliases Alias names for doctype * @param $aliases Alias names for doctype
* @return Reference to registered doctype (usable for further editing) * @return Editable registered doctype
*/ */
public function &register($doctype, $xml = true, $modules = array(), public function register($doctype, $xml = true, $modules = array(),
$tidy_modules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null $tidy_modules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null
) { ) {
if (!is_array($modules)) $modules = array($modules); if (!is_array($modules)) $modules = array($modules);
@@ -34,7 +34,7 @@ class HTMLPurifier_DoctypeRegistry
$doctype, $xml, $modules, $tidy_modules, $aliases, $dtd_public, $dtd_system $doctype, $xml, $modules, $tidy_modules, $aliases, $dtd_public, $dtd_system
); );
} }
$this->doctypes[$doctype->name] =& $doctype; $this->doctypes[$doctype->name] = $doctype;
$name = $doctype->name; $name = $doctype->name;
// hookup aliases // hookup aliases
foreach ($doctype->aliases as $alias) { foreach ($doctype->aliases as $alias) {
@@ -51,9 +51,9 @@ class HTMLPurifier_DoctypeRegistry
* @note This function resolves aliases * @note This function resolves aliases
* @note When possible, use the more fully-featured make() * @note When possible, use the more fully-featured make()
* @param $doctype Name of doctype * @param $doctype Name of doctype
* @return Reference to doctype object * @return Editable doctype object
*/ */
public function &get($doctype) { public function get($doctype) {
if (isset($this->aliases[$doctype])) $doctype = $this->aliases[$doctype]; if (isset($this->aliases[$doctype])) $doctype = $this->aliases[$doctype];
if (!isset($this->doctypes[$doctype])) { if (!isset($this->doctypes[$doctype])) {
trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR); trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR);

View File

@@ -14,6 +14,11 @@ class HTMLPurifier_Encoder
trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR); trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR);
} }
/**
* Error-handler that mutes errors, alternative to shut-up operator.
*/
private static function muteErrorHandler() {}
/** /**
* Cleans a UTF-8 string for well-formedness and SGML validity * Cleans a UTF-8 string for well-formedness and SGML validity
* *
@@ -57,9 +62,18 @@ class HTMLPurifier_Encoder
static $iconv = null; static $iconv = null;
if ($iconv === null) $iconv = function_exists('iconv'); if ($iconv === null) $iconv = function_exists('iconv');
// UTF-8 validity is checked since PHP 4.3.5
// This is an optimization: if the string is already valid UTF-8, no
// need to do iconv/php stuff. 99% of the time, this will be the case.
if (preg_match('/^.{1}/us', $str)) {
return strtr($str, $non_sgml_chars);
}
if ($iconv && !$force_php) { if ($iconv && !$force_php) {
// do the shortcut way // do the shortcut way
$str = @iconv('UTF-8', 'UTF-8//IGNORE', $str); set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
$str = iconv('UTF-8', 'UTF-8//IGNORE', $str);
restore_error_handler();
return strtr($str, $non_sgml_chars); return strtr($str, $non_sgml_chars);
} }
@@ -267,9 +281,15 @@ class HTMLPurifier_Encoder
$encoding = $config->get('Core', 'Encoding'); $encoding = $config->get('Core', 'Encoding');
if ($encoding === 'utf-8') return $str; if ($encoding === 'utf-8') return $str;
if ($iconv && !$config->get('Test', 'ForceNoIconv')) { if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
return @iconv($encoding, 'utf-8//IGNORE', $str); set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
$str = iconv($encoding, 'utf-8//IGNORE', $str);
restore_error_handler();
return $str;
} elseif ($encoding === 'iso-8859-1') { } elseif ($encoding === 'iso-8859-1') {
return @utf8_encode($str); set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
$str = utf8_encode($str);
restore_error_handler();
return $str;
} }
trigger_error('Encoding not supported', E_USER_ERROR); trigger_error('Encoding not supported', E_USER_ERROR);
} }
@@ -288,9 +308,15 @@ class HTMLPurifier_Encoder
$str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str); $str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str);
} }
if ($iconv && !$config->get('Test', 'ForceNoIconv')) { if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
return @iconv('utf-8', $encoding . '//IGNORE', $str); set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
$str = iconv('utf-8', $encoding . '//IGNORE', $str);
restore_error_handler();
return $str;
} elseif ($encoding === 'iso-8859-1') { } elseif ($encoding === 'iso-8859-1') {
return @utf8_decode($str); set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
$str = utf8_decode($str);
restore_error_handler();
return $str;
} }
trigger_error('Encoding not supported', E_USER_ERROR); trigger_error('Encoding not supported', E_USER_ERROR);
} }

View File

@@ -1,7 +0,0 @@
<?php
/**
* Return object from functions that signifies error when null doesn't cut it
*/
class HTMLPurifier_Error {}

View File

@@ -15,7 +15,7 @@ class HTMLPurifier_ErrorCollector
public function __construct($context) { public function __construct($context) {
$this->locale =& $context->get('Locale'); $this->locale =& $context->get('Locale');
$this->generator =& $context->get('Generator'); $this->generator =& $context->get('Generator');
$this->context =& $context; $this->context = $context;
} }
/** /**

View File

@@ -11,81 +11,79 @@ class HTMLPurifier_Generator
{ {
/** /**
* Bool cache of %HTML.XHTML * Whether or not generator should produce XML output
* @private
*/ */
private $_xhtml = true; private $_xhtml = true;
/** /**
* Bool cache of %Output.CommentScriptContents * :HACK: Whether or not generator should comment the insides of <script> tags
* @private
*/ */
private $_scriptFix = false; private $_scriptFix = false;
/** /**
* Cache of HTMLDefinition * Cache of HTMLDefinition during HTML output to determine whether or
* @private * not attributes should be minimized.
*/ */
private $_def; private $_def;
/**
* Configuration for the generator
*/
protected $config;
/**
* @param $config Instance of HTMLPurifier_Config
* @param $context Instance of HTMLPurifier_Context
*/
public function __construct($config = null, $context = null) {
if (!$config) $config = HTMLPurifier_Config::createDefault();
$this->config = $config;
$this->_scriptFix = $config->get('Output', 'CommentScriptContents');
$this->_def = $config->getHTMLDefinition();
$this->_xhtml = $this->_def->doctype->xml;
}
/** /**
* Generates HTML from an array of tokens. * Generates HTML from an array of tokens.
* @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
*/ */
public function generateFromTokens($tokens, $config, $context) { public function generateFromTokens($tokens) {
$html = '';
if (!$config) $config = HTMLPurifier_Config::createDefault();
$this->_scriptFix = $config->get('Output', 'CommentScriptContents');
$this->_def = $config->getHTMLDefinition();
$this->_xhtml = $this->_def->doctype->xml;
if (!$tokens) return ''; if (!$tokens) return '';
// Basic algorithm
$html = '';
for ($i = 0, $size = count($tokens); $i < $size; $i++) { for ($i = 0, $size = count($tokens); $i < $size; $i++) {
if ($this->_scriptFix && $tokens[$i]->name === 'script' if ($this->_scriptFix && $tokens[$i]->name === 'script'
&& $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) { && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) {
// script special case // script special case
// the contents of the script block must be ONE token // the contents of the script block must be ONE token
// for this to work // for this to work.
$html .= $this->generateFromToken($tokens[$i++]); $html .= $this->generateFromToken($tokens[$i++]);
$html .= $this->generateScriptFromToken($tokens[$i++]); $html .= $this->generateScriptFromToken($tokens[$i++]);
// We're not going to do this: it wouldn't be valid anyway
//while ($tokens[$i]->name != 'script') {
// $html .= $this->generateScriptFromToken($tokens[$i++]);
//}
} }
$html .= $this->generateFromToken($tokens[$i]); $html .= $this->generateFromToken($tokens[$i]);
} }
if ($config->get('Output', 'TidyFormat') && extension_loaded('tidy')) {
$tidy_options = array( // Tidy cleanup
if (extension_loaded('tidy') && $this->config->get('Output', 'TidyFormat')) {
$tidy = new Tidy;
$tidy->parseString($html, array(
'indent'=> true, 'indent'=> true,
'output-xhtml' => $this->_xhtml, 'output-xhtml' => $this->_xhtml,
'show-body-only' => true, 'show-body-only' => true,
'indent-spaces' => 2, 'indent-spaces' => 2,
'wrap' => 68, 'wrap' => 68,
); ), 'utf8');
if (version_compare(PHP_VERSION, '5', '<')) { $tidy->cleanRepair();
tidy_set_encoding('utf8'); $html = (string) $tidy; // explicit cast necessary
foreach ($tidy_options as $key => $value) {
tidy_setopt($key, $value);
}
tidy_parse_string($html);
tidy_clean_repair();
$html = tidy_get_output();
} else {
$tidy = new Tidy;
$tidy->parseString($html, $tidy_options, 'utf8');
$tidy->cleanRepair();
$html = (string) $tidy;
}
} }
// normalize newlines to system
$nl = $config->get('Output', 'Newline'); // Normalize newlines to system defined value
$nl = $this->config->get('Output', 'Newline');
if ($nl === null) $nl = PHP_EOL; if ($nl === null) $nl = PHP_EOL;
$html = str_replace("\n", $nl, $html); if ($nl !== "\n") $html = str_replace("\n", $nl, $html);
return $html; return $html;
} }
@@ -95,8 +93,11 @@ class HTMLPurifier_Generator
* @return Generated HTML * @return Generated HTML
*/ */
public function generateFromToken($token) { public function generateFromToken($token) {
if (!$token instanceof HTMLPurifier_Token) return ''; if (!$token instanceof HTMLPurifier_Token) {
if ($token instanceof HTMLPurifier_Token_Start) { trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING);
return '';
} elseif ($token instanceof HTMLPurifier_Token_Start) {
$attr = $this->generateAttributes($token->attr, $token->name); $attr = $this->generateAttributes($token->attr, $token->name);
return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>'; return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
@@ -106,11 +107,11 @@ class HTMLPurifier_Generator
} elseif ($token instanceof HTMLPurifier_Token_Empty) { } elseif ($token instanceof HTMLPurifier_Token_Empty) {
$attr = $this->generateAttributes($token->attr, $token->name); $attr = $this->generateAttributes($token->attr, $token->name);
return '<' . $token->name . ($attr ? ' ' : '') . $attr . return '<' . $token->name . ($attr ? ' ' : '') . $attr .
( $this->_xhtml ? ' /': '' ) ( $this->_xhtml ? ' /': '' ) // <br /> v. <br>
. '>'; . '>';
} elseif ($token instanceof HTMLPurifier_Token_Text) { } elseif ($token instanceof HTMLPurifier_Token_Text) {
return $this->escape($token->data); return $this->escape($token->data, ENT_NOQUOTES);
} elseif ($token instanceof HTMLPurifier_Token_Comment) { } elseif ($token instanceof HTMLPurifier_Token_Comment) {
return '<!--' . $token->data . '-->'; return '<!--' . $token->data . '-->';
@@ -127,25 +128,27 @@ class HTMLPurifier_Generator
*/ */
public function generateScriptFromToken($token) { public function generateScriptFromToken($token) {
if (!$token instanceof HTMLPurifier_Token_Text) return $this->generateFromToken($token); if (!$token instanceof HTMLPurifier_Token_Text) return $this->generateFromToken($token);
// return '<!--' . "\n" . trim($token->data) . "\n" . '// -->'; // Thanks <http://lachy.id.au/log/2005/05/script-comments>
// more advanced version:
// thanks <http://lachy.id.au/log/2005/05/script-comments>
$data = preg_replace('#//\s*$#', '', $token->data); $data = preg_replace('#//\s*$#', '', $token->data);
return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>'; return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>';
} }
/** /**
* Generates attribute declarations from attribute array. * Generates attribute declarations from attribute array.
* @note This does not include the leading or trailing space.
* @param $assoc_array_of_attributes Attribute array * @param $assoc_array_of_attributes Attribute array
* @param $element Name of element attributes are for, used to check
* attribute minimization.
* @return Generate HTML fragment for insertion. * @return Generate HTML fragment for insertion.
*/ */
public function generateAttributes($assoc_array_of_attributes, $element) { public function generateAttributes($assoc_array_of_attributes, $element = false) {
$html = ''; $html = '';
foreach ($assoc_array_of_attributes as $key => $value) { foreach ($assoc_array_of_attributes as $key => $value) {
if (!$this->_xhtml) { if (!$this->_xhtml) {
// remove namespaced attributes // Remove namespaced attributes
if (strpos($key, ':') !== false) continue; if (strpos($key, ':') !== false) continue;
if (!empty($this->_def->info[$element]->attr[$key]->minimized)) { // Check if we should minimize the attribute: val="val" -> val
if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) {
$html .= $key . ' '; $html .= $key . ' ';
continue; continue;
} }
@@ -157,11 +160,16 @@ class HTMLPurifier_Generator
/** /**
* Escapes raw text data. * Escapes raw text data.
* @todo This really ought to be protected, but until we have a facility
* for properly generating HTML here w/o using tokens, it stays
* public.
* @param $string String data to escape for HTML. * @param $string String data to escape for HTML.
* @param $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is
* permissible for non-attribute output.
* @return String escaped data. * @return String escaped data.
*/ */
public function escape($string) { public function escape($string, $quote = ENT_COMPAT) {
return htmlspecialchars($string, ENT_COMPAT, 'UTF-8'); return htmlspecialchars($string, $quote, 'UTF-8');
} }
} }

View File

@@ -95,11 +95,11 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
* HTMLPurifier_AttrTypes for details * HTMLPurifier_AttrTypes for details
*/ */
public function addAttribute($element_name, $attr_name, $def) { public function addAttribute($element_name, $attr_name, $def) {
$module =& $this->getAnonymousModule(); $module = $this->getAnonymousModule();
if (!isset($module->info[$element_name])) { if (!isset($module->info[$element_name])) {
$element =& $module->addBlankElement($element_name); $element = $module->addBlankElement($element_name);
} else { } else {
$element =& $module->info[$element_name]; $element = $module->info[$element_name];
} }
$element->attr[$attr_name] = $def; $element->attr[$attr_name] = $def;
} }
@@ -109,11 +109,11 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
* @note See HTMLPurifier_HTMLModule::addElement for detailed * @note See HTMLPurifier_HTMLModule::addElement for detailed
* parameter and return value descriptions. * parameter and return value descriptions.
*/ */
public function &addElement($element_name, $type, $contents, $attr_collections, $attributes) { public function addElement($element_name, $type, $contents, $attr_collections, $attributes) {
$module =& $this->getAnonymousModule(); $module = $this->getAnonymousModule();
// assume that if the user is calling this, the element // assume that if the user is calling this, the element
// is safe. This may not be a good idea // is safe. This may not be a good idea
$element =& $module->addElement($element_name, $type, $contents, $attr_collections, $attributes); $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes);
return $element; return $element;
} }
@@ -123,9 +123,9 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
* @note See HTMLPurifier_HTMLModule::addBlankElement for detailed * @note See HTMLPurifier_HTMLModule::addBlankElement for detailed
* parameter and return value descriptions. * parameter and return value descriptions.
*/ */
public function &addBlankElement($element_name) { public function addBlankElement($element_name) {
$module =& $this->getAnonymousModule(); $module = $this->getAnonymousModule();
$element =& $module->addBlankElement($element_name); $element = $module->addBlankElement($element_name);
return $element; return $element;
} }
@@ -134,7 +134,7 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
* bust out advanced features without having to make your own * bust out advanced features without having to make your own
* module. * module.
*/ */
public function &getAnonymousModule() { public function getAnonymousModule() {
if (!$this->_anonModule) { if (!$this->_anonModule) {
$this->_anonModule = new HTMLPurifier_HTMLModule(); $this->_anonModule = new HTMLPurifier_HTMLModule();
$this->_anonModule->name = 'Anonymous'; $this->_anonModule->name = 'Anonymous';
@@ -233,10 +233,10 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
$support = "(for information on implementing this, see the ". $support = "(for information on implementing this, see the ".
"support forums) "; "support forums) ";
// setup allowed elements // setup allowed elements -----------------------------------------
$allowed_elements = $config->get('HTML', 'AllowedElements'); $allowed_elements = $config->get('HTML', 'AllowedElements');
$allowed_attributes = $config->get('HTML', 'AllowedAttributes'); $allowed_attributes = $config->get('HTML', 'AllowedAttributes'); // retrieve early
if (!is_array($allowed_elements) && !is_array($allowed_attributes)) { if (!is_array($allowed_elements) && !is_array($allowed_attributes)) {
$allowed = $config->get('HTML', 'Allowed'); $allowed = $config->get('HTML', 'Allowed');
@@ -252,55 +252,80 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
} }
// emit errors // emit errors
foreach ($allowed_elements as $element => $d) { foreach ($allowed_elements as $element => $d) {
// :TODO: Is this htmlspecialchars() call really necessary? $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful!
$element = htmlspecialchars($element);
trigger_error("Element '$element' is not supported $support", E_USER_WARNING); trigger_error("Element '$element' is not supported $support", E_USER_WARNING);
} }
} }
// setup allowed attributes ---------------------------------------
$allowed_attributes_mutable = $allowed_attributes; // by copy! $allowed_attributes_mutable = $allowed_attributes; // by copy!
if (is_array($allowed_attributes)) { if (is_array($allowed_attributes)) {
foreach ($this->info_global_attr as $attr_key => $info) {
if (!isset($allowed_attributes["*.$attr_key"])) { // This actually doesn't do anything, since we went away from
unset($this->info_global_attr[$attr_key]); // global attributes. It's possible that userland code uses
} elseif (isset($allowed_attributes_mutable["*.$attr_key"])) { // it, but HTMLModuleManager doesn't!
unset($allowed_attributes_mutable["*.$attr_key"]); foreach ($this->info_global_attr as $attr => $x) {
$keys = array($attr, "*@$attr", "*.$attr");
$delete = true;
foreach ($keys as $key) {
if ($delete && isset($allowed_attributes[$key])) {
$delete = false;
}
if (isset($allowed_attributes_mutable[$key])) {
unset($allowed_attributes_mutable[$key]);
}
} }
if ($delete) unset($this->info_global_attr[$attr]);
} }
foreach ($this->info as $tag => $info) { foreach ($this->info as $tag => $info) {
foreach ($info->attr as $attr => $attr_info) { foreach ($info->attr as $attr => $x) {
if (!isset($allowed_attributes["$tag.$attr"]) && $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr");
!isset($allowed_attributes["*.$attr"])) { $delete = true;
unset($this->info[$tag]->attr[$attr]); foreach ($keys as $key) {
} else { if ($delete && isset($allowed_attributes[$key])) {
if (isset($allowed_attributes_mutable["$tag.$attr"])) { $delete = false;
unset($allowed_attributes_mutable["$tag.$attr"]); }
} elseif (isset($allowed_attributes_mutable["*.$attr"])) { if (isset($allowed_attributes_mutable[$key])) {
unset($allowed_attributes_mutable["*.$attr"]); unset($allowed_attributes_mutable[$key]);
} }
} }
if ($delete) unset($this->info[$tag]->attr[$attr]);
} }
} }
// emit errors // emit errors
foreach ($allowed_attributes_mutable as $elattr => $d) { foreach ($allowed_attributes_mutable as $elattr => $d) {
list($element, $attribute) = explode('.', $elattr); $bits = preg_split('/[.@]/', $elattr, 2);
// :TODO: Is this htmlspecialchars() call really necessary? $c = count($bits);
$element = htmlspecialchars($element); switch ($c) {
$attribute = htmlspecialchars($attribute); case 2:
if ($element == '*') { if ($bits[0] !== '*') {
trigger_error("Global attribute '$attribute' is not ". $element = htmlspecialchars($bits[0]);
"supported in any elements $support", $attribute = htmlspecialchars($bits[1]);
E_USER_WARNING); if (!isset($this->info[$element])) {
} else { trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support");
trigger_error("Attribute '$attribute' in element '$element' not supported $support", } else {
E_USER_WARNING); trigger_error("Attribute '$attribute' in element '$element' not supported $support",
E_USER_WARNING);
}
break;
}
// otherwise fall through
case 1:
$attribute = htmlspecialchars($bits[0]);
trigger_error("Global attribute '$attribute' is not ".
"supported in any elements $support",
E_USER_WARNING);
break;
} }
} }
} }
// setup forbidden elements // setup forbidden elements ---------------------------------------
$forbidden_elements = $config->get('HTML', 'ForbiddenElements');
$forbidden_elements = $config->get('HTML', 'ForbiddenElements');
$forbidden_attributes = $config->get('HTML', 'ForbiddenAttributes'); $forbidden_attributes = $config->get('HTML', 'ForbiddenAttributes');
foreach ($this->info as $tag => $info) { foreach ($this->info as $tag => $info) {
@@ -308,13 +333,28 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
unset($this->info[$tag]); unset($this->info[$tag]);
continue; continue;
} }
foreach ($info->attr as $name => $def) { foreach ($info->attr as $attr => $x) {
if (isset($forbidden_attributes["$tag.$name"])) { if (
unset($this->info[$tag]->attr[$name]); isset($forbidden_attributes["$tag@$attr"]) ||
isset($forbidden_attributes["*@$attr"]) ||
isset($forbidden_attributes[$attr])
) {
unset($this->info[$tag]->attr[$attr]);
continue; continue;
} // this segment might get removed eventually
elseif (isset($forbidden_attributes["$tag.$attr"])) {
// $tag.$attr are not user supplied, so no worries!
trigger_error("Error with $tag.$attr: tag.attr syntax not supported for HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING);
} }
} }
} }
foreach ($forbidden_attributes as $key => $v) {
if (strlen($key) < 2) continue;
if ($key[0] != '*') continue;
if ($key[1] == '.') {
trigger_error("Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING);
}
}
} }
@@ -329,6 +369,8 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
*/ */
public function parseTinyMCEAllowedList($list) { public function parseTinyMCEAllowedList($list) {
$list = str_replace(array(' ', "\t"), '', $list);
$elements = array(); $elements = array();
$attributes = array(); $attributes = array();

View File

@@ -116,10 +116,10 @@ class HTMLPurifier_HTMLModule
* element? * element?
* @param $attr What unique attributes does the element define? * @param $attr What unique attributes does the element define?
* @note See ElementDef for in-depth descriptions of these parameters. * @note See ElementDef for in-depth descriptions of these parameters.
* @return Reference to created element definition object, so you * @return Created element definition object, so you
* can set advanced parameters * can set advanced parameters
*/ */
public function &addElement($element, $type, $contents, $attr_includes = array(), $attr = array()) { public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array()) {
$this->elements[] = $element; $this->elements[] = $element;
// parse content_model // parse content_model
list($content_model_type, $content_model) = $this->parseContents($contents); list($content_model_type, $content_model) = $this->parseContents($contents);
@@ -140,9 +140,9 @@ class HTMLPurifier_HTMLModule
* Convenience function that creates a totally blank, non-standalone * Convenience function that creates a totally blank, non-standalone
* element. * element.
* @param $element Name of element to create * @param $element Name of element to create
* @return Reference to created element * @return Created element
*/ */
public function &addBlankElement($element) { public function addBlankElement($element) {
if (!isset($this->info[$element])) { if (!isset($this->info[$element])) {
$this->elements[] = $element; $this->elements[] = $element;
$this->info[$element] = new HTMLPurifier_ElementDef(); $this->info[$element] = new HTMLPurifier_ElementDef();

View File

@@ -13,7 +13,7 @@ class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule
); );
public function __construct() { public function __construct() {
$bdo =& $this->addElement( $bdo = $this->addElement(
'bdo', 'Inline', 'Inline', array('Core', 'Lang'), 'bdo', 'Inline', 'Inline', array('Core', 'Lang'),
array( array(
'dir' => 'Enum#ltr,rtl', // required 'dir' => 'Enum#ltr,rtl', // required

View File

@@ -9,7 +9,7 @@ class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule
public $name = 'Hypertext'; public $name = 'Hypertext';
public function __construct() { public function __construct() {
$a =& $this->addElement( $a = $this->addElement(
'a', 'Inline', 'Inline', 'Common', 'a', 'Inline', 'Inline', 'Common',
array( array(
// 'accesskey' => 'Character', // 'accesskey' => 'Character',

View File

@@ -11,7 +11,7 @@ class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule
public $name = 'Image'; public $name = 'Image';
public function __construct() { public function __construct() {
$img =& $this->addElement( $img = $this->addElement(
'img', 'Inline', 'Empty', 'Common', 'img', 'Inline', 'Empty', 'Common',
array( array(
'alt*' => 'Text', 'alt*' => 'Text',

View File

@@ -49,40 +49,40 @@ class HTMLPurifier_HTMLModule_Legacy extends HTMLPurifier_HTMLModule
$align = 'Enum#left,right,center,justify'; $align = 'Enum#left,right,center,justify';
$address =& $this->addBlankElement('address'); $address = $this->addBlankElement('address');
$address->content_model = 'Inline | #PCDATA | p'; $address->content_model = 'Inline | #PCDATA | p';
$address->content_model_type = 'optional'; $address->content_model_type = 'optional';
$address->child = false; $address->child = false;
$blockquote =& $this->addBlankElement('blockquote'); $blockquote = $this->addBlankElement('blockquote');
$blockquote->content_model = 'Flow | #PCDATA'; $blockquote->content_model = 'Flow | #PCDATA';
$blockquote->content_model_type = 'optional'; $blockquote->content_model_type = 'optional';
$blockquote->child = false; $blockquote->child = false;
$br =& $this->addBlankElement('br'); $br = $this->addBlankElement('br');
$br->attr['clear'] = 'Enum#left,all,right,none'; $br->attr['clear'] = 'Enum#left,all,right,none';
$caption =& $this->addBlankElement('caption'); $caption = $this->addBlankElement('caption');
$caption->attr['align'] = 'Enum#top,bottom,left,right'; $caption->attr['align'] = 'Enum#top,bottom,left,right';
$div =& $this->addBlankElement('div'); $div = $this->addBlankElement('div');
$div->attr['align'] = $align; $div->attr['align'] = $align;
$dl =& $this->addBlankElement('dl'); $dl = $this->addBlankElement('dl');
$dl->attr['compact'] = 'Bool#compact'; $dl->attr['compact'] = 'Bool#compact';
for ($i = 1; $i <= 6; $i++) { for ($i = 1; $i <= 6; $i++) {
$h =& $this->addBlankElement("h$i"); $h = $this->addBlankElement("h$i");
$h->attr['align'] = $align; $h->attr['align'] = $align;
} }
$hr =& $this->addBlankElement('hr'); $hr = $this->addBlankElement('hr');
$hr->attr['align'] = $align; $hr->attr['align'] = $align;
$hr->attr['noshade'] = 'Bool#noshade'; $hr->attr['noshade'] = 'Bool#noshade';
$hr->attr['size'] = 'Pixels'; $hr->attr['size'] = 'Pixels';
$hr->attr['width'] = 'Length'; $hr->attr['width'] = 'Length';
$img =& $this->addBlankElement('img'); $img = $this->addBlankElement('img');
$img->attr['align'] = 'Enum#top,middle,bottom,left,right'; $img->attr['align'] = 'Enum#top,middle,bottom,left,right';
$img->attr['border'] = 'Pixels'; $img->attr['border'] = 'Pixels';
$img->attr['hspace'] = 'Pixels'; $img->attr['hspace'] = 'Pixels';
@@ -90,43 +90,43 @@ class HTMLPurifier_HTMLModule_Legacy extends HTMLPurifier_HTMLModule
// figure out this integer business // figure out this integer business
$li =& $this->addBlankElement('li'); $li = $this->addBlankElement('li');
$li->attr['value'] = new HTMLPurifier_AttrDef_Integer(); $li->attr['value'] = new HTMLPurifier_AttrDef_Integer();
$li->attr['type'] = 'Enum#s:1,i,I,a,A,disc,square,circle'; $li->attr['type'] = 'Enum#s:1,i,I,a,A,disc,square,circle';
$ol =& $this->addBlankElement('ol'); $ol = $this->addBlankElement('ol');
$ol->attr['compact'] = 'Bool#compact'; $ol->attr['compact'] = 'Bool#compact';
$ol->attr['start'] = new HTMLPurifier_AttrDef_Integer(); $ol->attr['start'] = new HTMLPurifier_AttrDef_Integer();
$ol->attr['type'] = 'Enum#s:1,i,I,a,A'; $ol->attr['type'] = 'Enum#s:1,i,I,a,A';
$p =& $this->addBlankElement('p'); $p = $this->addBlankElement('p');
$p->attr['align'] = $align; $p->attr['align'] = $align;
$pre =& $this->addBlankElement('pre'); $pre = $this->addBlankElement('pre');
$pre->attr['width'] = 'Number'; $pre->attr['width'] = 'Number';
// script omitted // script omitted
$table =& $this->addBlankElement('table'); $table = $this->addBlankElement('table');
$table->attr['align'] = 'Enum#left,center,right'; $table->attr['align'] = 'Enum#left,center,right';
$table->attr['bgcolor'] = 'Color'; $table->attr['bgcolor'] = 'Color';
$tr =& $this->addBlankElement('tr'); $tr = $this->addBlankElement('tr');
$tr->attr['bgcolor'] = 'Color'; $tr->attr['bgcolor'] = 'Color';
$th =& $this->addBlankElement('th'); $th = $this->addBlankElement('th');
$th->attr['bgcolor'] = 'Color'; $th->attr['bgcolor'] = 'Color';
$th->attr['height'] = 'Length'; $th->attr['height'] = 'Length';
$th->attr['nowrap'] = 'Bool#nowrap'; $th->attr['nowrap'] = 'Bool#nowrap';
$th->attr['width'] = 'Length'; $th->attr['width'] = 'Length';
$td =& $this->addBlankElement('td'); $td = $this->addBlankElement('td');
$td->attr['bgcolor'] = 'Color'; $td->attr['bgcolor'] = 'Color';
$td->attr['height'] = 'Length'; $td->attr['height'] = 'Length';
$td->attr['nowrap'] = 'Bool#nowrap'; $td->attr['nowrap'] = 'Bool#nowrap';
$td->attr['width'] = 'Length'; $td->attr['width'] = 'Length';
$ul =& $this->addBlankElement('ul'); $ul = $this->addBlankElement('ul');
$ul->attr['compact'] = 'Bool#compact'; $ul->attr['compact'] = 'Bool#compact';
$ul->attr['type'] = 'Enum#square,disc,circle'; $ul->attr['type'] = 'Enum#square,disc,circle';

View File

@@ -15,9 +15,9 @@ class HTMLPurifier_HTMLModule_Ruby extends HTMLPurifier_HTMLModule
'Common'); 'Common');
$this->addElement('rbc', false, 'Required: rb', 'Common'); $this->addElement('rbc', false, 'Required: rb', 'Common');
$this->addElement('rtc', false, 'Required: rt', 'Common'); $this->addElement('rtc', false, 'Required: rt', 'Common');
$rb =& $this->addElement('rb', false, 'Inline', 'Common'); $rb = $this->addElement('rb', false, 'Inline', 'Common');
$rb->excludes = array('ruby' => true); $rb->excludes = array('ruby' => true);
$rt =& $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number')); $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number'));
$rt->excludes = array('ruby' => true); $rt->excludes = array('ruby' => true);
$this->addElement('rp', false, 'Optional: #PCDATA', 'Common'); $this->addElement('rp', false, 'Optional: #PCDATA', 'Common');
} }

View File

@@ -7,19 +7,6 @@ INSIDE HTML PURIFIER DOCUMENTS. USE ONLY WITH TRUSTED USER INPUT!!!
*/ */
/**
* Implements required attribute stipulation for <script>
*/
class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform
{
public function transform($attr, $config, $context) {
if (!isset($attr['type'])) {
$attr['type'] = 'text/javascript';
}
return $attr;
}
}
/** /**
* XHTML 1.1 Scripting module, defines elements that are used to contain * XHTML 1.1 Scripting module, defines elements that are used to contain
* information pertaining to executable scripts or the lack of support * information pertaining to executable scripts or the lack of support

View File

@@ -11,7 +11,7 @@ class HTMLPurifier_HTMLModule_Target extends HTMLPurifier_HTMLModule
public function __construct() { public function __construct() {
$elements = array('a'); $elements = array('a');
foreach ($elements as $name) { foreach ($elements as $name) {
$e =& $this->addBlankElement($name); $e = $this->addBlankElement($name);
$e->attr = array( $e->attr = array(
'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget() 'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget()
); );

View File

@@ -42,7 +42,7 @@ class HTMLPurifier_HTMLModule_Text extends HTMLPurifier_HTMLModule
// Block Phrasal -------------------------------------------------- // Block Phrasal --------------------------------------------------
$this->addElement('address', 'Block', 'Inline', 'Common'); $this->addElement('address', 'Block', 'Inline', 'Common');
$this->addElement('blockquote', 'Block', 'Optional: Heading | Block | List', 'Common', array('cite' => 'URI') ); $this->addElement('blockquote', 'Block', 'Optional: Heading | Block | List', 'Common', array('cite' => 'URI') );
$pre =& $this->addElement('pre', 'Block', 'Inline', 'Common'); $pre = $this->addElement('pre', 'Block', 'Inline', 'Common');
$pre->excludes = $this->makeLookup( $pre->excludes = $this->makeLookup(
'img', 'big', 'small', 'object', 'applet', 'font', 'basefont' ); 'img', 'big', 'small', 'object', 'applet', 'font', 'basefont' );
$this->addElement('h1', 'Heading', 'Inline', 'Common'); $this->addElement('h1', 'Heading', 'Inline', 'Common');

View File

@@ -128,14 +128,16 @@ class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule
if (isset($params['element'])) { if (isset($params['element'])) {
$element = $params['element']; $element = $params['element'];
if (empty($this->info[$element])) { if (empty($this->info[$element])) {
$e =& $this->addBlankElement($element); $e = $this->addBlankElement($element);
} else { } else {
$e =& $this->info[$element]; $e = $this->info[$element];
} }
} else { } else {
$type = "info_$type"; $type = "info_$type";
$e =& $this; $e = $this;
} }
// PHP does some weird parsing when I do
// $e->$type[$attr], so I have to assign a ref.
$f =& $e->$type; $f =& $e->$type;
$f[$attr] = $fix; $f[$attr] = $fix;
break; break;
@@ -146,9 +148,9 @@ class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule
case 'content_model_type': case 'content_model_type':
$element = $params['element']; $element = $params['element'];
if (empty($this->info[$element])) { if (empty($this->info[$element])) {
$e =& $this->addBlankElement($element); $e = $this->addBlankElement($element);
} else { } else {
$e =& $this->info[$element]; $e = $this->info[$element];
} }
$e->$type = $fix; $e->$type = $fix;
break; break;

View File

@@ -1,5 +1,9 @@
<?php <?php
/**
* Represents a language and defines localizable string formatting and
* other functions, as well as the localized messages for HTML Purifier.
*/
class HTMLPurifier_Language class HTMLPurifier_Language
{ {
@@ -23,6 +27,13 @@ class HTMLPurifier_Language
*/ */
public $errorNames = array(); public $errorNames = array();
/**
* True if no message file was found for this language, so English
* is being used instead. Check this if you'd like to notify the
* user that they've used a non-supported language.
*/
public $error = false;
/** /**
* Has the language object been loaded yet? * Has the language object been loaded yet?
* @todo Make it private, fix usage in HTMLPurifier_LanguageTest * @todo Make it private, fix usage in HTMLPurifier_LanguageTest
@@ -36,7 +47,7 @@ class HTMLPurifier_Language
public function __construct($config, $context) { public function __construct($config, $context) {
$this->config = $config; $this->config = $config;
$this->context =& $context; $this->context = $context;
} }
/** /**
@@ -122,7 +133,7 @@ class HTMLPurifier_Language
// could be introduced for all types of tokens. This // could be introduced for all types of tokens. This
// may need to be factored out into a dedicated class // may need to be factored out into a dedicated class
if (!empty($value->attr)) { if (!empty($value->attr)) {
$stripped_token = $value->copy(); $stripped_token = clone $value;
$stripped_token->attr = array(); $stripped_token->attr = array();
$subst['$'.$i.'.Compact'] = $generator->generateFromToken($stripped_token); $subst['$'.$i.'.Compact'] = $generator->generateFromToken($stripped_token);
} }

View File

@@ -0,0 +1,11 @@
<?php
// private language message file for unit testing purposes
// this language file has no class associated with it
$fallback = 'en';
$messages = array(
'HTMLPurifier' => 'HTML Purifier XNone'
);

View File

@@ -5,6 +5,7 @@
* caching and fallbacks. * caching and fallbacks.
* @note Thanks to MediaWiki for the general logic, although this version * @note Thanks to MediaWiki for the general logic, although this version
* has been entirely rewritten * has been entirely rewritten
* @todo Serialized cache for languages
*/ */
class HTMLPurifier_LanguageFactory class HTMLPurifier_LanguageFactory
{ {
@@ -53,7 +54,7 @@ class HTMLPurifier_LanguageFactory
* @param $prototype Optional prototype to overload sole instance with, * @param $prototype Optional prototype to overload sole instance with,
* or bool true to reset to default factory. * or bool true to reset to default factory.
*/ */
public static function &instance($prototype = null) { public static function instance($prototype = null) {
static $instance = null; static $instance = null;
if ($prototype !== null) { if ($prototype !== null) {
$instance = $prototype; $instance = $prototype;
@@ -77,41 +78,43 @@ class HTMLPurifier_LanguageFactory
* Creates a language object, handles class fallbacks * Creates a language object, handles class fallbacks
* @param $config Instance of HTMLPurifier_Config * @param $config Instance of HTMLPurifier_Config
* @param $context Instance of HTMLPurifier_Context * @param $context Instance of HTMLPurifier_Context
* @param $code Code to override configuration with. Private parameter.
*/ */
public function create($config, $context) { public function create($config, $context, $code = false) {
// validate language code // validate language code
$code = $this->validator->validate( if ($code === false) {
$config->get('Core', 'Language'), $config, $context $code = $this->validator->validate(
); $config->get('Core', 'Language'), $config, $context
);
} else {
$code = $this->validator->validate($code, $config, $context);
}
if ($code === false) $code = 'en'; // malformed code becomes English if ($code === false) $code = 'en'; // malformed code becomes English
$pcode = str_replace('-', '_', $code); // make valid PHP classname $pcode = str_replace('-', '_', $code); // make valid PHP classname
static $depth = 0; // recursion protection static $depth = 0; // recursion protection
if ($code == 'en') { if ($code == 'en') {
$class = 'HTMLPurifier_Language'; $lang = new HTMLPurifier_Language($config, $context);
$file = $this->dir . '/Language.php';
} else { } else {
$class = 'HTMLPurifier_Language_' . $pcode; $class = 'HTMLPurifier_Language_' . $pcode;
$file = $this->dir . '/Language/classes/' . $code . '.php'; $file = $this->dir . '/Language/classes/' . $code . '.php';
// PHP5/APC deps bug workaround can go here if (file_exists($file) || class_exists($class, false)) {
// you can bypass the conditional include by loading the $lang = new $class($config, $context);
// file yourself } else {
if (file_exists($file) && !class_exists($class, false)) { // Go fallback
include_once $file; $raw_fallback = $this->getFallbackFor($code);
$fallback = $raw_fallback ? $raw_fallback : 'en';
$depth++;
$lang = $this->create($config, $context, $fallback);
if (!$raw_fallback) {
$lang->error = true;
}
$depth--;
} }
} }
if (!class_exists($class, false)) {
// go fallback
$fallback = HTMLPurifier_LanguageFactory::getFallbackFor($code);
$depth++;
$lang = HTMLPurifier_LanguageFactory::factory( $fallback );
$depth--;
} else {
$lang = new $class($config, $context);
}
$lang->code = $code; $lang->code = $code;
return $lang; return $lang;

View File

@@ -11,7 +11,8 @@
* *
* A lexer is HTML-oriented: it might work with XML, but it's not * A lexer is HTML-oriented: it might work with XML, but it's not
* recommended, as we adhere to a subset of the specification for optimization * recommended, as we adhere to a subset of the specification for optimization
* reasons. * reasons. This might change in the future. Also, most tokenizers are not
* expected to handle DTDs or PIs.
* *
* This class should not be directly instantiated, but you may use create() to * This class should not be directly instantiated, but you may use create() to
* retrieve a default copy of the lexer. Being a supertype, this class * retrieve a default copy of the lexer. Being a supertype, this class
@@ -20,7 +21,8 @@
* *
* @note The unit tests will instantiate this class for testing purposes, as * @note The unit tests will instantiate this class for testing purposes, as
* many of the utility functions require a class to be instantiated. * many of the utility functions require a class to be instantiated.
* Be careful when porting this class to PHP 5. * This means that, even though this class is not runnable, it will
* not be declared abstract.
* *
* @par * @par
* *
@@ -28,18 +30,14 @@
* We use tokens rather than create a DOM representation because DOM would: * We use tokens rather than create a DOM representation because DOM would:
* *
* @par * @par
* -# Require more processing power to create, * -# Require more processing and memory to create,
* -# Require recursion to iterate, * -# Is not streamable, and
* -# Must be compatible with PHP 5's DOM (otherwise duplication), * -# Has the entire document structure (html and body not needed).
* -# Has the entire document structure (html and body not needed), and
* -# Has unknown readability improvement.
* *
* @par * @par
* What the last item means is that the functions for manipulating tokens are * However, DOM is helpful in that it makes it easy to move around nodes
* already fairly compact, and when well-commented, more abstraction may not * without a lot of lookaheads to see when a tag is closed. This is a
* be needed. * limitation of the token system and some workarounds would be nice.
*
* @see HTMLPurifier_Token
*/ */
class HTMLPurifier_Lexer class HTMLPurifier_Lexer
{ {
@@ -49,22 +47,16 @@ class HTMLPurifier_Lexer
/** /**
* Retrieves or sets the default Lexer as a Prototype Factory. * Retrieves or sets the default Lexer as a Prototype Factory.
* *
* Depending on what PHP version you are running, the abstract base * By default HTMLPurifier_Lexer_DOMLex will be returned. There are
* Lexer class will determine which concrete Lexer is best for you: * a few exceptions involving special features that only DirectLex
* HTMLPurifier_Lexer_DirectLex for PHP 4, and HTMLPurifier_Lexer_DOMLex * implements.
* for PHP 5 and beyond. This general rule has a few exceptions to it
* involving special features that only DirectLex implements.
* *
* @note The behavior of this class has changed, rather than accepting * @note The behavior of this class has changed, rather than accepting
* a prototype object, it now accepts a configuration object. * a prototype object, it now accepts a configuration object.
* To specify your own prototype, set %Core.LexerImpl to it. * To specify your own prototype, set %Core.LexerImpl to it.
* This change in behavior de-singletonizes the lexer object. * This change in behavior de-singletonizes the lexer object.
* *
* @note In PHP4, it is possible to call this factory method from * @param $config Instance of HTMLPurifier_Config
* subclasses, such usage is not recommended and not
* forwards-compatible.
*
* @param $prototype Optional prototype lexer or configuration object
* @return Concrete lexer. * @return Concrete lexer.
*/ */
public static function create($config) { public static function create($config) {
@@ -96,8 +88,9 @@ class HTMLPurifier_Lexer
break; break;
} }
if (version_compare(PHP_VERSION, "5", ">=") && // check for PHP5 if (class_exists('DOMDocument')) {
class_exists('DOMDocument')) { // check for DOM support // check for DOM support, because, surprisingly enough,
// it's *not* part of the core!
$lexer = 'DOMLex'; $lexer = 'DOMLex';
} else { } else {
$lexer = 'DirectLex'; $lexer = 'DirectLex';

View File

@@ -2,17 +2,28 @@
/** /**
* Experimental HTML5-based parser using Jeroen van der Meer's PH5P library. * Experimental HTML5-based parser using Jeroen van der Meer's PH5P library.
* Requires PHP5, and occupies space in the HTML5 pseudo-namespace (may * Occupies space in the HTML5 pseudo-namespace, which may cause conflicts.
* cause conflicts, sorry). *
* @note
* Recent changes to PHP's DOM extension have resulted in some fatal
* error conditions with the original version of PH5P. Pending changes,
* this lexer will punt to DirectLex if DOM throughs an exception.
*/ */
class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex { class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex {
public function tokenizeHTML($html, $config, $context) { public function tokenizeHTML($html, $config, $context) {
$html = $this->normalize($html, $config, $context); $new_html = $this->normalize($html, $config, $context);
$html = $this->wrapHTML( $html, $config, $context); $new_html = $this->wrapHTML($new_html, $config, $context);
$parser = new HTML5($html); try {
$doc = $parser->save(); $parser = new HTML5($new_html);
$doc = $parser->save();
} catch (DOMException $e) {
// Uh oh, it failed. Punt to DirectLex.
$lexer = new HTMLPurifier_Lexer_DirectLex();
$context->register('PH5PError', $e); // save the error, so we can detect it
return $lexer->tokenizeHTML($html, $config, $context); // use original HTML
}
$tokens = array(); $tokens = array();
$this->tokenizeDOM( $this->tokenizeDOM(
$doc->getElementsByTagName('html')->item(0)-> // <html> $doc->getElementsByTagName('html')->item(0)-> // <html>

View File

@@ -2,12 +2,68 @@
/** /**
* Class that handles operations involving percent-encoding in URIs. * Class that handles operations involving percent-encoding in URIs.
*
* @warning
* Be careful when reusing instances of PercentEncoder. The object
* you use for normalize() SHOULD NOT be used for encode(), or
* vice-versa.
*/ */
class HTMLPurifier_PercentEncoder class HTMLPurifier_PercentEncoder
{ {
/** /**
* Fix up percent-encoding by decoding unreserved characters and normalizing * Reserved characters to preserve when using encode().
*/
protected $preserve = array();
/**
* String of characters that should be preserved while using encode().
*/
public function __construct($preserve = false) {
// unreserved letters, ought to const-ify
for ($i = 48; $i <= 57; $i++) $this->preserve[$i] = true; // digits
for ($i = 65; $i <= 90; $i++) $this->preserve[$i] = true; // upper-case
for ($i = 97; $i <= 122; $i++) $this->preserve[$i] = true; // lower-case
$this->preserve[45] = true; // Dash -
$this->preserve[46] = true; // Period .
$this->preserve[95] = true; // Underscore _
$this->preserve[126]= true; // Tilde ~
// extra letters not to escape
if ($preserve !== false) {
for ($i = 0, $c = strlen($preserve); $i < $c; $i++) {
$this->preserve[ord($preserve[$i])] = true;
}
}
}
/**
* Our replacement for urlencode, it encodes all non-reserved characters,
* as well as any extra characters that were instructed to be preserved.
* @note
* Assumes that the string has already been normalized, making any
* and all percent escape sequences valid. Percents will not be
* re-escaped, regardless of their status in $preserve
* @param $string String to be encoded
* @return Encoded string.
*/
public function encode($string) {
$ret = '';
for ($i = 0, $c = strlen($string); $i < $c; $i++) {
if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])]) ) {
$ret .= '%' . sprintf('%02X', $int);
} else {
$ret .= $string[$i];
}
}
return $ret;
}
/**
* Fix up percent-encoding by decoding unreserved characters and normalizing.
* @warning This function is affected by $preserve, even though the
* usual desired behavior is for this not to preserve those
* characters. Be careful when reusing instances of PercentEncoder!
* @param $string String to normalize * @param $string String to normalize
*/ */
public function normalize($string) { public function normalize($string) {
@@ -27,12 +83,7 @@ class HTMLPurifier_PercentEncoder
continue; continue;
} }
$int = hexdec($encoding); $int = hexdec($encoding);
if ( if (isset($this->preserve[$int])) {
($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; $ret .= chr($int) . $text;
continue; continue;
} }

View File

@@ -1,6 +1,7 @@
<?php <?php
// OUT OF DATE, NEEDS UPDATING! // OUT OF DATE, NEEDS UPDATING!
// USE XMLWRITER!
class HTMLPurifier_Printer class HTMLPurifier_Printer
{ {

View File

@@ -55,14 +55,14 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
/** /**
* Retrieves styling, in case it is not accessible by webserver * Retrieves styling, in case it is not accessible by webserver
*/ */
public function getCSS() { public static function getCSS() {
return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css'); return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css');
} }
/** /**
* Retrieves JavaScript, in case it is not accessible by webserver * Retrieves JavaScript, in case it is not accessible by webserver
*/ */
public function getJavaScript() { public static function getJavaScript() {
return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js'); return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js');
} }
@@ -86,8 +86,8 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
$ret .= $this->start('table', array('class' => 'hp-config')); $ret .= $this->start('table', array('class' => 'hp-config'));
$ret .= $this->start('thead'); $ret .= $this->start('thead');
$ret .= $this->start('tr'); $ret .= $this->start('tr');
$ret .= $this->element('th', 'Directive'); $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive'));
$ret .= $this->element('th', 'Value'); $ret .= $this->element('th', 'Value', array('class' => 'hp-value'));
$ret .= $this->end('tr'); $ret .= $this->end('tr');
$ret .= $this->end('thead'); $ret .= $this->end('thead');
foreach ($all as $ns => $directives) { foreach ($all as $ns => $directives) {
@@ -194,6 +194,10 @@ class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
'id' => "$name:Null_$ns.$directive", 'id' => "$name:Null_$ns.$directive",
'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!! 'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!!
); );
if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) {
// modify inline javascript slightly
$attr['onclick'] = "toggleWriteability('$name:Yes_$ns.$directive',checked);toggleWriteability('$name:No_$ns.$directive',checked)";
}
if ($value === null) $attr['checked'] = 'checked'; if ($value === null) $attr['checked'] = 'checked';
$ret .= $this->elementEmpty('input', $attr); $ret .= $this->elementEmpty('input', $attr);
$ret .= $this->text(' or '); $ret .= $this->text(' or ');
@@ -291,7 +295,8 @@ class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer {
'id' => "$name:Yes_$ns.$directive", 'id' => "$name:Yes_$ns.$directive",
'value' => '1' 'value' => '1'
); );
if ($value) $attr['checked'] = 'checked'; if ($value === true) $attr['checked'] = 'checked';
if ($value === null) $attr['disabled'] = 'disabled';
$ret .= $this->elementEmpty('input', $attr); $ret .= $this->elementEmpty('input', $attr);
$ret .= $this->start('label', array('for' => "$name:No_$ns.$directive")); $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive"));
@@ -305,7 +310,8 @@ class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer {
'id' => "$name:No_$ns.$directive", 'id' => "$name:No_$ns.$directive",
'value' => '0' 'value' => '0'
); );
if (!$value) $attr['checked'] = 'checked'; if ($value === false) $attr['checked'] = 'checked';
if ($value === null) $attr['disabled'] = 'disabled';
$ret .= $this->elementEmpty('input', $attr); $ret .= $this->elementEmpty('input', $attr);
$ret .= $this->end('div'); $ret .= $this->end('div');

View File

@@ -37,7 +37,7 @@ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
public function transform($tag, $config, $context) { public function transform($tag, $config, $context) {
if ($tag instanceof HTMLPurifier_Token_End) { if ($tag instanceof HTMLPurifier_Token_End) {
$new_tag = $tag->copy(); $new_tag = clone $tag;
$new_tag->name = $this->transform_to; $new_tag->name = $this->transform_to;
return $new_tag; return $new_tag;
} }
@@ -81,7 +81,7 @@ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
$prepend_style; $prepend_style;
} }
$new_tag = $tag->copy(); $new_tag = clone $tag;
$new_tag->name = $this->transform_to; $new_tag->name = $this->transform_to;
$new_tag->attr = $attr; $new_tag->attr = $attr;

View File

@@ -20,7 +20,7 @@ class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform
} }
public function transform($tag, $config, $context) { public function transform($tag, $config, $context) {
$new_tag = $tag->copy(); $new_tag = clone $tag;
$new_tag->name = $this->transform_to; $new_tag->name = $this->transform_to;
if (!is_null($this->style) && if (!is_null($this->style) &&
($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty) ($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty)

View File

@@ -14,14 +14,6 @@ class HTMLPurifier_Token {
*/ */
public $armor = array(); public $armor = array();
/**
* Copies the tag into a new one (clone substitute).
* @return Copied token
*/
public function copy() {
return unserialize(serialize($this));
}
public function __get($n) { public function __get($n) {
if ($n === 'type') { if ($n === 'type') {
trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE); trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);

View File

@@ -1,15 +1,15 @@
<?php <?php
/** /**
* Factory for token generation (PHP 5 only). * Factory for token generation.
* *
* @note Doing some benchmarking indicates that the new operator is much * @note Doing some benchmarking indicates that the new operator is much
* slower than the clone operator (even discounting the cost of the * slower than the clone operator (even discounting the cost of the
* constructor). This class is for that optimization. We may want to * constructor). This class is for that optimization.
* consider porting this to PHP 4 by virtue of the fact it makes the code * Other then that, there's not much point as we don't
* easier to read. Other then that, there's not much point as we don't
* maintain parallel HTMLPurifier_Token hierarchies (the main reason why * maintain parallel HTMLPurifier_Token hierarchies (the main reason why
* you'd want to use an abstract factory). * you'd want to use an abstract factory).
* @todo Port DirectLex to use this
*/ */
class HTMLPurifier_TokenFactory class HTMLPurifier_TokenFactory
{ {

View File

@@ -1,7 +1,12 @@
<?php <?php
/** /**
* HTML Purifier's internal representation of a URI * HTML Purifier's internal representation of a URI.
* @note
* Internal data-structures are completely escaped. If the data needs
* to be used in a non-URI context (which is very unlikely), be sure
* to decode it first. The URI may not necessarily be well-formed until
* validate() is called.
*/ */
class HTMLPurifier_URI class HTMLPurifier_URI
{ {
@@ -49,13 +54,27 @@ class HTMLPurifier_URI
} }
/** /**
* Generic validation method applicable for all schemes * Generic validation method applicable for all schemes. May modify
* this URI in order to get it into a compliant form.
* @param $config Instance of HTMLPurifier_Config * @param $config Instance of HTMLPurifier_Config
* @param $context Instance of HTMLPurifier_Context * @param $context Instance of HTMLPurifier_Context
* @return True if validation/filtering succeeds, false if failure * @return True if validation/filtering succeeds, false if failure
*/ */
public function validate($config, $context) { public function validate($config, $context) {
// ABNF definitions from RFC 3986
$chars_sub_delims = '!$&\'()*+,;=';
$chars_gen_delims = ':/?#[]@';
$chars_pchar = $chars_sub_delims . ':@';
// validate scheme (MUST BE FIRST!)
if (!is_null($this->scheme) && is_null($this->host)) {
$def = $config->getDefinition('URI');
if ($def->defaultScheme === $this->scheme) {
$this->scheme = null;
}
}
// validate host // validate host
if (!is_null($this->host)) { if (!is_null($this->host)) {
$host_def = new HTMLPurifier_AttrDef_URI_Host(); $host_def = new HTMLPurifier_AttrDef_URI_Host();
@@ -63,18 +82,51 @@ class HTMLPurifier_URI
if ($this->host === false) $this->host = null; if ($this->host === false) $this->host = null;
} }
// validate username
if (!is_null($this->userinfo)) {
$encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':');
$this->userinfo = $encoder->encode($this->userinfo);
}
// validate port // validate port
if (!is_null($this->port)) { if (!is_null($this->port)) {
if ($this->port < 1 || $this->port > 65535) $this->port = null; if ($this->port < 1 || $this->port > 65535) $this->port = null;
} }
// query and fragment are quite simple in terms of definition: // validate path
// *( pchar / "/" / "?" ), so define their validation routines $path_parts = array();
// when we start fixing percent encoding $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/');
if (!is_null($this->host)) {
// path gets to be validated against a hodge-podge of rules depending // path-abempty (hier and relative)
// on the status of authority and scheme, but it's not that important, $this->path = $segments_encoder->encode($this->path);
// esp. since it won't be applicable to everyone } elseif ($this->path !== '' && $this->path[0] === '/') {
// path-absolute (hier and relative)
if (strlen($this->path) >= 2 && $this->path[1] === '/') {
// This shouldn't ever happen!
$this->path = '';
} else {
$this->path = $segments_encoder->encode($this->path);
}
} elseif (!is_null($this->scheme) && $this->path !== '') {
// path-rootless (hier)
// Short circuit evaluation means we don't need to check nz
$this->path = $segments_encoder->encode($this->path);
} elseif (is_null($this->scheme) && $this->path !== '') {
// path-noscheme (relative)
// (once again, not checking nz)
$segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@');
$c = strpos($this->path, '/');
if ($c !== false) {
$this->path =
$segment_nc_encoder->encode(substr($this->path, 0, $c)) .
$segments_encoder->encode(substr($this->path, $c));
} else {
$this->path = $segment_nc_encoder->encode($this->path);
}
} else {
// path-empty (hier and relative)
$this->path = ''; // just to be safe
}
return true; return true;

View File

@@ -26,7 +26,7 @@ abstract class HTMLPurifier_URIFilter
/** /**
* Filter a URI object * Filter a URI object
* @param &$uri Reference to URI object * @param $uri Reference to URI object variable
* @param $config Instance of HTMLPurifier_Config * @param $config Instance of HTMLPurifier_Config
* @param $context Instance of HTMLPurifier_Context * @param $context Instance of HTMLPurifier_Context
* @return bool Whether or not to continue processing: false indicates * @return bool Whether or not to continue processing: false indicates

View File

@@ -2,24 +2,39 @@
/** /**
* Parses a URI into the components and fragment identifier as specified * Parses a URI into the components and fragment identifier as specified
* by RFC 2396. * by RFC 3986.
* @todo Replace regexps with a native PHP parser
*/ */
class HTMLPurifier_URIParser class HTMLPurifier_URIParser
{ {
/** /**
* Parses a URI * Instance of HTMLPurifier_PercentEncoder to do normalization with.
*/
protected $percentEncoder;
public function __construct() {
$this->percentEncoder = new HTMLPurifier_PercentEncoder();
}
/**
* Parses a URI.
* @param $uri string URI to parse * @param $uri string URI to parse
* @return HTMLPurifier_URI representation of URI * @return HTMLPurifier_URI representation of URI. This representation has
* not been validated yet and may not conform to RFC.
*/ */
public function parse($uri) { public function parse($uri) {
$uri = $this->percentEncoder->normalize($uri);
// Regexp is as per Appendix B.
// Note that ["<>] are an addition to the RFC's recommended
// characters, because they represent external delimeters.
$r_URI = '!'. $r_URI = '!'.
'(([^:/?#<>\'"]+):)?'. // 2. Scheme '(([^:/?#"<>]+):)?'. // 2. Scheme
'(//([^/?#<>\'"]*))?'. // 4. Authority '(//([^/?#"<>]*))?'. // 4. Authority
'([^?#<>\'"]*)'. // 5. Path '([^?#"<>]*)'. // 5. Path
'(\?([^#<>\'"]*))?'. // 7. Query '(\?([^#"<>]*))?'. // 7. Query
'(#([^<>\'"]*))?'. // 8. Fragment '(#([^"<>]*))?'. // 8. Fragment
'!'; '!';
$matches = array(); $matches = array();
@@ -36,13 +51,7 @@ class HTMLPurifier_URIParser
// further parse authority // further parse authority
if ($authority !== null) { if ($authority !== null) {
// ridiculously inefficient: it's a stacked regex! $r_authority = "/^((.+?)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/";
$HEXDIG = '[A-Fa-f0-9]';
$unreserved = 'A-Za-z0-9-._~'; // make sure you wrap with []
$sub_delims = '!$&\'()'; // needs []
$pct_encoded = "%$HEXDIG$HEXDIG";
$r_userinfo = "(?:[$unreserved$sub_delims:]|$pct_encoded)*";
$r_authority = "/^(($r_userinfo)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/";
$matches = array(); $matches = array();
preg_match($r_authority, $authority, $matches); preg_match($r_authority, $authority, $matches);
$userinfo = !empty($matches[1]) ? $matches[2] : null; $userinfo = !empty($matches[1]) ? $matches[2] : null;

View File

@@ -62,7 +62,19 @@ foreach ($files as $file) {
$tokens = token_get_all(file_get_contents($file)); $tokens = token_get_all(file_get_contents($file));
$file = str_replace('\\', '/', $file); $file = str_replace('\\', '/', $file);
for ($i = 0, $c = count($tokens); $i < $c; $i++) { for ($i = 0, $c = count($tokens); $i < $c; $i++) {
if (!testToken($tokens[$i], T_VARIABLE, '$config')) continue; $ok = false;
// Match $config
if (!$ok && testToken($tokens[$i], T_VARIABLE, '$config')) $ok = true;
// Match $this->config
while (!$ok && testToken($tokens[$i], T_VARIABLE, '$this')) {
consumeWhitespace($tokens, $i);
if (!testToken($tokens[$i], T_OBJECT_OPERATOR)) break;
consumeWhitespace($tokens, $i);
if (testToken($tokens[$i], T_STRING, 'config')) $ok = true;
break;
}
if (!$ok) continue;
$ok = false; $ok = false;
for($i++; $i < $c; $i++) { for($i++; $i < $c; $i++) {
if ($tokens[$i] === ',' || $tokens[$i] === ')' || $tokens[$i] === ';') { if ($tokens[$i] === ',' || $tokens[$i] === ')' || $tokens[$i] === ';') {
@@ -86,31 +98,40 @@ foreach ($files as $file) {
$full_counter++; $full_counter++;
// The T_CONSTANT_ENCAPSED_STRING may hide some more obscure use-cases; $matched = false;
// it may be useful to log these. do {
consumeWhitespace($tokens, $i);
if (!testToken($tokens[$i], T_CONSTANT_ENCAPSED_STRING)) continue;
$namespace = substr($tokens[$i][1], 1, -1);
consumeWhitespace($tokens, $i); // What we currently don't match are batch retrievals, and
if (!testToken($tokens[$i], ',')) continue; // wildcard retrievals. This data might be useful in the future,
// which is why we have a do {} while loop that doesn't actually
// do anything.
consumeWhitespace($tokens, $i); consumeWhitespace($tokens, $i);
if (!testToken($tokens[$i], T_CONSTANT_ENCAPSED_STRING)) continue; if (!testToken($tokens[$i], T_CONSTANT_ENCAPSED_STRING)) continue;
$directive = substr($tokens[$i][1], 1, -1); $namespace = substr($tokens[$i][1], 1, -1);
$counter++; consumeWhitespace($tokens, $i);
if (!testToken($tokens[$i], ',')) continue;
$id = "$namespace.$directive"; consumeWhitespace($tokens, $i);
if (!isset($tracker[$id])) $tracker[$id] = array(); if (!testToken($tokens[$i], T_CONSTANT_ENCAPSED_STRING)) continue;
if (!isset($tracker[$id][$file])) $tracker[$id][$file] = array(); $directive = substr($tokens[$i][1], 1, -1);
$tracker[$id][$file][] = $line;
// echo "$file:$line uses $namespace.$directive\n"; $counter++;
$matched = true;
$id = "$namespace.$directive";
if (!isset($tracker[$id])) $tracker[$id] = array();
if (!isset($tracker[$id][$file])) $tracker[$id][$file] = array();
$tracker[$id][$file][] = $line;
} while (0);
//echo "$file:$line uses $namespace.$directive\n";
} }
} }
echo "\n$counter/$full_counter instances of \$config found in source code.\n"; echo "\n$counter/$full_counter instances of \$config or \$this->config found in source code.\n";
echo "Generating XML... "; echo "Generating XML... ";

View File

@@ -10,6 +10,7 @@ Changelog HTMLPurifier : Phorum Mod
========================== ==========================
Version 3.0.0.1 for Phorum 5.2, unknown release date Version 3.0.0.1 for Phorum 5.2, unknown release date
! Better installation documentation
- Fixed double encoded quotes - Fixed double encoded quotes
- Fixed fatal error when migrate.php is blank - Fixed fatal error when migrate.php is blank

79
plugins/phorum/INSTALL Normal file
View File

@@ -0,0 +1,79 @@
Install
How to install the Phorum HTML Purifier plugin
1. UNZIP
--------
Unzip phorum-htmlpurifier-x.y.z, producing an htmlpurifier folder.
You've already done this step if you're reading this!
2. MOVE
-------
Move the htmlpurifier folder to the mods/ folder of your Phorum
installation, so the directory structure looks like:
phorum/
mods/
htmlpurifier/
INSTALL - this install file
info.txt, ... - the module files
htmlpurifier/
3. INSTALL HTML PURIFIER
------------------------
Download and unzip HTML Purifier <htmlpurifier.org>. Place the contents of
the library/ folder in the htmlpurifier/htmlpurifier folder. Your directory
structure will look like:
phorum/
mods/
htmlpurifier/
htmlpurifier/
HTMLPurifier.auto.php
... - other files
HTMLPurifier/
Advanced users:
If you have HTML Purifier installed elsewhere on your server,
all you need is an HTMLPurifier.auto.php file in the library folder which
includes the HTMLPurifier.auto.php file in your install.
4. MIGRATE
----------
If you're setting up a new Phorum installation, all you need to do is create
a blank migrate.php file in the htmlpurifier module folder (NOT the library
folder.
If you have an old Phorum installation and was using BBCode,
copy migrate.bbcode.php to migrate.php. If you were using a different input
format, follow the instructions in migrate.bbcode.php to create your own custom
migrate.php file.
Your directory structure should now look like this:
phorum/
mods/
htmlpurifier/
migrate.php
5. ENABLE
---------
Navigate to your Phorum admin panel at http://example.com/phorum/admin.php,
click on Global Settings > Modules, scroll to "HTML Purifier Phorum Mod" and
turn it On.
6. MIGRATE SIGNATURES
---------------------
If you're setting up a new Phorum installation, skip this step.
If you allowed your users to make signatures, navigate to the module settings
page of HTML Purifier (Global Settings > Modules > HTML Purifier Phorum Mod >
Configure), type in "yes" in the "Confirm" box, and press "Migrate."
ONLY DO THIS ONCE! BE SURE TO BACK UP YOUR DATABASE!
7. CONFIGURE
------------
Configure using Edit settings. See that page for more information.

View File

@@ -24,21 +24,23 @@ $version = trim($argv[1]);
file_put_contents('VERSION', $version); file_put_contents('VERSION', $version);
// ...in NEWS // ...in NEWS
$date = date('Y-m-d'); if ($is_dev = (strpos($version, 'dev') === false)) {
$news_c = str_replace( $date = date('Y-m-d');
$l = "$version, unknown release date", $news_c = str_replace(
"$version, released $date", $l = "$version, unknown release date",
file_get_contents('NEWS'), "$version, released $date",
$c file_get_contents('NEWS'),
); $c
if (!$c) { );
echo 'Could not update NEWS, missing ' . $l . PHP_EOL; if (!$c) {
exit; echo 'Could not update NEWS, missing ' . $l . PHP_EOL;
} elseif ($c > 1) { exit;
echo 'More than one release declaration in NEWS replaced' . PHP_EOL; } elseif ($c > 1) {
exit; echo 'More than one release declaration in NEWS replaced' . PHP_EOL;
exit;
}
file_put_contents('NEWS', $news_c);
} }
file_put_contents('NEWS', $news_c);
// ...in Doxyfile // ...in Doxyfile
$doxyfile_c = preg_replace( $doxyfile_c = preg_replace(
@@ -72,7 +74,17 @@ $htmlpurifier_c = preg_replace(
1, $c 1, $c
); );
if (!$c) { if (!$c) {
echo 'Could not update HTMLPurifier.php, missing var $version.' . PHP_EOL; echo 'Could not update HTMLPurifier.php, missing public $version.' . PHP_EOL;
exit;
}
$htmlpurifier_c = preg_replace(
'/const VERSION = \'.+?\';/',
"const VERSION = '$version';",
$htmlpurifier_c,
1, $c
);
if (!$c) {
echo 'Could not update HTMLPurifier.php, missing const $version.' . PHP_EOL;
exit; exit;
} }
file_put_contents('library/HTMLPurifier.php', $htmlpurifier_c); file_put_contents('library/HTMLPurifier.php', $htmlpurifier_c);
@@ -85,10 +97,12 @@ $config_c = preg_replace(
1, $c 1, $c
); );
if (!$c) { if (!$c) {
echo 'Could not update Config.php, missing var $version.' . PHP_EOL; echo 'Could not update Config.php, missing public $version.' . PHP_EOL;
exit; exit;
} }
file_put_contents('library/HTMLPurifier/Config.php', $config_c); file_put_contents('library/HTMLPurifier/Config.php', $config_c);
echo "Review changes, write something in WHATSNEW, and then SVN commit with log 'Release $version.'" . PHP_EOL; passthru('php maintenance/flush.php');
if ($is_dev) echo "Review changes, write something in WHATSNEW, and then SVN commit with log 'Release $version.'" . PHP_EOL;
else echo "Numbers updated to dev, no other modifications necessary!";

View File

@@ -2,31 +2,37 @@
require_once 'common.php'; require_once 'common.php';
// Setup environment
require_once '../extras/HTMLPurifierExtras.auto.php';
$interchange = HTMLPurifier_ConfigSchema_InterchangeBuilder::buildFromDirectory('test-schema/');
$interchange->validate();
if (isset($_GET['doc'])) { if (isset($_GET['doc'])) {
if ( // Hijack page generation to supply documentation
file_exists('testSchema.html') &&
filemtime('testSchema.php') < filemtime('testSchema.html') && if (file_exists('test-schema.html') && !isset($_GET['purge'])) {
!isset($_GET['purge']) echo file_get_contents('test-schema.html');
) {
echo file_get_contents('testSchema.html');
exit; exit;
} }
if (version_compare('5', PHP_VERSION, '>')) exit('Requires PHP 5 or higher.'); $style = 'plain';
$configdoc_xml = 'test-schema.xml';
// setup ConfigDoc environment $xml_builder = new HTMLPurifier_ConfigSchema_Builder_Xml();
require_once '../configdoc/library/ConfigDoc.auto.php'; $xml_builder->openURI($configdoc_xml);
$xml_builder->build($interchange);
unset($xml_builder); // free handle
// perform the ConfigDoc generation $xslt = new ConfigDoc_HTMLXSLTProcessor();
$configdoc = new ConfigDoc(); $xslt->importStylesheet("../configdoc/styles/$style.xsl");
$html = $configdoc->generate($new_schema, 'plain', array( $xslt->setParameters(array(
'css' => '../configdoc/styles/plain.css', 'css' => '../configdoc/styles/plain.css',
'title' => 'Sample Configuration Documentation'
)); ));
$configdoc->cleanup(); $html = $xslt->transformToHTML($configdoc_xml);
file_put_contents('testSchema.html', $html); unlink('test-schema.xml');
file_put_contents('test-schema.html', $html);
echo $html; echo $html;
exit; exit;
@@ -50,15 +56,11 @@ of directive possible.</p>
style="float:right;"> style="float:right;">
<?php <?php
require_once 'HTMLPurifier/Printer/ConfigForm.php'; $schema_builder = new HTMLPurifier_ConfigSchema_Builder_ConfigSchema();
$schema = $schema_builder->build($interchange);
HTMLPurifier_ConfigSchema::instance($schema);
// fictional set, attempts to cover every possible data-type $config = HTMLPurifier_Config::loadArrayFromForm($_GET, 'config');
// see source at ConfigTest.php
require_once 'testSchema.php';
HTMLPurifier_ConfigSchema::instance($custom_schema);
// cleanup ( this should be rolled into Config )
$config = HTMLPurifier_Config::loadArrayFromForm($_GET, 'config');
$printer = new HTMLPurifier_Printer_ConfigForm('config', '?doc#%s'); $printer = new HTMLPurifier_Printer_ConfigForm('config', '?doc#%s');
echo $printer->render($config); echo $printer->render($config);
@@ -66,7 +68,7 @@ echo $printer->render($config);
</form> </form>
<pre> <pre>
<?php <?php
echo htmlspecialchars(print_r($config->getAll(), true)); echo htmlspecialchars(var_export($config->getAll(), true));
?> ?>
</pre> </pre>
</body> </body>

View File

@@ -0,0 +1,5 @@
Directive.Allowed
TYPE: string
DEFAULT: 'apple'
ALLOWED: 'apple', 'orange', 'pear', 'peach', 'mango'
DESCRIPTION: This directive has a constrained set of allowed values.

View File

@@ -0,0 +1,6 @@
Directive.Deprecated
TYPE: int
DEFAULT: 0
DESCRIPTION: This is a deprecated directive that shouldn't show up on the form.
DEPRECATED-VERSION: 1.0.0
DEPRECATED-USE: Directive.Allowed

View File

@@ -0,0 +1,2 @@
Directive
DESCRIPTION: Other custom options with directives.

View File

@@ -0,0 +1,4 @@
Type.bool
TYPE: bool
DEFAULT: false
DESCRIPTION: The boolean type is true or false.

View File

@@ -0,0 +1,4 @@
Type.float
TYPE: float
DEFAULT: 3.1415
DESCRIPTION: The float type is a floating point number.

View File

@@ -0,0 +1,4 @@
Type.hash
TYPE: hash
DEFAULT: array('key1' => 'val1', 'key2' => 'val2')
DESCRIPTION: The hash type is an associative array of string keys and string values.

View File

@@ -0,0 +1,4 @@
Type.int
TYPE: int
DEFAULT: 23
DESCRIPTION: The int type is an signed integer.

View File

@@ -0,0 +1,4 @@
Type.istring
TYPE: istring
DEFAULT: 'case insensitive'
DESCRIPTION: The istring type is short (no newlines), must be ASCII and is case-insensitive.

View File

@@ -0,0 +1,4 @@
Type.itext
TYPE: itext
DEFAULT: "case\ninsensitive\nand\npossibly\nquite\nlong"
DESCRIPTION: The text type has newlines, must be ASCII and is case-insensitive.

View File

@@ -0,0 +1,4 @@
Type.list
TYPE: list
DEFAULT: array('item1', 'item2')
DESCRIPTION: The list type is a numerically indexed array of strings.

View File

@@ -0,0 +1,4 @@
Type.lookup
TYPE: lookup
DEFAULT: array('key1' => true, 'key2' => true)
DESCRIPTION: The lookup type acts just like list, except its elements are unique and are checked with <code>isset($var[$key])</code>.

View File

@@ -0,0 +1,4 @@
Type.mixed
TYPE: mixed
DEFAULT: new stdclass()
DESCRIPTION: The mixed type allows any type, and is not form-editable.

View File

@@ -0,0 +1,6 @@
Type.nullbool
TYPE: bool/null
DEFAULT: null
--DESCRIPTION--
Null booleans need to be treated a little specially. See %Type.nullstring
for information on what the null flag does.

View File

@@ -0,0 +1,8 @@
Type.nullstring
TYPE: string/null
DEFAULT: null
--DESCRIPTION--
The null type is not a type, but a flag that can be added to any type
making null a valid value for that entry. It's useful for saying, "Let
the software pick the value for me," or "Don't use this element" when
false has a special meaning.

View File

@@ -0,0 +1,4 @@
Type.string
TYPE: string
DEFAULT: 'Case sensitive'
DESCRIPTION: The string type is short (no newlines) and case-sensitive.

View File

@@ -0,0 +1,4 @@
Type.text
TYPE: text
DEFAULT: "Case sensitive\nand\npossibly\nquite long..."
DESCRIPTION: The text type has newlines and is case-sensitive.

View File

@@ -0,0 +1,2 @@
Type
DESCRIPTION: Directives demonstration the variable types ConfigSchema supports.

View File

@@ -0,0 +1 @@
name = "Test Schema"

View File

@@ -1,41 +0,0 @@
<?php
// overload default configuration schema temporarily
$custom_schema = new HTMLPurifier_ConfigSchema();
$custom_schema->addNamespace('Element', 'Chemical substances that cannot be further decomposed');
$custom_schema->add('Element', 'Abbr', 'H', 'string', false, 'Abbreviation of element name.');
$custom_schema->add('Element', 'Name', 'hydrogen', 'istring', false, 'Full name of atoms.');
$custom_schema->add('Element', 'Number', 1, 'int', false, 'Atomic number, is identity.');
$custom_schema->add('Element', 'Mass', 1.00794, 'float', false, 'Atomic mass.');
$custom_schema->add('Element', 'Radioactive', false, 'bool', false, 'Does it have rapid decay?');
$custom_schema->add('Element', 'Isotopes', array('1' => true, '2' => true, '3' => true), 'lookup', false,
'What numbers of neutrons for this element have been observed?');
$custom_schema->add('Element', 'Traits', array('nonmetallic', 'odorless', 'flammable'), 'list', false,
'What are general properties of the element?');
$custom_schema->add('Element', 'IsotopeNames', array('1' => 'protium', '2' => 'deuterium', '3' => 'tritium'), 'hash', false,
'Lookup hash of neutron counts to formal names.');
$custom_schema->addNamespace('Instrument', 'Of the musical type.');
$custom_schema->add('Instrument', 'Manufacturer', 'Yamaha', 'string', false, 'Who made it?');
$custom_schema->addAllowedValues('Instrument', 'Manufacturer', array(
'Yamaha', 'Conn-Selmer', 'Vandoren', 'Laubin', 'Buffet', 'other'));
$custom_schema->addValueAliases('Instrument', 'Manufacturer', array(
'Selmer' => 'Conn-Selmer'));
$custom_schema->add('Instrument', 'Family', 'woodwind', 'istring', false, 'What family is it?');
$custom_schema->addAllowedValues('Instrument', 'Family', array(
'brass', 'woodwind', 'percussion', 'string', 'keyboard', 'electronic'));
$custom_schema->addValueAliases('Instrument', 'Family', array(
'synth' => 'electronic'));
$custom_schema->addNamespace('ReportCard', 'It is for grades.');
$custom_schema->add('ReportCard', 'English', null, 'string', true, 'Grade from English class.');
$custom_schema->add('ReportCard', 'Absences', 0, 'int', false, 'How many times missing from school?');
$custom_schema->addNamespace('Text', 'This stuff is long, boring, and English.');
$custom_schema->add('Text', 'AboutUs', 'Nothing much, but this should be decently long so that a textarea would be better', 'text', false, 'Who are we? What are we up to?');
$custom_schema->add('Text', 'Hash', "not-case-sensitive\nstill-not-case-sensitive\nsuper-not-case-sensitive", 'itext', false, 'This is of limited utility, but of course it ends up being used.');

View File

@@ -14,6 +14,27 @@ class HTMLPurifier_AttrDef_URI_HostTest extends HTMLPurifier_AttrDefHarness
$this->assertDef('124.15.6.89'); // IPv4 $this->assertDef('124.15.6.89'); // IPv4
$this->assertDef('www.google.com'); // reg-name $this->assertDef('www.google.com'); // reg-name
// more domain name tests
$this->assertDef('test.');
$this->assertDef('sub.test.');
$this->assertDef('.test', false);
$this->assertDef('ff');
$this->assertDef('1f', false);
$this->assertDef('-f', false);
$this->assertDef('f1');
$this->assertDef('f-', false);
$this->assertDef('sub.ff');
$this->assertDef('sub.1f', false);
$this->assertDef('sub.-f', false);
$this->assertDef('sub.f1');
$this->assertDef('sub.f-', false);
$this->assertDef('ff.top');
$this->assertDef('1f.top');
$this->assertDef('-f.top', false);
$this->assertDef('ff.top');
$this->assertDef('f1.top');
$this->assertDef('f-.top', false);
} }
} }

View File

@@ -29,6 +29,19 @@ class HTMLPurifier_AttrDef_URITest extends HTMLPurifier_AttrDefHarness
); );
} }
function testPercentEncoding() {
$this->assertDef(
'http:colon:mercenary',
'colon%3Amercenary'
);
}
function testPercentEncodingPreserve() {
$this->assertDef(
'http://www.example.com/abcABC123-_.!~*()\''
);
}
function testEmbeds() { function testEmbeds() {
$this->def = new HTMLPurifier_AttrDef_URI(true); $this->def = new HTMLPurifier_AttrDef_URI(true);
$this->assertDef('http://sub.example.com/alas?foo=asd'); $this->assertDef('http://sub.example.com/alas?foo=asd');
@@ -65,14 +78,31 @@ class HTMLPurifier_AttrDef_URITest extends HTMLPurifier_AttrDefHarness
$parser = new HTMLPurifier_URIParser(); $parser = new HTMLPurifier_URIParser();
$uri = $parser->parse('http://example.com'); $uri = $parser->parse('http://example.com');
$this->config->set('URI', 'DefinitionID', 'HTMLPurifier_AttrDef_URITest->testURIDefinitionValidation'); $this->config->set('URI', 'DefinitionID', 'HTMLPurifier_AttrDef_URITest->testURIDefinitionValidation');
$uri_def =& $this->config->getDefinition('URI');
// overload with mock
generate_mock_once('HTMLPurifier_URIDefinition'); generate_mock_once('HTMLPurifier_URIDefinition');
$uri_def = new HTMLPurifier_URIDefinitionMock(); $uri_def = new HTMLPurifier_URIDefinitionMock();
$uri_def->expectOnce('filter', array($uri, '*', '*')); $uri_def->expectOnce('filter', array($uri, '*', '*'));
$uri_def->setReturnValue('filter', true, array($uri, '*', '*')); $uri_def->setReturnValue('filter', true, array($uri, '*', '*'));
$uri_def->setup = true; $uri_def->setup = true;
// Since definitions are no longer passed by reference, we need
// to muck around with the cache to insert our mock. This is
// technically a little bad, since the cache shouldn't change
// behavior, but I don't feel too good about letting users
// overload entire definitions.
generate_mock_once('HTMLPurifier_DefinitionCache');
$cache_mock = new HTMLPurifier_DefinitionCacheMock();
$cache_mock->setReturnValue('get', $uri_def);
generate_mock_once('HTMLPurifier_DefinitionCacheFactory');
$factory_mock = new HTMLPurifier_DefinitionCacheFactoryMock();
$old = HTMLPurifier_DefinitionCacheFactory::instance();
HTMLPurifier_DefinitionCacheFactory::instance($factory_mock);
$factory_mock->setReturnValue('create', $cache_mock);
$this->assertDef('http://example.com'); $this->assertDef('http://example.com');
HTMLPurifier_DefinitionCacheFactory::instance($old);
} }
/* /*

View File

@@ -11,7 +11,7 @@ class HTMLPurifier_AttrValidator_ErrorsTest extends HTMLPurifier_ErrorsHarness
function testAttributesTransformedGlobalPre() { function testAttributesTransformedGlobalPre() {
$this->config->set('HTML', 'DefinitionID', $this->config->set('HTML', 'DefinitionID',
'HTMLPurifier_AttrValidator_ErrorsTest::testAttributesTransformedGlobalPre'); 'HTMLPurifier_AttrValidator_ErrorsTest::testAttributesTransformedGlobalPre');
$def =& $this->config->getHTMLDefinition(true); $def = $this->config->getHTMLDefinition(true);
generate_mock_once('HTMLPurifier_AttrTransform'); generate_mock_once('HTMLPurifier_AttrTransform');
$transform = new HTMLPurifier_AttrTransformMock(); $transform = new HTMLPurifier_AttrTransformMock();
$input = array('original' => 'value'); $input = array('original' => 'value');

View File

@@ -35,11 +35,6 @@ class HTMLPurifier_ComplexHarness extends HTMLPurifier_Harness
*/ */
protected $lexer; protected $lexer;
/**
* Instance of HTMLPurifier_Generator
*/
protected $generator;
/** /**
* Default config to fall back on if no config is available * Default config to fall back on if no config is available
*/ */
@@ -52,7 +47,6 @@ class HTMLPurifier_ComplexHarness extends HTMLPurifier_Harness
public function __construct() { public function __construct() {
$this->lexer = new HTMLPurifier_Lexer_DirectLex(); $this->lexer = new HTMLPurifier_Lexer_DirectLex();
$this->generator = new HTMLPurifier_Generator();
parent::__construct(); parent::__construct();
} }
@@ -110,7 +104,8 @@ class HTMLPurifier_ComplexHarness extends HTMLPurifier_Harness
* Generate textual HTML from tokens * Generate textual HTML from tokens
*/ */
protected function generate($tokens) { protected function generate($tokens) {
return $this->generator->generateFromTokens($tokens, $this->config, $this->context); $generator = new HTMLPurifier_Generator($this->config, $this->context);
return $generator->generateFromTokens($tokens);
} }
} }

View File

@@ -219,27 +219,25 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
$def = $config->getCSSDefinition(); $def = $config->getCSSDefinition();
$this->assertIsA($def, 'HTMLPurifier_CSSDefinition'); $this->assertIsA($def, 'HTMLPurifier_CSSDefinition');
$def =& $config->getHTMLDefinition(); $def = $config->getHTMLDefinition();
$def2 =& $config->getHTMLDefinition(); $def2 = $config->getHTMLDefinition();
$this->assertIsA($def, 'HTMLPurifier_HTMLDefinition'); $this->assertIsA($def, 'HTMLPurifier_HTMLDefinition');
$this->assertReference($def, $def2); $this->assertSame($def, $def2);
$this->assertTrue($def->setup); $this->assertTrue($def->setup);
// test re-calculation if HTML changes $old_def = clone $def2;
unset($def, $def2);
$def2 = $config->getHTMLDefinition(); // forcibly de-reference
$config->set('HTML', 'Doctype', 'HTML 4.01 Transitional'); $config->set('HTML', 'Doctype', 'HTML 4.01 Transitional');
$def = $config->getHTMLDefinition(); $def = $config->getHTMLDefinition();
$this->assertIsA($def, 'HTMLPurifier_HTMLDefinition'); $this->assertIsA($def, 'HTMLPurifier_HTMLDefinition');
$this->assertNotEqual($def, $def2); $this->assertNotEqual($def, $old_def);
$this->assertTrue($def->setup); $this->assertTrue($def->setup);
// test retrieval of raw definition // test retrieval of raw definition
$config->set('HTML', 'DefinitionID', 'HTMLPurifier_ConfigTest->test_getHTMLDefinition()'); $config->set('HTML', 'DefinitionID', 'HTMLPurifier_ConfigTest->test_getHTMLDefinition()');
$config->set('HTML', 'DefinitionRev', 3); $config->set('HTML', 'DefinitionRev', 3);
$def =& $config->getHTMLDefinition(true); $def = $config->getHTMLDefinition(true);
$this->assertNotEqual($def, $def2); $this->assertNotEqual($def, $old_def);
$this->assertEqual(false, $def->setup); $this->assertEqual(false, $def->setup);
// auto initialization // auto initialization
@@ -250,8 +248,8 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function test_getHTMLDefinition_rawError() { function test_getHTMLDefinition_rawError() {
$config = HTMLPurifier_Config::createDefault(); $config = HTMLPurifier_Config::createDefault();
$this->expectError('Cannot retrieve raw version without specifying %HTML.DefinitionID'); $this->expectException(new HTMLPurifier_Exception('Cannot retrieve raw version without specifying %HTML.DefinitionID'));
$def =& $config->getHTMLDefinition(true); $def = $config->getHTMLDefinition(true);
} }
function test_getCSSDefinition() { function test_getCSSDefinition() {
@@ -265,7 +263,7 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
$this->schema->add('Cache', 'DefinitionImpl', null, 'string', true); $this->schema->add('Cache', 'DefinitionImpl', null, 'string', true);
$this->schema->addNamespace('Crust', 'Krusty Krabs'); $this->schema->addNamespace('Crust', 'Krusty Krabs');
$config = new HTMLPurifier_Config($this->schema); $config = new HTMLPurifier_Config($this->schema);
$this->expectError("Definition of Crust type not supported"); $this->expectException(new HTMLPurifier_Exception("Definition of Crust type not supported"));
$config->getDefinition('Crust'); $config->getDefinition('Crust');
} }

View File

@@ -11,7 +11,7 @@ class HTMLPurifier_DefinitionCache_SerializerTest extends HTMLPurifier_Definitio
$config->setReturnValue('get', 2, array('Test', 'DefinitionRev')); $config->setReturnValue('get', 2, array('Test', 'DefinitionRev'));
$config->version = '1.0.0'; $config->version = '1.0.0';
$config_md5 = '1.0.0-serial-2'; $config_md5 = '1.0.0,serial,2';
$file = realpath( $file = realpath(
$rel_file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer/Test/' . $rel_file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer/Test/' .
@@ -186,9 +186,9 @@ class HTMLPurifier_DefinitionCache_SerializerTest extends HTMLPurifier_Definitio
$def_original = $this->generateDefinition(); $def_original = $this->generateDefinition();
$cache->add($def_original, $config); $cache->add($def_original, $config);
$this->assertFileExist($dir . '/Test/1.0.0-serial-1.ser'); $this->assertFileExist($dir . '/Test/1.0.0,serial,1.ser');
unlink($dir . '/Test/1.0.0-serial-1.ser'); unlink($dir . '/Test/1.0.0,serial,1.ser');
rmdir( $dir . '/Test'); rmdir( $dir . '/Test');
} }

View File

@@ -13,16 +13,17 @@ class HTMLPurifier_DefinitionCacheTest extends HTMLPurifier_Harness
$config->setReturnValue('get', 10, array('Test', 'DefinitionRev')); $config->setReturnValue('get', 10, array('Test', 'DefinitionRev'));
$config->setReturnValue('getBatchSerial', 'hash', array('Test')); $config->setReturnValue('getBatchSerial', 'hash', array('Test'));
$this->assertIdentical($cache->isOld('1.0.0-hash-10', $config), false); $this->assertIdentical($cache->isOld('1.0.0,hash,10', $config), false);
$this->assertIdentical($cache->isOld('1.5.0-hash-1', $config), true); $this->assertIdentical($cache->isOld('1.5.0,hash,1', $config), true);
$this->assertIdentical($cache->isOld('0.9.0-hash-1', $config), true); $this->assertIdentical($cache->isOld('0.9.0,hash,1', $config), true);
$this->assertIdentical($cache->isOld('1.0.0-hash-1', $config), true); $this->assertIdentical($cache->isOld('1.0.0,hash,1', $config), true);
$this->assertIdentical($cache->isOld('1.0.0beta-hash-11', $config), true); $this->assertIdentical($cache->isOld('1.0.0beta,hash,11', $config), true);
$this->assertIdentical($cache->isOld('0.9.0-hash2-1', $config), true); $this->assertIdentical($cache->isOld('0.9.0,hash2,1', $config), true);
$this->assertIdentical($cache->isOld('1.0.0-hash2-1', $config), false); // if hash is different, don't touch! $this->assertIdentical($cache->isOld('1.0.0,hash2,1', $config), false); // if hash is different, don't touch!
$this->assertIdentical($cache->isOld('1.0.0beta-hash2-11', $config), true); $this->assertIdentical($cache->isOld('1.0.0beta,hash2,11', $config), true);
$this->assertIdentical($cache->isOld('1.0.0-dev,hash2,11', $config), true);
} }

View File

@@ -7,7 +7,7 @@ class HTMLPurifier_DoctypeRegistryTest extends HTMLPurifier_Harness
$registry = new HTMLPurifier_DoctypeRegistry(); $registry = new HTMLPurifier_DoctypeRegistry();
$d =& $registry->register( $d = $registry->register(
$name = 'XHTML 1.0 Transitional', $name = 'XHTML 1.0 Transitional',
$xml = true, $xml = true,
$modules = array('module-one', 'module-two'), $modules = array('module-one', 'module-two'),
@@ -18,10 +18,10 @@ class HTMLPurifier_DoctypeRegistryTest extends HTMLPurifier_Harness
$d2 = new HTMLPurifier_Doctype($name, $xml, $modules, $tidyModules, $aliases); $d2 = new HTMLPurifier_Doctype($name, $xml, $modules, $tidyModules, $aliases);
$this->assertIdentical($d, $d2); $this->assertIdentical($d, $d2);
$this->assertReference($d, $registry->get('XHTML 1.0 Transitional')); $this->assertSame($d, $registry->get('XHTML 1.0 Transitional'));
// test shorthand // test shorthand
$d =& $registry->register( $d = $registry->register(
$name = 'XHTML 1.0 Strict', true, 'module', 'Tidy', 'X10S' $name = 'XHTML 1.0 Strict', true, 'module', 'Tidy', 'X10S'
); );
$d2 = new HTMLPurifier_Doctype($name, true, array('module'), array('Tidy'), array('X10S')); $d2 = new HTMLPurifier_Doctype($name, true, array('module'), array('Tidy'), array('X10S'));
@@ -49,26 +49,26 @@ class HTMLPurifier_DoctypeRegistryTest extends HTMLPurifier_Harness
$registry = new HTMLPurifier_DoctypeRegistry(); $registry = new HTMLPurifier_DoctypeRegistry();
$d1 =& $registry->register('Doc1', true, array(), array(), array('1')); $d1 = $registry->register('Doc1', true, array(), array(), array('1'));
$this->assertReference($d1, $registry->get('Doc1')); $this->assertSame($d1, $registry->get('Doc1'));
$this->assertReference($d1, $registry->get('1')); $this->assertSame($d1, $registry->get('1'));
$d2 =& $registry->register('Doc2', true, array(), array(), array('2')); $d2 = $registry->register('Doc2', true, array(), array(), array('2'));
$this->assertReference($d2, $registry->get('Doc2')); $this->assertSame($d2, $registry->get('Doc2'));
$this->assertReference($d2, $registry->get('2')); $this->assertSame($d2, $registry->get('2'));
$d3 =& $registry->register('1', true, array(), array(), array()); $d3 = $registry->register('1', true, array(), array(), array());
// literal name overrides alias // literal name overrides alias
$this->assertReference($d3, $registry->get('1')); $this->assertSame($d3, $registry->get('1'));
$d4 =& $registry->register('One', true, array(), array(), array('1')); $d4 = $registry->register('One', true, array(), array(), array('1'));
$this->assertReference($d4, $registry->get('One')); $this->assertSame($d4, $registry->get('One'));
// still it overrides // still it overrides
$this->assertReference($d3, $registry->get('1')); $this->assertSame($d3, $registry->get('1'));
} }

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