1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-08-03 12:47:56 +02:00

Compare commits

...

212 Commits

Author SHA1 Message Date
Edward Z. Yang
ed38579fa2 Fix duplicated items in TODO.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/branches/1.3@623 48356398-32a2-884e-a903-53898d9a118a
2006-12-26 17:13:26 +00:00
Edward Z. Yang
54f615f1d3 Merged r608-621 for 1.3.2 release from trunk.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/branches/1.3@622 48356398-32a2-884e-a903-53898d9a118a
2006-12-26 17:10:29 +00:00
Edward Z. Yang
3b979ee846 Merged revisions for 1.3.1 release into branch, with local modifications to keep NEWS items in present.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/branches/1.3@604 48356398-32a2-884e-a903-53898d9a118a
2006-12-06 23:19:59 +00:00
Edward Z. Yang
d151ffd9e6 Create 1.3 release series.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/branches/1.3@590 48356398-32a2-884e-a903-53898d9a118a
2006-11-26 23:30:22 +00:00
Edward Z. Yang
2a01cf786e Release 1.3.0 (bumped TODO items)
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@588 48356398-32a2-884e-a903-53898d9a118a
2006-11-26 23:21:19 +00:00
Edward Z. Yang
825b0671b5 [1.3.0] Bump version numbers.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@587 48356398-32a2-884e-a903-53898d9a118a
2006-11-26 23:18:32 +00:00
Edward Z. Yang
4bdc0446de [1.3.0] New directive %URI.HostBlacklist for blocking links to bad hosts. xssAttacks.php smoketest updated accordingly.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@586 48356398-32a2-884e-a903-53898d9a118a
2006-11-26 23:14:12 +00:00
Edward Z. Yang
45a70e8ae4 [1.3.0] Update xssAttacks.xml.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@585 48356398-32a2-884e-a903-53898d9a118a
2006-11-26 00:46:57 +00:00
Edward Z. Yang
1fe60c9b9d [1.3.0] Clarify docs on what printDefinition is for
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@584 48356398-32a2-884e-a903-53898d9a118a
2006-11-26 00:14:03 +00:00
Edward Z. Yang
dc0e2c6b3e Revise character estimate upwards.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@582 48356398-32a2-884e-a903-53898d9a118a
2006-11-25 21:18:20 +00:00
Edward Z. Yang
9bbbb87ffa [1.3.0] Add Printer_CSSDefinition.
- Added @public identifiers to properties that the Printers are using.
- Augmented Printer::getClass() to include meta-info about the object (contained inside parentheses). Currently supports: enum, composite and multiple.
- Remove all linebreaks from Printer output
- Document Printer_HTMLDefinition's methods.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@581 48356398-32a2-884e-a903-53898d9a118a
2006-11-25 05:05:32 +00:00
Edward Z. Yang
b63b0be21f [1.3.0] Some housekeeping after the last commit
- Add a few missing unit tests
- Allow for spaces between comma separated strings to be transformed into arrays
- smoketests/printDefinition.php now has documentation, links to more documentation and a friendly user-interface

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@579 48356398-32a2-884e-a903-53898d9a118a
2006-11-24 07:12:16 +00:00
Edward Z. Yang
73a1e31fad [1.3.0] Added spiffy new smoketest printDefinition.php, which lets you twiddle with the configuration settings and see how the internal rules are affected. (currently only complete for HTMLDefinition).
- HTMLPurifier -> HTML Purifier
. HTMLPurifier_Config->getBatch($namespace) added
. More lenient casting to bool from string in HTMLPurifier_ConfigSchema
. <?xml ... tags added to all smoketests

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@578 48356398-32a2-884e-a903-53898d9a118a
2006-11-24 06:26:02 +00:00
Edward Z. Yang
775763c583 [1.3.0] New directive %URI.Munge, munges URI so you can use some sort of redirector service to avoid PageRank leaks or warn users that they are exiting your site.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@576 48356398-32a2-884e-a903-53898d9a118a
2006-11-24 00:29:16 +00:00
Edward Z. Yang
49cb2a4a7c [1.3.0] More control of URIs granted
# Invalid images are now removed, rather than replaced with a dud <img src="" alt="Invalid image" />. Previous behavior can be restored with new directive %Core.RemoveInvalidImg set to false.
! New directives %URI.DisableExternalResources and %URI.DisableResources
! New directive %Attr.DisableURI, which eliminates all hyperlinking
- Missing "Available since" documentation added

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@575 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 23:59:20 +00:00
Edward Z. Yang
61b6ee7183 Update filter levels document in light of fact that user can now specify tags. We may want to upgrade this to HTML so users can be helped out in choosing things to allow.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@574 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 22:40:59 +00:00
Edward Z. Yang
d7ce6b4587 Add code quality advisory about demo.php.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@573 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 22:34:41 +00:00
Edward Z. Yang
f67ee19f31 [1.3.0] Add some forward thinking documents.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@572 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 22:33:07 +00:00
Edward Z. Yang
92b3f0e817 [1.3.0] <li value="4"> and <ul start="2"> now allowed in loose mode
- Updated progress with some more impl-no decisions
 - Loose vs. Strict now has better tallying on current behavior
 - Document what we're not allowing in loose
 - Strict boolean indicator added to HTMLDefinition
 - Added XHTML 1.1 to TODO.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@571 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 22:15:35 +00:00
Edward Z. Yang
3c4da9666f - Update TODO: Caching and Configuration profiles
- Added another code-quality issue

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@570 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 21:36:17 +00:00
Edward Z. Yang
925a07b828 [1.3.0] New directives %HTML.AllowedElements and %HTML.AllowedAttributes to let users narrow the set of allowed tags
. Added HTMLPurifier->info_parent_def, parent child processing made special

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@565 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 13:51:19 +00:00
Edward Z. Yang
94db380271 [1.3.0] Remove Tidy option from demo if there is not Tidy available
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@563 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 03:49:19 +00:00
Edward Z. Yang
b9e7ba6a2f [1.3.0] Move valid XHTML 1.0 button link to better spot.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@562 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 03:39:55 +00:00
Edward Z. Yang
b1b3377b9c [1.3.0] Huge upgrade, (X)HTML Strict now supported
+ Transparently handles inline elements in block context (blockquote)
! Added GET method to demo for easier validation, added 50kb max input size
! New directive %HTML.BlockWrapper, for block-ifying inline elements
! New directive %HTML.Parent, allows you to only allow inline content
- Added missing type to ChildDef_Chameleon
. ChildDef_Required guards against empty tags
. Lookup table HTMLDefinition->info_flow_elements added
. Added peace-of-mind variable initialization to Strategy_FixNesting

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@560 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 03:23:35 +00:00
Edward Z. Yang
d8673539ab - Add more documentation about proprietary tags
- Link to all text memos

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@559 48356398-32a2-884e-a903-53898d9a118a
2006-11-23 00:45:43 +00:00
Edward Z. Yang
3b26e5dc5b [1.3.0] Refactored ChildDef classes into their own files
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@558 48356398-32a2-884e-a903-53898d9a118a
2006-11-22 18:55:15 +00:00
Edward Z. Yang
c5ea987069 Fix parse error.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@557 48356398-32a2-884e-a903-53898d9a118a
2006-11-22 18:19:44 +00:00
Edward Z. Yang
b152448608 [1.3.0] Implement user-unfriendly implementation of Strict doctype. We will try not to ship this one.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@556 48356398-32a2-884e-a903-53898d9a118a
2006-11-22 18:17:39 +00:00
Edward Z. Yang
b0575cb888 Add more TODO items:
- Formatter caveat to strict XHTML
- YouTube video embedding

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@555 48356398-32a2-884e-a903-53898d9a118a
2006-11-22 17:46:38 +00:00
Edward Z. Yang
224ef774f7 Commit two new docs: loose-vs-strict and proprietary-tags, both research/reference.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@554 48356398-32a2-884e-a903-53898d9a118a
2006-11-22 04:49:26 +00:00
Edward Z. Yang
18a83acc5d Re-prioritize (X)HTML strict output TODO.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@553 48356398-32a2-884e-a903-53898d9a118a
2006-11-22 03:00:12 +00:00
Edward Z. Yang
f9090e45c0 [1.3.0] Add items for projected 1.3.0 and 1.2.1 releases.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@552 48356398-32a2-884e-a903-53898d9a118a
2006-11-20 03:58:56 +00:00
Edward Z. Yang
450523a9ca [1.2.0] [merged] Bump TODO items.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@547 48356398-32a2-884e-a903-53898d9a118a
2006-11-20 03:21:52 +00:00
Edward Z. Yang
1955527a11 Release 1.2.0.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@543 48356398-32a2-884e-a903-53898d9a118a
2006-11-20 03:16:32 +00:00
Edward Z. Yang
a5751c7f20 [1.2.0] Update new directives file.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@542 48356398-32a2-884e-a903-53898d9a118a
2006-11-20 03:07:46 +00:00
Edward Z. Yang
0960cf6ace [1.2.0] Converted enduser-id.txt to HTML. Fixed summary in index. Added extra style .subsubtitle
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@539 48356398-32a2-884e-a903-53898d9a118a
2006-11-20 02:47:00 +00:00
Edward Z. Yang
83ed9e0fe1 [1.2.0]
- Converted dev-naming and dev-optimization to HTML
- Fixed up failed validation in a few of the other HTML files

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@538 48356398-32a2-884e-a903-53898d9a118a
2006-11-19 04:56:50 +00:00
Edward Z. Yang
fe9238af3a [1.2.0] Nuke 1.1.3 release.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@537 48356398-32a2-884e-a903-53898d9a118a
2006-11-19 04:42:42 +00:00
Edward Z. Yang
f0fe829af4 [1.2.0] Update documentation paths.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@536 48356398-32a2-884e-a903-53898d9a118a
2006-11-19 04:37:26 +00:00
Edward Z. Yang
a3968a1ec7 [1.2.0] Update documentation infrastructure.
- Add filings and link to index
- Update descriptions
- Add an index

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@535 48356398-32a2-884e-a903-53898d9a118a
2006-11-19 04:31:48 +00:00
Edward Z. Yang
a8298172e1 [1.2.0] Rename so that docs have specific categories.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@534 48356398-32a2-884e-a903-53898d9a118a
2006-11-19 03:35:57 +00:00
Edward Z. Yang
90dd7f13ae [1.2.0] HTML-ization for code-quality and colors. Also added in missing $Id$ to progress, and allowed for subtitling in the style.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@533 48356398-32a2-884e-a903-53898d9a118a
2006-11-19 03:10:14 +00:00
Edward Z. Yang
780c7fd309 [1.2.0] Revamp docs
- Style existing HTML files (taken from AuthTools)
- Add svn:eol-style=native and svn:keywords=Id to all file
- Add metadata to HTML files
- Trim DevNetwork by using <base>

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@532 48356398-32a2-884e-a903-53898d9a118a
2006-11-19 02:36:47 +00:00
Edward Z. Yang
dec6c52695 [1.2.0] Add a i18n documentation text.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@531 48356398-32a2-884e-a903-53898d9a118a
2006-11-18 23:58:41 +00:00
Edward Z. Yang
1ea3c1e968 Ignore incubator/ directory.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@530 48356398-32a2-884e-a903-53898d9a118a
2006-11-18 03:40:39 +00:00
Edward Z. Yang
bdab77b59e [1.2.0] Update Devnetwork topic document.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@529 48356398-32a2-884e-a903-53898d9a118a
2006-11-18 03:33:30 +00:00
Edward Z. Yang
82afd890c4 [1.2.0] Non-accessible resources (ex. mailto) blocked from embedded URIs (img src)
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@528 48356398-32a2-884e-a903-53898d9a118a
2006-11-17 23:09:10 +00:00
Edward Z. Yang
b0df2f292f [1.2.0] Migrate feature requests in the code quality document to TODO.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@527 48356398-32a2-884e-a903-53898d9a118a
2006-11-17 22:13:16 +00:00
Edward Z. Yang
7a4c7b3777 [1.2.0] [BC] ID attributes now disabled by default. New directives:
+ %HTML.EnableAttrID - restores old behavior by allowing IDs
  + %Attr.IDPrefix - %Attr.IDBlacklist alternative that munges all user IDs so that they don't collide with your IDs
  + %Attr.IDPrefixLocal - Same as above, but for when there are multiple instances of user content on the page
  + Profuse documentation on how to use these available in id.txt

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@526 48356398-32a2-884e-a903-53898d9a118a
2006-11-17 01:05:41 +00:00
Edward Z. Yang
2dc8e9c3d5 [1.2.0] Unit test housekeeping:
- HTMLPurifier_Context doesn't throw a variable reference error if you attempt to retrieve a non-existent variable
. Cleaned up test-cases to remove unnecessary swallowErrors()

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@525 48356398-32a2-884e-a903-53898d9a118a
2006-11-16 23:58:33 +00:00
Edward Z. Yang
d48f9b6b21 [1.2.0]
- Update TODO
  . Add another possible plaintext formatter
  . Reference config-ideas.txt for URI options
- Update code-quality.txt, removing issues that have been addressed and updating time for post-beta
- Update config-ideas.txt
  . Added more possible URI directives
  . Removed silly language control directive
- Improved documentation on Class, CSS and Host

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@524 48356398-32a2-884e-a903-53898d9a118a
2006-11-12 19:26:49 +00:00
Edward Z. Yang
2df5896324 [1.2.0] Add more projected URI control values.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@523 48356398-32a2-884e-a903-53898d9a118a
2006-11-12 04:02:27 +00:00
Edward Z. Yang
f38fe431ed [1.2.0]
- Added %URI.DisableExternal, which prevents links to external websites. You can also use %URI.Host to permit absolute linking to subdomains
- Fixed a few bugs involving null configuration values

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@522 48356398-32a2-884e-a903-53898d9a118a
2006-11-12 03:35:41 +00:00
Edward Z. Yang
926b94bdd3 [1.2.0] Allow configuration directives to permit null values. ConfigDoc updated accordingly.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@521 48356398-32a2-884e-a903-53898d9a118a
2006-11-12 02:59:36 +00:00
Edward Z. Yang
ad934540da [1.2.0] Merge two comment strings.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@520 48356398-32a2-884e-a903-53898d9a118a
2006-11-12 02:01:39 +00:00
Edward Z. Yang
afee1ea9bf [1.2.0]
- Updated ConfigDoc TODO
- configdoc.xml now has xml:space attached to default value nodes

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@519 48356398-32a2-884e-a903-53898d9a118a
2006-11-12 00:05:27 +00:00
Edward Z. Yang
a6bbe60e7c [1.2.0] Configuration documentation now has table of contents
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@518 48356398-32a2-884e-a903-53898d9a118a
2006-11-08 14:21:06 +00:00
Edward Z. Yang
d2fd193bc4 [1.2.0] Implement primitive email regexp to be used for mailto. There are many spotty implementation issues, so this code is not actually called anywhere else currently.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@517 48356398-32a2-884e-a903-53898d9a118a
2006-11-08 03:10:43 +00:00
Edward Z. Yang
e1b29d7c25 [1.2.0] XSS attacks smoketest given facelift.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@516 48356398-32a2-884e-a903-53898d9a118a
2006-11-08 01:31:38 +00:00
Edward Z. Yang
9668ac1e38 [1.2.0] Add protection against stdclasses into HTMLDefinition.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@514 48356398-32a2-884e-a903-53898d9a118a
2006-11-08 00:11:10 +00:00
Edward Z. Yang
eb6950d7d0 [1.2.0] Fix improper instantiation of stdclasses for '' and '#PCDATA'
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@513 48356398-32a2-884e-a903-53898d9a118a
2006-11-08 00:07:42 +00:00
Edward Z. Yang
4a724d0230 [1.2.0] Add documentation to PercentEncoder.php
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@510 48356398-32a2-884e-a903-53898d9a118a
2006-11-07 17:42:41 +00:00
Edward Z. Yang
504203c0f3 [1.2.0] Added percent encoding normalization
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@509 48356398-32a2-884e-a903-53898d9a118a
2006-11-07 17:15:28 +00:00
Edward Z. Yang
e998b034d1 [1.2.0] Update TODO, reorganized and added an item
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@504 48356398-32a2-884e-a903-53898d9a118a
2006-11-04 05:05:19 +00:00
Edward Z. Yang
84e3a28001 [1.2.0] Type variable in HTMLDefinition was not being set properly, fixed. Minor bug because no other code actually uses the feature (todo: add unit test).
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@503 48356398-32a2-884e-a903-53898d9a118a
2006-11-04 05:03:53 +00:00
Edward Z. Yang
4ee1bf94e3 [1.2.0] Assorted tinyfixes
- Add TODO request about Phalanger, something to do if I'm really bored
- Update XSS attacks
- Minor formatting/grammar fixes in documentation

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@502 48356398-32a2-884e-a903-53898d9a118a
2006-11-03 02:40:37 +00:00
Edward Z. Yang
24f2771304 Add TODO items:
- RTL/LTR override UTF-8 character treatment
- Content compression by removing whitespace

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@501 48356398-32a2-884e-a903-53898d9a118a
2006-10-31 02:17:52 +00:00
Edward Z. Yang
74ba9b8629 [1.2.0] Add context parameter to URIScheme and URISchemeRegistry classes.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@500 48356398-32a2-884e-a903-53898d9a118a
2006-10-27 01:20:10 +00:00
Edward Z. Yang
b9caa35bf4 [1.2.0]
- Add missing reference operator to AttrTransform.php
- Add note on error collection for EntityParser.php
- Add note that IDAccumulator won't collect errors either.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@498 48356398-32a2-884e-a903-53898d9a118a
2006-10-22 16:09:36 +00:00
Edward Z. Yang
6ff78d2f79 Add $config and $context to TagTransform transform() calls.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@497 48356398-32a2-884e-a903-53898d9a118a
2006-10-22 15:56:38 +00:00
Edward Z. Yang
8256ca4376 [1.2.0] Migrate AttrTransform tests to use the Harness supertype.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@496 48356398-32a2-884e-a903-53898d9a118a
2006-10-22 03:38:32 +00:00
Edward Z. Yang
7d2fe4c5d7 [1.2.0]
- Factor out Config and Context object population through arrays
- Bring dependent assertions together in IDTest.php
- AttrDefHarness.php now resets context and configuration between tests
- Add missing reference operator in AttrDef/ID.php

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@494 48356398-32a2-884e-a903-53898d9a118a
2006-10-21 18:18:36 +00:00
Edward Z. Yang
f3646a3a06 [1.2.0]
- Add context parameter to AttrTransform objects.
- Update documentation on attribute transformations in ValidateAttributes.php


git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@493 48356398-32a2-884e-a903-53898d9a118a
2006-10-21 17:27:51 +00:00
Edward Z. Yang
29716bf8f4 Add version number to HTMLPurifier.php. It needs to be bumped on new releases.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@492 48356398-32a2-884e-a903-53898d9a118a
2006-10-21 17:18:40 +00:00
Edward Z. Yang
fb38b02135 [1.2.0] Documentation updated
- Moved docs from EntityParser to Encoder
- Removed/updated docs in Generator

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@490 48356398-32a2-884e-a903-53898d9a118a
2006-10-09 16:07:35 +00:00
Edward Z. Yang
13790c6db2 Added MODx plugin.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@486 48356398-32a2-884e-a903-53898d9a118a
2006-10-02 16:56:47 +00:00
Edward Z. Yang
2d6bf12fe0 [1.2.0]
- All important classes that use Context were migrated. Todo: Classes that currently use $config but not $context are AttrTransform (done in r493) and URIScheme+Registry (done in r500). There may be more classes, incl TagTransform (done in r497) that should have both $config and $context added.
- Strategy unit tests now migrated to use HTMLPurifier_Harness

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@485 48356398-32a2-884e-a903-53898d9a118a
2006-10-01 21:55:13 +00:00
Edward Z. Yang
8f515b9cda [1.2.0]
- Partially finished migrating to new Context object (done in r485).
- Created HTMLPurifier_Harness to assist with testing, ChildDefTest migrated to that framework.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@484 48356398-32a2-884e-a903-53898d9a118a
2006-10-01 20:47:07 +00:00
Edward Z. Yang
58be73fcf7 [1.2.0] Added exists() method to HTMLPurifier_Context.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@483 48356398-32a2-884e-a903-53898d9a118a
2006-10-01 18:39:48 +00:00
Edward Z. Yang
f432a40f50 [1.2.0] Commit initial implementation of Context object, we will be migrating all systems over to it next commit.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@482 48356398-32a2-884e-a903-53898d9a118a
2006-10-01 18:14:08 +00:00
Edward Z. Yang
d660b9018b [1.2.0]
- Add 1.1.3 section in NEWS
- Replace tabs with four spaces in INSTALL
- Renamed data.txt to entities.ser

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@481 48356398-32a2-884e-a903-53898d9a118a
2006-09-30 20:18:08 +00:00
Edward Z. Yang
4d96433c23 [1.1.2] Fix typo in NEWS file.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@479 48356398-32a2-884e-a903-53898d9a118a
2006-09-30 19:34:59 +00:00
Edward Z. Yang
a78f0f5f80 [1.1.2] Bump version number in Doxyfile
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@474 48356398-32a2-884e-a903-53898d9a118a
2006-09-30 19:03:51 +00:00
Edward Z. Yang
d941d30cfa Released 1.1.2.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@473 48356398-32a2-884e-a903-53898d9a118a
2006-09-30 19:02:32 +00:00
Edward Z. Yang
9af9c505e1 [1.1.2]
- Added notes on HTML versus XML attribute whitespace handling
- Noted that HTMLPurifier_ChildDef_Custom isn't being used
- Noted that config object's definitions are cached versions
- Hooked up HTMLPurifier_ChildDef_Custom's unit tests (they weren't being run)
- Tester named "HTML Purifier" not "HTMLPurifier"

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@472 48356398-32a2-884e-a903-53898d9a118a
2006-09-30 18:55:17 +00:00
Edward Z. Yang
7e6a3fc990 [1.1.2] ftp:// URIs now have their typecodes checked
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@471 48356398-32a2-884e-a903-53898d9a118a
2006-09-30 17:24:12 +00:00
Edward Z. Yang
c7e798080c [1.1.2]
- (meta) Updated NEWS document
- Rearranged NEWS into different segments: features/bugfixes/internals

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@470 48356398-32a2-884e-a903-53898d9a118a
2006-09-28 01:28:18 +00:00
Edward Z. Yang
32c5b5080b [1.1.2]
- Add HTMLPurifier.auto.php stub class that automatically configures include path
- Rewrite INSTALL document
- Add semi-lossy dumb character entity conversion to TODO list

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@469 48356398-32a2-884e-a903-53898d9a118a
2006-09-28 00:31:12 +00:00
Edward Z. Yang
cbdd48811d [1.1.2]
- (meta) Add internals note to NEWS document.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@467 48356398-32a2-884e-a903-53898d9a118a
2006-09-27 02:14:53 +00:00
Edward Z. Yang
37def0104b [1.1.2]
- Documentation updated
- API docs now exclude more files that are not classes
- Fixed lack of attribute parsing in HTMLPurifier_Lexer_PEARSax3
- (internal) Refactored parseData() to general Lexer class

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@466 48356398-32a2-884e-a903-53898d9a118a
2006-09-27 02:09:54 +00:00
Edward Z. Yang
d9bb97cc26 [1.1.2] Update NEWS for r464.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@465 48356398-32a2-884e-a903-53898d9a118a
2006-09-25 00:23:33 +00:00
Edward Z. Yang
8bff97ec08 [1.1.2] Mass svn:eol-style=native. data.txt had line ending info taken away, since it is unbiased
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@464 48356398-32a2-884e-a903-53898d9a118a
2006-09-25 00:05:33 +00:00
Edward Z. Yang
fab2b363d0 Released 1.1.1.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@463 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 23:42:14 +00:00
Edward Z. Yang
8e1cfb362d [1.1.1] Update INSTALL docs.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@457 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 22:03:48 +00:00
Edward Z. Yang
1fa5101511 [1.1.1]
- Clarify usage of %Core.TidyFormat
- Add test-settings.sample.php, to facilitate benchmark and unit test running

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@456 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 21:58:14 +00:00
Edward Z. Yang
24663d65ed [1.1.1] To make up for DOMLex's tendency to drop tags, we've added a configuration option to let Tidy cleanup the HTML afterwards. Good for hand-editors. Also, Tidy is a smart solution for pretty-printed HTML, so we're marking the related TODO wontfix.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@454 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 21:23:54 +00:00
Edward Z. Yang
6adbaf0e5c [1.1.1] Removed double-semicolon.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@453 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 19:58:59 +00:00
Edward Z. Yang
81cd9b1ee8 [1.1.1] Grey outputs that are negative.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@452 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 19:48:29 +00:00
Edward Z. Yang
f5ff8acbb0 [1.1.1]
- Gracefully handle error if test-settings.php is not present
- Let test-settings define number of runs.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@451 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 19:40:28 +00:00
Edward Z. Yang
ad8310c1f5 [1.1.1] Format millisecond timing, run the parser 3 times for a more fair comparison.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@450 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 18:45:24 +00:00
Edward Z. Yang
4b5198c5bc [1.1.1] Augment with time difference.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@449 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 18:36:32 +00:00
Edward Z. Yang
a251ec590f [1.1.1] Error out if PEAR is not enabled on the system, include the test-settings.php file.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@448 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 18:32:41 +00:00
Edward Z. Yang
2bfdfaa02c [1.1.1] Fix bad include paths from ConfigDef to ConfigSchema changes.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@447 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 18:17:05 +00:00
Edward Z. Yang
4abf83af62 [1.1.1] Update TODO.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@446 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 02:08:55 +00:00
Edward Z. Yang
1ad55e0ed5 [1.1.1] As far as possible, preserve whitespace is table internals.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@445 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 02:08:18 +00:00
Edward Z. Yang
6c04bbdac1 [1.1.1]
- Update documentation
- Fix parse error in configuration documentation

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@444 48356398-32a2-884e-a903-53898d9a118a
2006-09-24 02:06:12 +00:00
Edward Z. Yang
c046da638a [1.1.1] Update milestones in progress.html
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@443 48356398-32a2-884e-a903-53898d9a118a
2006-09-23 18:39:27 +00:00
Edward Z. Yang
801dbcafb7 - Update filter-levels document to cover CSS and attributes
- Add colors proposal, for constraining allowed colors in  document
- Add strictness proposal, for attributes that are permitted by Transitional but not by HTML Purifier

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@442 48356398-32a2-884e-a903-53898d9a118a
2006-09-23 18:37:30 +00:00
Edward Z. Yang
4f8d83506d [1.1.1]
- Shuffle around TODO items, we're going to handle the URI deficiencies first
- Fix bugs in documentation :-P

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@441 48356398-32a2-884e-a903-53898d9a118a
2006-09-23 00:43:21 +00:00
Edward Z. Yang
00fce29467 Add more documentation to HTMLDefinition in anticipation for refactoring.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@440 48356398-32a2-884e-a903-53898d9a118a
2006-09-22 02:47:41 +00:00
Edward Z. Yang
686824262e [1.1.1] Add to list of wanted formatters Linkify URLs.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@439 48356398-32a2-884e-a903-53898d9a118a
2006-09-18 01:13:49 +00:00
Edward Z. Yang
b93892a3b6 [1.1.1] Update documentation and TODO.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@436 48356398-32a2-884e-a903-53898d9a118a
2006-09-17 21:59:40 +00:00
Edward Z. Yang
7a6de55f76 [1.1.1] Update documentation.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@435 48356398-32a2-884e-a903-53898d9a118a
2006-09-17 21:53:12 +00:00
Edward Z. Yang
d7642b8c70 Bump release dates.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@425 48356398-32a2-884e-a903-53898d9a118a
2006-09-17 00:17:45 +00:00
Edward Z. Yang
3b30c2ca5b Renamed ConfigDef to ConfigSchema. (Required major internal restructuring but should not affect end-users)
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@424 48356398-32a2-884e-a903-53898d9a118a
2006-09-16 22:36:58 +00:00
Edward Z. Yang
f43616f72d Add default value information.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@423 48356398-32a2-884e-a903-53898d9a118a
2006-09-16 01:53:13 +00:00
Edward Z. Yang
6740ba61af - XHTML generation can now be turned off, allowing things like <br>
- Docs updated in preparation for 1.1 release

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@422 48356398-32a2-884e-a903-53898d9a118a
2006-09-16 00:37:33 +00:00
Edward Z. Yang
6a33945499 Update Test namespace description noting that it is developer-only.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@421 48356398-32a2-884e-a903-53898d9a118a
2006-09-16 00:00:17 +00:00
Edward Z. Yang
4660791682 Bump plaintext formatters to 1.3, they'll be difficult to implement.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@420 48356398-32a2-884e-a903-53898d9a118a
2006-09-15 23:33:35 +00:00
Edward Z. Yang
b5c69d8ca5 Update documentation.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@418 48356398-32a2-884e-a903-53898d9a118a
2006-09-15 01:59:43 +00:00
Edward Z. Yang
e440f25bce [1.1] Table child definition made more flexible, will fix up poorly ordered elements
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@417 48356398-32a2-884e-a903-53898d9a118a
2006-09-15 01:52:22 +00:00
Edward Z. Yang
665e80d223 Remove outdated and misleading DTD.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@416 48356398-32a2-884e-a903-53898d9a118a
2006-09-15 00:28:49 +00:00
Edward Z. Yang
69747ede8a Generalize custom test to use non-existent items. Table unit test was disabled (to be reused for table test).
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@415 48356398-32a2-884e-a903-53898d9a118a
2006-09-13 02:11:09 +00:00
Edward Z. Yang
49b3832ebf Add ToC todo to configdoc.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@414 48356398-32a2-884e-a903-53898d9a118a
2006-09-13 01:02:17 +00:00
Edward Z. Yang
a365d4c688 - Finished documentation generation.
- Modified namespace definitions so that they cannot be redefined

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@413 48356398-32a2-884e-a903-53898d9a118a
2006-09-13 00:59:20 +00:00
Edward Z. Yang
7038fad788 Add nicer comments to configdoc/generate.php. We may want to factor things out into classes.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@412 48356398-32a2-884e-a903-53898d9a118a
2006-09-11 02:52:47 +00:00
Edward Z. Yang
50b272d75e Fix up the NEWS document that was missing release dates.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@409 48356398-32a2-884e-a903-53898d9a118a
2006-09-11 02:20:33 +00:00
Edward Z. Yang
bfb642d32c Ehh... I liked 80-char linewrap better.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@408 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 23:02:36 +00:00
Edward Z. Yang
edb39601c7 Increase wrap slightly.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@407 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 22:55:58 +00:00
Edward Z. Yang
694139d3bb Fix parse error.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@406 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 22:53:40 +00:00
Edward Z. Yang
81721ded5c Disable HTML output when run from cli.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@405 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 22:47:27 +00:00
Edward Z. Yang
371fb7c3d2 Remove cli conditional code.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@404 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 22:40:18 +00:00
Edward Z. Yang
9e6953e619 Massive augmentation of ConfigDoc.
- Generate name to expanded name for types
- Change XML format so that constraints are grouped together
- Table-ize constraints
- Enable HTML output in descriptions
- Run output through Tidy to make it easier to read
- Load stylesheet

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@403 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 22:17:22 +00:00
Edward Z. Yang
2299f0c831 Add todo items to the files.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@402 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 20:21:19 +00:00
Edward Z. Yang
9dd4dcb27a Restructure directory to make it more amenable to CSS by merging output with ./
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@400 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 20:16:19 +00:00
Edward Z. Yang
aa0838492e Remove an outdated piece of information from Lexer's configuration documentation.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@399 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 19:56:49 +00:00
Edward Z. Yang
df075c96e0 Rename default skin to plain skin to allow directory browsing.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@397 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 19:48:40 +00:00
Edward Z. Yang
fbaa909d25 [1.1.0] Initial commit of ConfigDoc code, still needs cleanup, but basic proof-of-concept is there.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@394 48356398-32a2-884e-a903-53898d9a118a
2006-09-10 02:02:40 +00:00
Edward Z. Yang
967f40fc11 Make install docs more comprehensive about encoding. Prompted by http://hp.jpsband.org/vanilla/comments.php?DiscussionID=2
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@392 48356398-32a2-884e-a903-53898d9a118a
2006-09-09 21:10:04 +00:00
Edward Z. Yang
5ee6ffe20f Defer HTMLDefinition include to the classes that are actually tightly coupled to it.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@391 48356398-32a2-884e-a903-53898d9a118a
2006-09-07 02:13:17 +00:00
Edward Z. Yang
10d41d7130 Add notes on redefinition, what to do next?
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@390 48356398-32a2-884e-a903-53898d9a118a
2006-09-06 02:20:32 +00:00
Edward Z. Yang
65a628bcb7 [1.1.0] Enforce alphanumeric namespace and directive names for configuration.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@389 48356398-32a2-884e-a903-53898d9a118a
2006-09-06 02:07:46 +00:00
Edward Z. Yang
a5b4ed2126 [1.0.1] Fixed rejection of inline style declarations that had lots of extra space in them. This manifested in TinyMCE.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@382 48356398-32a2-884e-a903-53898d9a118a
2006-09-04 23:01:47 +00:00
Edward Z. Yang
d20bbd8db3 [1.0.1] Disambiguate between iconv and PHP test runs for cleanUTF8.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@381 48356398-32a2-884e-a903-53898d9a118a
2006-09-04 20:18:10 +00:00
Edward Z. Yang
b99573223d [1.1.0] Made URI validator more forgiving: will ignore leading and trailing quotes, apostrophes and less than or greater than signs.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@380 48356398-32a2-884e-a903-53898d9a118a
2006-09-04 02:31:27 +00:00
Edward Z. Yang
c6cfb68713 Bump formatting features release date to 1.1
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@376 48356398-32a2-884e-a903-53898d9a118a
2006-09-03 20:50:45 +00:00
Edward Z. Yang
2259bfa40e Merged 371:372 from branches/1.0/ to trunk/
- Add integration test.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@374 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 17:56:55 +00:00
Edward Z. Yang
de3b2b70fb Bump tested in version number.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@370 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 17:18:49 +00:00
Edward Z. Yang
4f0a5c0e22 - Add Test namespace
- Further fix the no iconv problem, and extend test cases to cover that case.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@368 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 16:54:23 +00:00
Edward Z. Yang
fdd583253c Fixed rejection of case-insensitive configuration values when there is a set of allowed values. This manifested in %Core.Encoding.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@367 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 16:40:14 +00:00
Edward Z. Yang
a4be6ffe4d Fix slight bug in DOMLex's attribute parsing.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@365 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 16:19:21 +00:00
Edward Z. Yang
6de42d8d1d Attempt to fix strange foreach troubles.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@364 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 16:17:56 +00:00
Edward Z. Yang
e9a519e589 Bump Doxyfile version number, add spaces in-between HTML Purifier.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@360 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 15:28:05 +00:00
Edward Z. Yang
709a17a504 Add shutup operator to iconv and utf_* calls.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@357 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 15:01:27 +00:00
Edward Z. Yang
47a6c9eb75 - Update install docs with PHP version compatibility not
- Bump 1.0.0 release date

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@356 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 14:57:47 +00:00
Edward Z. Yang
eddf474351 Remove call to PHP 5 only function.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@355 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 14:49:13 +00:00
Edward Z. Yang
0e715bdda6 Fix PHP 5.0 bug involving isset and DOM.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@354 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 14:44:50 +00:00
Edward Z. Yang
478fab1ad1 - Remove release candidate suffix
- Add licensing info to main file... too lazy to add it to the rest, haha.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@353 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 01:07:09 +00:00
Edward Z. Yang
f4f636a09c Add rudimentary extra encoding support. We are now release-ready!
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@352 48356398-32a2-884e-a903-53898d9a118a
2006-09-01 00:54:38 +00:00
Edward Z. Yang
b621602ac1 Speed up cleanUTF8 with iconv. Also factored out code point to character code.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@351 48356398-32a2-884e-a903-53898d9a118a
2006-08-31 22:25:48 +00:00
Edward Z. Yang
14aeafcf22 De-singleton-ized (HTML|CSS)Definition, tying them to the configuration and making them more amenable to changes.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@350 48356398-32a2-884e-a903-53898d9a118a
2006-08-31 20:33:07 +00:00
Edward Z. Yang
90279eaee2 HTMLPurifier.php - Minor styling and documentation updates.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@342 48356398-32a2-884e-a903-53898d9a118a
2006-08-30 02:24:43 +00:00
Edward Z. Yang
0ac97774d4 More refactoring: bundling charset and entity stuff together makes little sense, so new HTMLPurifier/EntityParser.php.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@341 48356398-32a2-884e-a903-53898d9a118a
2006-08-30 02:21:39 +00:00
Edward Z. Yang
89376a11e3 Remove a huge swath of duplicated function calls by factoring them into a normalize() function. Also made DirectLex's variable names consistent with the rest of the classes.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@340 48356398-32a2-884e-a903-53898d9a118a
2006-08-29 20:05:26 +00:00
Edward Z. Yang
1de3088276 Refactor encoding and entity specific processing to HTMLPurifier_Encoder. We also need to refactor the escaping to this class too.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@339 48356398-32a2-884e-a903-53898d9a118a
2006-08-29 19:36:40 +00:00
Edward Z. Yang
55503744ee Slight adjustments to demo: reset to default time limit and send out a header indicating character encoding.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@338 48356398-32a2-884e-a903-53898d9a118a
2006-08-29 19:06:43 +00:00
Edward Z. Yang
670d298a87 Implement list-style shorthand. Also, updated devnetwork.html with more recent threads.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@337 48356398-32a2-884e-a903-53898d9a118a
2006-08-29 02:01:58 +00:00
Edward Z. Yang
784e7356d1 Fix missing inheritance declaration.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@336 48356398-32a2-884e-a903-53898d9a118a
2006-08-28 20:29:36 +00:00
Edward Z. Yang
fbb0c486ec Doxygen needs to ignore test-settings.php.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@335 48356398-32a2-884e-a903-53898d9a118a
2006-08-28 20:28:19 +00:00
Edward Z. Yang
a1b60ad70f - Update Doxyfile to ignore PHPDoc files.
- More progress color twiddling, add new type feature

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@334 48356398-32a2-884e-a903-53898d9a118a
2006-08-28 20:24:25 +00:00
Edward Z. Yang
3e8b1d1148 Update progress, add target milestones.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@333 48356398-32a2-884e-a903-53898d9a118a
2006-08-28 20:18:56 +00:00
Edward Z. Yang
dd1b911183 Implement limited CSS property background, can only do colors (though this is desired behavior).
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@332 48356398-32a2-884e-a903-53898d9a118a
2006-08-28 20:10:01 +00:00
Edward Z. Yang
4e08389427 Modify TODO: we'll support lossy character encoding transformations (good for websites that are not i18n) but not much else.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@331 48356398-32a2-884e-a903-53898d9a118a
2006-08-28 19:21:46 +00:00
Edward Z. Yang
dc19ac9a2a Update TODO, organize items according to version numbers.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@330 48356398-32a2-884e-a903-53898d9a118a
2006-08-28 02:47:03 +00:00
Edward Z. Yang
15988980db - Switch to HTMLPurifier.php, a more logical "must include" for the tester
- Disable encoding options, we're going to defer implementation of them for a later release.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@329 48356398-32a2-884e-a903-53898d9a118a
2006-08-28 02:25:19 +00:00
Edward Z. Yang
7588068b7b Hacky full docuement parse thingy removed from DOMLex, fixes barfing on full HTML documents.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@328 48356398-32a2-884e-a903-53898d9a118a
2006-08-27 22:06:58 +00:00
Edward Z. Yang
24cde9c891 Revamp configuration files so that more rules can be added, internal organization is more logical, and descriptions are captured.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@327 48356398-32a2-884e-a903-53898d9a118a
2006-08-27 18:49:16 +00:00
Edward Z. Yang
0d4ee2ba37 Fix call-time pass by reference typos.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@326 48356398-32a2-884e-a903-53898d9a118a
2006-08-27 02:08:50 +00:00
Edward Z. Yang
78414abafd Implement all useful table properties.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@325 48356398-32a2-884e-a903-53898d9a118a
2006-08-27 01:45:23 +00:00
Edward Z. Yang
692a9abc0f Implement shorthand CSS property border.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@324 48356398-32a2-884e-a903-53898d9a118a
2006-08-27 00:49:34 +00:00
Edward Z. Yang
ffe39d7f30 Basic color keywords translated into hexadecimal values.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@323 48356398-32a2-884e-a903-53898d9a118a
2006-08-27 00:35:57 +00:00
Edward Z. Yang
5169fc7a3b Twiddle colors a little to remove all that red.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@322 48356398-32a2-884e-a903-53898d9a118a
2006-08-27 00:16:36 +00:00
Edward Z. Yang
80e79d906a Implement CSS property Font.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@321 48356398-32a2-884e-a903-53898d9a118a
2006-08-27 00:11:13 +00:00
Edward Z. Yang
a43a2730bc Add filter levels document, detailing how to extend Definition.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@320 48356398-32a2-884e-a903-53898d9a118a
2006-08-26 18:44:50 +00:00
Edward Z. Yang
ca1453401f Update documentation.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@319 48356398-32a2-884e-a903-53898d9a118a
2006-08-25 03:01:16 +00:00
Edward Z. Yang
dcec92e7b3 Fix bug: number spans should not allow zero as a value. This required augmenting HTMLPurifier/AttrDef/Integer.php to have a richer negative/zero/positive specification interface that can be extrapolated to Number and friends.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@318 48356398-32a2-884e-a903-53898d9a118a
2006-08-25 02:48:49 +00:00
Edward Z. Yang
fb0003a608 Update ignore lists.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@317 48356398-32a2-884e-a903-53898d9a118a
2006-08-23 20:12:21 +00:00
Edward Z. Yang
1b8f4fa5bc Update todo, added Microsoft Word cleaning and expanded some of the existing items.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@316 48356398-32a2-884e-a903-53898d9a118a
2006-08-23 20:09:55 +00:00
Edward Z. Yang
f46b15cb82 Document fact that inherit only works when its alone.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@315 48356398-32a2-884e-a903-53898d9a118a
2006-08-23 02:11:04 +00:00
Edward Z. Yang
6af60425b8 Modify demo so that outputted source in text area is SGML valid.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@314 48356398-32a2-884e-a903-53898d9a118a
2006-08-21 00:48:43 +00:00
Edward Z. Yang
f8839d56a0 Add missing extends.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@313 48356398-32a2-884e-a903-53898d9a118a
2006-08-21 00:36:36 +00:00
Edward Z. Yang
fb08b9c89b Document URISchemes, this completes internal API documentation.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@311 48356398-32a2-884e-a903-53898d9a118a
2006-08-20 22:06:11 +00:00
Edward Z. Yang
f7760c8cb6 Document Strategies.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@310 48356398-32a2-884e-a903-53898d9a118a
2006-08-20 21:59:41 +00:00
Edward Z. Yang
7813e79bda Document all AttrTransforms.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@309 48356398-32a2-884e-a903-53898d9a118a
2006-08-20 21:55:28 +00:00
Edward Z. Yang
314a48373c Document all AttrDefs, also remove duplicant NumberSpan in favor of Integer.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@308 48356398-32a2-884e-a903-53898d9a118a
2006-08-20 21:47:15 +00:00
Edward Z. Yang
ca0914789c Fix syntax error.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@307 48356398-32a2-884e-a903-53898d9a118a
2006-08-20 21:39:22 +00:00
Edward Z. Yang
2605257723 Finish documentation for all base classes.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@306 48356398-32a2-884e-a903-53898d9a118a
2006-08-20 20:59:13 +00:00
Edward Z. Yang
679302b161 Detail structural changes involving Config and *Definition
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@305 48356398-32a2-884e-a903-53898d9a118a
2006-08-19 23:08:58 +00:00
Edward Z. Yang
37cbdc25b1 Document core classes up to EntityLookup.php
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@304 48356398-32a2-884e-a903-53898d9a118a
2006-08-19 23:06:59 +00:00
Edward Z. Yang
973cc43b64 Malformed UTF-8 and non-SGML character detection and cleaning implemented
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@303 48356398-32a2-884e-a903-53898d9a118a
2006-08-19 17:53:59 +00:00
Edward Z. Yang
53808ee34a Attempt to fix inconsistent DOM behavior regarding insertion of P tags.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@302 48356398-32a2-884e-a903-53898d9a118a
2006-08-19 16:24:17 +00:00
Edward Z. Yang
2eef708557 Fix syntax error.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@301 48356398-32a2-884e-a903-53898d9a118a
2006-08-19 00:26:33 +00:00
Edward Z. Yang
42ba96e2de Put in cleanUTF8 function, currently unused but will be adapted for our needs.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@300 48356398-32a2-884e-a903-53898d9a118a
2006-08-18 20:06:40 +00:00
Edward Z. Yang
a33cd12f1a Fixed broken multibyte numeric entity conversion in Lexer::substituteNonSpecialEntities()
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@299 48356398-32a2-884e-a903-53898d9a118a
2006-08-18 17:49:33 +00:00
Edward Z. Yang
c393ef8a81 + DevNetwork thread "Non-SGML characters"
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@298 48356398-32a2-884e-a903-53898d9a118a
2006-08-18 15:13:18 +00:00
Edward Z. Yang
ad83ab430d Make unit tests UTF-8, add phpdoc.ini config file.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@297 48356398-32a2-884e-a903-53898d9a118a
2006-08-18 04:24:19 +00:00
Edward Z. Yang
4c52e42189 Fix typo: $attributes to $attr.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@296 48356398-32a2-884e-a903-53898d9a118a
2006-08-18 01:27:14 +00:00
Edward Z. Yang
50d5179dbd Add next release to NEWS.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@295 48356398-32a2-884e-a903-53898d9a118a
2006-08-18 00:04:05 +00:00
Edward Z. Yang
af0de616ae Add a variableWidthAttack smoketest, and add analysis to some smoketests.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@294 48356398-32a2-884e-a903-53898d9a118a
2006-08-17 23:36:35 +00:00
Edward Z. Yang
66ddc4cc5a Add lots of documentation.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@293 48356398-32a2-884e-a903-53898d9a118a
2006-08-17 20:29:34 +00:00
Edward Z. Yang
252c5afae0 Update NEWS and TODO after IPv6 was fixed.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@287 48356398-32a2-884e-a903-53898d9a118a
2006-08-17 01:49:38 +00:00
Edward Z. Yang
04c0953af0 Link back to home page.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@286 48356398-32a2-884e-a903-53898d9a118a
2006-08-17 01:41:32 +00:00
189 changed files with 9936 additions and 2886 deletions

View File

@@ -2,6 +2,6 @@
CREDITS
Almost everything written by Edward Z. Yang (Ambush Commander). Lots of thanks
to the DevNetwork Community for their help (see docs/devnetwork.html for more
details), Feyd especially (namely IPv6 and optimization). Thanks to RSnake for
letting me package his fantastic XSS cheatsheet for a smoketest.
to the DevNetwork Community for their help (see docs/ref-devnetwork.html for
more details), Feyd especially (namely IPv6 and optimization). Thanks to RSnake
for letting me package his fantastic XSS cheatsheet for a smoketest.

View File

@@ -3,8 +3,8 @@
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = HTMLPurifier
PROJECT_NUMBER = trunk
PROJECT_NAME = HTML Purifier
PROJECT_NUMBER = 1.3.2
OUTPUT_DIRECTORY = "C:/Documents and Settings/Edward/My Documents/My Webs/htmlpurifier/docs/doxygen"
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
@@ -88,7 +88,13 @@ RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS = */tests/* \
*/benchmarks/*
*/benchmarks/* \
*/docs/* \
*/test-settings.php \
*/configdoc/* \
*/test-settings.php \
*/maintenance/* \
*/smoketests/*
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO

208
INSTALL
View File

@@ -1,61 +1,189 @@
Install
How to install HTMLPurifier
How to install HTML Purifier
Being a library, there's no fancy GUI that will take you step-by-step through
configuring database credentials and other mumbo-jumbo. HTMLPurifier is
designed to run "out of the box." Regardless, there are still a couple of
things you should be mindful of.
HTML Purifier is designed to run out of the box, so actually using the library
is extremely easy. (Although, if you were looking for a step-by-step
installation GUI, you've come to the wrong place!) The impatient can scroll
down to the bottom of this INSTALL document to see the code, but you really
should make sure a few things are properly done.
1. Including the proper files
1. Compatibility
The library/ directory must be added to your path: HTMLPurifier will not be
able to find the necessary includes otherwise. This is as simple as:
HTML Purifier works in both PHP 4 and PHP 5, from PHP 4.3.9 and up. It has no
core dependencies with other libraries. (Whoopee!)
set_include_path('/path/to/htmlpurifier/library' . PATH_SEPARATOR . get_include_path());
...replacing /path/to/htmlpurifier with the actual location of the folder. Don't
worry, HTMLPurifier is namespaced so unless you have another file named
HTMLPurifier.php, the files won't collide with any of your includes.
Then, it's a simple matter of including the base file:
require_once 'HTMLPurifier.php';
...and you're good to go.
Optional extensions are iconv (usually installed) and tidy (also common).
If you use UTF-8 and don't plan on pretty-printing HTML, you can get away with
not having either of these extensions.
2. Preparing the proper environment
2. Including the library
While no configuration is necessary, you first should take precautions regarding
the other output HTML that the filtered content will be going along with. Here
is a (short) checklist:
Simply use:
* Have I specified XHTML 1.0 Transitional as the doctype?
* Have I specified UTF-8 as the character encoding?
require_once '/path/to/library/HTMLPurifier.auto.php';
I cannot stress the importance of these two bullets enough. Omitting either
of them could have dire consequences not only for security but for plain
old usability. You can find a more in-depth discussion of why this is needed
in docs/security.txt, in the meantime, try to change your output so this is
the case.
...and you're good to go. Since HTML Purifier's codebase is fairly
large, I recommend only including HTML Purifier when you need it.
If, for some reason, you are unable to switch to UTF-8 immediately, you can
use iconv to convert the output of HTMLPurifier to your desired encoding.
We may integrate support for other encodings in later releases, but for now,
UTF-8 is all you should need. (If you're not using UTF-8, switch now!)
If you don't like your include_path to be fiddled around with, simply set
HTML Purifier's library/ directory to the include path yourself and then:
require_once 'HTMLPurifier.php';
Only the contents in the library/ folder are necessary, so you can remove
everything else when using HTML Purifier in a production environment.
3. Using the code
3. Preparing the proper output environment
The interface is mind-numbingly simple.
HTML Purifier is all about web-standards, so accordingly your webpages should
be standards compliant. HTML Purifier can deal with these doctypes:
$purifier = new HTMLPurifier();
$clean_html = $purifier->purify($dirty_html);
* XHTML 1.0 Transitional (default)
* HTML 4.01 Transitional
That's it. For more examples, check out docs/examples/. Also, SLOW gives
advice on what to do if HTMLPurifier is slowing down your application.
...and these character encodings:
* UTF-8 (default)
* Any encoding iconv supports (support is crippled for i18n though)
The defaults are there for a reason: they are best-practice choices that
should not be changed lightly. For those of you in the dark, you can determine
the doctype from this code in your HTML documents:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
...and the character encoding from this code:
<meta http-equiv="Content-type" content="text/html;charset=ENCODING">
For legacy codebases these declarations may be missing. If that is the case,
STOP, and read up on character encodings and doctypes (in that order). Here
are some links:
* http://www.joelonsoftware.com/articles/Unicode.html
* http://alistapart.com/stories/doctype/
You may currently be vulnerable to XSS and other security threats, and HTML
Purifier won't be able to fix that.
4. Configuration
HTML Purifier is designed to run out-of-the-box, but occasionally HTML
Purifier needs to be told what to do. If you answered no to any of these
questions, read on, otherwise, you can skip to the next section (or, if you're
into configuring things just for the heck of it, skip to 4.3).
* Am I using UTF-8?
* Am I using XHTML 1.0 Transitional?
If you answered yes to any of these questions, instantiate a configuration
object and read on:
$config = HTMLPurifier_Config::createDefault();
4.1. Setting a different character encoding
You really shouldn't use any other encoding except UTF-8, especially if you
plan to support multilingual websites (read section three for more details).
However, switching to UTF-8 is not always immediately feasible, so we can
adapt.
HTML Purifier uses iconv to support other character encodings, as such,
any encoding that iconv supports <http://www.gnu.org/software/libiconv/>
HTML Purifier supports with this code:
$config->set('Core', 'Encoding', /* put your encoding here */);
An example usage for Latin-1 websites (the most common encoding for English
websites):
$config->set('Core', 'Encoding', 'ISO-8859-1');
Note that HTML Purifier's support for non-Unicode encodings is crippled by the
fact that any character not supported by that encoding will be silently
dropped, EVEN if it is ampersand escaped. This is a current limitation of
HTML Purifier that we are NOT actively working to fix. Patches are welcome,
but there are so many other gotchas and problems in I18N for non-Unicode
encodings that this functionality is low priority. See
<http://ppewww.ph.gla.ac.uk/~flavell/charset/form-i18n.html> for a more
detailed lowdown on the topic.
4.2. Setting a different doctype
For those of you stuck using HTML 4.01 Transitional, you can disable
XHTML output like this:
$config->set('Core', 'XHTML', false);
I recommend that you use XHTML, although not as much as I recommend UTF-8. If
your HTML 4.01 page validates, good for you!
Currently, we can only guarantee transitional-complaint output, future
versions will also allow strict-compliant output.
4.3. Other settings
There are more configuration directives which can be read about
here: <http://hp.jpsband.org/live/configdoc/plain.html> They're a bit boring,
but they can help out for those of you who like to exert maximum control over
your code.
5. Using the code
The interface is mind-numbingly simple:
$purifier = new HTMLPurifier();
$clean_html = $purifier->purify( $dirty_html );
...or, if you're using the configuration object:
$purifier = new HTMLPurifier($config);
$clean_html = $purifier->purify( $dirty_html );
That's it! For more examples, check out docs/examples/ (they aren't very
different though). Also, SLOW gives advice on what to do if HTML Purifier
is slowing down your application.
6. Quick install
If your website is in UTF-8 and XHTML Transitional, use this code:
<?php
require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';
$purifier = new HTMLPurifier();
$clean_html = $purifier->purify($dirty_html);
?>
If your website is in a different encoding or doctype, use this code:
<?php
require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';
$config = HTMLPurifier_Config::createDefault();
$config->set('Core', 'Encoding', 'ISO-8859-1'); //replace with your encoding
$config->set('Core', 'XHTML', true); //replace with false if HTML 4.01
$purifier = new HTMLPurifier($config);
$clean_html = $purifier->purify($dirty_html);
?>

154
NEWS
View File

@@ -1,9 +1,153 @@
NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
= KEY ====================
# Breaks back-compat
! Feature
- Bugfix
+ Sub-comment
. Internal change
==========================
1.3.2, released 2006-12-25
! HTMLPurifier object now accepts configuration arrays, no need to manually
instantiate a configuration object
! Context object now accessible to outside
! Added enduser-youtube.html, explains how to embed YouTube videos. See
also corresponding smoketest preserveYouTube.php.
! Added purifyArray(), which takes a list of HTML and purifies it all
! Added static member variable $version to HTML Purifier with PHP-compatible
version number string.
- Fixed fatal error thrown by upper-cased language attributes
- printDefinition.php: added labels, added better clarification
. HTMLPurifier_Config::create() added, takes mixed variable and converts into
a HTMLPurifier_Config object.
1.3.1, released 2006-12-06
! Added HTMLPurifier.func.php stub for a convenient function to call the library
- Fixed bug in RemoveInvalidImg code that caused all images to be dropped
(thanks to .mario for reporting this)
. Standardized all attribute handling variables to attr, made it plural
1.3.0, released 2006-11-26
# Invalid images are now removed, rather than replaced with a dud
<img src="" alt="Invalid image" />. Previous behavior can be restored
with new directive %Core.RemoveInvalidImg set to false.
! (X)HTML Strict now supported
+ Transparently handles inline elements in block context (blockquote)
! Added GET method to demo for easier validation, added 50kb max input size
! New directive %HTML.BlockWrapper, for block-ifying inline elements
! New directive %HTML.Parent, allows you to only allow inline content
! New directives %HTML.AllowedElements and %HTML.AllowedAttributes to let
users narrow the set of allowed tags
! <li value="4"> and <ul start="2"> now allowed in loose mode
! New directives %URI.DisableExternalResources and %URI.DisableResources
! New directive %Attr.DisableURI, which eliminates all hyperlinking
! New directive %URI.Munge, munges URI so you can use some sort of redirector
service to avoid PageRank leaks or warn users that they are exiting your site.
! Added spiffy new smoketest printDefinition.php, which lets you twiddle with
the configuration settings and see how the internal rules are affected.
! New directive %URI.HostBlacklist for blocking links to bad hosts.
xssAttacks.php smoketest updated accordingly.
- Added missing type to ChildDef_Chameleon
- Remove Tidy option from demo if there is not Tidy available
. ChildDef_Required guards against empty tags
. Lookup table HTMLDefinition->info_flow_elements added
. Added peace-of-mind variable initialization to Strategy_FixNesting
. Added HTMLPurifier->info_parent_def, parent child processing made special
. Added internal documents briefly summarizing future progression of HTML
. HTMLPurifier_Config->getBatch($namespace) added
. More lenient casting to bool from string in HTMLPurifier_ConfigSchema
. Refactored ChildDef classes into their own files
1.2.0, released 2006-11-19
# ID attributes now disabled by default. New directives:
+ %HTML.EnableAttrID - restores old behavior by allowing IDs
+ %Attr.IDPrefix - %Attr.IDBlacklist alternative that munges all user IDs
so that they don't collide with your IDs
+ %Attr.IDPrefixLocal - Same as above, but for when there are multiple
instances of user content on the page
+ Profuse documentation on how to use these available in docs/enduser-id.txt
! Added MODx plugin <http://modxcms.com/forums/index.php/topic,6604.0.html>
! Added percent encoding normalization
! XSS attacks smoketest given facelift
! Configuration documentation now has table of contents
! Added %URI.DisableExternal, which prevents links to external websites. You
can also use %URI.Host to permit absolute linking to subdomains
! Non-accessible resources (ex. mailto) blocked from embedded URIs (img src)
- Type variable in HTMLDefinition was not being set properly, fixed
- Documentation updated
+ TODO added request Phalanger
+ TODO added request Native compression
+ TODO added request Remove redundant tags
+ TODO added possible plaintext formatter for HTML Purifier documentation
+ Updated ConfigDoc TODO
+ Improved inline comments in AttrDef/Class.php, AttrDef/CSS.php
and AttrDef/Host.php
+ Revamped documentation into HTML, along with misc updates
- HTMLPurifier_Context doesn't throw a variable reference error if you attempt
to retrieve a non-existent variable
. Switched to purify()-wide Context object registry
. Refactored unit tests to minimize duplication
. XSS attack sheet updated
. configdoc.xml now has xml:space attached to default value nodes
. Allow configuration directives to permit null values
. Cleaned up test-cases to remove unnecessary swallowErrors()
1.1.2, released 2006-09-30
! Add HTMLPurifier.auto.php stub file that configures include_path
- Documentation updated
+ INSTALL document rewritten
+ TODO added semi-lossy conversion
+ API Doxygen docs' file exclusions updated
+ Added notes on HTML versus XML attribute whitespace handling
+ Noted that HTMLPurifier_ChildDef_Custom isn't being used
+ Noted that config object's definitions are cached versions
- Fixed lack of attribute parsing in HTMLPurifier_Lexer_PEARSax3
- ftp:// URIs now have their typecodes checked
- Hooked up HTMLPurifier_ChildDef_Custom's unit tests (they weren't being run)
. Line endings standardized throughout project (svn:eol-style standardized)
. Refactored parseData() to general Lexer class
. Tester named "HTML Purifier" not "HTMLPurifier"
1.1.1, released 2006-09-24
! Configuration option to optionally Tidy up output for indentation to make up
for dropped whitespace by DOMLex (pretty-printing for the entire application
should be done by a page-wide Tidy)
- Various documentation updates
- Fixed parse error in configuration documentation script
- Fixed fatal error in benchmark scripts, slightly augmented
- As far as possible, whitespace is preserved in-between table children
- Sample test-settings.php file included
1.1.0, released 2006-09-16
! Directive documentation generation using XSLT
! XHTML can now be turned off, output becomes <br>
- Made URI validator more forgiving: will ignore leading and trailing
quotes, apostrophes and less than or greater than signs.
- Enforce alphanumeric namespace and directive names for configuration.
- Table child definition made more flexible, will fix up poorly ordered elements
. Renamed ConfigDef to ConfigSchema
1.0.1, released 2006-09-04
- Fixed slight bug in DOMLex attribute parsing
- Fixed rejection of case-insensitive configuration values when there is a
set of allowed values. This manifested in %Core.Encoding.
- Fixed rejection of inline style declarations that had lots of extra
space in them. This manifested in TinyMCE.
1.0.0, released 2006-09-01
! Shorthand CSS properties implemented: font, border, background, list-style
! Basic color keywords translated into hexadecimal values
! Table CSS properties implemented
! Support for charsets other than UTF-8 (defined by iconv)
! Malformed UTF-8 and non-SGML character detection and cleaning implemented
- Fixed broken numeric entity conversion
- API documentation completed
. (HTML|CSS)Definition de-singleton-ized
1.0.0beta, released 2006-08-16
- First public release, most functionality implemented. Notable omissions are:
. Shorthand CSS properties
. Table CSS properties
. IPv6 validation
. Deprecated attribute transformations
! First public release, most functionality implemented. Notable omissions are:
+ Shorthand CSS properties
+ Table CSS properties
+ Deprecated attribute transformations

24
README
View File

@@ -1,13 +1,13 @@
README
All about HTMLPurifier
HTMLPurifier is an HTML filtering solution. It uses a unique combination of
robust whitelists and agressive parsing to ensure that not only are XSS
attacks thwarted, but the resulting HTML is standards compliant.
See INSTALL on how to use the library. See docs/ for more developer-oriented
documentation as well as some code examples. Users of TinyMCE or FCKeditor
may be especially interested in WYSIWYG.
HTMLPurifier can be found on the web at: http://hp.jpsband.org/
README
All about HTMLPurifier
HTMLPurifier is an HTML filtering solution. It uses a unique combination of
robust whitelists and agressive parsing to ensure that not only are XSS
attacks thwarted, but the resulting HTML is standards compliant.
See INSTALL on how to use the library. See docs/ for more developer-oriented
documentation as well as some code examples. Users of TinyMCE or FCKeditor
may be especially interested in WYSIWYG.
HTMLPurifier can be found on the web at: http://hp.jpsband.org/

18
SLOW
View File

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

110
TODO
View File

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

View File

@@ -1,6 +1,6 @@
WYSIWYG - What You See Is What You Get
HTMLPurifier: A Pretty Good Fit for TinyMCE and FCKeditor
HTML Purifier: A Pretty Good Fit for TinyMCE and FCKeditor
Javascript-based WYSIWYG editors, simply stated, are quite amazing. But I've
always been wary about using them due to security issues: they handle the
@@ -13,6 +13,9 @@ other markup languages still reign supreme. Put simply: filtering HTML is
hard work, and these WYSIWYG authors don't offer anything to alleviate that
trouble. Therein lies the solution:
HTMLPurifier is perfect for filtering pure-HTML input from WYSIWYG editors.
HTML Purifier is perfect for filtering pure-HTML input from WYSIWYG editors.
Enough said.
There is a proof-of-concept integration of HTML Purifier with the Mantis
bugtracker at http://hp.jpsband.org/mantis/

View File

@@ -3,15 +3,24 @@
// emulates inserting a dir called HTMLPurifier into your class dir
set_include_path(get_include_path() . PATH_SEPARATOR . '../library/');
require_once 'HTMLPurifier/ConfigDef.php';
require_once 'HTMLPurifier/Config.php';
require_once 'HTMLPurifier/Lexer/DirectLex.php';
require_once 'HTMLPurifier/Lexer/PEARSax3.php';
@include_once '../test-settings.php';
$LEXERS = array(
'DirectLex' => new HTMLPurifier_Lexer_DirectLex(),
'PEARSax3' => new HTMLPurifier_Lexer_PEARSax3()
);
require_once 'HTMLPurifier/ConfigSchema.php';
require_once 'HTMLPurifier/Config.php';
$LEXERS = array();
$RUNS = isset($GLOBALS['HTMLPurifierTest']['Runs'])
? $GLOBALS['HTMLPurifierTest']['Runs'] : 2;
require_once 'HTMLPurifier/Lexer/DirectLex.php';
$LEXERS['DirectLex'] = new HTMLPurifier_Lexer_DirectLex();
if (!empty($GLOBALS['HTMLPurifierTest']['PEAR'])) {
require_once 'HTMLPurifier/Lexer/PEARSax3.php';
$LEXERS['PEARSax3'] = new HTMLPurifier_Lexer_PEARSax3();
} else {
exit('PEAR required to perform benchmark.');
}
if (version_compare(PHP_VERSION, '5', '>=')) {
require_once 'HTMLPurifier/Lexer/DOMLex.php';
@@ -56,9 +65,12 @@ class RowTimer extends Benchmark_Timer
if ($standard == false) $standard = $v['diff'];
$perc = $v['diff'] * 100 / $standard;
$bad_run = ($v['diff'] < 0);
$out .= '<td align="right">' . number_format($perc, 2, '.', '') .
'%</td>';
$out .= '<td align="right"'.
($bad_run ? ' style="color:#AAA;"' : '').
'>' . number_format($perc, 2, '.', '') .
'%</td><td>'.number_format($v['diff'],4,'.','').'</td>';
}
@@ -79,13 +91,13 @@ function print_lexers() {
}
function do_benchmark($name, $document) {
global $LEXERS;
global $LEXERS, $RUNS;
$timer = new RowTimer($name);
$timer->start();
foreach($LEXERS as $key => $lexer) {
$tokens = $lexer->tokenizeHTML($document);
for ($i=0; $i<$RUNS; $i++) $tokens = $lexer->tokenizeHTML($document);
$timer->setMarker($key);
}
@@ -103,7 +115,7 @@ function do_benchmark($name, $document) {
<table border="1">
<tr><th>Case</th><?php
foreach ($LEXERS as $key => $value) {
echo '<th>' . htmlspecialchars($key) . '</th>';
echo '<th colspan="2">' . htmlspecialchars($key) . '</th>';
}
?></tr>
<?php

View File

@@ -2,7 +2,7 @@
set_include_path(get_include_path() . PATH_SEPARATOR . '../library/');
require_once 'HTMLPurifier/ConfigDef.php';
require_once 'HTMLPurifier/ConfigSchema.php';
require_once 'HTMLPurifier/Config.php';
require_once 'HTMLPurifier/Lexer/DirectLex.php';

218
configdoc/generate.php Normal file
View File

@@ -0,0 +1,218 @@
<?php
/**
* Generates XML and HTML documents describing configuration.
*/
/*
TODO:
- make XML format richer (see below)
- extend XSLT transformation (see the corresponding XSLT file)
- allow generation of packaged docs that can be easily moved
- multipage documentation
- determine how to multilingualize
- factor out code into classes
*/
// ---------------------------------------------------------------------------
// Check and configure environment
if (version_compare('5', PHP_VERSION, '>')) exit('Requires PHP 5 or higher.');
error_reporting(E_ALL);
// ---------------------------------------------------------------------------
// Include HTML Purifier library
set_include_path('../library' . PATH_SEPARATOR . get_include_path());
require_once 'HTMLPurifier.php';
// ---------------------------------------------------------------------------
// Setup convenience functions
function appendHTMLDiv($document, $node, $html) {
global $purifier;
$html = $purifier->purify($html);
$dom_html = $document->createDocumentFragment();
$dom_html->appendXML($html);
$dom_div = $document->createElement('div');
$dom_div->setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
$dom_div->appendChild($dom_html);
$node->appendChild($dom_div);
}
// ---------------------------------------------------------------------------
// Load copies of HTMLPurifier_ConfigDef and HTMLPurifier
$schema = HTMLPurifier_ConfigSchema::instance();
$purifier = new HTMLPurifier();
// ---------------------------------------------------------------------------
// Generate types.xml, a document describing the constraint "type"
$types_document = new DOMDocument('1.0', 'UTF-8');
$types_root = $types_document->createElement('types');
$types_document->appendChild($types_root);
$types_document->formatOutput = true;
foreach ($schema->types as $name => $expanded_name) {
$types_type = $types_document->createElement('type', $expanded_name);
$types_type->setAttribute('id', $name);
$types_root->appendChild($types_type);
}
$types_document->save('types.xml');
// ---------------------------------------------------------------------------
// Generate configdoc.xml, a document documenting configuration directives
$dom_document = new DOMDocument('1.0', 'UTF-8');
$dom_root = $dom_document->createElement('configdoc');
$dom_document->appendChild($dom_root);
$dom_document->formatOutput = true;
// add the name of the application
$dom_root->appendChild($dom_document->createElement('title', 'HTML Purifier'));
/*
TODO for XML format:
- create a definition (DTD or other) once interface stabilizes
*/
foreach($schema->info as $namespace_name => $namespace_info) {
$dom_namespace = $dom_document->createElement('namespace');
$dom_root->appendChild($dom_namespace);
$dom_namespace->setAttribute('id', $namespace_name);
$dom_namespace->appendChild(
$dom_document->createElement('name', $namespace_name)
);
$dom_namespace_description = $dom_document->createElement('description');
$dom_namespace->appendChild($dom_namespace_description);
appendHTMLDiv($dom_document, $dom_namespace_description,
$schema->info_namespace[$namespace_name]->description);
foreach ($namespace_info as $name => $info) {
$dom_directive = $dom_document->createElement('directive');
$dom_namespace->appendChild($dom_directive);
$dom_directive->setAttribute('id', $namespace_name . '.' . $name);
$dom_directive->appendChild(
$dom_document->createElement('name', $name)
);
$dom_constraints = $dom_document->createElement('constraints');
$dom_directive->appendChild($dom_constraints);
$dom_type = $dom_document->createElement('type', $info->type);
if ($info->allow_null) {
$dom_type->setAttribute('allow-null', 'yes');
}
$dom_constraints->appendChild($dom_type);
if ($info->allowed !== true) {
$dom_allowed = $dom_document->createElement('allowed');
$dom_constraints->appendChild($dom_allowed);
foreach ($info->allowed as $allowed => $bool) {
$dom_allowed->appendChild(
$dom_document->createElement('value', $allowed)
);
}
}
$raw_default = $schema->defaults[$namespace_name][$name];
if (is_bool($raw_default)) {
$default = $raw_default ? 'true' : 'false';
} elseif (is_string($raw_default)) {
$default = "\"$raw_default\"";
} elseif (is_null($raw_default)) {
$default = 'null';
} else {
$default = print_r(
$schema->defaults[$namespace_name][$name], true
);
}
$dom_default = $dom_document->createElement('default', $default);
// remove this once we get a DTD
$dom_default->setAttribute('xml:space', 'preserve');
$dom_constraints->appendChild($dom_default);
$dom_descriptions = $dom_document->createElement('descriptions');
$dom_directive->appendChild($dom_descriptions);
foreach ($info->descriptions as $file => $file_descriptions) {
foreach ($file_descriptions as $line => $description) {
$dom_description = $dom_document->createElement('description');
$dom_description->setAttribute('file', $file);
$dom_description->setAttribute('line', $line);
appendHTMLDiv($dom_document, $dom_description, $description);
$dom_descriptions->appendChild($dom_description);
}
}
}
}
// print_r($dom_document->saveXML());
// save a copy of the raw XML
$dom_document->save('configdoc.xml');
// ---------------------------------------------------------------------------
// Generate final output using XSLT
// load the stylesheet
$xsl_stylesheet_name = 'plain';
$xsl_stylesheet = "styles/$xsl_stylesheet_name.xsl";
$xsl_dom_stylesheet = new DOMDocument();
$xsl_dom_stylesheet->load($xsl_stylesheet);
// setup the XSLT processor
$xsl_processor = new XSLTProcessor();
// perform the transformation
$xsl_processor->importStylesheet($xsl_dom_stylesheet);
$html_output = $xsl_processor->transformToXML($dom_document);
// some slight fudges to preserve backwards compatibility
$html_output = str_replace('/>', ' />', $html_output); // <br /> not <br>
$html_output = str_replace(' xmlns=""', '', $html_output); // rm unnecessary xmlns
if (class_exists('Tidy')) {
// cleanup output
$config = array(
'indent' => true,
'output-xhtml' => true,
'wrap' => 80
);
$tidy = new Tidy;
$tidy->parseString($html_output, $config, 'utf8');
$tidy->cleanRepair();
$html_output = (string) $tidy;
}
// write it to a file (todo: parse into seperate pages)
file_put_contents("$xsl_stylesheet_name.html", $html_output);
// ---------------------------------------------------------------------------
// Output for instant feedback
if (php_sapi_name() != 'cli') {
echo $html_output;
} else {
echo 'Files generated successfully.';
}
?>

View File

@@ -0,0 +1,10 @@
table {border-collapse:collapse;}
table td, table th {padding:0.2em;}
table.constraints {margin:0 0 1em;}
table.constraints th {text-align:left;padding-left:0.4em;}
table.constraints td {padding-right:0.4em;}
table.constraints td pre {margin:0;}
#toc {list-style-type:none; font-weight:bold;}
#toc ul {list-style-type:disc; font-weight:normal;}

126
configdoc/styles/plain.xsl Normal file
View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version = "1.0"
xmlns = "http://www.w3.org/1999/xhtml"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
>
<xsl:output
method = "xml"
encoding = "UTF-8"
doctype-public = "-//W3C//DTD XHTML 1.0 Transitional//EN"
doctype-system = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
indent = "no"
media-type = "text/html"
/>
<xsl:variable name="typeLookup" select="document('../types.xml')" />
<xsl:template match="/">
<html lang="en" xml:lang="en">
<head>
<title><xsl:value-of select="/configdoc/title" /> Configuration Documentation</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="styles/plain.css" />
</head>
<body>
<h1><xsl:value-of select="/configdoc/title" /> Configuration Documentation</h1>
<h2>Table of Contents</h2>
<ul id="toc">
<xsl:apply-templates mode="toc" />
</ul>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="title" mode="toc" />
<xsl:template match="namespace" mode="toc">
<xsl:if test="count(directive)&gt;0">
<li>
<a href="#{@id}"><xsl:value-of select="name" /></a>
<ul>
<xsl:apply-templates select="directive" mode="toc" />
</ul>
</li>
</xsl:if>
</xsl:template>
<xsl:template match="directive" mode="toc">
<li><a href="#{@id}"><xsl:value-of select="name" /></a></li>
</xsl:template>
<xsl:template match="title" />
<xsl:template match="namespace">
<xsl:apply-templates />
<xsl:if test="count(directive)=0">
<p>No configuration directives defined for this namespace.</p>
</xsl:if>
</xsl:template>
<xsl:template match="namespace/name">
<h2 id="{../@id}"><xsl:value-of select="." /></h2>
</xsl:template>
<xsl:template match="namespace/description">
<div class="description">
<xsl:copy-of select="div/node()" />
</div>
</xsl:template>
<xsl:template match="directive">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="directive/name">
<h3 id="{../@id}"><xsl:value-of select="../@id" /></h3>
</xsl:template>
<xsl:template match="directive/constraints">
<table class="constraints">
<xsl:apply-templates />
<!-- Calculated other values -->
<tr>
<th>Used by:</th>
<td>
<xsl:for-each select="../descriptions/description">
<xsl:if test="position()&gt;1">, </xsl:if>
<xsl:value-of select="@file" />
</xsl:for-each>
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="directive//description">
<div class="description">
<xsl:copy-of select="div/node()" />
</div>
</xsl:template>
<xsl:template match="constraints/type">
<tr>
<th>Type:</th>
<td>
<xsl:variable name="type" select="text()" />
<xsl:attribute name="class">type type-<xsl:value-of select="$type" /></xsl:attribute>
<xsl:value-of select="$typeLookup/types/type[@id=$type]/text()" />
<xsl:if test="@allow-null='yes'">
(or null)
</xsl:if>
</td>
</tr>
</xsl:template>
<xsl:template match="constraints/allowed">
<tr>
<th>Allowed values:</th>
<td>
<xsl:for-each select="value"><!--
--><xsl:if test="position()&gt;1">, </xsl:if>
&quot;<xsl:value-of select="." />&quot;<!--
--></xsl:for-each>
</td>
</tr>
</xsl:template>
<xsl:template match="constraints/default">
<tr>
<th>Default:</th>
<td><pre><xsl:value-of select="." xml:space="preserve" /></pre></td>
</tr>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,38 +0,0 @@
Code Quality Issues
Okay, face it. Programmers can get lazy, cut corners, or make mistakes. They
also can do quick prototypes, and then forget to rewrite them later. Well,
while I can't list mistakes in here, I can list prototype-like segments
of code that should be aggressively refactored after the beta is released.
This does not list optimization issues, that needs to be done after intense
profiling.
Here we go:
AttrDef
Class - doesn't support Unicode characters, uses regular expressions
Lang - code duplication, premature optimization, doesn't consult official
lists
Pixels/Length/MultiLength - implemented according to HTML spec (excludes
code reuse in CSS)
URI - multiple regular expressions, needs host validation routines factored
out for mailto scheme, IPv6 validation is broken (fringe), unintuitive
variable overwriting, missing validation for query, fragment and path,
no percent-encode fixing
CSS - parser doesn't accept advanced CSS (fringe)
AttrTransform - doesn't accept AttrContext, non-validating
Lang - invalid xml:lang value can overwrite valid lang value (fringe)
ChildDef - not-allowed nodes translated to text, likely invalid handling
Config - "load configuration" hooks missing, rich set* accessors missing
Strategy
FixNesting - cannot bubble nodes out of structures
MakeWellFormed - insufficient automatic closing definitions
RemoveForeignElements - should be run in parallel with MakeWellFormed
URIScheme - needs to have callable generic checks
ftp - missing typecode check
mailto - doesn't validate emails
news - doesn't validate opaque path
nntp - doesn't constrain path
EOL

View File

@@ -1,45 +0,0 @@
Configuration Ideas
Here are some theoretical configuration ideas that we could implement some
time. Note the naming convention: %Namespace.Directive
%Attr.IDPrefix - prefix all ids with this
%Attr.RewriteFragments - if there's %Attr.IDPrefix we may want to transparently
rewrite the URLs we parse too. However, we can only do it when it's a pure
anchor link, so it's not foolproof
%Attr.ClassBlacklist,
%Attr.ClassWhitelist,
%Attr.ClassListMode - determines what classes are allowed. When
%Attr.ClassListMode is set to Blacklist, only allow those not in
%Attr.ClassBlacklist. When it's Whitelist, only allow those in
%Attr.ClassWhitelist.
%Attr.LangAlphaOnly - designate whether or not to allow numerals in language
code subtags
* RFC 1766, the current standard referenced by XML, does not permit
numbers, but,
* RFC 3066, the superseding best practice standard since January 2001,
permits them.
We allow numbers by default, but you generally never see them
at all, which makes this a little more sane.
%Attr.MaxWidth,
%Attr.MaxHeight - caps for width and height related checks.
%URI.Munge - will munge all URIs to a different URI, which should redirect
the user to the applicable page. A urlencoded version of the URI
will replace any instances of %s in the string. One possible
string is 'http://www.google.com/url?q=%s'. Useful for preventing
pagerank from being sent to other sites
%URI.AddRelNofollow - will add rel="nofollow" to all links, preventing the
spread of ill-gotten pagerank
%URI.Host - host of website, for external link checks
%URI.RelativeToAbsolute - transforms all relative URIs to absolute form
%URI.DisableExternal - disable external links

View File

@@ -1,18 +0,0 @@
Configuration
Configuration is documented on a per-use case: if a class uses a certain
value from the configuration object, it has to define its name and what the
value is used for. This means decentralized configuration declarations that
are nevertheless error checking and a centralized configuration object.
Directives are divided into namespaces, indicating the major portion of
functionality they cover (although there may be overlaps. Please consult
the documentation in ConfigDef for more information on these namespaces.
Since configuration is dependent on context, most of the internal classes
require a configuration object to be passed as a parameter. However, a few
make this optional: they will supply a default configuration object if none
are passed. These classes are: HTMLPurifier::*, Generator::generateFromTokens
and Lexer::tokenizeHTML. However, whenever a valid configuration object
is defined, that object should be used.

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Discusses code quality issues and places that need to be refactored in HTML Purifier." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Code Quality Issues - HTML Purifier</title>
</head><body>
<h1>Code Quality Issues</h1>
<div id="filing">Filed under Development</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<p>Okay, face it. Programmers can get lazy, cut corners, or make mistakes. They
also can do quick prototypes, and then forget to rewrite them later. Well,
while I can't list mistakes in here, I can list prototype-like segments
of code that should be aggressively refactored. This does not list
optimization issues, that needs to be done after intense profiling.</p>
<pre>
docs/examples/demo.php - ad hoc HTML/PHP soup to the extreme
AttrDef
Class - doesn't support Unicode characters (fringe); uses regular
expressions
Lang - code duplication; premature optimization
Length - easily mistaken for CSSLength
URI - multiple regular expressions; missing validation for parts (?)
CSS - parser doesn't accept advanced CSS (fringe)
Number - constructor interface inconsistent with Integer
ConfigSchema - redefinition is a mess
Strategy
FixNesting - cannot bubble nodes out of structures, duplicated checks
for special-case parent node
MakeWellFormed - insufficient automatic closing definitions (check HTML
spec for optional end tags, also, closing based on type (block/inline)
might be efficient).
RemoveForeignElements - should be run in parallel with MakeWellFormed
URIScheme - needs to have callable generic checks
mailto - doesn't validate emails, doesn't validate querystring
news - doesn't validate opaque path
nntp - doesn't constrain path
</pre>
<div id="version">$Id$</div>
</body></html>

81
docs/dev-naming.html Normal file
View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Defines class naming conventions in HTML Purifier." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Naming Conventions - HTML Purifier</title>
</head><body>
<h1>Naming Conventions</h1>
<div id="filing">Filed under Development</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<p>The classes in this library follow a few naming conventions, which may
help you find the correct functionality more quickly. Here they are:</p>
<dl>
<dt>All classes occupy the HTMLPurifier pseudo-namespace.</dt>
<dd>This means that all classes are prefixed with HTMLPurifier_. As such, all
names under HTMLPurifier_ are reserved. I recommend that you use the name
HTMLPurifierX_YourName_ClassName, especially if you want to take advantage
of HTMLPurifier_ConfigDef.</dd>
<dt>All classes correspond to their path if library/ was in the include path</dt>
<dd>HTMLPurifier_AttrDef is located at HTMLPurifier/AttrDef.php; replace
underscores with slashes and append .php and you'll have the location of
the class.</dd>
<dt>Harness and Test are reserved class names for unit tests</dt>
<dd>The suffix <code>Test</code> indicates that the class is a subclass of UnitTestCase
(of the Simpletest library) and is testable. "Harness" indicates a subclass
of UnitTestCase that is not meant to be run but to be extended into
concrete test cases and contains custom test methods (i.e. assert*())</dd>
<dt>Class names do not necessarily represent inheritance hierarchies</dt>
<dd>While we try to reflect inheritance in naming to some extent, it is not
guaranteed (for instance, none of the classes inherit from HTMLPurifier,
the base class). However, all class files have the require_once
declarations to whichever classes they are tightly coupled to.</dd>
<dt>Strategy has a meaning different from the Gang of Four pattern</dt>
<dd>In Design Patterns, the Gang of Four describes a Strategy object as
encapsulating an algorithm so that they can be switched at run-time. While
our strategies are indeed algorithms, they are not meant to be substituted:
all must be present in order for proper functioning.</dd>
<dt>Abbreviations are avoided</dt>
<dd>We try to avoid abbreviations as much as possible, but in some cases,
abbreviated version is more readable than the full version. Here, we
list common abbreviations:
<ul>
<li>Attr to Attributes (note that it is plural, i.e. <code>$attr = array()</code>)</li>
<li>Def to Definition</li>
<li><code>$ret</code> is the value to be returned in a function</li>
</ul>
</dd>
<dt>Ambiguity concerning the definition of Def/Definition</dt>
<dd>While a definition normally defines the structure/acceptable values of
an entity, most of the definitions in this application also attempt
to validate and fix the value. I am unsure of a better name, as
"Validator" would exclude fixing the value, "Fixer" doesn't invoke
the proper image of "fixing" something, and "ValidatorFixer" is too long!
Some other suggestions were "Handler", "Reference", "Check", "Fix",
"Repair" and "Heal".</dd>
<dt>Transform not Transformer</dt>
<dd>Transform is both a noun and a verb, and thus we define a "Transform" as
something that "transforms," leaving "Transformer" (which sounds like an
electrical device/robot toy).</dd>
</dl>
<div id="version">$Id$</div>
</body></html>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Discusses possible methods of optimizing HTML Purifier." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Optimization - HTML Purifier</title>
</head><body>
<h1>Optimization</h1>
<div id="filing">Filed under Development</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<p>Here are some possible optimization techniques we can apply to code sections if
they turn out to be slow. Be sure not to prematurely optimize: if you get
that itch, put it here!</p>
<ul>
<li>Make Tokens Flyweights (may prove problematic, probably not worth it)</li>
<li>Rewrite regexps into PHP code</li>
<li>Serialize the Definition object</li>
<li>Batch regexp validation (do as many per function call as possible)</li>
<li>Parallelize strategies</li>
</ul>
<div id="version">$Id$</div>
</body></html>

View File

@@ -1,290 +1,301 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<title>HTMLPurifier Progress</title>
<style type="text/css">
td {padding-right:1em;border-bottom:1px solid #000;padding-left:0.5em;}
th {text-align:left;padding-top:1.4em;font-size:13pt;
border-bottom:2px solid #000;background:#FFF;}
thead th {text-align:left;padding:0.1em;background-color:#EEE;}
.impl-yes {background:#AFA;}
.impl-partial {background:#FFA;}
.impl-no {background:#FAA;}
.danger {background:#FEE;}
.css1 {color:#060;}
.required {font-weight:bold;}
</style>
</head><body>
<h1>HTMLPurifier Progress</h1>
<h2>Key</h2>
<table cellspacing="0"><tbody>
<tr><td class="impl-yes">Implemented</td></tr>
<tr><td class="impl-partial">Partially implemented</td></tr>
<tr><td class="impl-no">Will not implement</td></tr>
<tr><td class="danger">Dangerous attribute/property</td></tr>
<tr><td class="css1">Present in CSS1</td></tr>
</tbody></table>
<h2>Interesting Attributes</h2>
<table cellspacing="0">
<thead>
<tr><th>Attribute</th><th>Tags</th><th>Notes</th></tr>
</thead>
<!--
<tr><th></th></tr>
<tbody>
<tr><td>-</td><td>-</td><td>-</td></tr>
</tbody>
-->
<tbody>
<tr><th colspan="3">CSS</th></tr>
<tr class="impl-partial"><td>style</td><td>All</td><td>Needs CSS parser</td></tr>
</tbody>
<tbody>
<tr><th colspan="3">Questionable</th></tr>
<tr class="impl-no"><td>accesskey</td><td>A</td><td>May interfere with main interface</td></tr>
<tr class="impl-no"><td>tabindex</td><td>A</td><td>May interfere with main interface</td></tr>
<tr><td>target</td><td>A</td><td>Config enabled, only useful for frame layouts</td></tr>
</tbody>
<tbody>
<tr><th colspan="3">Miscellaneous</th></tr>
<tr><td>datetime</td><td>DEL, INS</td><td>No visible effect, ISO format</td></tr>
<tr><td>rel</td><td>A</td><td>Largely user-defined: nofollow, tag (see microformats)</td></tr>
<tr><td>rev</td><td>A</td><td>Largely user-defined: vote-*</td></tr>
<tr class="impl-no"><td>axis</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
<tr class="impl-no"><td>char</td><td>COL, COLGROUP, TBODY, TD, TFOOT, TH, THEAD, TR</td><td>W3C only: No browser implementation</td></tr>
<tr class="impl-no"><td>headers</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
<tr class="impl-no"><td>scope</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
</tbody>
<tbody class="impl-yes">
<tr><th colspan="3">URI</th></tr>
<tr><td rowspan="2">cite</td><td>BLOCKQUOTE, Q</td><td>For attribution</td></tr>
<tr><td>DEL, INS</td><td>Link to explanation why it changed</td></tr>
<tr><td>href</td><td>A</td><td>-</td></tr>
<tr><td>longdesc</td><td>IMG</td><td>-</td></tr>
<tr class="required"><td>src</td><td>IMG</td><td>Required</td></tr>
</tbody>
<tbody>
<tr><th colspan="3">Transform</th></tr>
<tr><td rowspan="5">align</td><td>CAPTION</td><td>Near-equiv style 'caption-side', drop left and right</td></tr>
<tr><td>IMG</td><td rowspan="2">Margin-left and margin-right = auto or parent div</td></tr>
<tr><td>TABLE</td></tr>
<tr><td>HR</td><td>Equivalent style 'text-align' (IE tested)</td></tr>
<tr class="impl-yes"><td>H1, H2, H3, H4, H5, H6, P</td><td>Equivalent style 'text-align'</td></tr>
<tr class="required impl-yes"><td>alt</td><td>IMG</td><td>Required, insert image filename if src is present or default invalid image text</td></tr>
<tr><td rowspan="3">bgcolor</td><td>TABLE</td><td>Equivalent style 'background-color' (IE tested)</td></tr>
<tr><td>TR</td><td>Equivalent style 'background-color' (IE tested)</td></tr>
<tr><td>TD, TH</td><td>Equivalent style 'background-color'</td></tr>
<tr><td>border</td><td>IMG</td><td>Equivalent style 'border-width', only applies when link present</td></tr>
<tr><td>clear</td><td>BR</td><td>Near-equiv style 'clear', transform 'all' into 'both'</td></tr>
<tr class="impl-no"><td>compact</td><td>DL, OL, UL</td><td>Boolean, needs custom CSS class</td></tr>
<tr class="required impl-yes"><td>dir</td><td>BDO</td><td>Required, insert ltr (or configuration value) if none</td></tr>
<tr><td>height</td><td>TD, TH</td><td>Near-equiv style 'height', needs px suffix if original was in pixels</td></tr>
<tr><td>hspace</td><td>IMG</td><td>Near-equiv styles 'margin-top' and 'margin-bottom', needs px suffix</td></tr>
<tr class="impl-yes"><td>lang</td><td>*</td><td>Copy value to xml:lang</td></tr>
<tr><td rowspan="2">name</td><td>IMG</td><td>Turn into ID</td></tr>
<tr><td>A</td><td>Turn into ID? (not deprecated, though in which specs?)</td></tr>
<tr><td>noshade</td><td>HR</td><td>Boolean, style 'border-style:solid;'</td></tr>
<tr><td>nowrap</td><td>TD, TH</td><td>Boolean, style 'white-space:nowrap;' (not compat with IE5)</td></tr>
<tr><td>size</td><td>HR</td><td>Near-equiv 'width', needs px suffix if original was pixels</td></tr>
<tr class="required impl-yes"><td>src</td><td>IMG</td><td>Required, insert blank or default img if not set</td></tr>
<tr><td>start</td><td>OL</td><td>Poorly supported 'counter-reset', transform may not be desirable</td></tr>
<tr><td rowspan="3">type</td><td>LI</td><td rowspan="3">Equivalent style 'list-style-type', different allowed values though. (needs testing)</td></tr>
<tr><td>OL</td></tr>
<tr><td>UL</td></tr>
<tr><td>value</td><td>LI</td><td>Poorly supported 'counter-reset', transform may not be desirable, see ol.start. Configurable.</td></tr>
<tr><td>vspace</td><td>IMG</td><td>Near-equiv styles 'margin-left' and 'margin-right', needs px suffix, see hspace</td></tr>
<tr><td rowspan="2">width</td><td>HR</td><td rowspan="2">Near-equiv style 'width', needs px suffix if original was pixels</td></tr>
<tr><td>TD, TH</td></tr>
</tbody>
</table>
<h3>CSS</h3>
<table cellspacing="0">
<thead>
<tr><th>Name</th><th>Notes</th></tr>
</thead>
<!--
<tr><td>-</td><td>-</td></tr>
-->
<tbody>
<tr><th colspan="2">Standard</th></tr>
<tr class="css1 impl-yes"><td>background-color</td><td>COMPOSITE(&lt;color&gt;, transparent)</td></tr>
<tr class="css1"><td>background</td><td>SHORTHAND</td></tr>
<tr class="css1"><td>border</td><td>SHORTHAND, MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>border-color</td><td>MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>border-style</td><td>MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>border-width</td><td>MULTIPLE</td></tr>
<tr class="css1"><td>border-*</td><td>SHORTHAND</td></tr>
<tr class="impl-yes"><td>border-*-color</td><td>COMPOSITE(&lt;color&gt;, transparent)</td></tr>
<tr class="impl-yes"><td>border-*-style</td><td>ENUM(none, hidden, dotted, dashed,
solid, double, groove, ridge, inset, outset)</td></tr>
<tr class="css1 impl-yes"><td>border-*-width</td><td>COMPOSITE(&lt;length&gt;, thin, medium, thick)</td></tr>
<tr class="css1 impl-yes"><td>clear</td><td>ENUM(none, left, right, both)</td></tr>
<tr class="css1 impl-yes"><td>color</td><td>&lt;color&gt;</td></tr>
<tr class="css1 impl-yes"><td>float</td><td>ENUM(left, right, none), May require layout
precautions with clear</td></tr>
<tr class="css1"><td>font</td><td>SHORTHAND</td></tr>
<tr class="css1 impl-yes"><td>font-family</td><td>CSS validator may complain if fallback font
family not specified</td></tr>
<tr class="css1 impl-yes"><td>font-size</td><td>COMPOSITE(&lt;absolute-size&gt;,
&lt;relative-size&gt;, &lt;length&gt;, &lt;percentage&gt;)</td></tr>
<tr class="css1 impl-yes"><td>font-style</td><td>ENUM(normal, italic, oblique)</td></tr>
<tr class="css1 impl-yes"><td>font-variant</td><td>ENUM(normal, small-caps)</td></tr>
<tr class="css1 impl-yes"><td>font-weight</td><td>ENUM(normal, bold, bolder, lighter,
100, 200, 300, 400, 500, 600, 700, 800, 900), maybe special code for
in-between integers</td></tr>
<tr class="css1 impl-yes"><td>letter-spacing</td><td>COMPOSITE(&lt;length&gt;, normal)</td></tr>
<tr class="css1 impl-yes"><td>line-height</td><td>COMPOSITE(&lt;number&gt;,
&lt;length&gt;, &lt;percentage&gt;, normal)</td></tr>
<tr class="css1 impl-yes"><td>list-style-position</td><td>ENUM(inside, outside),
Strange behavior in browsers</td></tr>
<tr class="css1 impl-yes"><td>list-style-type</td><td>ENUM(...),
Well-supported values are: disc, circle, square,
decimal, lower-roman, upper-roman, lower-alpha and upper-alpha. See also
CSS 3. Mostly IE lack of support.</td></tr>
<tr class="css1"><td>list-style</td><td>SHORTHAND</td></tr>
<tr class="css1 impl-yes"><td>margin</td><td>MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>margin-*</td><td>COMPOSITE(&lt;length&gt;,
&lt;percentage&gt;, auto)</td></tr>
<tr class="css1 impl-yes"><td>padding</td><td>MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>padding-*</td><td>COMPOSITE(&lt;length&gt;(positive),
&lt;percentage&gt;(positive))</td></tr>
<tr class="css1 impl-yes"><td>text-align</td><td>ENUM(left, right,
center, justify)</td></tr>
<tr class="css1 impl-yes"><td>text-decoration</td><td>No blink (argh my eyes), not
enum, can be combined (composite sorta): underline, overline,
line-through</td></tr>
<tr class="css1 impl-yes"><td>text-indent</td><td>COMPOSITE(&lt;length&gt;,
&lt;percentage&gt;)</td></tr>
<tr class="css1 impl-yes"><td>text-transform</td><td>ENUM(capitalize, uppercase,
lowercase, none)</td></tr>
<tr class="css1 impl-yes"><td>width</td><td>COMPOSITE(&lt;length&gt;,
&lt;percentage&gt;, auto), Interesting</td></tr>
<tr class="css1 impl-yes"><td>word-spacing</td><td>COMPOSITE(&lt;length&gt;, auto),
IE 5 no support</td></tr>
</tbody>
<tbody>
<tr><th colspan="2">Table</th></tr>
<tr><td>border-collapse</td><td>ENUM(collapse, seperate)</td></tr>
<tr><td>caption-side</td><td>ENUM(top, bottom)</td></tr>
<tr><td>empty-cells</td><td>ENUM(show, hide), No IE support, possible fix
with &amp;nbsp;?</td></tr>
<tr><td>table-layout</td><td>ENUM(auto, fixed)</td></tr>
<tr class="css1"><td>vertical-align</td><td>COMPOSITE(ENUM(baseline, sub,
super, top, text-top, middle, bottom, text-bottom), &lt;percentage&gt;,
&lt;length&gt;) Also applies to others with explicit height</td></tr>
</tbody>
<tbody>
<tr><th colspan="2">Absolute positioning</th></tr>
<tr class="danger"><td>bottom</td><td rowspan="4">Dangerous, must be non-negative</td></tr>
<tr class="danger"><td>left</td></tr>
<tr class="danger"><td>right</td></tr>
<tr class="danger"><td>top</td></tr>
<tr><td>clip</td><td>-</td></tr>
<tr class="danger"><td>position</td><td>ENUM(static, relative, absolute, fixed), permit
relative not absolute?</td></tr>
<tr class="danger"><td>z-index</td><td>Dangerous</td></tr>
</tbody>
<tbody>
<tr><th colspan="2">Unknown</th></tr>
<tr class="danger css1"><td>background-image</td><td>Dangerous</td></tr>
<tr class="css1"><td>background-attachment</td><td>ENUM(scroll, fixed),
Depends on background-image</td></tr>
<tr class="css1"><td>background-position</td><td>Depends on background-image</td></tr>
<tr class="danger"><td>cursor</td><td>Dangerous but fluffy</td></tr>
<tr class="danger css1"><td>display</td><td>ENUM(...), Dangerous but interesting;
will not implement list-item, run-in (Opera only) or table (no IE);
inline-block has incomplete IE6 support and requires -moz-inline-box
for Mozilla.</td></tr>
<tr><td class="css1">height</td><td>Interesting, why use it?</td></tr>
<tr class="danger css1"><td>list-style-image</td><td>Dangerous?</td></tr>
<tr class="impl-no"><td>max-height</td><td rowspan="4">No IE 5/6</td></tr>
<tr class="impl-no"><td>min-height</td></tr>
<tr class="impl-no"><td>max-width</td></tr>
<tr class="impl-no"><td>min-width</td></tr>
<tr class="impl-no"><td>orphans</td><td>No IE support</td></tr>
<tr class="impl-no"><td>widows</td><td>No IE support</td></tr>
<tr><td>overflow</td><td>ENUM, IE 5/6 almost (remove visible if set)</td></tr>
<tr><td>page-break-after</td><td>ENUM(auto, always, avoid, left, right),
IE 5.5/6 and Opera</td></tr>
<tr><td>page-break-before</td><td>ENUM(auto, always, avoid, left, right),
Mostly supported</td></tr>
<tr><td>page-break-inside</td><td>ENUM(avoid, auto), Opera only</td></tr>
<tr class="impl-no"><td>quotes</td><td>May be dropped from CSS2</td></tr>
<tr class="impl-no"><td>visibility</td><td>ENUM(visible, hidden, collapse),
Dangerous</td></tr>
<tr><td class="css1">white-space</td><td>ENUM(normal, pre, nowrap, pre-wrap,
pre-line), Spotty implementation:
pre (no IE 5/6), nowrap (no IE 5),
pre-wrap (only Opera), pre-line (no support). Fixable?</td></tr>
</tbody>
<tbody class="impl-no">
<tr><th colspan="2">Aural</th></tr>
<tr><td>azimuth</td><td>-</td></tr>
<tr><td>cue</td><td>-</td></tr>
<tr><td>cue-after</td><td>-</td></tr>
<tr><td>cue-before</td><td>-</td></tr>
<tr><td>elevation</td><td>-</td></tr>
<tr><td>pause-after</td><td>-</td></tr>
<tr><td>pause-before</td><td>-</td></tr>
<tr><td>pause</td><td>-</td></tr>
<tr><td>pitch-range</td><td>-</td></tr>
<tr><td>pitch</td><td>-</td></tr>
<tr><td>play-during</td><td>-</td></tr>
<tr><td>richness</td><td>-</td></tr>
<tr><td>speak-header</td><td>Table related</td></tr>
<tr><td>speak-numeral</td><td>-</td></tr>
<tr><td>speak-punctuation</td><td>-</td></tr>
<tr><td>speak</td><td>-</td></tr>
<tr><td>speech-rate</td><td>-</td></tr>
<tr><td>stress</td><td>-</td></tr>
<tr><td>voice-family</td><td>-</td></tr>
<tr><td>volume</td><td>-</td></tr>
</tbody>
<tbody class="impl-no">
<tr><th colspan="2">Will not implement</th></tr>
<tr><td>content</td><td>Not applicable for inline styles</td></tr>
<tr><td>counter-increment</td><td>Needs content, Opera only</td></tr>
<tr><td>counter-reset</td><td>Needs content, Opera only</td></tr>
<tr><td>direction</td><td>No support</td></tr>
<tr><td>outline-color</td><td rowspan="4">IE Mac and Opera on outside,
Mozilla on inside and needs -moz-outline, no IE support.</td></tr>
<tr><td>outline-style</td></tr>
<tr><td>outline-width</td></tr>
<tr><td>outline</td></tr>
<tr><td>unicode-bidi</td><td>No support</td></tr>
</tbody>
</table>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Tables detailing HTML element and CSS property implementation coverage in HTML Purifier." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Implementation Progress - HTML Purifier</title>
<style type="text/css">
td {padding-right:1em;border-bottom:1px solid #000;padding-left:0.5em;}
th {text-align:left;padding-top:1.4em;font-size:13pt;
border-bottom:2px solid #000;background:#FFF;}
thead th {text-align:left;padding:0.1em;background-color:#EEE;}
.impl-yes {background:#9D9;}
.impl-partial {background:#FFA;}
.impl-no {background:#CCC;}
.danger {color:#600;}
.css1 {color:#060;}
.required {font-weight:bold;}
.feature {color:#999;}
</style>
</head><body>
<h1>Implementation Progress</h1>
<div id="filing">Filed under Development</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<h2>Key</h2>
<table cellspacing="0"><tbody>
<tr><td class="impl-yes">Implemented</td></tr>
<tr><td class="impl-partial">Partially implemented</td></tr>
<tr><td class="impl-no">Will not implement</td></tr>
<tr><td class="danger">Dangerous attribute/property</td></tr>
<tr><td class="css1">Present in CSS1</td></tr>
<tr><td class="feature">Feature, requires extra work</td></tr>
</tbody></table>
<h2>CSS</h2>
<table cellspacing="0">
<thead>
<tr><th>Name</th><th>Notes</th></tr>
</thead>
<!--
<tr><td>-</td><td>-</td></tr>
-->
<tbody>
<tr><th colspan="2">Standard</th></tr>
<tr class="css1 impl-yes"><td>background-color</td><td>COMPOSITE(&lt;color&gt;, transparent)</td></tr>
<tr class="css1 impl-yes"><td>background</td><td>SHORTHAND, only for color, see below for info on background-image and friends</td></tr>
<tr class="css1 impl-yes"><td>border</td><td>SHORTHAND, MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>border-color</td><td>MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>border-style</td><td>MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>border-width</td><td>MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>border-*</td><td>SHORTHAND</td></tr>
<tr class="impl-yes"><td>border-*-color</td><td>COMPOSITE(&lt;color&gt;, transparent)</td></tr>
<tr class="impl-yes"><td>border-*-style</td><td>ENUM(none, hidden, dotted, dashed,
solid, double, groove, ridge, inset, outset)</td></tr>
<tr class="css1 impl-yes"><td>border-*-width</td><td>COMPOSITE(&lt;length&gt;, thin, medium, thick)</td></tr>
<tr class="css1 impl-yes"><td>clear</td><td>ENUM(none, left, right, both)</td></tr>
<tr class="css1 impl-yes"><td>color</td><td>&lt;color&gt;</td></tr>
<tr class="css1 impl-yes"><td>float</td><td>ENUM(left, right, none), May require layout
precautions with clear</td></tr>
<tr class="css1 impl-yes"><td>font</td><td>SHORTHAND</td></tr>
<tr class="css1 impl-yes"><td>font-family</td><td>CSS validator may complain if fallback font
family not specified</td></tr>
<tr class="css1 impl-yes"><td>font-size</td><td>COMPOSITE(&lt;absolute-size&gt;,
&lt;relative-size&gt;, &lt;length&gt;, &lt;percentage&gt;)</td></tr>
<tr class="css1 impl-yes"><td>font-style</td><td>ENUM(normal, italic, oblique)</td></tr>
<tr class="css1 impl-yes"><td>font-variant</td><td>ENUM(normal, small-caps)</td></tr>
<tr class="css1 impl-yes"><td>font-weight</td><td>ENUM(normal, bold, bolder, lighter,
100, 200, 300, 400, 500, 600, 700, 800, 900), maybe special code for
in-between integers</td></tr>
<tr class="css1 impl-yes"><td>letter-spacing</td><td>COMPOSITE(&lt;length&gt;, normal)</td></tr>
<tr class="css1 impl-yes"><td>line-height</td><td>COMPOSITE(&lt;number&gt;,
&lt;length&gt;, &lt;percentage&gt;, normal)</td></tr>
<tr class="css1 impl-yes"><td>list-style-position</td><td>ENUM(inside, outside),
Strange behavior in browsers</td></tr>
<tr class="css1 impl-yes"><td>list-style-type</td><td>ENUM(...),
Well-supported values are: disc, circle, square,
decimal, lower-roman, upper-roman, lower-alpha and upper-alpha. See also
CSS 3. Mostly IE lack of support.</td></tr>
<tr class="css1 impl-yes"><td>list-style</td><td>SHORTHAND</td></tr>
<tr class="css1 impl-yes"><td>margin</td><td>MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>margin-*</td><td>COMPOSITE(&lt;length&gt;,
&lt;percentage&gt;, auto)</td></tr>
<tr class="css1 impl-yes"><td>padding</td><td>MULTIPLE</td></tr>
<tr class="css1 impl-yes"><td>padding-*</td><td>COMPOSITE(&lt;length&gt;(positive),
&lt;percentage&gt;(positive))</td></tr>
<tr class="css1 impl-yes"><td>text-align</td><td>ENUM(left, right,
center, justify)</td></tr>
<tr class="css1 impl-yes"><td>text-decoration</td><td>No blink (argh my eyes), not
enum, can be combined (composite sorta): underline, overline,
line-through</td></tr>
<tr class="css1 impl-yes"><td>text-indent</td><td>COMPOSITE(&lt;length&gt;,
&lt;percentage&gt;)</td></tr>
<tr class="css1 impl-yes"><td>text-transform</td><td>ENUM(capitalize, uppercase,
lowercase, none)</td></tr>
<tr class="css1 impl-yes"><td>width</td><td>COMPOSITE(&lt;length&gt;,
&lt;percentage&gt;, auto), Interesting</td></tr>
<tr class="css1 impl-yes"><td>word-spacing</td><td>COMPOSITE(&lt;length&gt;, auto),
IE 5 no support</td></tr>
</tbody>
<tbody>
<tr><th colspan="2">Table</th></tr>
<tr class="impl-yes"><td>border-collapse</td><td>ENUM(collapse, seperate)</td></tr>
<tr class="impl-yes"><td>caption-side</td><td>ENUM(top, bottom)</td></tr>
<tr class="feature"><td>empty-cells</td><td>ENUM(show, hide), No IE support makes this useless,
possible fix with &amp;nbsp;? Unknown release milestone.</td></tr>
<tr class="impl-yes"><td>table-layout</td><td>ENUM(auto, fixed)</td></tr>
<tr class="impl-yes css1"><td>vertical-align</td><td>COMPOSITE(ENUM(baseline, sub,
super, top, text-top, middle, bottom, text-bottom), &lt;percentage&gt;,
&lt;length&gt;) Also applies to others with explicit height</td></tr>
</tbody>
<tbody>
<tr><th colspan="2">Absolute positioning, unknown release milestone</th></tr>
<tr class="danger impl-no"><td>bottom</td><td rowspan="4">Dangerous, must be non-negative to even be considered,
but it's still possible to arbitrarily position by running over.</td></tr>
<tr class="danger impl-no"><td>left</td></tr>
<tr class="danger impl-no"><td>right</td></tr>
<tr class="danger impl-no"><td>top</td></tr>
<tr class="impl-no"><td>clip</td><td>-</td></tr>
<tr class="danger impl-no"><td>position</td><td>ENUM(static, relative, absolute, fixed)
relative not absolute?</td></tr>
<tr class="danger impl-no"><td>z-index</td><td>Dangerous</td></tr>
</tbody>
<tbody>
<tr><th colspan="2">Unknown</th></tr>
<tr class="danger css1"><td>background-image</td><td>Dangerous, target milestone 1.3</td></tr>
<tr class="css1"><td>background-attachment</td><td>ENUM(scroll, fixed),
Depends on background-image</td></tr>
<tr class="css1"><td>background-position</td><td>Depends on background-image</td></tr>
<tr class="danger impl-no"><td>cursor</td><td>Dangerous but fluffy</td></tr>
<tr class="danger css1"><td>display</td><td>ENUM(...), Dangerous but interesting;
will not implement list-item, run-in (Opera only) or table (no IE);
inline-block has incomplete IE6 support and requires -moz-inline-box
for Mozilla. Unknown target milestone.</td></tr>
<tr><td class="css1">height</td><td>Interesting, why use it? Unknown target milestone.</td></tr>
<tr class="danger css1"><td>list-style-image</td><td>Dangerous? Target milestone 1.3</td></tr>
<tr class="impl-no"><td>max-height</td><td rowspan="4">No IE 5/6</td></tr>
<tr class="impl-no"><td>min-height</td></tr>
<tr class="impl-no"><td>max-width</td></tr>
<tr class="impl-no"><td>min-width</td></tr>
<tr class="impl-no"><td>orphans</td><td>No IE support</td></tr>
<tr class="impl-no"><td>widows</td><td>No IE support</td></tr>
<tr><td>overflow</td><td>ENUM, IE 5/6 almost (remove visible if set). Unknown target milestone.</td></tr>
<tr><td>page-break-after</td><td>ENUM(auto, always, avoid, left, right),
IE 5.5/6 and Opera. Unknown target milestone.</td></tr>
<tr><td>page-break-before</td><td>ENUM(auto, always, avoid, left, right),
Mostly supported. Unknown target milestone.</td></tr>
<tr><td>page-break-inside</td><td>ENUM(avoid, auto), Opera only. Unknown target milestone.</td></tr>
<tr class="impl-no"><td>quotes</td><td>May be dropped from CSS2, fairly useless for inline context</td></tr>
<tr class="impl-no"><td>visibility</td><td>ENUM(visible, hidden, collapse),
Dangerous</td></tr>
<tr class="css1 feature"><td>white-space</td><td>ENUM(normal, pre, nowrap, pre-wrap,
pre-line), Spotty implementation:
pre (no IE 5/6), nowrap (no IE 5),
pre-wrap (only Opera), pre-line (no support). Fixable? Unknown target milestone.</td></tr>
</tbody>
<tbody class="impl-no">
<tr><th colspan="2">Aural</th></tr>
<tr><td>azimuth</td><td>-</td></tr>
<tr><td>cue</td><td>-</td></tr>
<tr><td>cue-after</td><td>-</td></tr>
<tr><td>cue-before</td><td>-</td></tr>
<tr><td>elevation</td><td>-</td></tr>
<tr><td>pause-after</td><td>-</td></tr>
<tr><td>pause-before</td><td>-</td></tr>
<tr><td>pause</td><td>-</td></tr>
<tr><td>pitch-range</td><td>-</td></tr>
<tr><td>pitch</td><td>-</td></tr>
<tr><td>play-during</td><td>-</td></tr>
<tr><td>richness</td><td>-</td></tr>
<tr><td>speak-header</td><td>Table related</td></tr>
<tr><td>speak-numeral</td><td>-</td></tr>
<tr><td>speak-punctuation</td><td>-</td></tr>
<tr><td>speak</td><td>-</td></tr>
<tr><td>speech-rate</td><td>-</td></tr>
<tr><td>stress</td><td>-</td></tr>
<tr><td>voice-family</td><td>-</td></tr>
<tr><td>volume</td><td>-</td></tr>
</tbody>
<tbody class="impl-no">
<tr><th colspan="2">Will not implement</th></tr>
<tr><td>content</td><td>Not applicable for inline styles</td></tr>
<tr><td>counter-increment</td><td>Needs content, Opera only</td></tr>
<tr><td>counter-reset</td><td>Needs content, Opera only</td></tr>
<tr><td>direction</td><td>No support</td></tr>
<tr><td>outline-color</td><td rowspan="4">IE Mac and Opera on outside,
Mozilla on inside and needs -moz-outline, no IE support.</td></tr>
<tr><td>outline-style</td></tr>
<tr><td>outline-width</td></tr>
<tr><td>outline</td></tr>
<tr><td>unicode-bidi</td><td>No support</td></tr>
</tbody>
</table>
<h2>Interesting Attributes</h2>
<table cellspacing="0">
<thead>
<tr><th>Attribute</th><th>Tags</th><th>Notes</th></tr>
</thead>
<!--
<tr><th></th></tr>
<tbody>
<tr><td>-</td><td>-</td><td>-</td></tr>
</tbody>
-->
<tbody>
<tr><th colspan="3">CSS</th></tr>
<tr class="impl-yes"><td>style</td><td>All</td><td>Not all properties may be implemented, parser is good though.</td></tr>
</tbody>
<tbody>
<tr><th colspan="3">Questionable</th></tr>
<tr class="impl-no"><td>accesskey</td><td>A</td><td>May interfere with main interface</td></tr>
<tr class="impl-no"><td>tabindex</td><td>A</td><td>May interfere with main interface</td></tr>
<tr><td>target</td><td>A</td><td>Config enabled, only useful for frame layouts, disallowed in strict</td></tr>
</tbody>
<tbody>
<tr><th colspan="3">Miscellaneous</th></tr>
<tr><td>datetime</td><td>DEL, INS</td><td>No visible effect, ISO format</td></tr>
<tr><td>rel</td><td>A</td><td>Largely user-defined: nofollow, tag (see microformats)</td></tr>
<tr><td>rev</td><td>A</td><td>Largely user-defined: vote-*</td></tr>
<tr class="feature"><td>axis</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
<tr class="feature"><td>char</td><td>COL, COLGROUP, TBODY, TD, TFOOT, TH, THEAD, TR</td><td>W3C only: No browser implementation</td></tr>
<tr class="feature"><td>headers</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
<tr class="feature"><td>scope</td><td>TD, TH</td><td>W3C only: No browser implementation</td></tr>
</tbody>
<tbody class="impl-yes">
<tr><th colspan="3">URI</th></tr>
<tr><td rowspan="2">cite</td><td>BLOCKQUOTE, Q</td><td>For attribution</td></tr>
<tr><td>DEL, INS</td><td>Link to explanation why it changed</td></tr>
<tr><td>href</td><td>A</td><td>-</td></tr>
<tr><td>longdesc</td><td>IMG</td><td>-</td></tr>
<tr class="required"><td>src</td><td>IMG</td><td>Required</td></tr>
</tbody>
<tbody>
<tr><th colspan="3">Transform, target milestone 1.4</th></tr>
<tr><td rowspan="5">align</td><td>CAPTION</td><td>Near-equiv style 'caption-side', drop left and right</td></tr>
<tr><td>IMG</td><td rowspan="2">Margin-left and margin-right = auto or parent div</td></tr>
<tr><td>TABLE</td></tr>
<tr><td>HR</td><td>Equivalent style 'text-align' (IE tested)</td></tr>
<tr class="impl-yes"><td>H1, H2, H3, H4, H5, H6, P</td><td>Equivalent style 'text-align'</td></tr>
<tr class="required impl-yes"><td>alt</td><td>IMG</td><td>Required, insert image filename if src is present or default invalid image text</td></tr>
<tr><td rowspan="3">bgcolor</td><td>TABLE</td><td>Equivalent style 'background-color' (IE tested)</td></tr>
<tr><td>TR</td><td>Equivalent style 'background-color' (IE tested)</td></tr>
<tr><td>TD, TH</td><td>Equivalent style 'background-color'</td></tr>
<tr><td>border</td><td>IMG</td><td>Equivalent style 'border-width', only applies when link present</td></tr>
<tr><td>clear</td><td>BR</td><td>Near-equiv style 'clear', transform 'all' into 'both'</td></tr>
<tr class="impl-no"><td>compact</td><td>DL, OL, UL</td><td>Boolean, needs custom CSS class; rarely used anyway</td></tr>
<tr class="required impl-yes"><td>dir</td><td>BDO</td><td>Required, insert ltr (or configuration value) if none</td></tr>
<tr><td>height</td><td>TD, TH</td><td>Near-equiv style 'height', needs px suffix if original was in pixels</td></tr>
<tr><td>hspace</td><td>IMG</td><td>Near-equiv styles 'margin-top' and 'margin-bottom', needs px suffix</td></tr>
<tr class="impl-yes"><td>lang</td><td>*</td><td>Copy value to xml:lang</td></tr>
<tr><td rowspan="2">name</td><td>IMG</td><td>Turn into ID</td></tr>
<tr><td>A</td><td>Turn into ID? (not deprecated, though in which specs?)</td></tr>
<tr><td>noshade</td><td>HR</td><td>Boolean, style 'border-style:solid;'</td></tr>
<tr><td>nowrap</td><td>TD, TH</td><td>Boolean, style 'white-space:nowrap;' (not compat with IE5)</td></tr>
<tr><td>size</td><td>HR</td><td>Near-equiv 'width', needs px suffix if original was pixels</td></tr>
<tr class="required impl-yes"><td>src</td><td>IMG</td><td>Required, insert blank or default img if not set</td></tr>
<tr class="impl-yes"><td>start</td><td>OL</td><td>Poorly supported 'counter-reset', allowed in loose, dropped in strict</td></tr>
<tr><td rowspan="3">type</td><td>LI</td><td rowspan="3">Equivalent style 'list-style-type', different allowed values though. (needs testing)</td></tr>
<tr><td>OL</td></tr>
<tr><td>UL</td></tr>
<tr class="impl-yes"><td>value</td><td>LI</td><td>Poorly supported 'counter-reset', allowed in loose, dropped in strict</td></tr>
<tr><td>vspace</td><td>IMG</td><td>Near-equiv styles 'margin-left' and 'margin-right', needs px suffix, see hspace</td></tr>
<tr><td rowspan="2">width</td><td>HR</td><td rowspan="2">Near-equiv style 'width', needs px suffix if original was pixels</td></tr>
<tr><td>TD, TH</td></tr>
</tbody>
</table>
<div id="version">$Id$</div>
</body></html>

View File

@@ -1,272 +0,0 @@
<!-- Transform %TextAlign to align:value in style -->
<!-- text alignment for p, div, h1-h6. The default is
align="left" for ltr headings, "right" for rtl
Move to style! -->
<!ENTITY % TextAlign "DEPRECATED align (left|center|right|justify) #IMPLIED">
<!-- type and start should have CSS equivalents, but they'll need to
be translated intelligently -->
<!ENTITY % ULStyle "(disc|square|circle)">
<!-- Ordered list numbering style
1 arabic numbers 1, 2, 3, ...
a lower alpha a, b, c, ...
A upper alpha A, B, C, ...
i lower roman i, ii, iii, ...
I upper roman I, II, III, ...
The style is applied to the sequence number which by default
is reset to 1 for the first list item in an ordered list.
-->
<!ENTITY % OLStyle "CDATA">
<!-- LIStyle is constrained to: "(%ULStyle;|%OLStyle;)" -->
<!ENTITY % LIStyle "CDATA">
<!ATTLIST ol
%attrs;
DEPRECATED type %OLStyle; #IMPLIED
DEPRECATED start %Number; #IMPLIED
>
<!ATTLIST li
%attrs;
DEPRECATED type %LIStyle; #IMPLIED
DEPRECATED value %Number; #IMPLIED
>
<!ATTLIST hr
%attrs;
DEPRECATED align (left|center|right) #IMPLIED
DEPRECATED size %Pixels; #IMPLIED
DEPRECATED width %Length; #IMPLIED
>
<!ATTLIST pre
%attrs;
DEPRECATED width %Number; #IMPLIED
>
<!ATTLIST blockquote
%attrs;
cite %URI; #IMPLIED
>
<!ATTLIST ins
%attrs;
cite %URI; #IMPLIED
datetime %Datetime; #IMPLIED
>
<!ATTLIST del
%attrs;
cite %URI; #IMPLIED
datetime %Datetime; #IMPLIED
>
<!ATTLIST a
%attrs;
name NMTOKEN #IMPLIED // ID
href %URI; #IMPLIED
rel %LinkTypes; #IMPLIED // needs policing
rev %LinkTypes; #IMPLIED // see rel
target %FrameTarget; #IMPLIED // usually not used, but might be
>
<!ATTLIST bdo
%coreattrs; // !#!
lang %LanguageCode; #IMPLIED
xml:lang %LanguageCode; #IMPLIED
dir (ltr|rtl) #REQUIRED
>
<!ATTLIST br
%coreattrs; // !#!
DEPRECATED clear (left|all|right|none) "none"
>
<!ELEMENT q %Inline;> <!-- inlined quote -->
<!ATTLIST q
%attrs;
cite %URI; #IMPLIED
>
<!ATTLIST img
%attrs;
src %URI; #REQUIRED
alt %Text; #REQUIRED
DEPRECATED name NMTOKEN #IMPLIED // ID
longdesc %URI; #IMPLIED
height %Length; #IMPLIED // dubious, but we'll allow
width %Length; #IMPLIED //
DEPRECATED align %ImgAlign; #IMPLIED
DEPRECATED border %Length; #IMPLIED
DEPRECATED hspace %Pixels; #IMPLIED // left/right margin
DEPRECATED vspace %Pixels; #IMPLIED // up/down margin
>
<!--
The border attribute sets the thickness of the frame around the
table. The default units are screen pixels.
The frame attribute specifies which parts of the frame around
the table should be rendered. The values are not the same as
CALS to avoid a name clash with the valign attribute.
-->
<!ENTITY % TFrame "(void|above|below|hsides|lhs|rhs|vsides|box|border)">
<!--
The rules attribute defines which rules to draw between cells:
If rules is absent then assume:
"none" if border is absent or border="0" otherwise "all"
-->
<!ENTITY % TRules "(none | groups | rows | cols | all)">
<!-- horizontal placement of table relative to document -->
<!ENTITY % TAlign "(left|center|right)">
<!-- horizontal alignment attributes for cell contents
char alignment char, e.g. char=':'
charoff offset for alignment char
-->
<!ENTITY % cellhalign
"align (left|center|right|justify|char) #IMPLIED
char %Character; #IMPLIED
charoff %Length; #IMPLIED"
>
<!-- vertical alignment attributes for cell contents -->
<!ENTITY % cellvalign
"valign (top|middle|bottom|baseline) #IMPLIED"
>
<!-- we may want to convert some of these nonetheless -->
<!ATTLIST table
%attrs;
summary %Text; #IMPLIED
width %Length; #IMPLIED
border %Pixels; #IMPLIED
frame %TFrame; #IMPLIED
rules %TRules; #IMPLIED
cellspacing %Length; #IMPLIED
cellpadding %Length; #IMPLIED
DEPRECATED align %TAlign; #IMPLIED
DEPRECATED bgcolor %Color; #IMPLIED
>
<!ENTITY % CAlign "(top|bottom|left|right)">
<!ATTLIST caption
%attrs;
DEPRECATED align %CAlign; #IMPLIED // watch, it's a special set
>
<!--
colgroup groups a set of col elements. It allows you to group
several semantically related columns together.
-->
<!ATTLIST colgroup
%attrs;
span %Number; "1"
width %MultiLength; #IMPLIED
%cellhalign; // very interesting
%cellvalign;
>
<!--
col elements define the alignment properties for cells in
one or more columns.
The width attribute specifies the width of the columns, e.g.
width=64 width in screen pixels
width=0.5* relative width of 0.5
The span attribute causes the attributes of one
col element to apply to more than one column.
-->
<!ATTLIST col
%attrs;
span %Number; "1"
width %MultiLength; #IMPLIED
%cellhalign;
%cellvalign;
>
<!--
Use thead to duplicate headers when breaking table
across page boundaries, or for static headers when
tbody sections are rendered in scrolling panel.
Use tfoot to duplicate footers when breaking table
across page boundaries, or for static footers when
tbody sections are rendered in scrolling panel.
Use multiple tbody sections when rules are needed
between groups of table rows.
-->
<!ATTLIST thead
%attrs;
%cellhalign;
%cellvalign;
>
<!ATTLIST tfoot
%attrs;
%cellhalign;
%cellvalign;
>
<!ATTLIST tbody
%attrs;
%cellhalign;
%cellvalign;
>
<!ATTLIST tr
%attrs;
%cellhalign;
%cellvalign;
DEPRECATED bgcolor %Color; #IMPLIED
>
<!-- Scope is simpler than headers attribute for common tables -->
<!ENTITY % Scope "(row|col|rowgroup|colgroup)">
<!-- th is for headers, td for data and for cells acting as both -->
<!ATTLIST th
%attrs;
abbr %Text; #IMPLIED
axis CDATA #IMPLIED
headers IDREFS #IMPLIED
scope %Scope; #IMPLIED
rowspan %Number; "1"
colspan %Number; "1"
%cellhalign;
%cellvalign;
DEPRECATED nowrap (nowrap) #IMPLIED
DEPRECATED bgcolor %Color; #IMPLIED
DEPRECATED width %Length; #IMPLIED
DEPRECATED height %Length; #IMPLIED
>
<!ATTLIST td
%attrs;
abbr %Text; #IMPLIED
axis CDATA #IMPLIED
headers IDREFS #IMPLIED
scope %Scope; #IMPLIED
rowspan %Number; "1"
colspan %Number; "1"
%cellhalign;
%cellvalign;
DEPRECATED nowrap (nowrap) #IMPLIED
DEPRECATED bgcolor %Color; #IMPLIED
DEPRECATED width %Length; #IMPLIED
DEPRECATED height %Length; #IMPLIED
>

146
docs/enduser-id.html Normal file
View File

@@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Explains various methods for allowing IDs in documents safely in HTML Purifier." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>IDs - HTML Purifier</title>
</head><body>
<h1 class="subtitled">IDs</h1>
<div class="subtitle">What they are, why you should(n't) wear them, and how to deal with it</div>
<div id="filing">Filed under End-User</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<p>Prior to HTML Purifier 1.2.0, this library blithely accepted user input that
looked like this:</p>
<pre>&lt;a id=&quot;fragment&quot;&gt;Anchor&lt;/a&gt;</pre>
<p>...presenting an attractive vector for those that would destroy standards
compliance: simply set the ID to one that is already used elsewhere in the
document and voila: validation breaks. There was a half-hearted attempt to
prevent this by allowing users to blacklist IDs, but I suspect that no one
really bothered, and thus, with the release of 1.2.0, IDs are now <em>removed</em>
by default.</p>
<p>IDs, however, are quite useful functionality to have, so if users start
complaining about broken anchors you'll probably want to turn them back on
with %HTML.EnableAttrID. But before you go mucking around with the config
object, it's probably worth to take some precautions to keep your page
validating. Why?</p>
<ol>
<li>Standards-compliant pages are good</li>
<li>Duplicated IDs interfere with anchors. If there are two id="foobar"s in a
document, which spot does a browser presented with the fragment #foobar go
to? Most browsers opt for the first appearing ID, making it impossible
to references the second section. Similarly, duplicated IDs can hijack
client-side scripting that relies on the IDs of elements.</li>
</ol>
<p>You have (currently) four ways of dealing with the problem.</p>
<h2 class="subtitled">Blacklisting IDs</h2>
<div class="subsubtitle">Good for pages with single content source and stable templates</div>
<p>Keeping in terms with the
<acronym title="Keep It Simple, Stupid">KISS</acronym> principle, let us
deal with the most obvious solution: preventing users from using any IDs that
appear elsewhere on the document. The method is simple:</p>
<pre>$config->set('HTML', 'EnableAttrID', true);
$config->set('Attr', 'IDBlacklist' array(
'list', 'of', 'attributes', 'that', 'are', 'forbidden'
));</pre>
<p>That being said, there are some notable drawbacks. First of all, you have to
know precisely which IDs are being used by the HTML surrounding the user code.
This is easier said than done: quite often the page designer and the system
coder work separately, so the designer has to constantly be talking with the
coder whenever he decides to add a new anchor. Miss one and you open yourself
to possible standards-compliance issues.</p>
<p>Furthermore, this position becomes untenable when a single web page must hold
multiple portions of user-submitted content. Since there's obviously no way
to find out before-hand what IDs users will use, the blacklist is helpless.
And even since HTML Purifier validates each segment seperately, perhaps doing
so at different times, it would be extremely difficult to dynamically update
the blacklist inbetween runs.</p>
<p>Finally, simply destroying the ID is extremely un-userfriendly behavior: after
all, they might have simply specified a duplicate ID by accident.</p>
<p>Thus, we get to our second method.</p>
<h2 class="subtitled">Namespacing IDs</h2>
<div class="subsubtitle">Lazy developer's way, but needs user education</div>
<p>This method, too, is quite simple: add a prefix to all user IDs. With this
code:</p>
<pre>$config->set('HTML', 'EnableAttrID', true);
$config->set('Attr', 'IDPrefix', 'user_');</pre>
<p>...this:</p>
<pre>&lt;a id=&quot;foobar&quot;&gt;Anchor!&lt;/a&gt;</pre>
<p>...turns into:</p>
<pre>&lt;a id=&quot;user_foobar&quot;&gt;Anchor!&lt;/a&gt;</pre>
<p>As long as you don't have any IDs that start with user_, collisions are
guaranteed not to happen. The drawback is obvious: if a user submits
id=&quot;foobar&quot;, they probably expect to be able to reference their page with
#foobar. You'll have to tell them, &quot;No, that doesn't work, you have to add
user_ to the beginning.&quot;</p>
<p>And yes, things get hairier. Even with a nice prefix, we still have done
nothing about multiple HTML Purifier outputs on one page. Thus, we have
a second configuration value to piggy-back off of: %Attr.IDPrefixLocal:</p>
<pre>$config->set('Attr', 'IDPrefixLocal', 'comment' . $id . '_');</pre>
<p>This new attributes does nothing but append on to regular IDPrefix, but is
special in that it is volatile: it's value is determined at run-time and
cannot possibly be cordoned into, say, a .ini config file. As for what to
put into the directive, is up to you, but I would recommend the ID number
the text has been assigned in the database. Whatever you pick, however, it
has to be unique and stable for the text you are validating. Note, however,
that we require that %Attr.IDPrefix be set before you use this directive.</p>
<p>And also remember: the user has to know what this prefix is too!</p>
<h2>Abstinence</h2>
<p>You may not want to bother. That's okay too, just don't enable IDs.</p>
<p>Personally, I would take this road whenever user-submitted content would be
possibly be shown together on one page. Why a blog comment would need to use
anchors is beyond me.</p>
<h2>Denial</h2>
<p>To revert back to pre-1.2.0 behavior, simply:</p>
<pre>$config->set('HTML', 'EnableAttrID', true);</pre>
<p>Don't come crying to me when your page mysteriously stops validating, though.</p>
<div id="version">$Id$</div>
</body>
</html>

View File

@@ -29,7 +29,8 @@ output is valid XHTML or send the HTML through a draconic XML parser (and yet
still get the nesting wrong: SafeHtmlChecker.class.php does not prevent <a>
tags from being nested within each other).
This document seeks to detail the inner workings of HTML Purifier. The first
This document no longer is a detailed description of how HTMLPurifier works,
as those descriptions have been moved to the appropriate code. The first
draft was drawn up after two rough code sketches and the implementation of a
forgiving lexer. You may also be interested in the unit tests located in the
tests/ folder, which provide a living document on how exactly the filter deals
@@ -52,4 +53,5 @@ In summary:
HTML Purifier is best suited for documents that require a rich array of
HTML tags. Things like blog comments are, in all likelihood, most appropriately
written in an extremely restrictive set of markup that doesn't require
all this functionality (or not written in HTML at all).
all this functionality (or not written in HTML at all), although this may
be changing in the future with the addition of levels of filtering.

View File

@@ -6,34 +6,43 @@ through negligence of people. This class will do its job: no more, no less,
and it's up to you to provide it the proper information and proper context
to be effective. Things to remember:
1. UTF-8. Currently, the parser runs under the assumption that it is dealing
1. Character Encoding: UTF-8.
Currently, the parser runs under the assumption that it is dealing
with UTF-8. Not ISO-8859-1 or Windows-1252, UTF-8. And definitely not "no
character encoding explicitly stated" or UTF-7. If you're not using UTF-8 as
your character encoding, you should switch. Now. (in future versions, however,
I may make the character encoding configurable, but there's only so much I
can do). Make sure any input is properly converted to UTF-8, or the parser
will mangle it badly (though it won't be a security risk if you're outputting
it as UTF-8 though).
your character encoding, make sure you configure HTML Purifier or switch
to UTF-8. Now. Also, make sure any input is properly converted to UTF-8, or
the parser will mangle it badly (though it won't be a security risk if you're
outputting it as UTF-8 though). Character encoding is, in general, a knotty
issue, but do yourself a favor and learn about it:
<http://www.joelonsoftware.com/articles/Unicode.html>
2. XHTML 1.0 Transitional. This is what the parser is outputting. For the most
2. Doctype: XHTML 1.0 Transitional
This is what the parser is outputting. For the most
part, it's compatible with HTML 4.01, but XHTML enforces some very nice things
that all web developers should use. Regardless, NO DOCTYPE is a NO. Quirks mode
has waaaay too many quirks for a little parser to handle. We did not select
strict in order to prevent ourselves from being too draconic on users, but
this may be configurable in the future.
this may be configurable in the future. Do you want standards compliance?
The doctype is a good place to start.
3. IDs. They need to be unique, but without some knowledge of the
rest of the document, it's difficult to know what's unique. Without setting
%Attr.IDBlacklist to the proper
3. IDs
They need to be unique, but without some knowledge of the
rest of the document, it's difficult to know what's unique. %Attr.IDBlacklist
needs to be set: we may want to consider disallowing IDs by default to
save lazy programmers.
4. [PROJECTED] Links. We're not going to try for spam protection (although
4. [PROJECTED] Links
We're not going to try for spam protection (although
some hooks for such a module might be nice) but we may offer the ability to
only accept relative URLs. Pick the one that's right for you.
5. CSS. While we can prevent the most flagrant cases from affecting your
5. CSS
While we can prevent the most flagrant cases from affecting your
layout (such as absolutely positioned elements), no amount of code is going
to protect your pages from being attacked by garish colors and plain old
bad taste. A neat feature would be the ability to define acceptable colors
in a document, but that's not likely to be implemented for a while. In the
meantime, be sure to make sure that floated elements (permitted, since they
can be quite useful) cna't mess up your layout.
can be quite useful) can't mess up your layout. Once again, we may want to
disable this by default to protect lazy developers.

179
docs/enduser-youtube.html Normal file
View File

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

View File

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

152
docs/index.html Normal file
View File

@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Index to all HTML Purifier documentation." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Documentation - HTML Purifier</title>
</head>
<body>
<h1>Documentation</h1>
<p><strong>HTML Purifier</strong> has documentation for all types of people.
Here is an index of all of them.</p>
<h2>End-user</h2>
<p>End-user documentation that contains articles, tutorials and useful
information for casual developers using HTML Purifier.</p>
<dl>
<dt><a href="enduser-id.html">IDs</a></dt>
<dd>Explains various methods for allowing IDs in documents safely.</dd>
<dt><a href="enduser-youtube.html">Embedding YouTube videos</a></dt>
<dd>Explains how to safely allow the embedding of flash from trusted sites.</dd>
</dl>
<h2>Development</h2>
<p>Developer documentation detailing code issues, roadmaps and project
conventions.</p>
<dl>
<dt><a href="dev-code-quality.html">Code Quality Issues</a></dt>
<dd>Discusses code quality issues and places that need to be refactored.</dd>
<dt><a href="dev-progress.html">Implementation Progress</a></dt>
<dd>Tables detailing HTML element and CSS property implementation coverage.</dd>
<dt><a href="dev-naming.html">Naming Conventions</a></dt>
<dd>Defines class naming conventions.</dd>
<dt><a href="dev-optimization.html">Optimization</a></dt>
<dd>Discusses possible methods of optimizing HTML Purifier.</dd>
</dl>
<h2>Proposals</h2>
<p>Proposed features, as well as the associated rambling to get a clear
objective in place before attempted implementation.</p>
<dl>
<dt><a href="proposal-colors.html">Colors</a></dt>
<dd>Proposal to allow for color constraints.</dd>
</dl>
<h2>Reference</h2>
<p>Miscellaneous essays, research pieces and other reference type material
that may not directly discuss HTML Purifier.</p>
<dl>
<dt><a href="ref-devnetwork.html">DevNetwork Credits</a></dt>
<dd>Credits and links to DevNetwork forum topics.</dd>
</dl>
<h2>Internal memos</h2>
<p>Plaintext documents that are more for use by active developers of
the code. They may be upgraded to HTML files or stay as TXT scratchpads.</p>
<table class="table">
<thead><tr>
<th width="10%">Type</th>
<th width="20%">Name</th>
<th>Description</th>
</tr></thead>
<tbody>
<tr>
<td>End-user</td>
<td><a href="enduser-overview.txt">Overview</a></td>
<td>High level overview of the general control flow (mostly obsolete).</td>
</tr>
<tr>
<td>End-user</td>
<td><a href="enduser-security.txt">Security</a></td>
<td>Common security issues that may still arise (half-baked).</td>
</tr>
<tr>
<td>Proposal</td>
<td><a href="proposal-filter-levels.txt">Filter levels</a></td>
<td>Outlines details of projected configurable level of filtering.</td>
</tr>
<tr>
<td>Proposal</td>
<td><a href="proposal-language.txt">Language</a></td>
<td>Specification of I18N for error messages derived from MediaWiki (half-baked).</td>
</tr>
<tr>
<td>Proposal</td>
<td><a href="proposal-new-directives.txt">New directives</a></td>
<td>Assorted configuration options that could be implemented.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-loose-vs-strict.txt">Loose vs.Strict</a></td>
<td>Differences between HTML Strict and Transitional versions.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-proprietary-tags.txt">Proprietary tags</a></td>
<td>List of vendor-specific tags we may want to transform to W3C compliant markup.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-strictness.txt">Strictness</a></td>
<td>Short essay on how loose definition isn't really loose.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-xhtml-1.1.txt">XHTML 1.1</a></td>
<td>What we'd have to do to support XHTML 1.1.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-whatwg.txt">WHATWG</a></td>
<td>How WHATWG plays into what we need to do.</td>
</tr>
</tbody>
</table>
<div id="version">$Id$</div>
</body>
</html>

View File

@@ -1,55 +0,0 @@
Naming
The classes in this library follow a few naming conventions, which may
help you find the correct functionality more quickly. Here they are:
All classes occupy the HTMLPurifier pseudo-namespace.
This means that all classes are prefixed with HTMLPurifier_. As such, all
names under HTMLPurifier_ are reserved, and userspace extensions should
be registered in a different namespace (or the main namespace).
All classes correspond to their path if library/ was in the include path
HTMLPurifier_AttrDef is located at HTMLPurifier/AttrDef.php; replace
underscores with slashes and append .php and you'll have the location of
the class.
Harness and Test are reserved class names for unit tests
The suffix "Test" indicates that the class is a subclass of UnitTestCase
(of the Simpletest library) and is testable. "Harness" indicates a subclass
of UnitTestCase that is not meant to be run but to be extended into
concrete test cases and contains custom test methods (i.e. assert*())
Class names do not necessarily represent inheritance hierarchies
While we try to reflect inheritance in naming to some extent, it is not
guaranteed (for instance, none of the classes inherit from HTMLPurifier,
the base class). However, all class files have the require_once
declarations to whichever classes they are tightly coupled to.
Strategy has a meaning different from the Gang of Four pattern
In Design Patterns, the Gang of Four describes a Strategy object as
encapsulating an algorithm so that they can be switched at run-time. While
our strategies are indeed algorithms, they are not meant to be substituted:
all must be present in order for proper functioning.
Abbreviations are avoided
We try to avoid abbreviations as much as possible, but in some cases,
abbreviated version is more readable than the full version. Here, we
list common abbreviations:
Attr(s) -> Attribute(s)
Def -> Definition
Ambiguity concerning the definition of Def/Definition
While a definition normally defines the structure/acceptable values of
an entity, most of the definitions in this application also attempt
to validate and fix the value. I am unsure of a better name, as
"Validator" would exclude fixing the value, "Fixer" doesn't invoke
the proper image of "fixing" something, and "ValidatorFixer" is too long!
Some other suggestions were "Handler", "Reference", "Check", "Fix",
"Repair" and "Heal".
Transform not Transformer
Transform is both a noun and a verb, and thus we define a "Transform" as
something that "transforms," leaving "Transformer" (which sounds like an
electrical device/robot toy).

View File

@@ -1,11 +0,0 @@
Optimization
Here are some possible optimization techniques we can apply to code sections if
they turn out to be slow. Be sure not to prematurely optimize though!
- Make Tokens Flyweights
- Rewrite regexps into PHP code
- Serialize the Definition object
- Batch regexp validation (do as many per function call as possible)
- Parallelize strategies

47
docs/proposal-colors.html Normal file
View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Proposal to allow for color constraints in HTML Purifier." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Proposal: Colors - HTML Purifier</title>
</head><body>
<h1 class="subtitled">Colors</h1>
<div class="subtitle">Hammering some sense into those color-blind newbies</div>
<div id="filing">Filed under Proposals</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<p>Your website probably has a color-scheme.
<span style="color:#090; background:#FFF;">Green on white</span>,
<span style="color:#A0F; background:#FF0;">purple on yellow</span>,
whatever. When you give users the ability to style their content, you may
want them to keep in line with your styling. If you're website is all
about light colors, you don't want a user to come in and vandalize your
page with a deep maroon.</p>
<p>This is an extremely silly feature proposal, but I'm writing it down anyway.</p>
<p>What if the user could constrain the colors specified in inline styles? You
are only allowed to use these shades of dark green for text and these shades
of light yellow for the background. At the very least, you could ensure
that we did not have pale yellow on white text.</p>
<h2>Implementation issues</h2>
<ol>
<li>Requires the color attribute definition to know, currently, what the text
and background colors are. This becomes difficult when classes are thrown
into the mix.</li>
<li>The user still has to define the permissible colors, how does one do
something like that?</li>
</ol>
<div id="version">$Id$</div>
</body>
</html>

35
docs/proposal-config.txt Normal file
View File

@@ -0,0 +1,35 @@
Configuration
Configuration is documented on a per-use case: if a class uses a certain
value from the configuration object, it has to define its name and what the
value is used for. This means decentralized configuration declarations that
are nevertheless error checking and a centralized configuration object.
Directives are divided into namespaces, indicating the major portion of
functionality they cover (although there may be overlaps. Please consult
the documentation in ConfigDef for more information on these namespaces.
Since configuration is dependant on context, internal classes require a
configuration object to be passed as a parameter. (They also require a
Context object).
In relation to HTMLDefinition and CSSDefinition, there is a special class
of directives that influence the *construction* of the Definition object.
A standard call pattern would look like:
1. Client calls Config->getHTMLDefinition()
2. Config calls HTMLDefinition->createNew(this)
3. HTMLDefinition constructs itself with base configuration
4. HTMLDefinition calls Config->get('HTMLDefinition')
5. Config returns array of directives that later construction
6. HTMLDefinition performs operations and changes specified by directives
7. HTMLPurifier returns constructed definition
8. Config caches definition so it doesn't have to be generated again
9. Config returns definition
You could also override Config's copy of the definition with your own
custom copy, which OVERRIDES all directives. Only the base, vanilla copy
is the Singleton, the object actually interfaced with is a operated-upon
clone of that object. Also, if an update to the directives would update
the definition, you'd have to force reconstruction.

View File

@@ -0,0 +1,130 @@
Filter Levels
When one size *does not* fit all
The more I think about it, the less sense it makes for maintaining one huge
monolithic HTMLDefinition class. There's simply so much variation that
could go into this definition: the set of HTML good for blog entries is
definitely too large for HTML that would be allowed in blog comments. Going
from Transitional to Strict requires changes to the definition.
Allowing users to specify their own whitelists is one step (implemented, btw),
but I have doubts on only doing this. Simply put, the typical programmer is too
lazy to actually go through the trouble of investigating which tags, attributes
and properties to allow. HTMLDefinition makes a big part of what HTMLPurifier
is.
The idea, then, is to setup fundamentally different set of definitions, which
can further be customized using simpler configuration options.
Here are some fuzzy levels you could set:
1. Comments - Wordpress recommends a, abbr, acronym, b, blockquote, cite,
code, em, i, strike, strong; however, you could get away with only a, em and
p; also having blockquote and pre tags would be helpful.
2. BBCode - Emulate the usual tagset for forums: b, i, img, a, blockquote,
pre, div, span and h[2-6] (the last three are for specially formatted
posts, div and span require associated classes or inline styling enabled
to be useful)
3. Pages - As permissive as possible without allowing XSS. No protection
against bad design sense, unfortunantely. Suitable for wiki and page
environments. (probably what we have now)
4. Lint - Accept everything in the spec, a Tidy wannabe. (This probably won't
get implemented as it would require routines for things like <object>
and friends to be implemented, which is a lot of work for not a lot of
benefit)
One final note: when you start axing tags that are more commonly used, you
run the risk of accidentally destroying user data, especially if the data
is incoming from a WYSIWYG eidtor that hasn't been synced accordingly. This may
make forbidden element to text transformations desirable (for example, images).
== Element Risk Analysis ==
Legend:
[danger level] - regular tags / uncommon tags ~ deprecated tags
[danger level]* - rare tags
1 - blockquote, code, em, i, p, tt / strong, sub, sup
1* - abbr, acronym, bdo, cite, dfn, kbd, q, samp
2 - b, br, del, div, pre, span / ins, s, strike ~ u
3 - h2, h3, h4, h5, h6 ~ center
4 - h1, big ~ font
5 - a
7 - area, map
These are special use tags, they should be enabled on a blanket basis.
Lists - dd, dl, dt, li, ol, ul ~ menu, dir
Tables - caption, table, td, th, tr / col, colgroup, tbody, tfoot, thead
Forms - fieldset, form, input, lable, legend, optgroup, option, select, textarea
XSS - noscript, object, script ~ applet
Meta - base, basefont, body, head, html, link, meta, style, title
Frames - frame, frameset, iframe
And tag specific notes:
a - general problems involving linkspam
b - too much bold is bad, typographically speaking bold is discouraged
br - often misused
center - CSS, usually no legit use
del - only useful in editing context
div - little meaning in certain contexts i.e. blog comment
h1 - usually no legit use, as header is already set by application
h* - not needed in blog comments
hr - usually not necessary in blog comments
img - could be extremely undesirable if linking to external pics (CSRF, goatse)
pre - could use formatting, only useful in code contexts
q - very little support
s - transform into span with styling or del?
small - technically presentational
span - depends on attribute allowances
sub, sup - specialized
u - little legit use, prefer class with text-decoration
Based on the riskiness of the items, we may want to offer %HTML.DisableImages
attribute and put URI filtering higher up on the priority list.
== Attribute Risk Analysis ==
We actually have a suprisingly small assortment of allowed attributes (the
rest are deprecated in strict, and thus we opted not to allow them, even
though our output is XHTML Transitional by default.)
Required URI - img.alt, img.src, a.href
Medium risk - *.class, *.dir
High risk - img.height, img.width, *.id, *.style
Table - colgroup/col.span, td/th.rowspan, td/th.colspan
Uncommon - *.title, *.lang, *.xml:lang
Rare - td/th.abbr, table.summary, {table}.charoff
Rare URI - del.cite, ins.cite, blockquote.cite, q.cite, img.longdesc
Presentational - {table}.align, {table}.valign, table.frame, table.rules,
table.border
Partially presentational - table.cellpadding, table.cellspacing,
table.width, col.width, colgroup.width
== CSS Risk Analysis ==
There are certain CSS elements that are extremely useful inline, but then
as you get to more presentation oriented styling it may not always be
appropriate to inline them.
Useful - clear, float, border-collapse, caption-side
These CSS properties can break layouts if used improperly. We have excluded
any CSS properties that are not currently implemented (such as position).
Dangerous, can go outside container - float
Easy to abuse - font-size, font-family (font), width
Colored - background-color (background), border-color (border), color
Dramatic - border, list-style-position (list-style), margin, padding,
text-align, text-indent, text-transform, vertical-align, line-height
Dramatic elements substantially change the look of text in ways that should
probably have been reserved to other areas.

View File

@@ -0,0 +1,98 @@
We are going to model our I18N/L10N off of MediaWiki's system. Their's is
obviously quite complicated, so we're going to simplify it a bit for our needs.
== Structure ==
First, you have a Language object. This object contains all the localisable
message strings, as well as other important language-specific settings and
custom behavior (uppercasing, lowercasing, printing dates, formatting
numbers, etc.)
The object is constructed from two sources: subclassed versions of itself
(classes) and Message files (messages).
== General use ==
You load a language object by calling the Language::factory() function.
This function the class file for the object (taking in account fallback
languages by using the fallback langauge's object but overloading the
language key) and returns that object. Nothing else happens.
When a message/etc is requested, a lazy load initializor is called. Now the
real work starts. We're first going to take the scenario that the language
is not cached. The system loads the Messages file by:
require( $filename );
$cache = compact( self::$mLocalisationKeys );
...where self::$mLocalisationKeys is the name of variables that could be used
in the localization file. This lets you use things like:
$fallback = false;
$rtl = false;
...and easily siphon them into arrays.
Then, we load the $fallback language (if not set, English) to fill in the gaps in
the messages. There is specialized behavior for certain keys, as they can be
mergeable maps, lists or alias lists (not sure what the last one is).
== Caching ==
MediaWiki has lots of caching mechanisms built in, which make the code somewhat
more difficult to understand. Before doing any loading, MediaWiki will check
the following places to see if we can be lazy:
1. $mLocalisationCache[$code] - just a variable where it may have been stashed
2. serialized/$code.ser - compiled serialized language file
3. Memcached version of file (with expiration checking)
Expiration checking consists of by ensuring all dependencies have filemtime
that match the ones bundled with the cached copy. Similar checking could be
implemented for serialized versions, as it seems that they are not updated
until manually recompiled.
== Behavior ==
Things that are localizable:
- Weekdays (and abbrev)
- Months (and abbrev)
- Bookstores
- Skin names
- Date preferences / Custom date format
- Default date format
- Default user option overrides
-+ Language names
- Timezones
-+ Character encoding conversion via iconv
- UpperLowerCase first (needs casemaps for some)
- UpperLowerCase
- Uppercase words
- Uppercase word breaks
- Case folding
- Strip punctuation for MySQL search
- Get first character
-+ Alternate encoding
-+ Recoding for edit (and then recode input)
-+ RTL
-+ Direction mark character depending on RTL
-? Arrow depending on RTL
- Languages where italics cannot be used
-+ Number formatting (commafy, transform digits, transform separators)
- Truncate (multibyte)
- Grammar conversions for inflected languages
- Plural transformations
- Formatting expiry times
- Segmenting for diffs (Chinese)
- Convert to variants of language
- Language specific user preference options
- Link trails [[foo]]bar
-+ Language code (RFC 3066)
Neat functionality:
- I18N sprintfDate
- Roman numeral formatting
Items marked with a + likely need to be addressed by HTML Purifier

View File

@@ -0,0 +1,46 @@
Configuration Ideas
Here are some theoretical configuration ideas that we could implement some
time. Note the naming convention: %Namespace.Directive
%Attr.IDPrefix - prefix all ids with this
%Attr.RewriteFragments - if there's %Attr.IDPrefix we may want to transparently
rewrite the URLs we parse too. However, we can only do it when it's a pure
anchor link, so it's not foolproof
%Attr.ClassBlacklist,
%Attr.ClassWhitelist,
%Attr.ClassPolicy - determines what classes are allowed. When
%Attr.ClassPolicy is set to Blacklist, only allow those not in
%Attr.ClassBlacklist. When it's Whitelist, only allow those in
%Attr.ClassWhitelist.
%Attr.MaxWidth,
%Attr.MaxHeight - caps for width and height related checks.
(the hack in Pixels for an image crashing attack could be replaced by this)
%URI.AddRelNofollow - will add rel="nofollow" to all links, preventing the
spread of ill-gotten pagerank
%URI.RelativeToAbsolute - transforms all relative URIs to absolute form
%URI.HostBlacklistRegex - regexes that if matching the host are disallowed
%URI.HostWhitelist - domain names that are excluded from the host blacklist
%URI.HostPolicy - determines whether or not its reject all and then whitelist
or allow all in then do specific blacklists with whitelist intervening.
'DenyAll' or 'AllowAll' (default)
%URI.DisableIPHosts - URIs that have IP addresses for hosts are disallowed.
Be sure to also grab unusual encodings (dword, hex and octal), which may
be currently be caught by regular DNS
%URI.DisableIDN - Disallow raw internationalized domain names. Punycode
will still be permitted.
%URI.ConvertUnusualIPHosts - transform dword/hex/octal IP addresses to the
regular form
%URI.ConvertAbsoluteDNS - Remove extra dots after host names that trigger
absolute DNS. While this is actually the preferred method according to
the RFC, most people opt to use a relative domain name relative to . (root).

View File

@@ -1,28 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<title>DevNetwork Forums</title>
</head>
<body>
<p>Many thanks to the DevNetwork community for answering questions,
theorizing about design, and offering encouragement during
the development of this library in these forum threads:</p>
<ul>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=52905">HTMLPurifier PHP Library hompeage</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53056">How much of CSS to implement?</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53083">Parsing URL only according to URI : Security Risk?</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53096">Gimme a name : URI and friends</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53415">How to document configuration directives</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53479">IPv6</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53539">http and ftp versus news and mailto</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53579">HTMLPurifier - Take your best shot</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53664">Need help optimizing a block of code</a>
</ul>
</body>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Credits and links to DevNetwork forum topics on HTML Purifier." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>DevNetwork Credits - HTML Purifier</title>
</head>
<body>
<h1>DevNetwork Credits</h1>
<div id="filing">Filed under Reference</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<p>Many thanks to the DevNetwork community for answering questions,
theorizing about design, and offering encouragement during
the development of this library in these forum threads:</p>
<ul>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=52905">HTMLPurifier PHP Library hompeage</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53056">How much of CSS to implement?</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53083">Parsing URL only according to URI : Security Risk?</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53096">Gimme a name : URI and friends</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53415">How to document configuration directives</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53479">IPv6</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53539">http and ftp versus news and mailto</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53579">HTMLPurifier - Take your best shot</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53664">Need help optimizing a block of code</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=53861">Non-SGML characters</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=54283">Wordpress makes me cry</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=54478">Parameter Object vs. Parameter Array vs. Parameter Functions</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=54521">Convert encoding where output cannot represent characters</a></li>
<li><a href="http://forums.devnetwork.net/viewtopic.php?t=56411">Reporting errors in a document without line numbers</a></li>
</ul>
<p>...as well as any I may have forgotten.</p>
<div id="version">$Id$</div>
</body>
</html>

View File

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

View File

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

36
docs/ref-strictness.txt Normal file
View File

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

9
docs/ref-whatwg.txt Normal file
View File

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

21
docs/ref-xhtml-1.1.txt Normal file
View File

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

40
docs/style.css Normal file
View File

@@ -0,0 +1,40 @@
html {font-size:1em; font-family:serif; }
body {margin-left:4em; margin-right:4em; }
dt {font-weight:bold; }
pre {margin-left:2em; }
pre, code, tt {font-family:monospace; font-size:1em; }
h1 {text-align:center; font-family:Garamond, serif;
font-variant:small-caps;}
h2 {border-bottom:1px solid #CCC; font-family:sans-serif; font-weight:normal;
font-size:1.3em;}
h3 {font-family:sans-serif; font-size:1.1em; font-weight:bold; }
h4 {font-family:sans-serif; font-size:0.9em; font-weight:bold; }
/* For witty quips */
.subtitled {margin-bottom:0em;}
.subtitle , .subsubtitle {font-size:.8em; margin-bottom:1em;
font-style:italic; margin-top:-.2em;text-align:center;}
.subsubtitle {text-align:left;margin-left:2em;}
/* Used for special "See also" links. */
.reference {font-style:italic;margin-left:2em;}
/* Marks off asides, discussions on why something is the way it is */
.aside {margin-left:2em; font-family:sans-serif; font-size:0.9em; }
/* A regular table */
.table {border-collapse:collapse; border-bottom:2px solid #888; margin-left:2em; }
.table thead th {margin:0; background:#888; color:#FFF; }
.table thead th:first-child {-moz-border-radius-topleft:1em;}
.table tbody td {border-bottom:1px solid #CCC; padding-right:0.6em;padding-left:0.6em;}
/* Category of the file */
#filing {font-weight:bold; font-size:smaller; }
/* Contains, without exception, Return to index. */
#index {font-size:smaller; }
/* Contains, without exception, $Id$, for SVN version info. */
#version {text-align:right; font-style:italic; margin:2em 0;}

View File

@@ -0,0 +1,10 @@
<?php
/**
* This is a stub include that automatically configures the include path.
*/
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
require_once 'HTMLPurifier.php';
?>

View File

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

View File

@@ -3,7 +3,7 @@
/*!
* @mainpage
*
* HTMLPurifier is a purification class that will take an arbitrary snippet of
* HTML Purifier is an HTML filter that will take an arbitrary snippet of
* HTML and rigorously test, validate and filter it into a version that
* is safe for output onto webpages. It achieves this by:
*
@@ -15,15 +15,41 @@
* -# Validating attributes of the nodes; and
* -# Generating HTML from the purified tokens.
*
* See /docs/spec.txt for more details.
* However, most users will only need to interface with the HTMLPurifier
* class, so this massive amount of infrastructure is usually concealed.
* If you plan on working with the internals, be sure to include
* HTMLPurifier_ConfigSchema and HTMLPurifier_Config.
*/
require_once 'HTMLPurifier/ConfigDef.php';
/*
HTML Purifier 1.3.2 - Standards Compliant HTML Filtering
Copyright (C) 2006 Edward Z. Yang
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// almost every class has an undocumented dependency to these, so make sure
// they get included
require_once 'HTMLPurifier/ConfigSchema.php';
require_once 'HTMLPurifier/Config.php';
require_once 'HTMLPurifier/Context.php';
require_once 'HTMLPurifier/Lexer.php';
require_once 'HTMLPurifier/HTMLDefinition.php';
require_once 'HTMLPurifier/Generator.php';
require_once 'HTMLPurifier/Strategy/Core.php';
require_once 'HTMLPurifier/Encoder.php';
/**
* Main library execution class.
@@ -31,41 +57,98 @@ require_once 'HTMLPurifier/Strategy/Core.php';
* Facade that performs calls to the HTMLPurifier_Lexer,
* HTMLPurifier_Strategy and HTMLPurifier_Generator subsystems in order to
* purify HTML.
*
* @todo We need an easier way to inject strategies, it'll probably end
* up getting done through config though.
*/
class HTMLPurifier
{
var $version = '1.3.2';
var $config;
var $lexer, $strategy, $generator;
/**
* Final HTMLPurifier_Context of last run purification. Might be an array.
* @public
*/
var $context;
/**
* Initializes the purifier.
* @param $config Configuration for all instances of the purifier
* @param $config Optional HTMLPurifier_Config object for all instances of
* the purifier, if omitted, a default configuration is
* supplied (which can be overridden on a per-use basis).
* The parameter can also be any type that
* HTMLPurifier_Config::create() supports.
*/
function HTMLPurifier($config = null) {
$this->config = $config ? $config : HTMLPurifier_Config::createDefault();
$this->config = HTMLPurifier_Config::create($config);
$this->lexer = HTMLPurifier_Lexer::create();
$this->strategy = new HTMLPurifier_Strategy_Core();
$this->generator = new HTMLPurifier_Generator();
$this->encoder = new HTMLPurifier_Encoder();
}
/**
* Purifies HTML.
* Filters an HTML snippet/document to be XSS-free and standards-compliant.
*
* @param $html String of HTML to purify
* @param $config HTMLPurifier_Config object for this specific round
* @param $config HTMLPurifier_Config object for this operation, if omitted,
* defaults to the config object specified during this
* object's construction. The parameter can also be any type
* that HTMLPurifier_Config::create() supports.
* @return Purified HTML
*/
function purify($html, $config = null) {
$config = $config ? $config : $this->config;
$lexer = HTMLPurifier_Lexer::create();
$strategy = new HTMLPurifier_Strategy_Core();
$generator = new HTMLPurifier_Generator();
return $generator->generateFromTokens(
$strategy->execute(
$lexer->tokenizeHTML($html, $config),
$config
),
$config
);
$config = $config ? HTMLPurifier_Config::create($config) : $this->config;
$context =& new HTMLPurifier_Context();
$html = $this->encoder->convertToUTF8($html, $config, $context);
// purified HTML
$html =
$this->generator->generateFromTokens(
// list of tokens
$this->strategy->execute(
// list of un-purified tokens
$this->lexer->tokenizeHTML(
// un-purified HTML
$html, $config, $context
),
$config, $context
),
$config, $context
);
$html = $this->encoder->convertFromUTF8($html, $config, $context);
$this->context =& $context;
return $html;
}
/**
* Filters an array of HTML snippets
* @param $config Optional HTMLPurifier_Config object for this operation.
* See HTMLPurifier::purify() for more details.
* @return Array of purified HTML
*/
function purifyArray($array_of_html, $config = null) {
$context_array = array();
foreach ($array_of_html as $key => $html) {
$array_of_html[$key] = $this->purify($html, $config);
$context_array[$key] = $this->context;
}
$this->context = $context_array;
return $array_of_html;
}
}
?>

View File

@@ -1,15 +0,0 @@
<?php
/**
* Internal data-structure used in attribute validation to accumulate state.
*
* All it is is a data-structure that holds objects that accumulate state, like
* HTMLPurifier_IDAccumulator.
*/
class HTMLPurifier_AttrContext
{
var $id_accumulator;
}
?>

View File

@@ -1,16 +1,61 @@
<?php
require_once 'HTMLPurifier/AttrContext.php';
/**
* Base class for all validating attribute definitions.
*
* This family of classes forms the core for not only HTML attribute validation,
* but also any sort of string that needs to be validated or cleaned (which
* means CSS properties and composite definitions are defined here too).
* Besides defining (through code) what precisely makes the string valid,
* subclasses are also responsible for cleaning the code if possible.
*/
// AttrDef = Attribute Definition
class HTMLPurifier_AttrDef
{
function HTMLPurifier_AttrDef() {}
/**
* Tells us whether or not an HTML attribute is minimized. Only the
* boolean attribute vapourware would use this.
*/
var $minimized = false;
/**
* Validates and cleans passed string according to a definition.
*
* @public
* @param $string String to be validated and cleaned.
* @param $config Mandatory HTMLPurifier_Config object.
* @param $context Mandatory HTMLPurifier_AttrContext object.
*/
function validate($string, $config, &$context) {
trigger_error('Cannot call abstract function', E_USER_ERROR);
}
/**
* Convenience method that parses a string as if it were CDATA.
*
* This method process a string in the manner specified at
* <http://www.w3.org/TR/html4/types.html#h-6.2> by removing
* leading and trailing whitespace, ignoring line feeds, and replacing
* carriage returns and tabs with spaces. While most useful for HTML
* attributes specified as CDATA, it can also be applied to most CSS
* values.
*
* @note This method is not entirely standards compliant, as trim() removes
* more types of whitespace than specified in the spec. In practice,
* this is rarely a problem, as those extra characters usually have
* already been removed by HTMLPurifier_Encoder.
*
* @warning This processing is inconsistent with XML's whitespace handling
* as specified by section 3.3.3 and referenced XHTML 1.0 section
* 4.7. Compliant processing requires all line breaks normalized
* to "\n", so the fix is not as simple as fixing it in this
* function. Trim and whitespace collapsing are supposed to only
* occur in NMTOKENs. However, note that we are NOT necessarily
* parsing XML, thus, this behavior may still be correct.
*
* @public
*/
function parseCDATA($string) {
$string = trim($string);
$string = str_replace("\n", '', $string);

View File

@@ -0,0 +1,45 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates the border property as defined by CSS.
*/
class HTMLPurifier_AttrDef_Border extends HTMLPurifier_AttrDef
{
/**
* Local copy of properties this property is shorthand for.
*/
var $info = array();
function HTMLPurifier_AttrDef_Border($config) {
$def = $config->getCSSDefinition();
$this->info['border-width'] = $def->info['border-width'];
$this->info['border-style'] = $def->info['border-style'];
$this->info['border-top-color'] = $def->info['border-top-color'];
}
function validate($string, $config, &$context) {
$string = $this->parseCDATA($string);
// we specifically will not support rgb() syntax with spaces
$bits = explode(' ', $string);
$done = array(); // segments we've finished
$ret = ''; // return value
foreach ($bits as $bit) {
foreach ($this->info as $propname => $validator) {
if (isset($done[$propname])) continue;
$r = $validator->validate($bit, $config, $context);
if ($r !== false) {
$ret .= $r . ' ';
$done[$propname] = true;
break;
}
}
}
return rtrim($ret);
}
}
?>

View File

@@ -3,6 +3,12 @@
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/CSSDefinition.php';
/**
* Validates the HTML attribute style, otherwise known as CSS.
* @note We don't implement the whole CSS specification, so it might be
* difficult to reuse this component in the context of validating
* actual stylesheet declarations.
*/
class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
{
@@ -10,7 +16,7 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
$css = $this->parseCDATA($css);
$definition = HTMLPurifier_CSSDefinition::instance();
$definition = $config->getCSSDefinition();
// we're going to break the spec and explode by semicolons.
// This is because semicolon rarely appears in escaped form
@@ -22,10 +28,12 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
if (!$declaration) continue;
if (!strpos($declaration, ':')) continue;
list($property, $value) = explode(':', $declaration, 2);
$property = trim($property);
$value = trim($value);
if (!isset($definition->info[$property])) continue;
// inefficient call, since the validator will do this again
// inherit works for everything
if (strtolower(trim($value)) !== 'inherit') {
// inherit works for everything (but only on the base property)
$result = $definition->info[$property]->validate(
$value, $config, $context );
} else {
@@ -35,6 +43,7 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
$propvalues[$property] = $result;
}
// procedure does not write the new CSS simultaneously, so it's
// slightly inefficient, but it's the only way of getting rid of
// duplicates. Perhaps config to optimize it, but not now.

View File

@@ -3,13 +3,29 @@
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Number.php';
/**
* Represents a Length as defined by CSS.
* @warning Be sure not to confuse this with HTMLPurifier_AttrDef_Length!
*/
class HTMLPurifier_AttrDef_CSSLength extends HTMLPurifier_AttrDef
{
/**
* Valid unit lookup table.
* @warning The code assumes all units are two characters long. Be careful
* if we have to change this behavior!
*/
var $units = array('em' => true, 'ex' => true, 'px' => true, 'in' => true,
'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true);
/**
* Instance of HTMLPurifier_AttrDef_Number to defer number validation to
*/
var $number_def;
/**
* @param $non_negative Bool indication whether or not negative values are
* allowed.
*/
function HTMLPurifier_AttrDef_CSSLength($non_negative = false) {
$this->number_def = new HTMLPurifier_AttrDef_Number($non_negative);
}

View File

@@ -3,6 +3,9 @@
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/Config.php';
/**
* Validates the contents of the global HTML attribute class.
*/
class HTMLPurifier_AttrDef_Class extends HTMLPurifier_AttrDef
{
@@ -21,13 +24,14 @@ class HTMLPurifier_AttrDef_Class extends HTMLPurifier_AttrDef
// and plus it would complicate optimization efforts (you never
// see that anyway).
$matches = array();
$pattern = '/(?:(?<=\s)|\A)'.
$pattern = '/(?:(?<=\s)|\A)'. // look behind for space or string start
'((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'.
'(?:(?=\s)|\z)/';
'(?:(?=\s)|\z)/'; // look ahead for space or string end
preg_match_all($pattern, $string, $matches);
if (empty($matches[1])) return false;
// reconstruct class string
$new_string = '';
foreach ($matches[1] as $class_names) {
$new_string .= $class_names . ' ';

View File

@@ -2,14 +2,44 @@
require_once 'HTMLPurifier/AttrDef.php';
class HTMLPurifier_AttrDef_Color
/**
* Validates Color as defined by CSS.
*/
class HTMLPurifier_AttrDef_Color extends HTMLPurifier_AttrDef
{
/**
* Color keyword lookup table.
* @todo Extend it to include all usually allowed colors.
*/
var $colors = array(
'maroon' => '#800000',
'red' => '#F00',
'orange' => '#FFA500',
'yellow' => '#FF0',
'olive' => '#808000',
'purple' => '#800080',
'fuchsia' => '#F0F',
'white' => '#FFF',
'lime' => '#0F0',
'green' => '#008000',
'navy' => '#000080',
'blue' => '#00F',
'aqua' => '#0FF',
'teal' => '#008080',
'black' => '#000',
'silver' => '#C0C0C0',
'gray' => '#808080'
);
function validate($color, $config, &$context) {
$color = trim($color);
if (!$color) return false;
$lower = strtolower($color);
if (isset($this->colors[$lower])) return $this->colors[$lower];
if ($color[0] === '#') {
// hexadecimal handling
$hex = substr($color, 1);

View File

@@ -1,10 +1,26 @@
<?php
/**
* Allows multiple validators to attempt to validate attribute.
*
* Composite is just what it sounds like: a composite of many validators.
* This means that multiple HTMLPurifier_AttrDef objects will have a whack
* at the string. If one of them passes, that's what is returned. This is
* especially useful for CSS values, which often are a choice between
* an enumerated set of predefined values or a flexible data type.
*/
class HTMLPurifier_AttrDef_Composite extends HTMLPurifier_AttrDef
{
/**
* List of HTMLPurifier_AttrDef objects that may process strings
* @protected
*/
var $defs;
/**
* @param $defs List of HTMLPurifier_AttrDef objects
*/
function HTMLPurifier_AttrDef_Composite($defs) {
$this->defs = $defs;
}

View File

@@ -0,0 +1,17 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
class HTMLPurifier_AttrDef_Email extends HTMLPurifier_AttrDef
{
/**
* Unpacks a mailbox into its display-name and address
*/
function unpack($string) {
// needs to be implemented
}
}
?>

View File

@@ -0,0 +1,23 @@
<?php
require_once 'HTMLPurifier/AttrDef/Email.php';
/**
* Primitive email validation class based on the regexp found at
* http://www.regular-expressions.info/email.html
*/
class HTMLPurifier_AttrDef_Email_SimpleCheck extends HTMLPurifier_AttrDef_Email
{
function validate($string, $config, &$context) {
// no support for named mailboxes i.e. "Bob <bob@example.com>"
// that needs more percent encoding to be done
if ($string == '') return false;
$string = trim($string);
$result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string);
return $result ? $string : false;
}
}
?>

View File

@@ -3,12 +3,27 @@
require_once 'HTMLPurifier/AttrDef.php';
// Enum = Enumerated
/**
* Validates a keyword against a list of valid values.
*/
class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef
{
/**
* Lookup table of valid values.
*/
var $valid_values = array();
/**
* Bool indicating whether or not enumeration is case sensitive.
* @note In general this is always case insensitive.
*/
var $case_sensitive = false; // values according to W3C spec
/**
* @param $valid_values List of valid values
* @param $case_sensitive Bool indicating whether or not case sensitive
*/
function HTMLPurifier_AttrDef_Enum(
$valid_values = array(), $case_sensitive = false) {

View File

@@ -0,0 +1,154 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates shorthand CSS property font.
*/
class HTMLPurifier_AttrDef_Font extends HTMLPurifier_AttrDef
{
/**
* Local copy of component validators.
*
* @note If we moved specific CSS property definitions to their own
* classes instead of having them be assembled at run time by
* CSSDefinition, this wouldn't be necessary. We'd instantiate
* our own copies.
*/
var $info = array();
/**
* System font keywords.
*/
var $system_fonts = array(
'caption' => true,
'icon' => true,
'menu' => true,
'message-box' => true,
'small-caption' => true,
'status-bar' => true
);
function HTMLPurifier_AttrDef_Font($config) {
$def = $config->getCSSDefinition();
$this->info['font-style'] = $def->info['font-style'];
$this->info['font-variant'] = $def->info['font-variant'];
$this->info['font-weight'] = $def->info['font-weight'];
$this->info['font-size'] = $def->info['font-size'];
$this->info['line-height'] = $def->info['line-height'];
$this->info['font-family'] = $def->info['font-family'];
}
function validate($string, $config, &$context) {
// regular pre-processing
$string = $this->parseCDATA($string);
if ($string === '') return false;
// check if it's one of the keywords
$lowercase_string = strtolower($string);
if (isset($this->system_fonts[$lowercase_string])) {
return $lowercase_string;
}
$bits = explode(' ', $string); // bits to process
$stage = 0; // this indicates what we're looking for
$caught = array(); // which stage 0 properties have we caught?
$stage_1 = array('font-style', 'font-variant', 'font-weight');
$final = ''; // output
for ($i = 0, $size = count($bits); $i < $size; $i++) {
if ($bits[$i] === '') continue;
switch ($stage) {
// attempting to catch font-style, font-variant or font-weight
case 0:
foreach ($stage_1 as $validator_name) {
if (isset($caught[$validator_name])) continue;
$r = $this->info[$validator_name]->validate(
$bits[$i], $config, $context);
if ($r !== false) {
$final .= $r . ' ';
$caught[$validator_name] = true;
break;
}
}
// all three caught, continue on
if (count($caught) >= 3) $stage = 1;
if ($r !== false) break;
// attempting to catch font-size and perhaps line-height
case 1:
$found_slash = false;
if (strpos($bits[$i], '/') !== false) {
list($font_size, $line_height) =
explode('/', $bits[$i]);
if ($line_height === '') {
// ooh, there's a space after the slash!
$line_height = false;
$found_slash = true;
}
} else {
$font_size = $bits[$i];
$line_height = false;
}
$r = $this->info['font-size']->validate(
$font_size, $config, $context);
if ($r !== false) {
$final .= $r;
// attempt to catch line-height
if ($line_height === false) {
// we need to scroll forward
for ($j = $i + 1; $j < $size; $j++) {
if ($bits[$j] === '') continue;
if ($bits[$j] === '/') {
if ($found_slash) {
return false;
} else {
$found_slash = true;
continue;
}
}
$line_height = $bits[$j];
break;
}
} else {
// slash already found
$found_slash = true;
$j = $i;
}
if ($found_slash) {
$i = $j;
$r = $this->info['line-height']->validate(
$line_height, $config, $context);
if ($r !== false) {
$final .= '/' . $r;
}
}
$final .= ' ';
$stage = 2;
break;
}
return false;
// attempting to catch font-family
case 2:
$font_family =
implode(' ', array_slice($bits, $i, $size - $i));
$r = $this->info['font-family']->validate(
$font_family, $config, $context);
if ($r !== false) {
$final .= $r . ' ';
// processing completed successfully
return rtrim($final);
}
return false;
}
}
return false;
}
}
?>

View File

@@ -4,9 +4,16 @@ require_once 'HTMLPurifier/AttrDef.php';
// whitelisting allowed fonts would be nice
/**
* Validates a font family list according to CSS spec
*/
class HTMLPurifier_AttrDef_FontFamily extends HTMLPurifier_AttrDef
{
/**
* Generic font family keywords.
* @protected
*/
var $generic_names = array(
'serif' => true,
'sans-serif' => true,

View File

@@ -4,10 +4,21 @@ require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/IPv4.php';
require_once 'HTMLPurifier/AttrDef/IPv6.php';
/**
* Validates a host according to the IPv4, IPv6 and DNS (future) specifications.
*/
class HTMLPurifier_AttrDef_Host extends HTMLPurifier_AttrDef
{
var $ipv4, $ipv6;
/**
* Instance of HTMLPurifier_AttrDef_IPv4 sub-validator
*/
var $ipv4;
/**
* Instance of HTMLPurifier_AttrDef_IPv6 sub-validator
*/
var $ipv6;
function HTMLPurifier_AttrDef_Host() {
$this->ipv4 = new HTMLPurifier_AttrDef_IPv4();
@@ -24,6 +35,8 @@ class HTMLPurifier_AttrDef_Host extends HTMLPurifier_AttrDef
if ($valid === false) return false;
return '['. $valid . ']';
}
// need to do checks on unusual encodings too
$ipv4 = $this->ipv4->validate($string, $config, $context);
if ($ipv4 !== false) return $ipv4;

View File

@@ -2,12 +2,39 @@
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/IDAccumulator.php';
// NOTE QUIRKY BEHAVIOR: even though this is the id processor, it
// will ignore directive Attr:IDBlacklist, since it will only
// go according to the ID accumulator. Since the accumulator is
// automatically generated, it will have already absorbed the
// blacklist. If you're hacking around, make sure you use load()!
HTMLPurifier_ConfigSchema::define(
'Attr', 'IDPrefix', '', 'string',
'String to prefix to IDs. If you have no idea what IDs your pages '.
'may use, you may opt to simply add a prefix to all user-submitted ID '.
'attributes so that they are still usable, but will not conflict with '.
'core page IDs. Example: setting the directive to \'user_\' will result in '.
'a user submitted \'foo\' to become \'user_foo\' Be sure to set '.
'%HTML.EnableAttrID to true before using '.
'this. This directive was available since 1.2.0.'
);
HTMLPurifier_ConfigSchema::define(
'Attr', 'IDPrefixLocal', '', 'string',
'Temporary prefix for IDs used in conjunction with %Attr.IDPrefix. If '.
'you need to allow multiple sets of '.
'user content on web page, you may need to have a seperate prefix that '.
'changes with each iteration. This way, seperately submitted user content '.
'displayed on the same page doesn\'t clobber each other. Ideal values '.
'are unique identifiers for the content it represents (i.e. the id of '.
'the row in the database). Be sure to add a seperator (like an underscore) '.
'at the end. Warning: this directive will not work unless %Attr.IDPrefix '.
'is set to a non-empty value! This directive was available since 1.2.0.'
);
/**
* Validates the HTML attribute ID.
* @warning Even though this is the id processor, it
* will ignore the directive Attr:IDBlacklist, since it will only
* go according to the ID accumulator. Since the accumulator is
* automatically generated, it will have already absorbed the
* blacklist. If you're hacking around, make sure you use load()!
*/
class HTMLPurifier_AttrDef_ID extends HTMLPurifier_AttrDef
{
@@ -17,7 +44,19 @@ class HTMLPurifier_AttrDef_ID extends HTMLPurifier_AttrDef
$id = trim($id); // trim it first
if ($id === '') return false;
if (isset($context->id_accumulator->ids[$id])) return false;
$prefix = $config->get('Attr', 'IDPrefix');
if ($prefix !== '') {
$prefix .= $config->get('Attr', 'IDPrefixLocal');
// prevent re-appending the prefix
if (strpos($id, $prefix) !== 0) $id = $prefix . $id;
} elseif ($config->get('Attr', 'IDPrefixLocal') !== '') {
trigger_error('%Attr.IDPrefixLocal cannot be used unless '.
'%Attr.IDPrefix is set', E_USER_WARNING);
}
$id_accumulator =& $context->get('IDAccumulator');
if (isset($id_accumulator->ids[$id])) return false;
// we purposely avoid using regex, hopefully this is faster
@@ -32,7 +71,7 @@ class HTMLPurifier_AttrDef_ID extends HTMLPurifier_AttrDef
$result = ($trim === '');
}
if ($result) $context->id_accumulator->add($id);
if ($result) $id_accumulator->add($id);
// if no change was made to the ID, return the result
// else, return the new id if stripping whitespace made it

View File

@@ -2,12 +2,17 @@
require_once 'HTMLPurifier/AttrDef.php';
// spliced from Feyd's IPv6 function (pd)
/**
* Validates an IPv4 address
* @author Feyd @ forums.devnetwork.net (public domain)
*/
class HTMLPurifier_AttrDef_IPv4 extends HTMLPurifier_AttrDef
{
// regex is public so that IPv6 can reuse it
/**
* IPv4 regex, protected so that IPv6 can reuse it
* @protected
*/
var $ip4;
function HTMLPurifier_AttrDef_IPv4() {

View File

@@ -2,11 +2,12 @@
require_once 'HTMLPurifier/AttrDef/IPv4.php';
// IPv6 by Feyd, source is in public domain
// note that this expects the brackets to be removed from IPv6 addresses
// extends from the IPv4 impl. so we can borrow its regex
/**
* Validates an IPv6 address.
* @author Feyd @ forums.devnetwork.net (public domain)
* @note This function requires brackets to have been removed from address
* in URI.
*/
class HTMLPurifier_AttrDef_IPv6 extends HTMLPurifier_AttrDef_IPv4
{

View File

@@ -2,16 +2,42 @@
require_once 'HTMLPurifier/AttrDef.php';
// appears to be a dud class: no currently allowed CSS uses this type
// Uses this: widows, orphans, z-index, counter-increment, counter-reset
/**
* Validates an integer.
* @note While this class was modeled off the CSS definition, no currently
* allowed CSS uses this type. The properties that do are: widows,
* orphans, z-index, counter-increment, counter-reset. Some of the
* HTML attributes, however, find use for a non-negative version of this.
*/
class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef
{
var $non_negative = false;
/**
* Bool indicating whether or not negative values are allowed
*/
var $negative = true;
function HTMLPurifier_AttrDef_Integer($non_negative = false) {
$this->non_negative = $non_negative;
/**
* Bool indicating whether or not zero is allowed
*/
var $zero = true;
/**
* Bool indicating whether or not positive values are allowed
*/
var $positive = true;
/**
* @param $negative Bool indicating whether or not negative values are allowed
* @param $zero Bool indicating whether or not zero is allowed
* @param $positive Bool indicating whether or not positive values are allowed
*/
function HTMLPurifier_AttrDef_Integer(
$negative = true, $zero = true, $positive = true
) {
$this->negative = $negative;
$this->zero = $zero;
$this->positive = $positive;
}
function validate($integer, $config, &$context) {
@@ -19,15 +45,27 @@ class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef
$integer = $this->parseCDATA($integer);
if ($integer === '') return false;
if ( !$this->non_negative && $integer[0] === '-' ) {
// we could possibly simply typecast it to integer, but there are
// certain fringe cases that must not return an integer.
// clip leading sign
if ( $this->negative && $integer[0] === '-' ) {
$digits = substr($integer, 1);
} elseif( $integer[0] === '+' ) {
$digits = $integer = substr($integer, 1);
if ($digits === '0') $integer = '0'; // rm minus sign for zero
} elseif( $this->positive && $integer[0] === '+' ) {
$digits = $integer = substr($integer, 1); // rm unnecessary plus
} else {
$digits = $integer;
}
// test if it's numeric
if (!ctype_digit($digits)) return false;
// perform scope tests
if (!$this->zero && $integer == 0) return false;
if (!$this->positive && $integer > 0) return false;
if (!$this->negative && $integer < 0) return false;
return $integer;
}

View File

@@ -2,8 +2,10 @@
require_once 'HTMLPurifier/AttrDef.php';
// built according to RFC 3066, which obsoleted RFC 1766
/**
* Validates the HTML attribute lang, effectively a language code.
* @note Built according to RFC 3066, which obsoleted RFC 1766
*/
class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
{
@@ -47,7 +49,7 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
if ($length == 0 || $length == 1 || $length > 8 || !ctype_alnum($subtags[1])) {
return $new_string;
}
if (!ctype_lower($subtags[1])) $subtags[1] = strotolower($subtags[1]);
if (!ctype_lower($subtags[1])) $subtags[1] = strtolower($subtags[1]);
$new_string .= '-' . $subtags[1];
if ($num_subtags == 2) return $new_string;
@@ -59,7 +61,7 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
return $new_string;
}
if (!ctype_lower($subtags[$i])) {
$subtags[$i] = strotolower($subtags[$i]);
$subtags[$i] = strtolower($subtags[$i]);
}
$new_string .= '-' . $subtags[$i];
}

View File

@@ -0,0 +1,78 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates shorthand CSS property list-style.
* @note This currently does not support list-style-image, as that functionality
* is not implemented yet elsewhere.
*/
class HTMLPurifier_AttrDef_ListStyle extends HTMLPurifier_AttrDef
{
/**
* Local copy of component validators.
* @note See HTMLPurifier_AttrDef_Font::$info for a similar impl.
*/
var $info;
function HTMLPurifier_AttrDef_ListStyle($config) {
$def = $config->getCSSDefinition();
$this->info['list-style-type'] = $def->info['list-style-type'];
$this->info['list-style-position'] = $def->info['list-style-position'];
}
function validate($string, $config, &$context) {
// regular pre-processing
$string = $this->parseCDATA($string);
if ($string === '') return false;
$bits = explode(' ', strtolower($string)); // bits to process
$caught_type = false;
$caught_position = false;
$caught_none = false; // as in keyword none, which is in all of them
$ret = '';
foreach ($bits as $bit) {
if ($caught_none && ($caught_type || $caught_position)) break;
if ($caught_type && $caught_position) break;
if ($bit === '') continue;
if ($bit === 'none') {
if ($caught_none) continue;
$caught_none = true;
$ret .= 'none ';
continue;
}
// if we add anymore, roll it into a loop
$r = $this->info['list-style-type']->validate($bit, $config, $context);
if ($r !== false) {
if ($caught_type) continue;
$caught_type = true;
$ret .= $r . ' ';
continue;
}
$r = $this->info['list-style-position']->validate($bit, $config, $context);
if ($r !== false) {
if ($caught_position) continue;
$caught_position = true;
$ret .= $r . ' ';
continue;
}
}
$ret = rtrim($ret);
return $ret ? $ret : false;
}
}
?>

View File

@@ -3,6 +3,12 @@
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Length.php';
/**
* Validates a MultiLength as defined by the HTML spec.
*
* A multilength is either a integer (pixel count), a percentage, or
* a relative number.
*/
class HTMLPurifier_AttrDef_MultiLength extends HTMLPurifier_AttrDef_Length
{

View File

@@ -2,12 +2,34 @@
require_once 'HTMLPurifier/AttrDef.php';
/**
* Framework class for strings that involve multiple values.
*
* Certain CSS properties such as border-width and margin allow multiple
* lengths to be specified. This class can take a vanilla border-width
* definition and multiply it, usually into a max of four.
*
* @note Even though the CSS specification isn't clear about it, inherit
* can only be used alone: it will never manifest as part of a multi
* shorthand declaration. Thus, this class does not allow inherit.
*/
class HTMLPurifier_AttrDef_Multiple extends HTMLPurifier_AttrDef
{
/**
* Instance of component definition to defer validation to.
*/
var $single;
/**
* Max number of values allowed.
*/
var $max;
/**
* @param $single HTMLPurifier_AttrDef to multiply
* @param $max Max number of values allowed (usually four)
*/
function HTMLPurifier_AttrDef_Multiple($single, $max = 4) {
$this->single = $single;
$this->max = $max;

View File

@@ -1,10 +1,19 @@
<?php
/**
* Validates a number as defined by the CSS spec.
*/
class HTMLPurifier_AttrDef_Number extends HTMLPurifier_AttrDef
{
/**
* Bool indicating whether or not only positive values allowed.
*/
var $non_negative = false;
/**
* @param $non_negative Bool indicating whether negatives are forbidden
*/
function HTMLPurifier_AttrDef_Number($non_negative = false) {
$this->non_negative = $non_negative;
}

View File

@@ -1,23 +0,0 @@
<?php
require_once 'HTMLPurifier/AttrDef.php';
// for col and row spans, essentially, a positive integer
class HTMLPurifier_AttrDef_NumberSpan extends HTMLPurifier_AttrDef
{
function validate($string, $config, &$context) {
$string = trim($string);
if ($string === '') return false;
if ($string === '1') return false; // this is the default value
if (!is_numeric($string)) return false;
$int = (int) $string;
if ($int <= 0) return false;
return (string) $int;
}
}
?>

View File

@@ -3,11 +3,21 @@
require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Number.php';
/**
* Validates a Percentage as defined by the HTML spec.
* @note This also allows integer pixel values.
*/
class HTMLPurifier_AttrDef_Percentage extends HTMLPurifier_AttrDef
{
/**
* Instance of HTMLPurifier_AttrDef_Number to defer pixel validation
*/
var $number_def;
/**
* @param Bool indicating whether to forbid negative values
*/
function HTMLPurifier_AttrDef_Percentage($non_negative = false) {
$this->number_def = new HTMLPurifier_AttrDef_Number($non_negative);
}

View File

@@ -2,6 +2,9 @@
require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates an integer representation of pixels according to the HTML spec.
*/
class HTMLPurifier_AttrDef_Pixels extends HTMLPurifier_AttrDef
{

View File

@@ -2,6 +2,9 @@
require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates arbitrary text according to the HTML spec.
*/
class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef
{

View File

@@ -2,9 +2,18 @@
require_once 'HTMLPurifier/AttrDef.php';
/**
* Validates the value for the CSS property text-decoration
* @note This class could be generalized into a version that acts sort of
* like Enum except you can compound the allowed values.
*/
class HTMLPurifier_AttrDef_TextDecoration extends HTMLPurifier_AttrDef
{
/**
* Lookup table of allowed values.
* @protected
*/
var $allowed_values = array(
'line-through' => true,
'overline' => true,

View File

@@ -4,20 +4,97 @@ require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/URIScheme.php';
require_once 'HTMLPurifier/URISchemeRegistry.php';
require_once 'HTMLPurifier/AttrDef/Host.php';
require_once 'HTMLPurifier/PercentEncoder.php';
HTMLPurifier_ConfigDef::define(
'URI', 'DefaultScheme', 'http',
HTMLPurifier_ConfigSchema::define(
'URI', 'DefaultScheme', 'http', 'string',
'Defines through what scheme the output will be served, in order to '.
'select the proper object validator when no scheme information is present.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'Host', null, 'string/null',
'Defines the domain name of the server, so we can determine whether or '.
'an absolute URI is from your website or not. Not strictly necessary, '.
'as users should be using relative URIs to reference resources on your '.
'website. It will, however, let you use absolute URIs to link to '.
'subdomains of the domain you post here: i.e. example.com will allow '.
'sub.example.com. However, higher up domains will still be excluded: '.
'if you set %URI.Host to sub.example.com, example.com will be blocked. '.
'This directive has been available since 1.2.0.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'DisableExternal', false, 'bool',
'Disables links to external websites. This is a highly effective '.
'anti-spam and anti-pagerank-leech measure, but comes at a hefty price: no'.
'links or images outside of your domain will be allowed. Non-linkified '.
'URIs will still be preserved. If you want to be able to link to '.
'subdomains or use absolute URIs, specify %URI.Host for your website. '.
'This directive has been available since 1.2.0.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'DisableExternalResources', false, 'bool',
'Disables the embedding of external resources, preventing users from '.
'embedding things like images from other hosts. This prevents '.
'access tracking (good for email viewers), bandwidth leeching, '.
'cross-site request forging, goatse.cx posting, and '.
'other nasties, but also results in '.
'a loss of end-user functionality (they can\'t directly post a pic '.
'they posted from Flickr anymore). Use it if you don\'t have a '.
'robust user-content moderation team. This directive has been '.
'available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'DisableResources', false, 'bool',
'Disables embedding resources, essentially meaning no pictures. You can '.
'still link to them though. See %URI.DisableExternalResources for why '.
'this might be a good idea. This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'Munge', null, 'string/null',
'Munges all browsable (usually http, https and ftp) URI\'s into some URL '.
'redirection service. Pass this directive a URI, with %s inserted where '.
'the url-encoded original URI should be inserted (sample: '.
'<code>http://www.google.com/url?q=%s</code>). '.
'This prevents PageRank leaks, while being as transparent as possible '.
'to users (you may also want to add some client side JavaScript to '.
'override the text in the statusbar). Warning: many security experts '.
'believe that this form of protection does not deter spam-bots. '.
'You can also use this directive to redirect users to a splash page '.
'telling them they are leaving your website. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'URI', 'HostBlacklist', array(), 'list',
'List of strings that are forbidden in the host of any URI. Use it to '.
'kill domain names of spam, etc. Note that it will catch anything in '.
'the domain, so <tt>moo.com</tt> will catch <tt>moo.com.example.com</tt>. '.
'This directive has been available since 1.3.0.'
);
/**
* Validates a URI as defined by RFC 3986.
* @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
*/
class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
{
var $host;
var $PercentEncoder;
var $embeds_resource;
function HTMLPurifier_AttrDef_URI() {
/**
* @param $embeds_resource_resource Does the URI here result in an extra HTTP request?
*/
function HTMLPurifier_AttrDef_URI($embeds_resource = false) {
$this->host = new HTMLPurifier_AttrDef_Host();
$this->PercentEncoder = new HTMLPurifier_PercentEncoder();
$this->embeds_resource = (bool) $embeds_resource;
}
function validate($uri, $config, &$context) {
@@ -28,17 +105,20 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
// parse as CDATA
$uri = $this->parseCDATA($uri);
// fix up percent-encoding
$uri = $this->PercentEncoder->normalize($uri);
// while it would be nice to use parse_url(), that's specifically
// for HTTP and thus won't work for our generic URI parsing
// according to the RFC... (but this cuts corners, i.e. non-validating)
$r_URI = '!^'.
'(([^:/?#<>]+):)?'. // 2. Scheme
'(//([^/?#<>]*))?'. // 4. Authority
'([^?#<>]*)'. // 5. Path
'(\?([^#<>]*))?'. // 7. Query
'(#([^<>]*))?'. // 8. Fragment
'$!';
$r_URI = '!'.
'(([^:/?#<>\'"]+):)?'. // 2. Scheme
'(//([^/?#<>\'"]*))?'. // 4. Authority
'([^?#<>\'"]*)'. // 5. Path
'(\?([^#<>\'"]*))?'. // 7. Query
'(#([^<>\'"]*))?'. // 8. Fragment
'!';
$matches = array();
$result = preg_match($r_URI, $uri, $matches);
@@ -59,18 +139,38 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
// no need to validate the scheme's fmt since we do that when we
// retrieve the specific scheme object from the registry
$scheme = ctype_lower($scheme) ? $scheme : strtolower($scheme);
$scheme_obj =& $registry->getScheme($scheme, $config);
$scheme_obj =& $registry->getScheme($scheme, $config, $context);
if (!$scheme_obj) return false; // invalid scheme, clean it out
} else {
$scheme_obj =& $registry->getScheme(
$config->get('URI', 'DefaultScheme'), $config
$config->get('URI', 'DefaultScheme'), $config, $context
);
}
// the URI we're processing embeds_resource a resource in the page, but the URI
// it references cannot be located
if ($this->embeds_resource && !$scheme_obj->browsable) {
return false;
}
if ($authority !== null) {
// remove URI if it's absolute and we disabled externals or
// if it's absolute and embedded and we disabled external resources
unset($our_host);
if (
$config->get('URI', 'DisableExternal') ||
(
$config->get('URI', 'DisableExternalResources') &&
$this->embeds_resource
)
) {
$our_host = $config->get('URI', 'Host');
if ($our_host === null) return false;
}
$HEXDIG = '[A-Fa-f0-9]';
$unreserved = 'A-Za-z0-9-._~'; // make sure you wrap with []
$sub_delims = '!$&\'()'; // needs []
@@ -93,6 +193,19 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
$host = $this->host->validate($host, $config, $context);
if ($host === false) $host = null;
if ($this->checkBlacklist($host, $config, $context)) return false;
// more lenient absolute checking
if (isset($our_host)) {
$host_parts = array_reverse(explode('.', $host));
// could be cached
$our_host_parts = array_reverse(explode('.', $our_host));
foreach ($our_host_parts as $i => $discard) {
if (!isset($host_parts[$i])) return false;
if ($host_parts[$i] != $our_host_parts[$i]) return false;
}
}
// userinfo and host are validated within the regexp
} else {
@@ -116,7 +229,7 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
// note that $fragment is omitted
list($userinfo, $host, $port, $path, $query) =
$scheme_obj->validateComponents(
$userinfo, $host, $port, $path, $query, $config
$userinfo, $host, $port, $path, $query, $config, $context
);
@@ -137,10 +250,37 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
if ($query !== null) $result .= "?$query";
if ($fragment !== null) $result .= "#$fragment";
// munge if necessary
$munge = $config->get('URI', 'Munge');
if (!empty($scheme_obj->browsable) && $munge !== null) {
if ($authority !== null) {
$result = str_replace('%s', rawurlencode($result), $munge);
}
}
return $result;
}
/**
* Checks a host against an array blacklist
* @param $host Host to check
* @param $config HTMLPurifier_Config instance
* @param $context HTMLPurifier_Context instance
* @return bool Is spam?
*/
function checkBlacklist($host, &$config, &$context) {
$blacklist = $config->get('URI', 'HostBlacklist');
if (!empty($blacklist)) {
foreach($blacklist as $blacklisted_host_fragment) {
if (strpos($host, $blacklisted_host_fragment) !== false) {
return true;
}
}
}
return false;
}
}
?>

View File

@@ -1,12 +1,32 @@
<?php
// AttrTransform = Attribute Transformation, when handling one attribute
// isn't enough
/**
* Processes an entire attribute array for corrections needing multiple values.
*
* Occasionally, a certain attribute will need to be removed and popped onto
* another value. Instead of creating a complex return syntax for
* HTMLPurifier_AttrDef, we just pass the whole attribute array to a
* specialized object and have that do the special work. That is the
* family of HTMLPurifier_AttrTransform.
*
* An attribute transformation can be assigned to run before or after
* HTMLPurifier_AttrDef validation. See HTMLPurifier_HTMLDefinition for
* more details.
*/
class HTMLPurifier_AttrTransform
{
function HTMLPurifier_AttrTransform() {}
function transform($token, $config = null) {
/**
* Abstract: makes changes to the attributes dependent on multiple values.
*
* @param $attr Assoc array of attributes, usually from
* HTMLPurifier_Token_Tag::$attr
* @param $config Mandatory HTMLPurifier_Config object.
* @param $context Mandatory HTMLPurifier_Context object
* @returns Processed attribute array.
*/
function transform($attr, $config, &$context) {
trigger_error('Cannot call abstract function', E_USER_ERROR);
}
}

View File

@@ -4,20 +4,26 @@ require_once 'HTMLPurifier/AttrTransform.php';
// this MUST be placed in post, as it assumes that any value in dir is valid
HTMLPurifier_ConfigDef::define(
'Attr', 'DefaultTextDir', 'ltr',
HTMLPurifier_ConfigSchema::define(
'Attr', 'DefaultTextDir', 'ltr', 'string',
'Defines the default text direction (ltr or rtl) of the document '.
'being parsed. This generally is the same as the value of the dir '.
'attribute in HTML, or ltr if that is not specified.'
);
HTMLPurifier_ConfigSchema::defineAllowedValues(
'Attr', 'DefaultTextDir', array( 'ltr', 'rtl' )
);
/**
* Post-trasnform that ensures that bdo tags have the dir attribute set.
*/
class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
{
function transform($attributes, $config) {
if (isset($attributes['dir'])) return $attributes;
$attributes['dir'] = $config->get('Attr', 'DefaultTextDir');
return $attributes;
function transform($attr, $config, $context) {
if (isset($attr['dir'])) return $attr;
$attr['dir'] = $config->get('Attr', 'DefaultTextDir');
return $attr;
}
}

View File

@@ -4,41 +4,44 @@ require_once 'HTMLPurifier/AttrTransform.php';
// must be called POST validation
HTMLPurifier_ConfigDef::define(
'Attr', 'DefaultInvalidImage', '',
HTMLPurifier_ConfigSchema::define(
'Attr', 'DefaultInvalidImage', '', 'string',
'This is the default image an img tag will be pointed to if it does '.
'not have a valid src attribute. In future versions, we may allow the '.
'image tag to be removed completely, but due to design issues, this is '.
'not possible right now.'
);
HTMLPurifier_ConfigDef::define(
'Attr', 'DefaultInvalidImageAlt', 'Invalid image',
HTMLPurifier_ConfigSchema::define(
'Attr', 'DefaultInvalidImageAlt', 'Invalid image', 'string',
'This is the content of the alt tag of an invalid image if the user '.
'had not previously specified an alt attribute. It has no effect when the '.
'image is valid but there was no alt attribute present.'
);
/**
* Post-transform that ensures the required attrs of img (alt and src) are set
*/
class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
{
function transform($attributes, $config) {
function transform($attr, $config, $context) {
$src = true;
if (!isset($attributes['src'])) {
$attributes['src'] = $config->get('Attr', 'DefaultInvalidImage');
if (!isset($attr['src'])) {
$attr['src'] = $config->get('Attr', 'DefaultInvalidImage');
$src = false;
}
if (!isset($attributes['alt'])) {
if (!isset($attr['alt'])) {
if ($src) {
$attributes['alt'] = basename($attributes['src']);
$attr['alt'] = basename($attr['src']);
} else {
$attributes['alt'] = $config->get('Attr', 'DefaultInvalidImageAlt');
$attr['alt'] = $config->get('Attr', 'DefaultInvalidImageAlt');
}
}
return $attributes;
return $attr;
}

View File

@@ -2,13 +2,15 @@
require_once 'HTMLPurifier/AttrTransform.php';
// this transformation may be done pre or post validation, but post is
// preferred, since invalid languages then will have been dropped.
/**
* Post-transform that copies lang's value to xml:lang (and vice-versa)
* @note Theoretically speaking, this could be a pre-transform, but putting
* post is more efficient.
*/
class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform
{
function transform($attr, $config) {
function transform($attr, $config, $context) {
$lang = isset($attr['lang']) ? $attr['lang'] : false;
$xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;

View File

@@ -2,10 +2,13 @@
require_once 'HTMLPurifier/AttrTransform.php';
/**
* Pre-transform that changes deprecated align attribute to text-align.
*/
class HTMLPurifier_AttrTransform_TextAlign
extends HTMLPurifier_AttrTransform {
function transform($attr, $config) {
function transform($attr, $config, $context) {
if (!isset($attr['align'])) return $attr;

View File

@@ -8,26 +8,26 @@ require_once 'HTMLPurifier/AttrDef/Percentage.php';
require_once 'HTMLPurifier/AttrDef/Multiple.php';
require_once 'HTMLPurifier/AttrDef/TextDecoration.php';
require_once 'HTMLPurifier/AttrDef/FontFamily.php';
require_once 'HTMLPurifier/AttrDef/Font.php';
require_once 'HTMLPurifier/AttrDef/Border.php';
require_once 'HTMLPurifier/AttrDef/ListStyle.php';
/**
* Defines allowed CSS attributes and what their values are.
* @see HTMLPurifier_HTMLDefinition
*/
class HTMLPurifier_CSSDefinition
{
/**
* Assoc array of attribute name to definition object.
*/
var $info = array();
function &instance($prototype = null) {
static $instance = null;
if ($prototype) {
$instance = $prototype;
} elseif (!$instance) {
$instance = new HTMLPurifier_CSSDefinition();
$instance->setup();
}
return $instance;
}
function HTMLPurifier_CSSDefinition() {}
function setup() {
/**
* Constructs the info array. The meat of this class.
*/
function setup($config) {
$this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
array('left', 'right', 'center', 'justify'), false);
@@ -50,15 +50,28 @@ class HTMLPurifier_CSSDefinition
array('normal', 'italic', 'oblique'), false);
$this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
array('normal', 'small-caps'), false);
$this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
array('inside', 'outside'), false);
$this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
array('disc', 'circle', 'square', 'decimal', 'lower-roman',
'upper-roman', 'lower-alpha', 'upper-alpha'), false);
$this->info['list-style'] = new HTMLPurifier_AttrDef_ListStyle($config);
$this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
array('capitalize', 'uppercase', 'lowercase', 'none'), false);
$this->info['color'] = new HTMLPurifier_AttrDef_Color();
// technically speaking, this one should get its own validator, but
// since we don't support background images, it effectively is
// equivalent to color. The only trouble is that if the author
// specifies an image and a color, they'll both end up getting dropped,
// even though we ought to implement it and just discard the image
// info. This will be fixed in a later version (see TODO) when
// better URI filtering is implemented.
$this->info['background'] =
$border_color =
$this->info['border-top-color'] =
$this->info['border-bottom-color'] =
@@ -151,6 +164,33 @@ class HTMLPurifier_CSSDefinition
array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300',
'400', '500', '600', '700', '800', '900'), false);
// MUST be called after other font properties, as it references
// a CSSDefinition object
$this->info['font'] = new HTMLPurifier_AttrDef_Font($config);
// same here
$this->info['border'] =
$this->info['border-bottom'] =
$this->info['border-top'] =
$this->info['border-left'] =
$this->info['border-right'] = new HTMLPurifier_AttrDef_Border($config);
$this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array(
'collapse', 'seperate'));
$this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array(
'top', 'bottom'));
$this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array(
'auto', 'fixed'));
$this->info['vertical-align'] = new HTMLPurifier_AttrDef_Composite(array(
new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super',
'top', 'text-top', 'middle', 'bottom', 'text-bottom')),
new HTMLPurifier_AttrDef_CSSLength(),
new HTMLPurifier_AttrDef_Percentage()
));
}
}

View File

@@ -5,15 +5,8 @@
// false = delete parent node and all children
// array(...) = replace children nodes with these
// this is the hardest one to implement. We'll use fancy regexp tricks
// right now, we only expect it to return TRUE or FALSE (it won't attempt
// to fix the tree)
// we may end up writing custom code for each HTML case
// in order to make it self correcting
HTMLPurifier_ConfigDef::define(
'Core', 'EscapeInvalidChildren', false,
HTMLPurifier_ConfigSchema::define(
'Core', 'EscapeInvalidChildren', false, 'bool',
'When true, a child is found that is not allowed in the context of the '.
'parent element will be transformed into text as if it were ASCII. When '.
'false, that element and all internal tags will be dropped, though text '.
@@ -21,202 +14,42 @@ HTMLPurifier_ConfigDef::define(
'preserving child nodes.'
);
/**
* Defines allowed child nodes and validates tokens against it.
*/
class HTMLPurifier_ChildDef
{
/**
* Type of child definition, usually right-most part of class name lowercase.
* Used occasionally in terms of context.
* @public
*/
var $type;
/**
* Bool that indicates whether or not an empty array of children is okay
*
* This is necessary for redundant checking when changes affecting
* a child node may cause a parent node to now be disallowed.
*
* @public
*/
var $allow_empty;
function validateChildren($tokens_of_children) {
/**
* Validates nodes according to definition and returns modification.
*
* @public
* @param $tokens_of_children Array of HTMLPurifier_Token
* @param $config HTMLPurifier_Config object
* @param $context HTMLPurifier_Context object
* @return bool true to leave nodes as is
* @return bool false to remove parent node
* @return array of replacement child tokens
*/
function validateChildren($tokens_of_children, $config, &$context) {
trigger_error('Call to abstract function', E_USER_ERROR);
}
}
class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef
{
var $type = 'custom';
var $allow_empty = false;
var $dtd_regex;
var $_pcre_regex;
function HTMLPurifier_ChildDef_Custom($dtd_regex) {
$this->dtd_regex = $dtd_regex;
$this->_compileRegex();
}
function _compileRegex() {
$raw = str_replace(' ', '', $this->dtd_regex);
if ($raw{0} != '(') {
$raw = "($raw)";
}
$reg = str_replace(',', ',?', $raw);
$reg = preg_replace('/([#a-zA-Z0-9_.-]+)/', '(,?\\0)', $reg);
$this->_pcre_regex = $reg;
}
function validateChildren($tokens_of_children, $config, $context) {
$list_of_children = '';
$nesting = 0; // depth into the nest
foreach ($tokens_of_children as $token) {
if (!empty($token->is_whitespace)) continue;
$is_child = ($nesting == 0); // direct
if ($token->type == 'start') {
$nesting++;
} elseif ($token->type == 'end') {
$nesting--;
}
if ($is_child) {
$list_of_children .= $token->name . ',';
}
}
$list_of_children = rtrim($list_of_children, ',');
$okay =
preg_match(
'/^'.$this->_pcre_regex.'$/',
$list_of_children
);
return (bool) $okay;
}
}
class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
{
var $elements = array();
function HTMLPurifier_ChildDef_Required($elements) {
if (is_string($elements)) {
$elements = str_replace(' ', '', $elements);
$elements = explode('|', $elements);
}
$elements = array_flip($elements);
foreach ($elements as $i => $x) $elements[$i] = true;
$this->elements = $elements;
$this->gen = new HTMLPurifier_Generator();
}
var $allow_empty = false;
var $type = 'required';
function validateChildren($tokens_of_children, $config, $context) {
// if there are no tokens, delete parent node
if (empty($tokens_of_children)) return false;
// the new set of children
$result = array();
// current depth into the nest
$nesting = 0;
// whether or not we're deleting a node
$is_deleting = false;
// whether or not parsed character data is allowed
// this controls whether or not we silently drop a tag
// or generate escaped HTML from it
$pcdata_allowed = isset($this->elements['#PCDATA']);
// a little sanity check to make sure it's not ALL whitespace
$all_whitespace = true;
// some configuration
$escape_invalid_children = $config->get('Core', 'EscapeInvalidChildren');
foreach ($tokens_of_children as $token) {
if (!empty($token->is_whitespace)) {
$result[] = $token;
continue;
}
$all_whitespace = false; // phew, we're not talking about whitespace
$is_child = ($nesting == 0);
if ($token->type == 'start') {
$nesting++;
} elseif ($token->type == 'end') {
$nesting--;
}
if ($is_child) {
$is_deleting = false;
if (!isset($this->elements[$token->name])) {
$is_deleting = true;
if ($pcdata_allowed && $token->type == 'text') {
$result[] = $token;
} elseif ($pcdata_allowed && $escape_invalid_children) {
$result[] = new HTMLPurifier_Token_Text(
$this->gen->generateFromToken($token, $config)
);
}
continue;
}
}
if (!$is_deleting || ($pcdata_allowed && $token->type == 'text')) {
$result[] = $token;
} elseif ($pcdata_allowed && $escape_invalid_children) {
$result[] =
new HTMLPurifier_Token_Text(
$this->gen->generateFromToken( $token, $config )
);
} else {
// drop silently
}
}
if (empty($result)) return false;
if ($all_whitespace) return false;
if ($tokens_of_children == $result) return true;
return $result;
}
}
// only altered behavior is that it returns an empty array
// instead of a false (to delete the node)
class HTMLPurifier_ChildDef_Optional extends HTMLPurifier_ChildDef_Required
{
var $allow_empty = true;
var $type = 'optional';
function validateChildren($tokens_of_children, $config, $context) {
$result = parent::validateChildren($tokens_of_children, $config, $context);
if ($result === false) return array();
return $result;
}
}
// placeholder
class HTMLPurifier_ChildDef_Empty extends HTMLPurifier_ChildDef
{
var $allow_empty = true;
var $type = 'empty';
function HTMLPurifier_ChildDef_Empty() {}
function validateChildren($tokens_of_children, $config, $context) {
return false;
}
}
class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef
{
var $inline;
var $block;
function HTMLPurifier_ChildDef_Chameleon($inline, $block) {
$this->inline = new HTMLPurifier_ChildDef_Optional($inline);
$this->block = new HTMLPurifier_ChildDef_Optional($block);
}
function validateChildren($tokens_of_children, $config, $context) {
switch ($context) {
case 'unknown':
case 'inline':
$result = $this->inline->validateChildren(
$tokens_of_children, $config, $context);
break;
case 'block':
$result = $this->block->validateChildren(
$tokens_of_children, $config, $context);
break;
default:
trigger_error('Invalid context', E_USER_ERROR);
return false;
}
return $result;
}
}
?>
?>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,70 @@
<?php
require_once 'HTMLPurifier/ChildDef/Required.php';
/**
* Takes the contents of blockquote when in strict and reformats for validation.
*
* From XHTML 1.0 Transitional to Strict, there is a notable change where
*/
class HTMLPurifier_ChildDef_StrictBlockquote
extends HTMLPurifier_ChildDef_Required
{
var $allow_empty = true;
var $type = 'strictblockquote';
var $init = false;
function HTMLPurifier_ChildDef_StrictBlockquote() {}
function validateChildren($tokens_of_children, $config, &$context) {
$def = $config->getHTMLDefinition();
if (!$this->init) {
// allow all inline elements
$this->elements = $def->info_flow_elements;
$this->elements['#PCDATA'] = true;
$this->init = true;
}
$result = parent::validateChildren($tokens_of_children, $config, $context);
if ($result === false) return array();
if ($result === true) $result = $tokens_of_children;
$block_wrap_start = new HTMLPurifier_Token_Start($def->info_block_wrapper);
$block_wrap_end = new HTMLPurifier_Token_End( $def->info_block_wrapper);
$is_inline = false;
$depth = 0;
$ret = array();
// assuming that there are no comment tokens
foreach ($result as $i => $token) {
$token = $result[$i];
// ifs are nested for readability
if (!$is_inline) {
if (!$depth) {
if (($token->type == 'text') ||
($def->info[$token->name]->type == 'inline')) {
$is_inline = true;
$ret[] = $block_wrap_start;
}
}
} else {
if (!$depth) {
// starting tokens have been inline text / empty
if ($token->type == 'start' || $token->type == 'empty') {
if ($def->info[$token->name]->type == 'block') {
// ended
$ret[] = $block_wrap_end;
$is_inline = false;
}
}
}
}
$ret[] = $token;
if ($token->type == 'start') $depth++;
if ($token->type == 'end') $depth--;
}
if ($is_inline) $ret[] = $block_wrap_end;
return $ret;
}
}
?>

View File

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

View File

@@ -1,39 +1,181 @@
<?php
// subclass this to add custom settings
/**
* Configuration object that triggers customizable behavior.
*
* @warning This class is strongly defined: that means that the class
* will fail if an undefined directive is retrieved or set.
*
* @note Many classes that could (although many times don't) use the
* configuration object make it a mandatory parameter. This is
* because a configuration object should always be forwarded,
* otherwise, you run the risk of missing a parameter and then
* being stumped when a configuration directive doesn't work.
*/
class HTMLPurifier_Config
{
/**
* Two-level associative array of configuration directives
*/
var $conf;
/**
* Reference HTMLPurifier_ConfigSchema for value checking
*/
var $def;
/**
* Cached instance of HTMLPurifier_HTMLDefinition
*/
var $html_definition;
/**
* Cached instance of HTMLPurifier_CSSDefinition
*/
var $css_definition;
/**
* @param $definition HTMLPurifier_ConfigSchema that defines what directives
* are allowed.
*/
function HTMLPurifier_Config(&$definition) {
$this->conf = $definition->info; // set up the defaults
$this->conf = $definition->defaults; // set up, copy in defaults
$this->def = $definition; // keep a copy around for checking
}
/**
* Convenience constructor that creates a config object based on a mixed var
* @param mixed $config Variable that defines the state of the config
* object. Can be: a HTMLPurifier_Config() object or
* an array of directives based on loadArray().
* @return Configured HTMLPurifier_Config object
*/
function create($config) {
if (is_a($config, 'HTMLPurifier_Config')) return $config;
$ret = HTMLPurifier_Config::createDefault();
if (is_array($config)) $ret->loadArray($config);
return $ret;
}
/**
* Convenience constructor that creates a default configuration object.
* @return Default HTMLPurifier_Config object.
*/
function createDefault() {
$definition =& HTMLPurifier_ConfigDef::instance();
$definition =& HTMLPurifier_ConfigSchema::instance();
$config = new HTMLPurifier_Config($definition);
return $config;
}
/**
* Retreives a value from the configuration.
* @param $namespace String namespace
* @param $key String key
*/
function get($namespace, $key) {
if (!isset($this->conf[$namespace][$key])) {
if (!isset($this->def->info[$namespace][$key])) {
trigger_error('Cannot retrieve value of undefined directive',
E_USER_ERROR);
E_USER_WARNING);
return;
}
return $this->conf[$namespace][$key];
}
/**
* Retreives an array of directives to values from a given namespace
* @param $namespace String namespace
*/
function getBatch($namespace) {
if (!isset($this->def->info[$namespace])) {
trigger_error('Cannot retrieve undefined namespace',
E_USER_WARNING);
return;
}
return $this->conf[$namespace];
}
/**
* Sets a value to configuration.
* @param $namespace String namespace
* @param $key String key
* @param $value Mixed value
*/
function set($namespace, $key, $value) {
if (!isset($this->conf[$namespace][$key])) {
if (!isset($this->def->info[$namespace][$key])) {
trigger_error('Cannot set undefined directive to value',
E_USER_ERROR);
E_USER_WARNING);
return;
}
$value = $this->def->validate(
$value,
$this->def->info[$namespace][$key]->type,
$this->def->info[$namespace][$key]->allow_null
);
if (is_string($value)) {
// resolve value alias if defined
if (isset($this->def->info[$namespace][$key]->aliases[$value])) {
$value = $this->def->info[$namespace][$key]->aliases[$value];
}
if ($this->def->info[$namespace][$key]->allowed !== true) {
// check to see if the value is allowed
if (!isset($this->def->info[$namespace][$key]->allowed[$value])) {
trigger_error('Value not supported', E_USER_WARNING);
return;
}
}
}
if ($this->def->isError($value)) {
trigger_error('Value is of invalid type', E_USER_WARNING);
return;
}
$this->conf[$namespace][$key] = $value;
}
/**
* Retrieves a copy of the HTML definition.
*/
function getHTMLDefinition() {
if ($this->html_definition === null) {
$this->html_definition = new HTMLPurifier_HTMLDefinition();
$this->html_definition->setup($this);
}
return $this->html_definition;
}
/**
* Retrieves a copy of the CSS definition
*/
function getCSSDefinition() {
if ($this->css_definition === null) {
$this->css_definition = new HTMLPurifier_CSSDefinition();
$this->css_definition->setup($this);
}
return $this->css_definition;
}
/**
* Loads configuration values from an array with the following structure:
* Namespace.Directive => Value
* @param $config_array Configuration associative array
*/
function loadArray($config_array) {
foreach ($config_array as $key => $value) {
$key = str_replace('_', '.', $key);
if (strpos($key, '.') !== false) {
// condensed form
list($namespace, $directive) = explode('.', $key);
$this->set($namespace, $directive, $value);
} else {
$namespace = $key;
$namespace_values = $value;
foreach ($namespace_values as $directive => $value) {
$this->set($namespace, $directive, $value);
}
}
}
}
}
?>

View File

@@ -1,50 +0,0 @@
<?php
class HTMLPurifier_ConfigDef {
var $info = array();
function initialize() {
$this->defineNamespace('Core', 'Core features that are always available.');
$this->defineNamespace('Attr', 'Features regarding attribute validation.');
$this->defineNamespace('URI', 'Features regarding Uniform Resource Identifiers.');
}
function &instance($prototype = null) {
static $instance;
if ($prototype !== null) {
$instance = $prototype;
} elseif ($instance === null || $prototype === true) {
$instance = new HTMLPurifier_ConfigDef();
$instance->initialize();
}
return $instance;
}
function define($namespace, $name, $default, $description) {
$def =& HTMLPurifier_ConfigDef::instance();
if (!isset($def->info[$namespace])) {
trigger_error('Cannot define directive for undefined namespace',
E_USER_ERROR);
return;
}
if (isset($def->info[$namespace][$name])) {
// this behavior is at risk of change
trigger_error('Cannot redefine directive', E_USER_ERROR);
return;
}
$def->info[$namespace][$name] = $default;
}
function defineNamespace($namespace, $description) {
$def =& HTMLPurifier_ConfigDef::instance();
if (isset($def->info[$namespace])) {
trigger_error('Cannot redefine namespace', E_USER_ERROR);
return;
}
$def->info[$namespace] = array();
}
}
?>

View File

@@ -0,0 +1,383 @@
<?php
require_once 'HTMLPurifier/Error.php';
/**
* Configuration definition, defines directives and their defaults.
* @todo The ability to define things multiple times is confusing and should
* be factored out to its own function named registerDependency() or
* addNote(), where only the namespace.name and an extra descriptions
* documenting the nature of the dependency are needed. Since it's
* possible that the dependency is registered before the configuration
* is defined, deferring it to some sort of cache until it actually
* gets defined would be wise, keeping it opaque until it does get
* defined. We could add a finalize() method which would cause it to
* error out if we get a dangling dependency. It's difficult, however,
* to know whether or not it's a dependency, or a codependency, that is
* neither of them fully depends on it. Where does the configuration go
* then? This could be partially resolved by allowing blanket definitions
* and then splitting them up into finer-grained versions, however, there
* might be implementation difficulties in ini files regarding order of
* execution.
*/
class HTMLPurifier_ConfigSchema {
/**
* Defaults of the directives and namespaces.
* @note This shares the exact same structure as HTMLPurifier_Config::$conf
*/
var $defaults = array();
/**
* Definition of the directives.
*/
var $info = array();
/**
* Definition of namespaces.
*/
var $info_namespace = array();
/**
* Lookup table of allowed types.
*/
var $types = array(
'string' => 'String',
'istring' => 'Case-insensitive string',
'int' => 'Integer',
'float' => 'Float',
'bool' => 'Boolean',
'lookup' => 'Lookup array',
'list' => 'Array list',
'hash' => 'Associative array',
'mixed' => 'Mixed'
);
/**
* Initializes the default namespaces.
*/
function initialize() {
$this->defineNamespace('Core', 'Core features that are always available.');
$this->defineNamespace('Attr', 'Features regarding attribute validation.');
$this->defineNamespace('URI', 'Features regarding Uniform Resource Identifiers.');
$this->defineNamespace('HTML', 'Configuration regarding allowed HTML.');
$this->defineNamespace('CSS', 'Configuration regarding allowed CSS.');
$this->defineNamespace('Test', 'Developer testing configuration for our unit tests.');
}
/**
* Retrieves an instance of the application-wide configuration definition.
*/
function &instance($prototype = null) {
static $instance;
if ($prototype !== null) {
$instance = $prototype;
} elseif ($instance === null || $prototype === true) {
$instance = new HTMLPurifier_ConfigSchema();
$instance->initialize();
}
return $instance;
}
/**
* Defines a directive for configuration
* @warning Will fail of directive's namespace is defined
* @param $namespace Namespace the directive is in
* @param $name Key of directive
* @param $default Default value of directive
* @param $type Allowed type of the directive. See
* HTMLPurifier_DirectiveDef::$type for allowed values
* @param $description Description of directive for documentation
*/
function define(
$namespace, $name, $default, $type,
$description
) {
$def =& HTMLPurifier_ConfigSchema::instance();
if (!isset($def->info[$namespace])) {
trigger_error('Cannot define directive for undefined namespace',
E_USER_ERROR);
return;
}
if (!ctype_alnum($name)) {
trigger_error('Directive name must be alphanumeric',
E_USER_ERROR);
return;
}
if (isset($def->info[$namespace][$name])) {
if (
$def->info[$namespace][$name]->type !== $type ||
$def->defaults[$namespace][$name] !== $default
) {
trigger_error('Inconsistent default or type, cannot redefine');
return;
}
} else {
// process modifiers
$type_values = explode('/', $type, 2);
$type = $type_values[0];
$modifier = isset($type_values[1]) ? $type_values[1] : false;
$allow_null = ($modifier === 'null');
if (!isset($def->types[$type])) {
trigger_error('Invalid type for configuration directive',
E_USER_ERROR);
return;
}
$default = $def->validate($default, $type, $allow_null);
if ($def->isError($default)) {
trigger_error('Default value does not match directive type',
E_USER_ERROR);
return;
}
$def->info[$namespace][$name] =
new HTMLPurifier_ConfigEntity_Directive();
$def->info[$namespace][$name]->type = $type;
$def->info[$namespace][$name]->allow_null = $allow_null;
$def->defaults[$namespace][$name] = $default;
}
$backtrace = debug_backtrace();
$file = $def->mungeFilename($backtrace[0]['file']);
$line = $backtrace[0]['line'];
$def->info[$namespace][$name]->addDescription($file,$line,$description);
}
/**
* Defines a namespace for directives to be put into.
* @param $namespace Namespace's name
* @param $description Description of the namespace
*/
function defineNamespace($namespace, $description) {
$def =& HTMLPurifier_ConfigSchema::instance();
if (isset($def->info[$namespace])) {
trigger_error('Cannot redefine namespace', E_USER_ERROR);
return;
}
if (!ctype_alnum($namespace)) {
trigger_error('Namespace name must be alphanumeric',
E_USER_ERROR);
return;
}
$def->info[$namespace] = array();
$def->info_namespace[$namespace] = new HTMLPurifier_ConfigEntity_Namespace();
$def->info_namespace[$namespace]->description = $description;
$def->defaults[$namespace] = array();
}
/**
* Defines a directive value alias.
*
* Directive value aliases are convenient for developers because it lets
* them set a directive to several values and get the same result.
* @param $namespace Directive's namespace
* @param $name Name of Directive
* @param $alias Name of aliased value
* @param $real Value aliased value will be converted into
*/
function defineValueAliases($namespace, $name, $aliases) {
$def =& HTMLPurifier_ConfigSchema::instance();
if (!isset($def->info[$namespace][$name])) {
trigger_error('Cannot set value alias for non-existant directive',
E_USER_ERROR);
return;
}
foreach ($aliases as $alias => $real) {
if (!$def->info[$namespace][$name] !== true &&
!isset($def->info[$namespace][$name]->allowed[$real])
) {
trigger_error('Cannot define alias to value that is not allowed',
E_USER_ERROR);
return;
}
if (isset($def->info[$namespace][$name]->allowed[$alias])) {
trigger_error('Cannot define alias over allowed value',
E_USER_ERROR);
return;
}
$def->info[$namespace][$name]->aliases[$alias] = $real;
}
}
/**
* Defines a set of allowed values for a directive.
* @param $namespace Namespace of directive
* @param $name Name of directive
* @param $allowed_values Arraylist of allowed values
*/
function defineAllowedValues($namespace, $name, $allowed_values) {
$def =& HTMLPurifier_ConfigSchema::instance();
if (!isset($def->info[$namespace][$name])) {
trigger_error('Cannot define allowed values for undefined directive',
E_USER_ERROR);
return;
}
if ($def->info[$namespace][$name]->allowed === true) {
$def->info[$namespace][$name]->allowed = array();
}
foreach ($allowed_values as $value) {
$def->info[$namespace][$name]->allowed[$value] = true;
}
}
/**
* Validate a variable according to type. Return null if invalid.
*/
function validate($var, $type, $allow_null = false) {
if (!isset($this->types[$type])) {
trigger_error('Invalid type', E_USER_ERROR);
return;
}
if ($allow_null && $var === null) return null;
switch ($type) {
case 'mixed':
return $var;
case 'istring':
case 'string':
if (!is_string($var)) break;
if ($type === 'istring') $var = strtolower($var);
return $var;
case 'int':
if (is_string($var) && ctype_digit($var)) $var = (int) $var;
elseif (!is_int($var)) break;
return $var;
case 'float':
if (is_string($var) && is_numeric($var)) $var = (float) $var;
elseif (!is_float($var)) break;
return $var;
case 'bool':
if (is_int($var) && ($var === 0 || $var === 1)) {
$var = (bool) $var;
} elseif (is_string($var)) {
if ($var == 'on' || $var == 'true' || $var == '1') {
$var = true;
} elseif ($var == 'off' || $var == 'false' || $var == '0') {
$var = false;
} else {
break;
}
} elseif (!is_bool($var)) break;
return $var;
case 'list':
case 'hash':
case 'lookup':
if (is_string($var)) {
// simplistic string to array method that only works
// for simple lists of tag names or alphanumeric characters
$var = explode(',',$var);
// remove spaces
foreach ($var as $i => $j) $var[$i] = trim($j);
}
if (!is_array($var)) break;
$keys = array_keys($var);
if ($keys === array_keys($keys)) {
if ($type == 'list') return $var;
elseif ($type == 'lookup') {
$new = array();
foreach ($var as $key) {
$new[$key] = true;
}
return $new;
} else break;
}
if ($type === 'lookup') {
foreach ($var as $key => $value) {
$var[$key] = true;
}
}
return $var;
}
$error = new HTMLPurifier_Error();
return $error;
}
/**
* Takes an absolute path and munges it into a more manageable relative path
*/
function mungeFilename($filename) {
$offset = strrpos($filename, 'HTMLPurifier');
$filename = substr($filename, $offset);
$filename = str_replace('\\', '/', $filename);
return $filename;
}
/**
* Checks if var is an HTMLPurifier_Error object
*/
function isError($var) {
if (!is_object($var)) return false;
if (!is_a($var, 'HTMLPurifier_Error')) return false;
return true;
}
}
/**
* Base class for configuration entity
*/
class HTMLPurifier_ConfigEntity {}
/**
* Structure object describing of a namespace
*/
class HTMLPurifier_ConfigEntity_Namespace extends HTMLPurifier_ConfigEntity {
/**
* String description of what kinds of directives go in this namespace.
*/
var $description;
}
/**
* Structure object containing definition of a directive.
* @note This structure does not contain default values
*/
class HTMLPurifier_ConfigEntity_Directive extends HTMLPurifier_ConfigEntity
{
/**
* Hash of value aliases, i.e. values that are equivalent.
*/
var $aliases = array();
/**
* Lookup table of allowed values of the element, bool true if all allowed.
*/
var $allowed = true;
/**
* Allowed type of the directive. Values are:
* - string
* - istring (case insensitive string)
* - int
* - float
* - bool
* - lookup (array of value => true)
* - list (regular numbered index array)
* - hash (array of key => value)
* - mixed (anything goes)
*/
var $type = 'mixed';
/**
* Is null allowed? Has no affect for mixed type.
* @bool
*/
var $allow_null = false;
/**
* Plaintext descriptions of the configuration entity is. Organized by
* file and line number, so multiple descriptions are allowed.
*/
var $descriptions = array();
/**
* Adds a description to the array
*/
function addDescription($file, $line, $description) {
if (!isset($this->descriptions[$file])) $this->descriptions[$file] = array();
$this->descriptions[$file][$line] = $description;
}
}
?>

View File

@@ -0,0 +1,76 @@
<?php
/**
* Registry object that contains information about the current context.
*/
class HTMLPurifier_Context
{
/**
* Private array that stores the references.
* @private
*/
var $_storage = array();
/**
* Registers a variable into the context.
* @param $name String name
* @param $ref Variable to be registered
*/
function register($name, &$ref) {
if (isset($this->_storage[$name])) {
trigger_error('Name collision, cannot re-register',
E_USER_ERROR);
return;
}
$this->_storage[$name] =& $ref;
}
/**
* Retrieves a variable reference from the context.
* @param $name String name
*/
function &get($name) {
if (!isset($this->_storage[$name])) {
trigger_error('Attempted to retrieve non-existent variable',
E_USER_ERROR);
$var = null; // so we can return by reference
return $var;
}
return $this->_storage[$name];
}
/**
* Destorys a variable in the context.
* @param $name String name
*/
function destroy($name) {
if (!isset($this->_storage[$name])) {
trigger_error('Attempted to destroy non-existent variable',
E_USER_ERROR);
return;
}
unset($this->_storage[$name]);
}
/**
* Checks whether or not the variable exists.
* @param $name String name
*/
function exists($name) {
return isset($this->_storage[$name]);
}
/**
* Loads a series of variables from an associative array
* @param $context_array Assoc array of variables to load
*/
function loadArray(&$context_array) {
foreach ($context_array as $key => $discard) {
$this->register($key, $context_array[$key]);
}
}
}
?>

View File

@@ -0,0 +1,324 @@
<?php
require_once 'HTMLPurifier/EntityLookup.php';
HTMLPurifier_ConfigSchema::define(
'Core', 'Encoding', 'utf-8', 'istring',
'If for some reason you are unable to convert all webpages to UTF-8, '.
'you can use this directive as a stop-gap compatibility change to '.
'let HTMLPurifier deal with non UTF-8 input. This technique has '.
'notable deficiencies: absolutely no characters outside of the selected '.
'character encoding will be preserved, not even the ones that have '.
'been ampersand escaped (this is due to a UTF-8 specific <em>feature</em> '.
'that automatically resolves all entities), making it pretty useless '.
'for anything except the most I18N-blind applications. This directive '.
'only accepts ISO-8859-1 if iconv is not enabled.'
);
if ( !function_exists('iconv') ) {
// only encodings with native PHP support
HTMLPurifier_ConfigSchema::defineAllowedValues(
'Core', 'Encoding', array(
'utf-8',
'iso-8859-1'
)
);
HTMLPurifier_ConfigSchema::defineValueAliases(
'Core', 'Encoding', array(
'iso8859-1' => 'iso-8859-1'
)
);
}
HTMLPurifier_ConfigSchema::define(
'Test', 'ForceNoIconv', false, 'bool',
'When set to true, HTMLPurifier_Encoder will act as if iconv does not '.
'exist and use only pure PHP implementations.'
);
/**
* A UTF-8 specific character encoder that handles cleaning and transforming.
*/
class HTMLPurifier_Encoder
{
/**
* Cleans a UTF-8 string for well-formedness and SGML validity
*
* It will parse according to UTF-8 and return a valid UTF8 string, with
* non-SGML codepoints excluded.
*
* @note Just for reference, the non-SGML code points are 0 to 31 and
* 127 to 159, inclusive. However, we allow code points 9, 10
* and 13, which are the tab, line feed and carriage return
* respectively. 128 and above the code points map to multibyte
* UTF-8 representations.
*
* @note Fallback code adapted from utf8ToUnicode by Henri Sivonen and
* hsivonen@iki.fi at <http://iki.fi/hsivonen/php-utf8/> under the
* LGPL license. Notes on what changed are inside, but in general,
* the original code transformed UTF-8 text into an array of integer
* Unicode codepoints. Understandably, transforming that back to
* a string would be somewhat expensive, so the function was modded to
* directly operate on the string. However, this discourages code
* reuse, and the logic enumerated here would be useful for any
* function that needs to be able to understand UTF-8 characters.
* As of right now, only smart lossless character encoding converters
* would need that, and I'm probably not going to implement them.
* Once again, PHP 6 should solve all our problems.
*/
function cleanUTF8($str, $force_php = false) {
static $non_sgml_chars = array();
if (empty($non_sgml_chars)) {
for ($i = 0; $i <= 31; $i++) {
// non-SGML ASCII chars
// save \r, \t and \n
if ($i == 9 || $i == 13 || $i == 10) continue;
$non_sgml_chars[chr($i)] = '';
}
for ($i = 127; $i <= 159; $i++) {
$non_sgml_chars[HTMLPurifier_Encoder::unichr($i)] = '';
}
}
static $iconv = null;
if ($iconv === null) $iconv = function_exists('iconv');
if ($iconv && !$force_php) {
// do the shortcut way
$str = @iconv('UTF-8', 'UTF-8//IGNORE', $str);
return strtr($str, $non_sgml_chars);
}
$mState = 0; // cached expected number of octets after the current octet
// until the beginning of the next UTF8 character sequence
$mUcs4 = 0; // cached Unicode character
$mBytes = 1; // cached expected number of octets in the current sequence
// original code involved an $out that was an array of Unicode
// codepoints. Instead of having to convert back into UTF-8, we've
// decided to directly append valid UTF-8 characters onto a string
// $out once they're done. $char accumulates raw bytes, while $mUcs4
// turns into the Unicode code point, so there's some redundancy.
$out = '';
$char = '';
$len = strlen($str);
for($i = 0; $i < $len; $i++) {
$in = ord($str{$i});
$char .= $str[$i]; // append byte to char
if (0 == $mState) {
// When mState is zero we expect either a US-ASCII character
// or a multi-octet sequence.
if (0 == (0x80 & ($in))) {
// US-ASCII, pass straight through.
if (($in <= 31 || $in == 127) &&
!($in == 9 || $in == 13 || $in == 10) // save \r\t\n
) {
// control characters, remove
} else {
$out .= $char;
}
// reset
$char = '';
$mBytes = 1;
} elseif (0xC0 == (0xE0 & ($in))) {
// First octet of 2 octet sequence
$mUcs4 = ($in);
$mUcs4 = ($mUcs4 & 0x1F) << 6;
$mState = 1;
$mBytes = 2;
} elseif (0xE0 == (0xF0 & ($in))) {
// First octet of 3 octet sequence
$mUcs4 = ($in);
$mUcs4 = ($mUcs4 & 0x0F) << 12;
$mState = 2;
$mBytes = 3;
} elseif (0xF0 == (0xF8 & ($in))) {
// First octet of 4 octet sequence
$mUcs4 = ($in);
$mUcs4 = ($mUcs4 & 0x07) << 18;
$mState = 3;
$mBytes = 4;
} elseif (0xF8 == (0xFC & ($in))) {
// First octet of 5 octet sequence.
//
// This is illegal because the encoded codepoint must be
// either:
// (a) not the shortest form or
// (b) outside the Unicode range of 0-0x10FFFF.
// Rather than trying to resynchronize, we will carry on
// until the end of the sequence and let the later error
// handling code catch it.
$mUcs4 = ($in);
$mUcs4 = ($mUcs4 & 0x03) << 24;
$mState = 4;
$mBytes = 5;
} elseif (0xFC == (0xFE & ($in))) {
// First octet of 6 octet sequence, see comments for 5
// octet sequence.
$mUcs4 = ($in);
$mUcs4 = ($mUcs4 & 1) << 30;
$mState = 5;
$mBytes = 6;
} else {
// Current octet is neither in the US-ASCII range nor a
// legal first octet of a multi-octet sequence.
$mState = 0;
$mUcs4 = 0;
$mBytes = 1;
$char = '';
}
} else {
// When mState is non-zero, we expect a continuation of the
// multi-octet sequence
if (0x80 == (0xC0 & ($in))) {
// Legal continuation.
$shift = ($mState - 1) * 6;
$tmp = $in;
$tmp = ($tmp & 0x0000003F) << $shift;
$mUcs4 |= $tmp;
if (0 == --$mState) {
// End of the multi-octet sequence. mUcs4 now contains
// the final Unicode codepoint to be output
// Check for illegal sequences and codepoints.
// From Unicode 3.1, non-shortest form is illegal
if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
((4 == $mBytes) && ($mUcs4 < 0x10000)) ||
(4 < $mBytes) ||
// From Unicode 3.2, surrogate characters = illegal
(($mUcs4 & 0xFFFFF800) == 0xD800) ||
// Codepoints outside the Unicode range are illegal
($mUcs4 > 0x10FFFF)
) {
} elseif (0xFEFF != $mUcs4 && // omit BOM
!($mUcs4 >= 128 && $mUcs4 <= 159) // omit non-SGML
) {
$out .= $char;
}
// initialize UTF8 cache (reset)
$mState = 0;
$mUcs4 = 0;
$mBytes = 1;
$char = '';
}
} else {
// ((0xC0 & (*in) != 0x80) && (mState != 0))
// Incomplete multi-octet sequence.
// used to result in complete fail, but we'll reset
$mState = 0;
$mUcs4 = 0;
$mBytes = 1;
$char ='';
}
}
}
return $out;
}
/**
* Translates a Unicode codepoint into its corresponding UTF-8 character.
* @note Based on Feyd's function at
* <http://forums.devnetwork.net/viewtopic.php?p=191404#191404>,
* which is in public domain.
* @note While we're going to do code point parsing anyway, a good
* optimization would be to refuse to translate code points that
* are non-SGML characters. However, this could lead to duplication.
* @note This is very similar to the unichr function in
* maintenance/generate-entity-file.php (although this is superior,
* due to its sanity checks).
*/
// +----------+----------+----------+----------+
// | 33222222 | 22221111 | 111111 | |
// | 10987654 | 32109876 | 54321098 | 76543210 | bit
// +----------+----------+----------+----------+
// | | | | 0xxxxxxx | 1 byte 0x00000000..0x0000007F
// | | | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF
// | | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF
// | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF
// +----------+----------+----------+----------+
// | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF)
// | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes
// +----------+----------+----------+----------+
function unichr($code) {
if($code > 1114111 or $code < 0 or
($code >= 55296 and $code <= 57343) ) {
// bits are set outside the "valid" range as defined
// by UNICODE 4.1.0
return '';
}
$x = $y = $z = $w = 0;
if ($code < 128) {
// regular ASCII character
$x = $code;
} else {
// set up bits for UTF-8
$x = ($code & 63) | 128;
if ($code < 2048) {
$y = (($code & 2047) >> 6) | 192;
} else {
$y = (($code & 4032) >> 6) | 128;
if($code < 65536) {
$z = (($code >> 12) & 15) | 224;
} else {
$z = (($code >> 12) & 63) | 128;
$w = (($code >> 18) & 7) | 240;
}
}
}
// set up the actual character
$ret = '';
if($w) $ret .= chr($w);
if($z) $ret .= chr($z);
if($y) $ret .= chr($y);
$ret .= chr($x);
return $ret;
}
/**
* Converts a string to UTF-8 based on configuration.
*/
function convertToUTF8($str, $config, &$context) {
static $iconv = null;
if ($iconv === null) $iconv = function_exists('iconv');
$encoding = $config->get('Core', 'Encoding');
if ($encoding === 'utf-8') return $str;
if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
return @iconv($encoding, 'utf-8//IGNORE', $str);
} elseif ($encoding === 'iso-8859-1') {
return @utf8_encode($str);
}
}
/**
* Converts a string from UTF-8 based on configuration.
* @note Currently, this is a lossy conversion, with unexpressable
* characters being omitted.
*/
function convertFromUTF8($str, $config, &$context) {
static $iconv = null;
if ($iconv === null) $iconv = function_exists('iconv');
$encoding = $config->get('Core', 'Encoding');
if ($encoding === 'utf-8') return $str;
if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
return @iconv('utf-8', $encoding . '//IGNORE', $str);
} elseif ($encoding === 'iso-8859-1') {
return @utf8_decode($str);
}
}
}
?>

View File

@@ -1,19 +1,33 @@
<?php
/**
* Object that provides entity lookup table from entity name to character
*/
class HTMLPurifier_EntityLookup {
/**
* Assoc array of entity name to character represented.
* @public
*/
var $table;
function HTMLPurifier_EntityLookup() {}
// to enforce Singleton-ness
/**
* Sets up the entity lookup table from the serialized file contents.
* @note The serialized contents are versioned, but were generated
* using the maintenance script generate_entity_file.php
* @warning This is not in constructor to help enforce the Singleton
*/
function setup($file = false) {
if (!$file) {
$file = dirname(__FILE__) . '/EntityLookup/data.txt';
$file = dirname(__FILE__) . '/EntityLookup/entities.ser';
}
$this->table = unserialize(file_get_contents($file));
}
/**
* Retrieves sole instance of the object.
* @param Optional prototype of custom lookup table to overload with.
*/
function instance($prototype = false) {
// no references, since PHP doesn't copy unless modified
static $instance = null;

View File

@@ -0,0 +1,158 @@
<?php
require_once 'HTMLPurifier/EntityLookup.php';
require_once 'HTMLPurifier/Encoder.php';
// if want to implement error collecting here, we'll need to use some sort
// of global data (probably trigger_error) because it's impossible to pass
// $config or $context to the callback functions.
/**
* Handles referencing and derefencing character entities
*/
class HTMLPurifier_EntityParser
{
/**
* Reference to entity lookup table.
* @protected
*/
var $_entity_lookup;
/**
* Callback regex string for parsing entities.
* @protected
*/
var $_substituteEntitiesRegex =
'/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z]+));?/';
// 1. hex 2. dec 3. string
/**
* Decimal to parsed string conversion table for special entities.
* @protected
*/
var $_special_dec2str =
array(
34 => '"',
38 => '&',
39 => "'",
60 => '<',
62 => '>'
);
/**
* Stripped entity names to decimal conversion table for special entities.
* @protected
*/
var $_special_ent2dec =
array(
'quot' => 34,
'amp' => 38,
'lt' => 60,
'gt' => 62
);
/**
* Substitutes non-special entities with their parsed equivalents. Since
* running this whenever you have parsed character is t3h 5uck, we run
* it before everything else.
*
* @protected
* @param $string String to have non-special entities parsed.
* @returns Parsed string.
*/
function substituteNonSpecialEntities($string) {
// it will try to detect missing semicolons, but don't rely on it
return preg_replace_callback(
$this->_substituteEntitiesRegex,
array($this, 'nonSpecialEntityCallback'),
$string
);
}
/**
* Callback function for substituteNonSpecialEntities() that does the work.
*
* @warning Though this is public in order to let the callback happen,
* calling it directly is not recommended.
* @param $matches PCRE matches array, with 0 the entire match, and
* either index 1, 2 or 3 set with a hex value, dec value,
* or string (respectively).
* @returns Replacement string.
*/
function nonSpecialEntityCallback($matches) {
// replaces all but big five
$entity = $matches[0];
$is_num = (@$matches[0][1] === '#');
if ($is_num) {
$is_hex = (@$entity[2] === 'x');
$code = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
// abort for special characters
if (isset($this->_special_dec2str[$code])) return $entity;
return HTMLPurifier_Encoder::unichr($code);
} else {
if (isset($this->_special_ent2dec[$matches[3]])) return $entity;
if (!$this->_entity_lookup) {
require_once 'HTMLPurifier/EntityLookup.php';
$this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
}
if (isset($this->_entity_lookup->table[$matches[3]])) {
return $this->_entity_lookup->table[$matches[3]];
} else {
return $entity;
}
}
}
/**
* Substitutes only special entities with their parsed equivalents.
*
* @notice We try to avoid calling this function because otherwise, it
* would have to be called a lot (for every parsed section).
*
* @protected
* @param $string String to have non-special entities parsed.
* @returns Parsed string.
*/
function substituteSpecialEntities($string) {
return preg_replace_callback(
$this->_substituteEntitiesRegex,
array($this, 'specialEntityCallback'),
$string);
}
/**
* Callback function for substituteSpecialEntities() that does the work.
*
* This callback has same syntax as nonSpecialEntityCallback().
*
* @warning Though this is public in order to let the callback happen,
* calling it directly is not recommended.
* @param $matches PCRE-style matches array, with 0 the entire match, and
* either index 1, 2 or 3 set with a hex value, dec value,
* or string (respectively).
* @returns Replacement string.
*/
function specialEntityCallback($matches) {
$entity = $matches[0];
$is_num = (@$matches[0][1] === '#');
if ($is_num) {
$is_hex = (@$entity[2] === 'x');
$int = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
return isset($this->_special_dec2str[$int]) ?
$this->_special_dec2str[$int] :
$entity;
} else {
return isset($this->_special_ent2dec[$matches[3]]) ?
$this->_special_ent2dec[$matches[3]] :
$entity;
}
}
}
?>

View File

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

View File

@@ -1,36 +1,123 @@
<?php
// pretty-printing with indentation would be pretty cool
require_once 'HTMLPurifier/Lexer.php';
HTMLPurifier_ConfigSchema::define(
'Core', 'CleanUTF8DuringGeneration', false, 'bool',
'When true, HTMLPurifier_Generator will also check all strings it '.
'escapes for UTF-8 well-formedness as a defense in depth measure. '.
'This could cause a considerable performance impact, and is not '.
'strictly necessary due to the fact that the Lexers should have '.
'ensured that all the UTF-8 strings were well-formed. Note that '.
'the configuration value is only read at the beginning of '.
'generateFromTokens.'
);
HTMLPurifier_ConfigSchema::define(
'Core', 'XHTML', true, 'bool',
'Determines whether or not output is XHTML or not. When disabled, HTML '.
'Purifier goes into HTML 4.01 removes XHTML-specific markup constructs, '.
'such as boolean attribute expansion and trailing slashes in empty tags. '.
'This directive was available since 1.1.'
);
// extension constraints could be factored into ConfigSchema
HTMLPurifier_ConfigSchema::define(
'Core', 'TidyFormat', false, 'bool',
'<p>Determines whether or not to run Tidy on the final output for pretty '.
'formatting reasons, such as indentation and wrap.</p><p>This can greatly '.
'improve readability for editors who are hand-editing the HTML, but is '.
'by no means necessary as HTML Purifier has already fixed all major '.
'errors the HTML may have had. Tidy is a non-default extension, and this directive '.
'will silently fail if Tidy is not available.</p><p>If you are looking to make '.
'the overall look of your page\'s source better, I recommend running Tidy '.
'on the entire page rather than just user-content (after all, the '.
'indentation relative to the containing blocks will be incorrect).</p><p>This '.
'directive was available since 1.1.1.</p>'
);
/**
* Generates HTML from tokens.
*/
class HTMLPurifier_Generator
{
// only unit tests may omit configuration: internals MUST pass config
function generateFromTokens($tokens, $config = null) {
/**
* Bool cache of %Core.CleanUTF8DuringGeneration
* @private
*/
var $_clean_utf8 = false;
/**
* Bool cache of %Core.XHTML
* @private
*/
var $_xhtml = true;
/**
* Generates HTML from an array of tokens.
* @param $tokens Array of HTMLPurifier_Token
* @param $config HTMLPurifier_Config object
* @return Generated HTML
*/
function generateFromTokens($tokens, $config, &$context) {
$html = '';
if (!$config) $config = HTMLPurifier_Config::createDefault();
$this->_clean_utf8 = $config->get('Core', 'CleanUTF8DuringGeneration');
$this->_xhtml = $config->get('Core', 'XHTML');
if (!$tokens) return '';
foreach ($tokens as $token) {
$html .= $this->generateFromToken($token, $config);
$html .= $this->generateFromToken($token);
}
if ($config->get('Core', 'TidyFormat') && extension_loaded('tidy')) {
$tidy_options = array(
'indent'=> true,
'output-xhtml' => $this->_xhtml,
'show-body-only' => true,
'indent-spaces' => 2,
'wrap' => 68,
);
if (version_compare(PHP_VERSION, '5', '<')) {
tidy_set_encoding('utf8');
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;
}
}
return $html;
}
function generateFromToken($token, $config) {
/**
* Generates HTML from a single token.
* @param $token HTMLPurifier_Token object.
* @return Generated HTML
*/
function generateFromToken($token) {
if (!isset($token->type)) return '';
if ($token->type == 'start') {
$attr = $this->generateAttributes($token->attributes, $config);
$attr = $this->generateAttributes($token->attr);
return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
} elseif ($token->type == 'end') {
return '</' . $token->name . '>';
} elseif ($token->type == 'empty') {
$attr = $this->generateAttributes($token->attributes, $config);
return '<' . $token->name . ($attr ? ' ' : '') . $attr . ' />';
$attr = $this->generateAttributes($token->attr);
return '<' . $token->name . ($attr ? ' ' : '') . $attr .
( $this->_xhtml ? ' /': '' )
. '>';
} elseif ($token->type == 'text') {
return htmlspecialchars($token->data, ENT_COMPAT, 'UTF-8');
return $this->escape($token->data);
} else {
return '';
@@ -38,14 +125,34 @@ class HTMLPurifier_Generator
}
}
function generateAttributes($assoc_array_of_attributes, $config) {
/**
* Generates attribute declarations from attribute array.
* @param $assoc_array_of_attributes Attribute array
* @return Generate HTML fragment for insertion.
*/
function generateAttributes($assoc_array_of_attributes) {
$html = '';
foreach ($assoc_array_of_attributes as $key => $value) {
$html .= $key.'="'.htmlspecialchars($value, ENT_COMPAT, 'UTF-8').'" ';
if (!$this->_xhtml) {
// remove namespaced attributes
if (strpos($key, ':') !== false) continue;
// also needed: check for attribute minimization
}
$html .= $key.'="'.$this->escape($value).'" ';
}
return rtrim($html);
}
/**
* Escapes raw text data.
* @param $string String data to escape for HTML.
* @return String escaped data.
*/
function escape($string) {
if ($this->_clean_utf8) $string = HTMLPurifier_Lexer::cleanUTF8($string);
return htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
}
}
?>

View File

@@ -9,7 +9,7 @@ require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/AttrDef/Pixels.php';
require_once 'HTMLPurifier/AttrDef/Length.php';
require_once 'HTMLPurifier/AttrDef/MultiLength.php';
require_once 'HTMLPurifier/AttrDef/NumberSpan.php';
require_once 'HTMLPurifier/AttrDef/Integer.php';
require_once 'HTMLPurifier/AttrDef/URI.php';
require_once 'HTMLPurifier/AttrDef/CSS.php';
require_once 'HTMLPurifier/AttrTransform.php';
@@ -18,10 +18,86 @@ require_once 'HTMLPurifier/AttrTransform.php';
require_once 'HTMLPurifier/AttrTransform/BdoDir.php';
require_once 'HTMLPurifier/AttrTransform/ImgRequired.php';
require_once 'HTMLPurifier/ChildDef.php';
require_once 'HTMLPurifier/ChildDef/Chameleon.php';
require_once 'HTMLPurifier/ChildDef/Empty.php';
require_once 'HTMLPurifier/ChildDef/Required.php';
require_once 'HTMLPurifier/ChildDef/Optional.php';
require_once 'HTMLPurifier/ChildDef/Table.php';
require_once 'HTMLPurifier/ChildDef/StrictBlockquote.php';
require_once 'HTMLPurifier/Generator.php';
require_once 'HTMLPurifier/Token.php';
require_once 'HTMLPurifier/TagTransform.php';
HTMLPurifier_ConfigSchema::define(
'HTML', 'EnableAttrID', false, 'bool',
'Allows the ID attribute in HTML. This is disabled by default '.
'due to the fact that without proper configuration user input can '.
'easily break the validation of a webpage by specifying an ID that is '.
'already on the surrounding HTML. If you don\'t mind throwing caution to '.
'the wind, enable this directive, but I strongly recommend you also '.
'consider blacklisting IDs you use (%Attr.IDBlacklist) or prefixing all '.
'user supplied IDs (%Attr.IDPrefix). This directive has been available '.
'since 1.2.0, and when set to true reverts to the behavior of pre-1.2.0 '.
'versions.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'Strict', false, 'bool',
'Determines whether or not to use Transitional (loose) or Strict rulesets. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'BlockWrapper', 'p', 'string',
'String name of element to wrap inline elements that are inside a block '.
'context. This only occurs in the children of blockquote in strict mode. '.
'Example: by default value, <code>&lt;blockquote&gt;Foo&lt;/blockquote&gt;</code> '.
'would become <code>&lt;blockquote&gt;&lt;p&gt;Foo&lt;/p&gt;&lt;/blockquote&gt;</code>. The '.
'<code>&lt;p&gt;</code> tags can be replaced '.
'with whatever you desire, as long as it is a block level element. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'Parent', 'div', 'string',
'String name of element that HTML fragment passed to library will be '.
'inserted in. An interesting variation would be using span as the '.
'parent element, meaning that only inline tags would be allowed. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'AllowedElements', null, 'lookup/null',
'If HTML Purifier\'s tag set is unsatisfactory for your needs, you '.
'can overload it with your own list of tags to allow. Note that this '.
'method is subtractive: it does its job by taking away from HTML Purifier '.
'usual feature set, so you cannot add a tag that HTML Purifier never '.
'supported in the first place (like embed, form or head). If you change this, you '.
'probably also want to change %HTML.AllowedAttributes. '.
'<strong>Warning:</strong> If another directive conflicts with the '.
'elements here, <em>that</em> directive will win and override. '.
'This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'HTML', 'AllowedAttributes', null, 'lookup/null',
'IF HTML Purifier\'s attribute set is unsatisfactory, overload it! '.
'The syntax is \'tag.attr\' or \'*.attr\' for the global attributes '.
'(style, id, class, dir, lang, xml:lang).'.
'<strong>Warning:</strong> If another directive conflicts with the '.
'elements here, <em>that</em> directive will win and override. For '.
'example, %HTML.EnableAttrID will take precedence over *.id in this '.
'directive. You must set that directive to true before you can use '.
'IDs at all. This directive has been available since 1.3.0.'
);
HTMLPurifier_ConfigSchema::define(
'Attr', 'DisableURI', false, 'bool',
'Disables all URIs in all forms. Not sure why you\'d want to do that '.
'(after all, the Internet\'s founded on the notion of a hyperlink). '.
'This directive has been available since 1.3.0.'
);
/**
* Defines the purified HTML type with large amounts of objects.
*
@@ -30,12 +106,8 @@ require_once 'HTMLPurifier/TagTransform.php';
* each allowed element. It also contains special use information (always
* prefixed by info) for intelligent tag closing and global attributes.
*
* Planned improvements include attribute transformation objects as well as
* migration of auto-tag-closing from HTMLPurifier_Strategy_MakeWellFormed
* (these can likely just be extensions of ElementDef).
*
* After development drops off, the definition generation will be moved to
* a maintenance script and we will stipulate that definition be created
* For optimization, the definition generation may be moved to
* a maintenance script and stipulate that definition be created
* by a factory method that unserializes a serialized version of Definition.
* Customization would entail copying the maintenance script, making the
* necessary changes, generating the serialized object, and then hooking it
@@ -46,44 +118,75 @@ require_once 'HTMLPurifier/TagTransform.php';
class HTMLPurifier_HTMLDefinition
{
/**
* Associative array of element names to HTMLPurifier_ElementDef
* @public
*/
var $info = array();
// used solely by HTMLPurifier_Strategy_ValidateAttributes
/**
* Associative array of global attribute name to attribute definition.
* @public
*/
var $info_global_attr = array();
// used solely by HTMLPurifier_Strategy_FixNesting
/**
* String name of parent element HTML will be going into.
* @public
*/
var $info_parent = 'div';
// used solely by HTMLPurifier_Strategy_RemoveForeignElements
/**
* Definition for parent element, allows parent element to be a
* tag that's not allowed inside the HTML fragment.
* @public
*/
var $info_parent_def;
/**
* String name of element used to wrap inline elements in block context
* @note This is rarely used except for BLOCKQUOTEs in strict mode
* @public
*/
var $info_block_wrapper = 'p';
/**
* Associative array of deprecated tag name to HTMLPurifier_TagTransform
* @public
*/
var $info_tag_transform = array();
// used solely by HTMLPurifier_Strategy_ValidateAttributes
/**
* List of HTMLPurifier_AttrTransform to be performed before validation.
* @public
*/
var $info_attr_transform_pre = array();
/**
* List of HTMLPurifier_AttrTransform to be performed after validation/
* @public
*/
var $info_attr_transform_post = array();
// WARNING! Prototype is not passed by reference, so in order to get
// a copy of the real one, you'll have to destroy your copy and
// use instance() to get it.
// Usually, however, modifying the returned definition (reference) should be
// sufficient
function &instance($prototype = null) {
static $instance = null;
if ($prototype) {
$instance = $prototype;
} elseif (!$instance) {
$instance = new HTMLPurifier_HTMLDefinition();
$instance->setup();
}
return $instance;
}
/**
* Lookup table of flow elements
* @public
*/
var $info_flow_elements = array();
function HTMLPurifier_HTMLDefinition() {}
/**
* Boolean is a strict definition?
* @public
*/
var $strict;
function setup() {
/**
* Initializes the definition, the meat of the class.
*/
function setup($config) {
// emulates the structure of the DTD
// these are condensed, however, with bad stuff taken out
// screening process was done by hand
// some cached config values
$this->strict = $config->get('HTML', 'Strict');
//////////////////////////////////////////////////////////////////////
// info[] : initializes the definition objects
@@ -95,13 +198,19 @@ class HTMLPurifier_HTMLDefinition
array(
'ins', 'del', 'blockquote', 'dd', 'li', 'div', 'em', 'strong',
'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym',
'q', 'sub', 'tt', 'sup', 'i', 'b', 'big', 'small', 'u', 's',
'strike', 'bdo', 'span', 'dt', 'p', 'h1', 'h2', 'h3', 'h4',
'q', 'sub', 'tt', 'sup', 'i', 'b', 'big', 'small',
'bdo', 'span', 'dt', 'p', 'h1', 'h2', 'h3', 'h4',
'h5', 'h6', 'ol', 'ul', 'dl', 'address', 'img', 'br', 'hr',
'pre', 'a', 'table', 'caption', 'thead', 'tfoot', 'tbody',
'colgroup', 'col', 'td', 'th', 'tr'
);
if (!$this->strict) {
$allowed_tags[] = 'u';
$allowed_tags[] = 's';
$allowed_tags[] = 'strike';
}
foreach ($allowed_tags as $tag) {
$this->info[$tag] = new HTMLPurifier_ElementDef();
}
@@ -109,12 +218,23 @@ class HTMLPurifier_HTMLDefinition
//////////////////////////////////////////////////////////////////////
// info[]->child : defines allowed children for elements
// entities: prefixed with e_ and _ replaces .
// emulates the structure of the DTD
// however, these are condensed, with bad stuff taken out
// screening process was done by hand
// entities: prefixed with e_ and _ replaces . from DTD
// double underlines are entities we made up
// we don't use an array because that complicates interpolation
// strings are used instead of arrays because if you use arrays,
// you have to do some hideous manipulation with array_merge()
// todo: determine whether or not having allowed children
// that aren't allowed globally affects security (it shouldn't)
// if above works out, extend children definitions to include all
// possible elements (allowed elements will dictate which ones
// get dropped
$e_special_extra = 'img';
$e_special_basic = 'br | span | bdo';
$e_special = "$e_special_basic | $e_special_extra";
@@ -125,11 +245,10 @@ class HTMLPurifier_HTMLDefinition
$e_phrase_basic = 'em | strong | dfn | code | q | samp | kbd | var'.
' | cite | abbr | acronym';
$e_phrase = "$e_phrase_basic | $e_phrase_extra";
$e_inline_forms = ''; // humor the dtd
$e_misc_inline = 'ins | del';
$e_misc = "$e_misc_inline";
$e_inline = "a | $e_special | $e_fontstyle | $e_phrase".
" | $e_inline_forms";
$e_inline = "a | $e_special | $e_fontstyle | $e_phrase";
// pseudo-property we created for convenience, see later on
$e__inline = "#PCDATA | $e_inline | $e_misc_inline";
// note the casing
$e_Inline = new HTMLPurifier_ChildDef_Optional($e__inline);
@@ -137,24 +256,31 @@ class HTMLPurifier_HTMLDefinition
$e_lists = 'ul | ol | dl';
$e_blocktext = 'pre | hr | blockquote | address';
$e_block = "p | $e_heading | div | $e_lists | $e_blocktext | table";
$e_Block = new HTMLPurifier_ChildDef_Optional($e_block);
$e__flow = "#PCDATA | $e_block | $e_inline | $e_misc";
$e_Flow = new HTMLPurifier_ChildDef_Optional($e__flow);
$e_a_content = new HTMLPurifier_ChildDef_Optional("#PCDATA | $e_special".
" | $e_fontstyle | $e_phrase | $e_inline_forms | $e_misc_inline");
$e_a_content = new HTMLPurifier_ChildDef_Optional("#PCDATA".
" | $e_special | $e_fontstyle | $e_phrase | $e_misc_inline");
$e_pre_content = new HTMLPurifier_ChildDef_Optional("#PCDATA | a".
" | $e_special_basic | $e_fontstyle_basic | $e_phrase_basic".
" | $e_inline_forms | $e_misc_inline");
$e_form_content = new HTMLPurifier_ChildDef_Optional(''); //unused
$e_form_button_content = new HTMLPurifier_ChildDef_Optional(''); // unused
" | $e_misc_inline");
$e_form_content = new HTMLPurifier_ChildDef_Optional('');//unused
$e_form_button_content = new HTMLPurifier_ChildDef_Optional('');//unused
$this->info['ins']->child =
$this->info['del']->child = new HTMLPurifier_ChildDef_Chameleon($e__inline, $e__flow);
$this->info['del']->child =
new HTMLPurifier_ChildDef_Chameleon($e__inline, $e__flow);
$this->info['blockquote']->child=
$this->info['dd']->child =
$this->info['li']->child =
$this->info['div']->child = $e_Flow;
if ($this->strict) {
$this->info['blockquote']->child = new HTMLPurifier_ChildDef_StrictBlockquote();
} else {
$this->info['blockquote']->child = $e_Flow;
}
$this->info['caption']->child =
$this->info['em']->child =
$this->info['strong']->child =
@@ -194,9 +320,13 @@ class HTMLPurifier_HTMLDefinition
$this->info['dl']->child = new HTMLPurifier_ChildDef_Required('dt|dd');
$this->info['address']->child =
new HTMLPurifier_ChildDef_Optional("#PCDATA | p | $e_inline".
" | $e_misc_inline");
if ($this->strict) {
$this->info['address']->child = $e_Inline;
} else {
$this->info['address']->child =
new HTMLPurifier_ChildDef_Optional("#PCDATA | p | $e_inline".
" | $e_misc_inline");
}
$this->info['img']->child =
$this->info['br']->child =
@@ -206,8 +336,7 @@ class HTMLPurifier_HTMLDefinition
$this->info['a']->child = $e_a_content;
$this->info['table']->child = new HTMLPurifier_ChildDef_Custom(
'(caption?, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+))');
$this->info['table']->child = new HTMLPurifier_ChildDef_Table();
// not a real entity, watch the double underscore
$e__row = new HTMLPurifier_ChildDef_Required('tr');
@@ -223,17 +352,20 @@ class HTMLPurifier_HTMLDefinition
//////////////////////////////////////////////////////////////////////
// info[]->type : defines the type of the element (block or inline)
// reuses $e_Inline and $e_block
foreach ($e_Inline->elements as $name) {
// reuses $e_Inline and $e_Block
foreach ($e_Inline->elements as $name => $bool) {
if ($name == '#PCDATA') continue;
$this->info[$name]->type = 'inline';
}
$e_Block = new HTMLPurifier_ChildDef_Optional($e_block);
foreach ($e_Block->elements as $name) {
foreach ($e_Block->elements as $name => $bool) {
$this->info[$name]->type = 'block';
}
foreach ($e_Flow->elements as $name => $bool) {
$this->info_flow_elements[$name] = true;
}
//////////////////////////////////////////////////////////////////////
// info[]->excludes : defines elements that aren't allowed in here
@@ -241,7 +373,7 @@ class HTMLPurifier_HTMLDefinition
$this->info['a']->excludes = array('a' => true);
$this->info['pre']->excludes = array_flip(array('img', 'big', 'small',
// technically in spec, but we don't allow em anyway
// technically useless, but good to be indepth
'object', 'applet', 'font', 'basefont'));
//////////////////////////////////////////////////////////////////////
@@ -251,13 +383,14 @@ class HTMLPurifier_HTMLDefinition
// by the transform classes. It will, however, do simple and slightly
// complex attribute value substitution
// the question of varying allowed attributes is more entangling.
$e_Text = new HTMLPurifier_AttrDef_Text();
// attrs, included in almost every single one except for a few,
// which manually override these in their local definitions
$this->info_global_attr = array(
// core attrs
'id' => new HTMLPurifier_AttrDef_ID(),
'class' => new HTMLPurifier_AttrDef_Class(),
'title' => $e_Text,
'style' => new HTMLPurifier_AttrDef_CSS(),
@@ -267,6 +400,10 @@ class HTMLPurifier_HTMLDefinition
'xml:lang' => new HTMLPurifier_AttrDef_Lang(),
);
if ($config->get('HTML', 'EnableAttrID')) {
$this->info_global_attr['id'] = new HTMLPurifier_AttrDef_ID();
}
// required attribute stipulation handled in attribute transformation
$this->info['bdo']->attr = array(); // nothing else
@@ -295,7 +432,8 @@ class HTMLPurifier_HTMLDefinition
$this->info['table']->attr['summary'] = $e_Text;
$this->info['table']->attr['border'] = new HTMLPurifier_AttrDef_Pixels();
$this->info['table']->attr['border'] =
new HTMLPurifier_AttrDef_Pixels();
$e_Length = new HTMLPurifier_AttrDef_Length();
$this->info['table']->attr['cellpadding'] =
@@ -309,7 +447,7 @@ class HTMLPurifier_HTMLDefinition
$this->info['col']->attr['width'] =
$this->info['colgroup']->attr['width'] = $e_MultiLength;
$e__NumberSpan = new HTMLPurifier_AttrDef_NumberSpan();
$e__NumberSpan = new HTMLPurifier_AttrDef_Integer(false, false, true);
$this->info['colgroup']->attr['span'] =
$this->info['col']->attr['span'] =
$this->info['td']->attr['rowspan'] =
@@ -317,17 +455,26 @@ class HTMLPurifier_HTMLDefinition
$this->info['td']->attr['colspan'] =
$this->info['th']->attr['colspan'] = $e__NumberSpan;
$e_URI = new HTMLPurifier_AttrDef_URI();
$this->info['a']->attr['href'] =
$this->info['img']->attr['longdesc'] =
$this->info['img']->attr['src'] =
$this->info['del']->attr['cite'] =
$this->info['ins']->attr['cite'] =
$this->info['blockquote']->attr['cite'] =
$this->info['q']->attr['cite'] = $e_URI;
if (!$config->get('Attr', 'DisableURI')) {
$e_URI = new HTMLPurifier_AttrDef_URI();
$this->info['a']->attr['href'] =
$this->info['img']->attr['longdesc'] =
$this->info['del']->attr['cite'] =
$this->info['ins']->attr['cite'] =
$this->info['blockquote']->attr['cite'] =
$this->info['q']->attr['cite'] = $e_URI;
// URI that causes HTTP request
$this->info['img']->attr['src'] = new HTMLPurifier_AttrDef_URI(true);
}
if (!$this->strict) {
$this->info['li']->attr['value'] = new HTMLPurifier_AttrDef_Integer();
$this->info['ol']->attr['start'] = new HTMLPurifier_AttrDef_Integer();
}
//////////////////////////////////////////////////////////////////////
// UNIMP : info_tag_transform : transformations of tags
// info_tag_transform : transformations of tags
$this->info_tag_transform['font'] = new HTMLPurifier_TagTransform_Font();
$this->info_tag_transform['menu'] = new HTMLPurifier_TagTransform_Simple('ul');
@@ -337,6 +484,9 @@ class HTMLPurifier_HTMLDefinition
//////////////////////////////////////////////////////////////////////
// info[]->auto_close : tags that automatically close another
// todo: determine whether or not SGML-like modeling based on
// mandatory/optional end tags would be a better policy
// make sure you test using isset() not !empty()
// these are all block elements: blocks aren't allowed in P
@@ -379,6 +529,60 @@ class HTMLPurifier_HTMLDefinition
$this->info_attr_transform_post[] = new HTMLPurifier_AttrTransform_Lang();
// protect against stdclasses floating around
foreach ($this->info as $key => $obj) {
if (is_a($obj, 'stdclass')) {
unset($this->info[$key]);
}
}
//////////////////////////////////////////////////////////////////////
// info_block_wrapper : wraps inline elements in block context
$block_wrapper = $config->get('HTML', 'BlockWrapper');
if (isset($e_Block->elements[$block_wrapper])) {
$this->info_block_wrapper = $block_wrapper;
} else {
trigger_error('Cannot use non-block element as block wrapper.',
E_USER_ERROR);
}
//////////////////////////////////////////////////////////////////////
// info_parent : parent element of the HTML fragment
$parent = $config->get('HTML', 'Parent');
if (isset($this->info[$parent])) {
$this->info_parent = $parent;
} else {
trigger_error('Cannot use unrecognized element as parent.',
E_USER_ERROR);
}
$this->info_parent_def = $this->info[$this->info_parent];
//////////////////////////////////////////////////////////////////////
// %HTML.Allowed(Elements|Attributes) : cut non-allowed elements
$allowed_elements = $config->get('HTML', 'AllowedElements');
if (is_array($allowed_elements)) {
foreach ($this->info as $name => $d) {
if(!isset($allowed_elements[$name])) unset($this->info[$name]);
}
}
$allowed_attributes = $config->get('HTML', 'AllowedAttributes');
if (is_array($allowed_attributes)) {
foreach ($this->info_global_attr as $attr_key => $info) {
if (!isset($allowed_attributes["*.$attr_key"])) {
unset($this->info_global_attr[$attr_key]);
}
}
foreach ($this->info as $tag => $info) {
foreach ($info->attr as $attr => $attr_info) {
if (!isset($allowed_attributes["$tag.$attr"])) {
unset($this->info[$tag]->attr[$attr]);
}
}
}
}
}
function setAttrForTableElements($attr, $def) {
@@ -394,15 +598,52 @@ class HTMLPurifier_HTMLDefinition
}
/**
* Structure that stores an element definition.
*/
class HTMLPurifier_ElementDef
{
/**
* Associative array of attribute name to HTMLPurifier_AttrDef
* @public
*/
var $attr = array();
/**
* List of tag's HTMLPurifier_AttrTransform to be done before validation
* @public
*/
var $attr_transform_pre = array();
/**
* List of tag's HTMLPurifier_AttrTransform to be done after validation
* @public
*/
var $attr_transform_post = array();
/**
* Lookup table of tags that close this tag.
* @public
*/
var $auto_close = array();
/**
* HTMLPurifier_ChildDef of this tag.
* @public
*/
var $child;
/**
* Type of the tag: inline or block or unknown?
* @public
*/
var $type = 'unknown';
/**
* Lookup table of tags excluded from all descendants of this tag.
* @public
*/
var $excludes = array();
}

View File

@@ -1,15 +1,36 @@
<?php
/**
* Component of HTMLPurifier_AttrContext that accumulates IDs to prevent dupes
* @note In Slashdot-speak, dupe means duplicate.
* @note This class does not accept $config or $context, thus, it is the
* burden of the callee to register the appropriate errors or
* configuration.
*/
class HTMLPurifier_IDAccumulator
{
/**
* Lookup table of IDs we've accumulated.
* @public
*/
var $ids = array();
/**
* Add an ID to the lookup table.
* @param $id ID to be added.
* @return Bool status, true if success, false if there's a dupe
*/
function add($id) {
if (isset($this->ids[$id])) return false;
return $this->ids[$id] = true;
}
/**
* Load a list of IDs into the lookup table
* @param $array_of_ids Array of IDs to load
* @note This function doesn't care about duplicates
*/
function load($array_of_ids) {
foreach ($array_of_ids as $id) {
$this->ids[$id] = true;

View File

@@ -1,13 +1,14 @@
<?php
require_once 'HTMLPurifier/Token.php';
require_once 'HTMLPurifier/Encoder.php';
require_once 'HTMLPurifier/EntityParser.php';
HTMLPurifier_ConfigDef::define(
'Core', 'AcceptFullDocuments', true,
HTMLPurifier_ConfigSchema::define(
'Core', 'AcceptFullDocuments', true, 'bool',
'This parameter determines whether or not the filter should accept full '.
'HTML documents, not just HTML fragments. When on, it will '.
'drop all sections except the content between body. Depending on '.
'the implementation in use, this may speed up document parse times.'
'drop all sections except the content between body.'
);
/**
@@ -54,13 +55,74 @@ HTMLPurifier_ConfigDef::define(
class HTMLPurifier_Lexer
{
function HTMLPurifier_Lexer() {
$this->_encoder = new HTMLPurifier_Encoder();
$this->_entity_parser = new HTMLPurifier_EntityParser();
}
/**
* Most common entity to raw value conversion table for special entities.
* @protected
*/
var $_special_entity2str =
array(
'&quot;' => '"',
'&amp;' => '&',
'&lt;' => '<',
'&gt;' => '>',
'&#39;' => "'",
'&#039;' => "'",
'&#x27;' => "'"
);
/**
* Parses special entities into the proper characters.
*
* This string will translate escaped versions of the special characters
* into the correct ones.
*
* @warning
* You should be able to treat the output of this function as
* completely parsed, but that's only because all other entities should
* have been handled previously in substituteNonSpecialEntities()
*
* @param $string String character data to be parsed.
* @returns Parsed character data.
*/
function parseData($string) {
// following functions require at least one character
if ($string === '') return '';
// subtracts amps that cannot possibly be escaped
$num_amp = substr_count($string, '&') - substr_count($string, '& ') -
($string[strlen($string)-1] === '&' ? 1 : 0);
if (!$num_amp) return $string; // abort if no entities
$num_esc_amp = substr_count($string, '&amp;');
$string = strtr($string, $this->_special_entity2str);
// code duplication for sake of optimization, see above
$num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') -
($string[strlen($string)-1] === '&' ? 1 : 0);
if ($num_amp_2 <= $num_esc_amp) return $string;
// hmm... now we have some uncommon entities. Use the callback.
$string = $this->_entity_parser->substituteSpecialEntities($string);
return $string;
}
var $_encoder;
/**
* Lexes an HTML string into tokens.
*
* @param $string String HTML.
* @return HTMLPurifier_Token array representation of HTML.
*/
function tokenizeHTML($string, $config = null) {
function tokenizeHTML($string, $config, &$context) {
trigger_error('Call to abstract class', E_USER_ERROR);
}
@@ -101,112 +163,6 @@ class HTMLPurifier_Lexer
return $lexer;
}
/**
* Decimal to parsed string conversion table for special entities.
* @protected
*/
var $_special_dec2str =
array(
34 => '"',
38 => '&',
39 => "'",
60 => '<',
62 => '>'
);
/**
* Stripped entity names to decimal conversion table for special entities.
* @protected
*/
var $_special_ent2dec =
array(
'quot' => 34,
'amp' => 38,
'lt' => 60,
'gt' => 62
);
/**
* Most common entity to raw value conversion table for special entities.
* @protected
*/
var $_special_entity2str =
array(
'&quot;' => '"',
'&amp;' => '&',
'&lt;' => '<',
'&gt;' => '>',
'&#39;' => "'",
'&#039;' => "'",
'&#x27;' => "'"
);
/**
* Callback regex string for parsing entities.
* @protected
*/
var $_substituteEntitiesRegex =
'/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z]+));?/';
// 1. hex 2. dec 3. string
/**
* Substitutes non-special entities with their parsed equivalents. Since
* running this whenever you have parsed character is t3h 5uck, we run
* it before everything else.
*
* @protected
* @param $string String to have non-special entities parsed.
* @returns Parsed string.
*/
function substituteNonSpecialEntities($string) {
// it will try to detect missing semicolons, but don't rely on it
return preg_replace_callback(
$this->_substituteEntitiesRegex,
array($this, 'nonSpecialEntityCallback'),
$string
);
}
/**
* Callback function for substituteNonSpecialEntities() that does the work.
*
* @warning Though this is public in order to let the callback happen,
* calling it directly is not recommended.
* @param $matches PCRE matches array, with 0 the entire match, and
* either index 1, 2 or 3 set with a hex value, dec value,
* or string (respectively).
* @returns Replacement string.
* @todo Implement string translations
*/
function nonSpecialEntityCallback($matches) {
// replaces all but big five
$entity = $matches[0];
$is_num = (@$matches[0][1] === '#');
if ($is_num) {
$is_hex = (@$entity[2] === 'x');
$int = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
if (isset($this->_special_dec2str[$int])) return $entity;
return chr($int);
} else {
if (isset($this->_special_ent2dec[$matches[3]])) return $entity;
if (!$this->_entity_lookup) {
require_once 'HTMLPurifier/EntityLookup.php';
$this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
}
if (isset($this->_entity_lookup->table[$matches[3]])) {
return $this->_entity_lookup->table[$matches[3]];
} else {
return $entity;
}
}
}
/**
* Contains a copy of the EntityLookup table.
* @protected
*/
var $_entity_lookup;
/**
* Translates CDATA sections into regular sections (through escaping).
*
@@ -236,13 +192,37 @@ class HTMLPurifier_Lexer
return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8');
}
/**
* Takes a piece of HTML and normalizes it by converting entities, fixing
* encoding, extracting bits, and other good stuff.
*/
function normalize($html, $config, &$context) {
// extract body from document if applicable
if ($config->get('Core', 'AcceptFullDocuments')) {
$html = $this->extractBody($html);
}
// escape CDATA
$html = $this->escapeCDATA($html);
// expand entities that aren't the big five
$html = $this->_entity_parser->substituteNonSpecialEntities($html);
// clean into wellformed UTF-8 string for an SGML context: this has
// to be done after entity expansion because the entities sometimes
// represent non-SGML characters (horror, horror!)
$html = $this->_encoder->cleanUTF8($html);
return $html;
}
/**
* Takes a string of HTML (fragment or document) and returns the content
*/
function extractBody($html, $return_bool = false) {
function extractBody($html) {
$matches = array();
$result = preg_match('!<body[^>]*>(.+?)</body>!is', $html, $matches);
if ($return_bool) return $result;
if ($result) {
return $matches[1];
} else {

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