code:
Filter.ExtractStyleBlocks.Scope
TYPE: string/null
VERSION: 3.0.0
DEFAULT: NULL
ALIASES: Filter.ExtractStyleBlocksScope, FilterParam.ExtractStyleBlocksScope
--DESCRIPTION--

  If you would like users to be able to define external stylesheets, but
  only allow them to specify CSS declarations for a specific node and
  prevent them from fiddling with other elements, use this directive.
  It accepts any valid CSS selector, and will prepend this to any
  CSS declaration extracted from the document. For example, if this
  directive is set to #user-content and a user uses the
  selector a:hover, the final selector will be
  #user-content a:hover.

  The comma shorthand may be used; consider the above example, with
  #user-content, #user-content2, the final selector will
  be #user-content a:hover, #user-content2 a:hover.

  Warning: It is possible for users to bypass this measure
  using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML
  Purifier, and I am working to get it fixed. Until then, HTML Purifier
  performs a basic check to prevent this.

--# vim: et sw=4 sts=4


code:
Filter.ExtractStyleBlocks.TidyImpl
TYPE: mixed/null
VERSION: 3.1.0
DEFAULT: NULL
ALIASES: FilterParam.ExtractStyleBlocksTidyImpl
--DESCRIPTION--

  If left NULL, HTML Purifier will attempt to instantiate a csstidy
  class to use for internal cleaning. This will usually be good enough.

  However, for trusted user input, you can set this to false to
  disable cleaning. In addition, you can supply your own concrete implementation
  of Tidy's interface to use, although I don't know why you'd want to do that.

--# vim: et sw=4 sts=4

license: mit


code:
Filter.ExtractStyleBlocks
TYPE: bool
VERSION: 3.1.0
DEFAULT: false
EXTERNAL: CSSTidy
--DESCRIPTION--

  This directive turns on the style block extraction filter, which removes
  style blocks from input HTML, cleans them up with CSSTidy,
  and places them in the StyleBlocks context variable, for further
  use by you, usually to be placed in an external stylesheet, or a
  style block in the head of your document.

  Sample usage:

<![CDATA[
<?php
    header('Content-type: text/html; charset=utf-8');
    echo '<?xml version="1.0" encoding="UTF-8"?>';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

  <title>Filter.ExtractStyleBlocks</title>
<?php
    require_once '/path/to/library/HTMLPurifier.auto.php';
    require_once '/path/to/csstidy.class.php';

    $dirty = ' Some text';

    $config = HTMLPurifier_Config::createDefault();
    $config->set('Filter', 'ExtractStyleBlocks', true);
    $purifier = new HTMLPurifier($config);

    $html = $purifier->purify($dirty);

    // This implementation writes the stylesheets to the styles/ directory.
    // You can also echo the styles inside the document, but it's a bit
    // more difficult to make sure they get interpreted properly by
    // browsers; try the usual CSS armoring techniques.
    $styles = $purifier->context->get('StyleBlocks');
    $dir = 'styles/';
    if (!is_dir($dir)) mkdir($dir);
    $hash = sha1($_GET['html']);
    foreach ($styles as $i => $style) {
        file_put_contents($name = $dir . $hash . "_$i");
        echo '<link rel="stylesheet" type="text/css" href="'.$name.'" />';
    }
?>

    <?php echo $html; ?>

<![CDATA[ody>

]]>

  Warning: It is possible for a user to mount an
  imagecrash attack using this CSS. Counter-measures are difficult;
  it is not simply enough to limit the range of CSS lengths (using
  relative lengths with many nesting levels allows for large values
  to be attained without actually specifying them in the stylesheet),
  and the flexible nature of selectors makes it difficult to selectively
  disable lengths on image tags (HTML Purifier, however, does disable
  CSS width and height in inline styling). There are probably two effective
  counter measures: an explicit width and height set to auto in all
  images in your document (unlikely) or the disabling of width and
  height (somewhat reasonable). Whether or not these measures should be
  used is left to the reader.

--# vim: et sw=4 sts=4


code:
Filter.YouTube
TYPE: bool
VERSION: 3.1.0
DEFAULT: false
--DESCRIPTION--

  Warning: Deprecated in favor of %HTML.SafeObject and
  %Output.FlashCompat (turn both on to allow YouTube videos and other
  Flash content).

  This directive enables YouTube video embedding in HTML Purifier. Check
  this document
  on embedding videos for more information on what this filter does.

--# vim: et sw=4 sts=4


code:
HTML.Allowed
TYPE: itext/null
VERSION: 2.0.0
DEFAULT: NULL
--DESCRIPTION--

    This is a preferred convenience directive that combines
    %HTML.AllowedElements and %HTML.AllowedAttributes.
    Specify elements and attributes that are allowed using:
    element1[attr1|attr2],element2....  For example,
    if you would like to only allow paragraphs and links, specify
    a[href],p.  You can specify attributes that apply
    to all elements using an asterisk, e.g. *[lang].
    You can also use newlines instead of commas to separate elements.

    Warning:
    All of the constraints on the component directives are still enforced.
    The syntax is a subset of TinyMCE's valid_elements
    whitelist: directly copy-pasting it here will probably result in
    broken whitelists. If %HTML.AllowedElements or %HTML.AllowedAttributes
    are set, this directive has no effect.

--# vim: et sw=4 sts=4


code:
HTML.AllowedAttributes
TYPE: lookup/null
VERSION: 1.3.0
DEFAULT: NULL
--DESCRIPTION--

    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).

    Warning: If another directive conflicts with the
    elements here, that 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.

--# vim: et sw=4 sts=4


code:
HTML.AllowedComments
TYPE: lookup
VERSION: 4.4.0
DEFAULT: array()
--DESCRIPTION--
A whitelist which indicates what explicit comment bodies should be
allowed, modulo leading and trailing whitespace.  See also %HTML.AllowedCommentsRegexp
(these directives are union'ed together, so a comment is considered
valid if any directive deems it valid.)
--# vim: et sw=4 sts=4


code:
HTML.AllowedCommentsRegexp
TYPE: string/null
VERSION: 4.4.0
DEFAULT: NULL
--DESCRIPTION--
A regexp, which if it matches the body of a comment, indicates that
it should be allowed. Trailing and leading spaces are removed prior
to running this regular expression.
Warning: Make sure you specify
correct anchor metacharacters ^regex$, otherwise you may accept
comments that you did not mean to! In particular, the regex /foo|bar/
is probably not sufficiently strict, since it also allows foobar.
See also %HTML.AllowedComments (these directives are union'ed together,
so a comment is considered valid if any directive deems it valid.)
--# vim: et sw=4 sts=4


code:
HTML.AllowedElements
TYPE: lookup/null
VERSION: 1.3.0
DEFAULT: NULL
--DESCRIPTION--

    If HTML Purifier's tag set is unsatisfactory for your needs, you can
    overload it with your own list of tags to allow.  If you change
    this, you probably also want to change %HTML.AllowedAttributes; see
    also %HTML.Allowed which lets you set allowed elements and
    attributes at the same time.

    If you attempt to allow an element that HTML Purifier does not know
    about, HTML Purifier will raise an error.  You will need to manually
    tell HTML Purifier about this element by using the
    advanced customization features.

    Warning: If another directive conflicts with the
    elements here, that directive will win and override.

--# vim: et sw=4 sts=4


code:
HTML.AllowedModules
TYPE: lookup/null
VERSION: 2.0.0
DEFAULT: NULL
--DESCRIPTION--

    A doctype comes with a set of usual modules to use. Without having
    to mucking about with the doctypes, you can quickly activate or
    disable these modules by specifying which modules you wish to allow
    with this directive. This is most useful for unit testing specific
    modules, although end users may find it useful for their own ends.

    If you specify a module that does not exist, the manager will silently
    fail to use it, so be careful! User-defined modules are not affected
    by this directive. Modules defined in %HTML.CoreModules are not
    affected by this directive.

--# vim: et sw=4 sts=4


code:
HTML.Attr.Name.UseCDATA
TYPE: bool
DEFAULT: false
VERSION: 4.0.0
--DESCRIPTION--
The W3C specification DTD defines the name attribute to be CDATA, not ID, due
to limitations of DTD.  In certain documents, this relaxed behavior is desired,
whether it is to specify duplicate names, or to specify names that would be
illegal IDs (for example, names that begin with a digit.) Set this configuration
directive to true to use the relaxed parsing rules.
--# vim: et sw=4 sts=4


code:
HTML.BlockWrapper
TYPE: string
VERSION: 1.3.0
DEFAULT: 'p'
--DESCRIPTION--

    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,
    <blockquote>Foo</blockquote> would become
    <blockquote><p>Foo</p></blockquote>.
    The <p> tags can be replaced with whatever you desire,
    as long as it is a block level element.

--# vim: et sw=4 sts=4


code:
HTML.CoreModules
TYPE: lookup
VERSION: 2.0.0
--DEFAULT--
array (
  'Structure' => true,
  'Text' => true,
  'Hypertext' => true,
  'List' => true,
  'NonXMLCommonAttributes' => true,
  'XMLCommonAttributes' => true,
  'CommonAttributes' => true,
)
--DESCRIPTION--

<p>
    Certain modularized doctypes (XHTML, namely), have certain modules
    that must be included for the doctype to be an conforming document
    type: put those modules here. By default, XHTML's core modules
    are used. You can set this to a blank array to disable core module
    protection, but this is not recommended.
</p>
--# vim: et sw=4 sts=4


code:
HTML.CustomDoctype
TYPE: string/null
VERSION: 2.0.1
DEFAULT: NULL
--DESCRIPTION--

A custom doctype for power-users who defined their own document
type. This directive only applies when %HTML.Doctype is blank.
--# vim: et sw=4 sts=4


code:
HTML.DefinitionID
TYPE: string/null
DEFAULT: NULL
VERSION: 2.0.0
--DESCRIPTION--

    Unique identifier for a custom-built HTML definition. If you edit
    the raw version of the HTMLDefinition, introducing changes that the
    configuration object does not reflect, you must specify this variable.
    If you change your custom edits, you should change this directive, or
    clear your cache. Example:

$config = HTMLPurifier_Config::createDefault();
$config->set('HTML', 'DefinitionID', '1');
$def = $config->getHTMLDefinition();
$def->addAttribute('a', 'tabindex', 'Number');

    In the above example, the configuration is still at the defaults, but
    using the advanced API, an extra attribute has been added. The
    configuration object normally has no way of knowing that this change
    has taken place, so it needs an extra directive: %HTML.DefinitionID.
    If someone else attempts to use the default configuration, these two
    pieces of code will not clobber each other in the cache, since one has
    an extra directive attached to it.

    You must specify a value to this directive to use the
    advanced API features.

--# vim: et sw=4 sts=4


code:
HTML.DefinitionRev
TYPE: int
VERSION: 2.0.0
DEFAULT: 1
--DESCRIPTION--

<p>
    Revision identifier for your custom definition specified in
    %HTML.DefinitionID.  This serves the same purpose: uniquely identifying
    your custom definition, but this one does so in a chronological
    context: revision 3 is more up-to-date then revision 2.  Thus, when
    this gets incremented, the cache handling is smart enough to clean
    up any older revisions of your definition as well as flush the
    cache.
</p>
--# vim: et sw=4 sts=4


code:
HTML.Doctype
TYPE: string/null
DEFAULT: NULL
--DESCRIPTION--
Doctype to use during filtering. Technically speaking this is not actually
a doctype (as it does not identify a corresponding DTD), but we are using
this name for sake of simplicity. When non-blank, this will override any
older directives like %HTML.XHTML or %HTML.Strict.
--ALLOWED--
'HTML 4.01 Transitional', 'HTML 4.01 Strict', 'XHTML 1.0 Transitional', 'XHTML 1.0 Strict', 'XHTML 1.1'
--# vim: et sw=4 sts=4


code:
HTML.FlashAllowFullScreen
TYPE: bool
VERSION: 4.2.0
DEFAULT: false
--DESCRIPTION--

    Whether or not to permit embedded Flash content from
    %HTML.SafeObject to expand to the full screen.  Corresponds to
    the allowFullScreen parameter.

--# vim: et sw=4 sts=4


code:
HTML.ForbiddenAttributes
TYPE: lookup
VERSION: 3.1.0
DEFAULT: array()
--DESCRIPTION--

    While this directive is similar to %HTML.AllowedAttributes, for
    forwards-compatibility with XML, this attribute has a different syntax. Instead of
    tag.attr, use tag@attr. To disallow href
    attributes in a tags, set this directive to
    a@href. You can also disallow an attribute globally with
    attr or *@attr (either syntax is fine; the latter
    is provided for consistency with %HTML.AllowedAttributes).

    Warning: This directive complements %HTML.ForbiddenElements,
    accordingly, check
    out that directive for a discussion of why you
    should think twice before using this directive.

--# vim: et sw=4 sts=4


code:
HTML.ForbiddenElements
TYPE: lookup
VERSION: 3.1.0
DEFAULT: array()
--DESCRIPTION--

    This was, perhaps, the most requested feature ever in HTML
    Purifier. Please don't abuse it! This is the logical inverse of
    %HTML.AllowedElements, and it will override that directive, or any
    other directive.

    If possible, %HTML.Allowed is recommended over this directive, because it
    can sometimes be difficult to tell whether or not you've forbidden all of
    the behavior you would like to disallow. If you forbid img
    with the expectation of preventing images on your site, you'll be in for
    a nasty surprise when people start using the background-image
    CSS property.

--# vim: et sw=4 sts=4


code:
HTML.MaxImgLength
TYPE: int/null
DEFAULT: 1200
VERSION: 3.1.1
--DESCRIPTION--

 This directive controls the maximum number of pixels in the width and
 height attributes in img tags. This is
 in place to prevent imagecrash attacks, disable with null at your own risk.
 This directive is similar to %CSS.MaxImgLength, and both should be
 concurrently edited, although there are
 subtle differences in the input format (the HTML max is an integer).

--# vim: et sw=4 sts=4


code:
HTML.Nofollow
TYPE: bool
VERSION: 4.3.0
DEFAULT: FALSE
--DESCRIPTION--
If enabled, nofollow rel attributes are added to all outgoing links.
--# vim: et sw=4 sts=4


code:
HTML.Parent
TYPE: string
VERSION: 1.3.0
DEFAULT: 'div'
--DESCRIPTION--

<p>
    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.
</p>
--# vim: et sw=4 sts=4


code:
HTML.Proprietary
TYPE: bool
VERSION: 3.1.0
DEFAULT: false
--DESCRIPTION--

    Whether or not to allow proprietary elements and attributes in your
    documents, as per HTMLPurifier_HTMLModule_Proprietary.
    Warning: This can cause your documents to stop
    validating!

--# vim: et sw=4 sts=4


code:
HTML.SafeEmbed
TYPE: bool
VERSION: 3.1.1
DEFAULT: false
--DESCRIPTION--

    Whether or not to permit embed tags in documents, with a number of extra
    security features added to prevent script execution. This is similar to
    what websites like MySpace do to embed tags. Embed is a proprietary
    element and will cause your website to stop validating; you should
    see if you can use %Output.FlashCompat with %HTML.SafeObject instead
    first.

--# vim: et sw=4 sts=4


code:
HTML.SafeIframe
TYPE: bool
VERSION: 4.4.0
DEFAULT: false
--DESCRIPTION--
<p>
    Whether or not to permit iframe tags in untrusted documents.  This
    directive must be accompanied by a whitelist of permitted iframes,
    such as %URI.SafeIframeRegexp, otherwise it will fatally error.
    This directive has no effect on strict doctypes, as iframes are not
    valid.
</p>
--# vim: et sw=4 sts=4


code:
HTML.SafeObject
TYPE: bool
VERSION: 3.1.1
DEFAULT: false
--DESCRIPTION--
<p>
    Whether or not to permit object tags in documents, with a number of extra
    security features added to prevent script execution. This is similar to
    what websites like MySpace do to object tags.  You should also enable
    %Output.FlashCompat in order to generate Internet Explorer
    compatibility code for your object tags.
</p>
--# vim: et sw=4 sts=4


code:
HTML.SafeScripting
TYPE: lookup
VERSION: 4.5.0
DEFAULT: array()
--DESCRIPTION--
<p>
    Whether or not to permit script tags to external scripts in documents.
    Inline scripting is not allowed, and the script must match an explicit whitelist.
</p>
--# vim: et sw=4 sts=4


code:
HTML.Strict
TYPE: bool
VERSION: 1.3.0
DEFAULT: false
DEPRECATED-VERSION: 1.7.0
DEPRECATED-USE: HTML.Doctype
--DESCRIPTION--
Determines whether or not to use Transitional (loose) or Strict rulesets.
--# vim: et sw=4 sts=4


code:
HTML.TargetBlank
TYPE: bool
VERSION: 4.4.0
DEFAULT: FALSE
--DESCRIPTION--
If enabled, target=blank attributes are added to all outgoing links.
(This includes links from an HTTPS version of a page to an HTTP version.)
--# vim: et sw=4 sts=4


code:
--# vim: et sw=4 sts=4
HTML.TargetNoopener
TYPE: bool
VERSION: 4.8.0
DEFAULT: TRUE
--DESCRIPTION--
If enabled, noopener rel attributes are added to links which have
a target attribute associated with them.  This prevents malicious
destinations from overwriting the original window.
--# vim: et sw=4 sts=4


code:
HTML.TargetNoreferrer
TYPE: bool
VERSION: 4.8.0
DEFAULT: TRUE
--DESCRIPTION--
If enabled, noreferrer rel attributes are added to links which have
a target attribute associated with them.  This prevents malicious
destinations from overwriting the original window.
--# vim: et sw=4 sts=4


code:
HTML.TidyAdd
TYPE: lookup
VERSION: 2.0.0
DEFAULT: array()
--DESCRIPTION--

Fixes to add to the default set of Tidy fixes as per your level.
--# vim: et sw=4 sts=4


code:
HTML.TidyLevel
TYPE: string
VERSION: 2.0.0
DEFAULT: 'medium'
--DESCRIPTION--

General level of cleanliness the Tidy module should enforce.
There are four allowed values:

none

No extra tidying should be done

light

Only fix elements that would be discarded otherwise due to
    lack of support in doctype

medium

Enforce best practices

heavy

Transform all deprecated elements and attributes to standards
    compliant equivalents

--ALLOWED--
'none', 'light', 'medium', 'heavy'
--# vim: et sw=4 sts=4


code:
HTML.TidyRemove
TYPE: lookup
VERSION: 2.0.0
DEFAULT: array()
--DESCRIPTION--

Fixes to remove from the default set of Tidy fixes as per your level.
--# vim: et sw=4 sts=4


code:
HTML.Trusted
TYPE: bool
VERSION: 2.0.0
DEFAULT: false
--DESCRIPTION--
Indicates whether or not the user input is trusted or not. If the input is
trusted, a more expansive set of allowed tags and attributes will be used.
See also %CSS.Trusted.
--# vim: et sw=4 sts=4


code:
HTML.XHTML
TYPE: bool
DEFAULT: true
VERSION: 1.1.0
DEPRECATED-VERSION: 1.7.0
DEPRECATED-USE: HTML.Doctype
--DESCRIPTION--
Determines whether or not output is XHTML 1.0 or HTML 4.01 flavor.
--ALIASES--
Core.XHTML
--# vim: et sw=4 sts=4


code:
Output.CommentScriptContents
TYPE: bool
VERSION: 2.0.0
DEFAULT: true
--DESCRIPTION--
Determines whether or not HTML Purifier should attempt to fix up the
contents of script tags for legacy browsers with comments.
--ALIASES--
Core.CommentScriptContents
--# vim: et sw=4 sts=4


code:
Output.FixInnerHTML
TYPE: bool
VERSION: 4.3.0
DEFAULT: true
--DESCRIPTION--

  If true, HTML Purifier will protect against Internet Explorer's
  mishandling of the innerHTML attribute by appending
  a space to any attribute that does not contain angled brackets, spaces
  or quotes, but contains a backtick.  This slightly changes the
  semantics of any given attribute, so if this is unacceptable and
  you do not use innerHTML on any of your pages, you can
  turn this directive off.

--# vim: et sw=4 sts=4


code:
Output.FlashCompat
TYPE: bool
VERSION: 4.1.0
DEFAULT: false
--DESCRIPTION--
<p>
  If true, HTML Purifier will generate Internet Explorer compatibility
  code for all object code.  This is highly recommended if you enable
  %HTML.SafeObject.
</p>
--# vim: et sw=4 sts=4


code:
Output.Newline
TYPE: string/null
VERSION: 2.0.1
DEFAULT: NULL
--DESCRIPTION--

<p>
    Newline string to format final output with. If left null, HTML Purifier
    will auto-detect the default newline type of the system and use that;
    you can manually override it here. Remember, \r\n is Windows, \r
    is Mac, and \n is Unix.
</p>
--# vim: et sw=4 sts=4


code:
Output.SortAttr
TYPE: bool
VERSION: 3.2.0
DEFAULT: false
--DESCRIPTION--

  If true, HTML Purifier will sort attributes by name before writing them back
  to the document, converting a tag like: <el b="" a="" c="" />
  to <el a="" b="" c="" />. This is a workaround for
  a bug in FCKeditor which causes it to swap attributes order, adding noise
  to text diffs. If you're not seeing this bug, chances are, you don't need
  this directive.

--# vim: et sw=4 sts=4


code:
Output.TidyFormat
TYPE: bool
VERSION: 1.1.1
DEFAULT: false
--DESCRIPTION--

    Determines whether or not to run Tidy on the final output for pretty
    formatting reasons, such as indentation and wrap.

    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.

    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).

--ALIASES--
Core.TidyFormat
--# vim: et sw=4 sts=4


code:
Test.ForceNoIconv
TYPE: bool
DEFAULT: false
--DESCRIPTION--
When set to true, HTMLPurifier_Encoder will act as if iconv does not exist
and use only pure PHP implementations.
--# vim: et sw=4 sts=4


code:
URI.AllowedSchemes
TYPE: lookup
--DEFAULT--
array (
  'http' => true,
  'https' => true,
  'mailto' => true,
  'ftp' => true,
  'nntp' => true,
  'news' => true,
  'tel' => true,
)
--DESCRIPTION--
Whitelist that defines the schemes that a URI is allowed to have.  This
prevents XSS attacks from using pseudo-schemes like javascript or mocha.
There is also support for the data and file
URI schemes, but they are not enabled by default.
--# vim: et sw=4 sts=4


code:
URI.Base
TYPE: string/null
VERSION: 2.1.0
DEFAULT: NULL
--DESCRIPTION--

    The base URI is the URI of the document this purified HTML will be
    inserted into.  This information is important if HTML Purifier needs
    to calculate absolute URIs from relative URIs, such as when %URI.MakeAbsolute
    is on.  You may use a non-absolute URI for this value, but behavior
    may vary (%URI.MakeAbsolute deals nicely with both absolute and
    relative paths, but forwards-compatibility is not guaranteed).
    Warning: If set, the scheme on this URI
    overrides the one specified by %URI.DefaultScheme.

--# vim: et sw=4 sts=4


code:
URI.DefaultScheme
TYPE: string/null
DEFAULT: 'http'
--DESCRIPTION--

    Defines through what scheme the output will be served, in order to
    select the proper object validator when no scheme information is present.

    Starting with HTML Purifier 4.9.0, the default scheme can be null, in
    which case we reject all URIs which do not have explicit schemes.

--# vim: et sw=4 sts=4


code:
URI.DefinitionID
TYPE: string/null
VERSION: 2.1.0
DEFAULT: NULL
--DESCRIPTION--

<p>
    Unique identifier for a custom-built URI definition. If you  want
    to add custom URIFilters, you must specify this value.
</p>
--# vim: et sw=4 sts=4


code:
URI.DefinitionRev
TYPE: int
VERSION: 2.1.0
DEFAULT: 1
--DESCRIPTION--

<p>
    Revision identifier for your custom definition. See
    %HTML.DefinitionRev for details.
</p>
--# vim: et sw=4 sts=4


code:
URI.Disable
TYPE: bool
VERSION: 1.3.0
DEFAULT: false
--DESCRIPTION--

<p>
    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).
</p>

--ALIASES--
Attr.DisableURI
--# vim: et sw=4 sts=4


code:
URI.DisableExternal
TYPE: bool
VERSION: 1.2.0
DEFAULT: false
--DESCRIPTION--
Disables links to external websites.  This is a highly effective anti-spam
and anti-pagerank-leech measure, but comes at a hefty price: nolinks 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.
--# vim: et sw=4 sts=4


code:
URI.DisableExternalResources
TYPE: bool
VERSION: 1.3.0
DEFAULT: false
--DESCRIPTION--
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.
--# vim: et sw=4 sts=4


code:
URI.DisableResources
TYPE: bool
VERSION: 4.2.0
DEFAULT: false
--DESCRIPTION--

    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.

    Note: While this directive has been available since 1.3.0,
    it didn't actually start doing anything until 4.2.0.

--# vim: et sw=4 sts=4


code:
URI.Host
TYPE: string/null
VERSION: 1.2.0
DEFAULT: NULL
--DESCRIPTION--

    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.
    Note: This directive overrides %URI.Base because
    a given page may be on a sub-domain, but you wish HTML Purifier to be
    more relaxed and allow some of the parent domains too.

--# vim: et sw=4 sts=4


code:
URI.HostBlacklist
TYPE: list
VERSION: 1.3.0
DEFAULT: array()
--DESCRIPTION--
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>.
--# vim: et sw=4 sts=4


code:
URI.MakeAbsolute
TYPE: bool
VERSION: 2.1.0
DEFAULT: false
--DESCRIPTION--

<p>
    Converts all URIs into absolute forms. This is useful when the HTML
    being filtered assumes a specific base path, but will actually be
    viewed in a different context (and setting an alternate base URI is
    not possible). %URI.Base must be set for this directive to work.
</p>
--# vim: et sw=4 sts=4


code:
URI.Munge
TYPE: string/null
VERSION: 1.3.0
DEFAULT: NULL
--DESCRIPTION--

    Munges all browsable (usually http, https and ftp)
    absolute URIs into another URI, usually a URI redirection service.
    This directive accepts a URI, formatted with a %s where
    the url-encoded original URI should be inserted (sample:
    http://www.google.com/url?q=%s).

    Uses for this directive:

        Prevent PageRank leaks, while being fairly transparent
        to users (you may also want to add some client side JavaScript to
        override the text in the statusbar). Notice:
        Many security experts believe that this form of protection does not deter spam-bots.

        Redirect users to a splash page telling them they are leaving your
        website. While this is poor usability practice, it is often mandated
        in corporate environments.

    Prior to HTML Purifier 3.1.1, this directive also enabled the munging
    of browsable external resources, which could break things if your redirection
    script was a splash page or used meta tags. To revert to
    previous behavior, please use %URI.MungeResources.

    You may want to also use %URI.MungeSecretKey along with this directive
    in order to enforce what URIs your redirector script allows. Open
    redirector scripts can be a security risk and negatively affect the
    reputation of your domain name.

    Starting with HTML Purifier 3.1.1, there is also these substitutions:

            Key
            Description
            Example <a href="">

            %r
            1 - The URI embeds a resource
(blank) - The URI is merely a link

            %n
            The name of the tag this URI came from
            a

            %m
            The name of the attribute this URI came from
            href

            %p
            The name of the CSS property this URI came from, or blank if irrelevant

    Admittedly, these letters are somewhat arbitrary; the only stipulation
    was that they couldn't be a through f. r is for resource (I would have preferred
    e, but you take what you can get), n is for name, m
    was picked because it came after n (and I couldn't use a), p is for
    property.

--# vim: et sw=4 sts=4


code:
URI.MungeResources
TYPE: bool
VERSION: 3.1.1
DEFAULT: false
--DESCRIPTION--

    If true, any URI munging directives like %URI.Munge
    will also apply to embedded resources, such as <img src="">.
    Be careful enabling this directive if you have a redirector script
    that does not use the Location HTTP header; all of your images
    and other embedded resources will break.

    Warning: It is strongly advised you use this in conjunction
    %URI.MungeSecretKey to mitigate the security risk of an open redirector.

--# vim: et sw=4 sts=4


code:
URI.MungeSecretKey
TYPE: string/null
VERSION: 3.1.1
DEFAULT: NULL
--DESCRIPTION--

    This directive enables secure checksum generation along with %URI.Munge.
    It should be set to a secure key that is not shared with anyone else.
    The checksum can be placed in the URI using %t. Use of this checksum
    affords an additional level of protection by allowing a redirector
    to check if a URI has passed through HTML Purifier with this line:

$checksum === hash_hmac("sha256", $url, $secret_key)

    If the output is TRUE, the redirector script should accept the URI.

    Please note that it would still be possible for an attacker to procure
    secure hashes en-mass by abusing your website's Preview feature or the
    like, but this service affords an additional level of protection
    that should be combined with website blacklisting.

    Remember this has no effect if %URI.Munge is not on.

--# vim: et sw=4 sts=4


code:
URI.OverrideAllowedSchemes
TYPE: bool
DEFAULT: true
--DESCRIPTION--
If this is set to true (which it is by default), you can override
%URI.AllowedSchemes by simply registering a HTMLPurifier_URIScheme to the
registry.  If false, you will also have to update that directive in order
to add more schemes.
--# vim: et sw=4 sts=4


code:
URI.SafeIframeRegexp
TYPE: string/null
VERSION: 4.4.0
DEFAULT: NULL
--DESCRIPTION--

    A PCRE regular expression that will be matched against an iframe URI.  This is
    a relatively inflexible scheme, but works well enough for the most common
    use-case of iframes: embedded video.  This directive only has an effect if
    %HTML.SafeIframe is enabled.  Here are some example values:

%^http://www.youtube.com/embed/% - Allow YouTube videos

%^http://player.vimeo.com/video/% - Allow Vimeo videos

%^http://(www.youtube.com/embed/|player.vimeo.com/video/)% - Allow both

    Note that this directive does not give you enough granularity to, say, disable
    all autoplay videos.  Pipe up on the HTML Purifier forums if this
    is a capability you want.

--# vim: et sw=4 sts=4


code:
name = "HTML Purifier"

; vim: et sw=4 sts=4


code:
<?php

/**
 * @todo Unit test
 */
class HTMLPurifier_ContentSets
{

    /**
     * List of content set strings (pipe separators) indexed by name.
     * @type array
     */
    public $info = array();

    /**
     * List of content set lookups (element => true) indexed by name.
     * @type array
     * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets
     */
    public $lookup = array();

    /**
     * Synchronized list of defined content sets (keys of info).
     * @type array
     */
    protected $keys = array();
    /**
     * Synchronized list of defined content values (values of info).
     * @type array
     */
    protected $values = array();

    /**
     * Merges in module's content sets, expands identifiers in the content
     * sets and populates the keys, values and lookup member variables.
     * @param HTMLPurifier_HTMLModule[] $modules List of HTMLPurifier_HTMLModule
     */
    public function __construct($modules)
    {
        if (!is_array($modules)) {
            $modules = array($modules);
        }
        // populate content_sets based on module hints
        // sorry, no way of overloading
        foreach ($modules as $module) {
            foreach ($module->content_sets as $key => $value) {
                $temp = $this->convertToLookup($value);
                if (isset($this->lookup[$key])) {
                    // add it into the existing content set
                    $this->lookup[$key] = array_merge($this->lookup[$key], $temp);
                } else {
                    $this->lookup[$key] = $temp;
                }
            }
        }
        $old_lookup = false;
        while ($old_lookup !== $this->lookup) {
            $old_lookup = $this->lookup;
            foreach ($this->lookup as $i => $set) {
                $add = array();
                foreach ($set as $element => $x) {
                    if (isset($this->lookup[$element])) {
                        $add += $this->lookup[$element];
                        unset($this->lookup[$i][$element]);
                    }
                }
                $this->lookup[$i] += $add;
            }
        }

        foreach ($this->lookup as $key => $lookup) {
            $this->info[$key] = implode(' | ', array_keys($lookup));
        }
        $this->keys   = array_keys($this->info);
        $this->values = array_values($this->info);
    }

    /**
     * Accepts a definition; generates and assigns a ChildDef for it
     * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef reference
     * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
     */
    public function generateChildDef(&$def, $module)
    {
        if (!empty($def->child)) { // already done!
            return;
        }
        $content_model = $def->content_model;
        if (is_string($content_model)) {
            // Assume that $this->keys is alphanumeric
            $def->content_model = preg_replace_callback(
                '/\b(' . implode('|', $this->keys) . ')\b/',
                array($this, 'generateChildDefCallback'),
                $content_model
            );
            //$def->content_model = str_replace(
            //    $this->keys, $this->values, $content_model);
        }
        $def->child = $this->getChildDef($def, $module);
    }

    public function generateChildDefCallback($matches)
    {
        return $this->info[$matches[0]];
    }

    /**
     * Instantiates a ChildDef based on content_model and content_model_type
     * member variables in HTMLPurifier_ElementDef
     * @note This will also defer to modules for custom HTMLPurifier_ChildDef
     *       subclasses that need content set expansion
     * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef to have ChildDef extracted
     * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
     * @return HTMLPurifier_ChildDef corresponding to ElementDef
     */
    public function getChildDef($def, $module)
    {
        $value = $def->content_model;
        if (is_object($value)) {
            trigger_error(
                'Literal object child definitions should be stored in '.
                'ElementDef->child not ElementDef->content_model',
                E_USER_NOTICE
            );
            return $value;
        }
        switch ($def->content_model_type) {
            case 'required':
                return new HTMLPurifier_ChildDef_Required($value);
            case 'optional':
                return new HTMLPurifier_ChildDef_Optional($value);
            case 'empty':
                return new HTMLPurifier_ChildDef_Empty();
            case 'custom':
                return new HTMLPurifier_ChildDef_Custom($value);
        }
        // defer to its module
        $return = false;
        if ($module->defines_child_def) { // save a func call
            $return = $module->getChildDef($def);
        }
        if ($return !== false) {
            return $return;
        }
        // error-out
        trigger_error(
            'Could not determine which ChildDef class to instantiate',
            E_USER_ERROR
        );
        return false;
    }

    /**
     * Converts a string list of elements separated by pipes into
     * a lookup array.
     * @param string $string List of elements
     * @return array Lookup array of elements
     */
    protected function convertToLookup($string)
    {
        $array = explode('|', str_replace(' ', '', $string));
        $ret = array();
        foreach ($array as $k) {
            $ret[$k] = true;
        }
        return $ret;
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Registry object that contains information about the current context.
 * @warning Is a bit buggy when variables are set to null: it thinks
 *          they don't exist! So use false instead, please.
 * @note Since the variables Context deals with may not be objects,
 *       references are very important here! Do not remove!
 */
class HTMLPurifier_Context
{

    /**
     * Private array that stores the references.
     * @type array
     */
    private $_storage = array();

    /**
     * Registers a variable into the context.
     * @param string $name String name
     * @param mixed $ref Reference to variable to be registered
     */
    public function register($name, &$ref)
    {
        if (array_key_exists($name, $this->_storage)) {
            trigger_error(
                "Name $name produces collision, cannot re-register",
                E_USER_ERROR
            );
            return;
        }
        $this->_storage[$name] =& $ref;
    }

    /**
     * Retrieves a variable reference from the context.
     * @param string $name String name
     * @param bool $ignore_error Boolean whether or not to ignore error
     * @return mixed
     */
    public function &get($name, $ignore_error = false)
    {
        if (!array_key_exists($name, $this->_storage)) {
            if (!$ignore_error) {
                trigger_error(
                    "Attempted to retrieve non-existent variable $name",
                    E_USER_ERROR
                );
            }
            $var = null; // so we can return by reference
            return $var;
        }
        return $this->_storage[$name];
    }

    /**
     * Destroys a variable in the context.
     * @param string $name String name
     */
    public function destroy($name)
    {
        if (!array_key_exists($name, $this->_storage)) {
            trigger_error(
                "Attempted to destroy non-existent variable $name",
                E_USER_ERROR
            );
            return;
        }
        unset($this->_storage[$name]);
    }

    /**
     * Checks whether or not the variable exists.
     * @param string $name String name
     * @return bool
     */
    public function exists($name)
    {
        return array_key_exists($name, $this->_storage);
    }

    /**
     * Loads a series of variables from an associative array
     * @param array $context_array Assoc array of variables to load
     */
    public function loadArray($context_array)
    {
        foreach ($context_array as $key => $discard) {
            $this->register($key, $context_array[$key]);
        }
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Super-class for definition datatype objects, implements serialization
 * functions for the class.
 */
abstract class HTMLPurifier_Definition
{

    /**
     * Has setup() been called yet?
     * @type bool
     */
    public $setup = false;

    /**
     * If true, write out the final definition object to the cache after
     * setup.  This will be true only if all invocations to get a raw
     * definition object are also optimized.  This does not cause file
     * system thrashing because on subsequent calls the cached object
     * is used and any writes to the raw definition object are short
     * circuited.  See enduser-customize.html for the high-level
     * picture.
     * @type bool
     */
    public $optimized = null;

    /**
     * What type of definition is it?
     * @type string
     */
    public $type;

    /**
     * Sets up the definition object into the final form, something
     * not done by the constructor
     * @param HTMLPurifier_Config $config
     */
    abstract protected function doSetup($config);

    /**
     * Setup function that aborts if already setup
     * @param HTMLPurifier_Config $config
     */
    public function setup($config)
    {
        if ($this->setup) {
            return;
        }
        $this->setup = true;
        $this->doSetup($config);
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Abstract class representing Definition cache managers that implements
 * useful common methods and is a factory.
 * @todo Create a separate maintenance file advanced users can use to
 *       cache their custom HTMLDefinition, which can be loaded
 *       via a configuration directive
 * @todo Implement memcached
 */
abstract class HTMLPurifier_DefinitionCache
{
    /**
     * @type string
     */
    public $type;

    /**
     * @param string $type Type of definition objects this instance of the
     *      cache will handle.
     */
    public function __construct($type)
    {
        $this->type = $type;
    }

    /**
     * Generates a unique identifier for a particular configuration
     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
     * @return string
     */
    public function generateKey($config)
    {
        return $config->version . ',' . // possibly replace with function calls
               $config->getBatchSerial($this->type) . ',' .
               $config->get($this->type . '.DefinitionRev');
    }

    /**
     * Tests whether or not a key is old with respect to the configuration's
     * version and revision number.
     * @param string $key Key to test
     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config to test against
     * @return bool
     */
    public function isOld($key, $config)
    {
        if (substr_count($key, ',') < 2) {
            return true;
        }
        list($version, $hash, $revision) = explode(',', $key, 3);
        $compare = version_compare($version, $config->version);
        // version mismatch, is always old
        if ($compare != 0) {
            return true;
        }
        // versions match, ids match, check revision number
        if ($hash == $config->getBatchSerial($this->type) &&
            $revision < $config->get($this->type . '.DefinitionRev')) {
            return true;
        }
        return false;
    }

    /**
     * Checks if a definition's type jives with the cache's type
     * @note Throws an error on failure
     * @param HTMLPurifier_Definition $def Definition object to check
     * @return bool true if good, false if not
     */
    public function checkDefType($def)
    {
        if ($def->type !== $this->type) {
            trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}");
            return false;
        }
        return true;
    }

    /**
     * Adds a definition object to the cache
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     */
    abstract public function add($def, $config);

    /**
     * Unconditionally saves a definition object to the cache
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     */
    abstract public function set($def, $config);

    /**
     * Replace an object in the cache
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     */
    abstract public function replace($def, $config);

    /**
     * Retrieves a definition object from the cache
     * @param HTMLPurifier_Config $config
     */
    abstract public function get($config);

    /**
     * Removes a definition object to the cache
     * @param HTMLPurifier_Config $config
     */
    abstract public function remove($config);

    /**
     * Clears all objects from cache
     * @param HTMLPurifier_Config $config
     */
    abstract public function flush($config);

    /**
     * Clears all expired (older version or revision) objects from cache
     * @note Be careful implementing this method as flush. Flush must
     *       not interfere with other Definition types, and cleanup()
     *       should not be repeatedly called by userland code.
     * @param HTMLPurifier_Config $config
     */
    abstract public function cleanup($config);
}

// vim: et sw=4 sts=4


code:
<?php

class HTMLPurifier_DefinitionCache_Decorator extends HTMLPurifier_DefinitionCache
{

    /**
     * Cache object we are decorating
     * @type HTMLPurifier_DefinitionCache
     */
    public $cache;

    /**
     * The name of the decorator
     * @var string
     */
    public $name;

    public function __construct()
    {
    }

    /**
     * Lazy decorator function
     * @param HTMLPurifier_DefinitionCache $cache Reference to cache object to decorate
     * @return HTMLPurifier_DefinitionCache_Decorator
     */
    public function decorate(&$cache)
    {
        $decorator = $this->copy();
        // reference is necessary for mocks in PHP 4
        $decorator->cache =& $cache;
        $decorator->type = $cache->type;
        return $decorator;
    }

    /**
     * Cross-compatible clone substitute
     * @return HTMLPurifier_DefinitionCache_Decorator
     */
    public function copy()
    {
        return new HTMLPurifier_DefinitionCache_Decorator();
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function add($def, $config)
    {
        return $this->cache->add($def, $config);
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function set($def, $config)
    {
        return $this->cache->set($def, $config);
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function replace($def, $config)
    {
        return $this->cache->replace($def, $config);
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function get($config)
    {
        return $this->cache->get($config);
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function remove($config)
    {
        return $this->cache->remove($config);
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function flush($config)
    {
        return $this->cache->flush($config);
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function cleanup($config)
    {
        return $this->cache->cleanup($config);
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Definition cache decorator class that cleans up the cache
 * whenever there is a cache miss.
 */
class HTMLPurifier_DefinitionCache_Decorator_Cleanup extends HTMLPurifier_DefinitionCache_Decorator
{
    /**
     * @type string
     */
    public $name = 'Cleanup';

    /**
     * @return HTMLPurifier_DefinitionCache_Decorator_Cleanup
     */
    public function copy()
    {
        return new HTMLPurifier_DefinitionCache_Decorator_Cleanup();
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function add($def, $config)
    {
        $status = parent::add($def, $config);
        if (!$status) {
            parent::cleanup($config);
        }
        return $status;
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function set($def, $config)
    {
        $status = parent::set($def, $config);
        if (!$status) {
            parent::cleanup($config);
        }
        return $status;
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function replace($def, $config)
    {
        $status = parent::replace($def, $config);
        if (!$status) {
            parent::cleanup($config);
        }
        return $status;
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function get($config)
    {
        $ret = parent::get($config);
        if (!$ret) {
            parent::cleanup($config);
        }
        return $ret;
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Definition cache decorator class that saves all cache retrievals
 * to PHP's memory; good for unit tests or circumstances where
 * there are lots of configuration objects floating around.
 */
class HTMLPurifier_DefinitionCache_Decorator_Memory extends HTMLPurifier_DefinitionCache_Decorator
{
    /**
     * @type array
     */
    protected $definitions;

    /**
     * @type string
     */
    public $name = 'Memory';

    /**
     * @return HTMLPurifier_DefinitionCache_Decorator_Memory
     */
    public function copy()
    {
        return new HTMLPurifier_DefinitionCache_Decorator_Memory();
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function add($def, $config)
    {
        $status = parent::add($def, $config);
        if ($status) {
            $this->definitions[$this->generateKey($config)] = $def;
        }
        return $status;
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function set($def, $config)
    {
        $status = parent::set($def, $config);
        if ($status) {
            $this->definitions[$this->generateKey($config)] = $def;
        }
        return $status;
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function replace($def, $config)
    {
        $status = parent::replace($def, $config);
        if ($status) {
            $this->definitions[$this->generateKey($config)] = $def;
        }
        return $status;
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function get($config)
    {
        $key = $this->generateKey($config);
        if (isset($this->definitions[$key])) {
            return $this->definitions[$key];
        }
        $this->definitions[$key] = parent::get($config);
        return $this->definitions[$key];
    }
}

// vim: et sw=4 sts=4


code:
<?php

require_once 'HTMLPurifier/DefinitionCache/Decorator.php';

/**
 * Definition cache decorator template.
 */
class HTMLPurifier_DefinitionCache_Decorator_Template extends HTMLPurifier_DefinitionCache_Decorator
{

    /**
     * @type string
     */
    public $name = 'Template'; // replace this

    public function copy()
    {
        // replace class name with yours
        return new HTMLPurifier_DefinitionCache_Decorator_Template();
    }

    // remove methods you don't need

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function add($def, $config)
    {
        return parent::add($def, $config);
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function set($def, $config)
    {
        return parent::set($def, $config);
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function replace($def, $config)
    {
        return parent::replace($def, $config);
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function get($config)
    {
        return parent::get($config);
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function flush($config)
    {
        return parent::flush($config);
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return mixed
     */
    public function cleanup($config)
    {
        return parent::cleanup($config);
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Null cache object to use when no caching is on.
 */
class HTMLPurifier_DefinitionCache_Null extends HTMLPurifier_DefinitionCache
{

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return bool
     */
    public function add($def, $config)
    {
        return false;
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return bool
     */
    public function set($def, $config)
    {
        return false;
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return bool
     */
    public function replace($def, $config)
    {
        return false;
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return bool
     */
    public function remove($config)
    {
        return false;
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return bool
     */
    public function get($config)
    {
        return false;
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return bool
     */
    public function flush($config)
    {
        return false;
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return bool
     */
    public function cleanup($config)
    {
        return false;
    }
}

// vim: et sw=4 sts=4


code:
<?php

class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCache
{

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return int|bool
     */
    public function add($def, $config)
    {
        if (!$this->checkDefType($def)) {
            return;
        }
        $file = $this->generateFilePath($config);
        if (file_exists($file)) {
            return false;
        }
        if (!$this->_prepareDir($config)) {
            return false;
        }
        return $this->_write($file, serialize($def), $config);
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return int|bool
     */
    public function set($def, $config)
    {
        if (!$this->checkDefType($def)) {
            return;
        }
        $file = $this->generateFilePath($config);
        if (!$this->_prepareDir($config)) {
            return false;
        }
        return $this->_write($file, serialize($def), $config);
    }

    /**
     * @param HTMLPurifier_Definition $def
     * @param HTMLPurifier_Config $config
     * @return int|bool
     */
    public function replace($def, $config)
    {
        if (!$this->checkDefType($def)) {
            return;
        }
        $file = $this->generateFilePath($config);
        if (!file_exists($file)) {
            return false;
        }
        if (!$this->_prepareDir($config)) {
            return false;
        }
        return $this->_write($file, serialize($def), $config);
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return bool|HTMLPurifier_Config
     */
    public function get($config)
    {
        $file = $this->generateFilePath($config);
        if (!file_exists($file)) {
            return false;
        }
        return unserialize(file_get_contents($file));
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return bool
     */
    public function remove($config)
    {
        $file = $this->generateFilePath($config);
        if (!file_exists($file)) {
            return false;
        }
        return unlink($file);
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return bool
     */
    public function flush($config)
    {
        if (!$this->_prepareDir($config)) {
            return false;
        }
        $dir = $this->generateDirectoryPath($config);
        $dh = opendir($dir);
        // Apparently, on some versions of PHP, readdir will return
        // an empty string if you pass an invalid argument to readdir.
        // So you need this test.  See #49.
        if (false === $dh) {
            return false;
        }
        while (false !== ($filename = readdir($dh))) {
            if (empty($filename)) {
                continue;
            }
            if ($filename[0] === '.') {
                continue;
            }
            unlink($dir . '/' . $filename);
        }
        closedir($dh);
        return true;
    }

    /**
     * @param HTMLPurifier_Config $config
     * @return bool
     */
    public function cleanup($config)
    {
        if (!$this->_prepareDir($config)) {
            return false;
        }
        $dir = $this->generateDirectoryPath($config);
        $dh = opendir($dir);
        // See #49 (and above).
        if (false === $dh) {
            return false;
        }
        while (false !== ($filename = readdir($dh))) {
            if (empty($filename)) {
                continue;
            }
            if ($filename[0] === '.') {
                continue;
            }
            $key = substr($filename, 0, strlen($filename) - 4);
            if ($this->isOld($key, $config)) {
                unlink($dir . '/' . $filename);
            }
        }
        closedir($dh);
        return true;
    }

    /**
     * Generates the file path to the serial file corresponding to
     * the configuration and definition name
     * @param HTMLPurifier_Config $config
     * @return string
     * @todo Make protected
     */
    public function generateFilePath($config)
    {
        $key = $this->generateKey($config);
        return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
    }

    /**
     * Generates the path to the directory contain this cache's serial files
     * @param HTMLPurifier_Config $config
     * @return string
     * @note No trailing slash
     * @todo Make protected
     */
    public function generateDirectoryPath($config)
    {
        $base = $this->generateBaseDirectoryPath($config);
        return $base . '/' . $this->type;
    }

    /**
     * Generates path to base directory that contains all definition type
     * serials
     * @param HTMLPurifier_Config $config
     * @return mixed|string
     * @todo Make protected
     */
    public function generateBaseDirectoryPath($config)
    {
        $base = $config->get('Cache.SerializerPath');
        $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
        return $base;
    }

    /**
     * Convenience wrapper function for file_put_contents
     * @param string $file File name to write to
     * @param string $data Data to write into file
     * @param HTMLPurifier_Config $config
     * @return int|bool Number of bytes written if success, or false if failure.
     */
    private function _write($file, $data, $config)
    {
        $result = file_put_contents($file, $data);
        if ($result !== false) {
            // set permissions of the new file (no execute)
            $chmod = $config->get('Cache.SerializerPermissions');
            if ($chmod !== null) {
                chmod($file, $chmod & 0666);
            }
        }
        return $result;
    }

    /**
     * Prepares the directory that this type stores the serials in
     * @param HTMLPurifier_Config $config
     * @return bool True if successful
     */
    private function _prepareDir($config)
    {
        $directory = $this->generateDirectoryPath($config);
        $chmod = $config->get('Cache.SerializerPermissions');
        if ($chmod === null) {
            if (!@mkdir($directory) && !is_dir($directory)) {
                trigger_error(
                    'Could not create directory ' . $directory . '',
                    E_USER_WARNING
                );
                return false;
            }
            return true;
        }
        if (!is_dir($directory)) {
            $base = $this->generateBaseDirectoryPath($config);
            if (!is_dir($base)) {
                trigger_error(
                    'Base directory ' . $base . ' does not exist,
                    please create or change using %Cache.SerializerPath',
                    E_USER_WARNING
                );
                return false;
            } elseif (!$this->_testPermissions($base, $chmod)) {
                return false;
            }
            if (!@mkdir($directory, $chmod) && !is_dir($directory)) {
                trigger_error(
                    'Could not create directory ' . $directory . '',
                    E_USER_WARNING
                );
                return false;
            }
            if (!$this->_testPermissions($directory, $chmod)) {
                return false;
            }
        } elseif (!$this->_testPermissions($directory, $chmod)) {
            return false;
        }
        return true;
    }

    /**
     * Tests permissions on a directory and throws out friendly
     * error messages and attempts to chmod it itself if possible
     * @param string $dir Directory path
     * @param int $chmod Permissions
     * @return bool True if directory is writable
     */
    private function _testPermissions($dir, $chmod)
    {
        // early abort, if it is writable, everything is hunky-dory
        if (is_writable($dir)) {
            return true;
        }
        if (!is_dir($dir)) {
            // generally, you'll want to handle this beforehand
            // so a more specific error message can be given
            trigger_error(
                'Directory ' . $dir . ' does not exist',
                E_USER_WARNING
            );
            return false;
        }
        if (function_exists('posix_getuid') && $chmod !== null) {
            // POSIX system, we can give more specific advice
            if (fileowner($dir) === posix_getuid()) {
                // we can chmod it ourselves
                $chmod = $chmod | 0700;
                if (chmod($dir, $chmod)) {
                    return true;
                }
            } elseif (filegroup($dir) === posix_getgid()) {
                $chmod = $chmod | 0070;
            } else {
                // PHP's probably running as nobody, so we'll
                // need to give global permissions
                $chmod = $chmod | 0777;
            }
            trigger_error(
                'Directory ' . $dir . ' not writable, ' .
                'please chmod to ' . decoct($chmod),
                E_USER_WARNING
            );
        } else {
            // generic error message
            trigger_error(
                'Directory ' . $dir . ' not writable, ' .
                'please alter file permissions',
                E_USER_WARNING
            );
        }
        return false;
    }
}

// vim: et sw=4 sts=4


code:
This is a dummy file to prevent Git from ignoring this empty directory.

    vim: et sw=4 sts=4


code:
<?php

/**
 * Responsible for creating definition caches.
 */
class HTMLPurifier_DefinitionCacheFactory
{
    /**
     * @type array
     */
    protected $caches = array('Serializer' => array());

    /**
     * @type array
     */
    protected $implementations = array();

    /**
     * @type HTMLPurifier_DefinitionCache_Decorator[]
     */
    protected $decorators = array();

    /**
     * Initialize default decorators
     */
    public function setup()
    {
        $this->addDecorator('Cleanup');
    }

    /**
     * Retrieves an instance of global definition cache factory.
     * @param HTMLPurifier_DefinitionCacheFactory $prototype
     * @return HTMLPurifier_DefinitionCacheFactory
     */
    public static function instance($prototype = null)
    {
        static $instance;
        if ($prototype !== null) {
            $instance = $prototype;
        } elseif ($instance === null || $prototype === true) {
            $instance = new HTMLPurifier_DefinitionCacheFactory();
            $instance->setup();
        }
        return $instance;
    }

    /**
     * Registers a new definition cache object
     * @param string $short Short name of cache object, for reference
     * @param string $long Full class name of cache object, for construction
     */
    public function register($short, $long)
    {
        $this->implementations[$short] = $long;
    }

    /**
     * Factory method that creates a cache object based on configuration
     * @param string $type Name of definitions handled by cache
     * @param HTMLPurifier_Config $config Config instance
     * @return mixed
     */
    public function create($type, $config)
    {
        $method = $config->get('Cache.DefinitionImpl');
        if ($method === null) {
            return new HTMLPurifier_DefinitionCache_Null($type);
        }
        if (!empty($this->caches[$method][$type])) {
            return $this->caches[$method][$type];
        }
        if (isset($this->implementations[$method]) &&
            class_exists($class = $this->implementations[$method], false)) {
            $cache = new $class($type);
        } else {
            if ($method != 'Serializer') {
                trigger_error("Unrecognized DefinitionCache $method, using Serializer instead", E_USER_WARNING);
            }
            $cache = new HTMLPurifier_DefinitionCache_Serializer($type);
        }
        foreach ($this->decorators as $decorator) {
            $new_cache = $decorator->decorate($cache);
            // prevent infinite recursion in PHP 4
            unset($cache);
            $cache = $new_cache;
        }
        $this->caches[$method][$type] = $cache;
        return $this->caches[$method][$type];
    }

    /**
     * Registers a decorator to add to all new cache objects
     * @param HTMLPurifier_DefinitionCache_Decorator|string $decorator An instance or the name of a decorator
     */
    public function addDecorator($decorator)
    {
        if (is_string($decorator)) {
            $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator";
            $decorator = new $class;
        }
        $this->decorators[$decorator->name] = $decorator;
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Represents a document type, contains information on which modules
 * need to be loaded.
 * @note This class is inspected by Printer_HTMLDefinition->renderDoctype.
 *       If structure changes, please update that function.
 */
class HTMLPurifier_Doctype
{
    /**
     * Full name of doctype
     * @type string
     */
    public $name;

    /**
     * List of standard modules (string identifiers or literal objects)
     * that this doctype uses
     * @type array
     */
    public $modules = array();

    /**
     * List of modules to use for tidying up code
     * @type array
     */
    public $tidyModules = array();

    /**
     * Is the language derived from XML (i.e. XHTML)?
     * @type bool
     */
    public $xml = true;

    /**
     * List of aliases for this doctype
     * @type array
     */
    public $aliases = array();

    /**
     * Public DTD identifier
     * @type string
     */
    public $dtdPublic;

    /**
     * System DTD identifier
     * @type string
     */
    public $dtdSystem;

    public function __construct(
        $name = null,
        $xml = true,
        $modules = array(),
        $tidyModules = array(),
        $aliases = array(),
        $dtd_public = null,
        $dtd_system = null
    ) {
        $this->name         = $name;
        $this->xml          = $xml;
        $this->modules      = $modules;
        $this->tidyModules  = $tidyModules;
        $this->aliases      = $aliases;
        $this->dtdPublic    = $dtd_public;
        $this->dtdSystem    = $dtd_system;
    }
}

// vim: et sw=4 sts=4


code:
<?php

class HTMLPurifier_DoctypeRegistry
{

    /**
     * Hash of doctype names to doctype objects.
     * @type array
     */
    protected $doctypes;

    /**
     * Lookup table of aliases to real doctype names.
     * @type array
     */
    protected $aliases;

    /**
     * Registers a doctype to the registry
     * @note Accepts a fully-formed doctype object, or the
     *       parameters for constructing a doctype object
     * @param string $doctype Name of doctype or literal doctype object
     * @param bool $xml
     * @param array $modules Modules doctype will load
     * @param array $tidy_modules Modules doctype will load for certain modes
     * @param array $aliases Alias names for doctype
     * @param string $dtd_public
     * @param string $dtd_system
     * @return HTMLPurifier_Doctype Editable registered doctype
     */
    public function register(
        $doctype,
        $xml = true,
        $modules = array(),
        $tidy_modules = array(),
        $aliases = array(),
        $dtd_public = null,
        $dtd_system = null
    ) {
        if (!is_array($modules)) {
            $modules = array($modules);
        }
        if (!is_array($tidy_modules)) {
            $tidy_modules = array($tidy_modules);
        }
        if (!is_array($aliases)) {
            $aliases = array($aliases);
        }
        if (!is_object($doctype)) {
            $doctype = new HTMLPurifier_Doctype(
                $doctype,
                $xml,
                $modules,
                $tidy_modules,
                $aliases,
                $dtd_public,
                $dtd_system
            );
        }
        $this->doctypes[$doctype->name] = $doctype;
        $name = $doctype->name;
        // hookup aliases
        foreach ($doctype->aliases as $alias) {
            if (isset($this->doctypes[$alias])) {
                continue;
            }
            $this->aliases[$alias] = $name;
        }
        // remove old aliases
        if (isset($this->aliases[$name])) {
            unset($this->aliases[$name]);
        }
        return $doctype;
    }

    /**
     * Retrieves reference to a doctype of a certain name
     * @note This function resolves aliases
     * @note When possible, use the more fully-featured make()
     * @param string $doctype Name of doctype
     * @return HTMLPurifier_Doctype Editable doctype object
     */
    public function get($doctype)
    {
        if (isset($this->aliases[$doctype])) {
            $doctype = $this->aliases[$doctype];
        }
        if (!isset($this->doctypes[$doctype])) {
            trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR);
            $anon = new HTMLPurifier_Doctype($doctype);
            return $anon;
        }
        return $this->doctypes[$doctype];
    }

    /**
     * Creates a doctype based on a configuration object,
     * will perform initialization on the doctype
     * @note Use this function to get a copy of doctype that config
     *       can hold on to (this is necessary in order to tell
     *       Generator whether or not the current document is XML
     *       based or not).
     * @param HTMLPurifier_Config $config
     * @return HTMLPurifier_Doctype
     */
    public function make($config)
    {
        return clone $this->get($this->getDoctypeFromConfig($config));
    }

    /**
     * Retrieves the doctype from the configuration object
     * @param HTMLPurifier_Config $config
     * @return string
     */
    public function getDoctypeFromConfig($config)
    {
        // recommended test
        $doctype = $config->get('HTML.Doctype');
        if (!empty($doctype)) {
            return $doctype;
        }
        $doctype = $config->get('HTML.CustomDoctype');
        if (!empty($doctype)) {
            return $doctype;
        }
        // backwards-compatibility
        if ($config->get('HTML.XHTML')) {
            $doctype = 'XHTML 1.0';
        } else {
            $doctype = 'HTML 4.01';
        }
        if ($config->get('HTML.Strict')) {
            $doctype .= ' Strict';
        } else {
            $doctype .= ' Transitional';
        }
        return $doctype;
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Structure that stores an HTML element definition. Used by
 * HTMLPurifier_HTMLDefinition and HTMLPurifier_HTMLModule.
 * @note This class is inspected by HTMLPurifier_Printer_HTMLDefinition.
 *       Please update that class too.
 * @warning If you add new properties to this class, you MUST update
 *          the mergeIn() method.
 */
class HTMLPurifier_ElementDef
{
    /**
     * Does the definition work by itself, or is it created solely
     * for the purpose of merging into another definition?
     * @type bool
     */
    public $standalone = true;

    /**
     * Associative array of attribute name to HTMLPurifier_AttrDef.
     * @type array
     * @note Before being processed by HTMLPurifier_AttrCollections
     *       when modules are finalized during
     *       HTMLPurifier_HTMLDefinition->setup(), this array may also
     *       contain an array at index 0 that indicates which attribute
     *       collections to load into the full array. It may also
     *       contain string indentifiers in lieu of HTMLPurifier_AttrDef,
     *       see HTMLPurifier_AttrTypes on how they are expanded during
     *       HTMLPurifier_HTMLDefinition->setup() processing.
     */
    public $attr = array();

    // XXX: Design note: currently, it's not possible to override
    // previously defined AttrTransforms without messing around with
    // the final generated config. This is by design; a previous version
    // used an associated list of attr_transform, but it was extremely
    // easy to accidentally override other attribute transforms by
    // forgetting to specify an index (and just using 0.)  While we
    // could check this by checking the index number and complaining,
    // there is a second problem which is that it is not at all easy to
    // tell when something is getting overridden. Combine this with a
    // codebase where this isn't really being used, and it's perfect for
    // nuking.

    /**
     * List of tags HTMLPurifier_AttrTransform to be done before validation.
     * @type array
     */
    public $attr_transform_pre = array();

    /**
     * List of tags HTMLPurifier_AttrTransform to be done after validation.
     * @type array
     */
    public $attr_transform_post = array();

    /**
     * HTMLPurifier_ChildDef of this tag.
     * @type HTMLPurifier_ChildDef
     */
    public $child;

    /**
     * Abstract string representation of internal ChildDef rules.
     * @see HTMLPurifier_ContentSets for how this is parsed and then transformed
     * into an HTMLPurifier_ChildDef.
     * @warning This is a temporary variable that is not available after
     *      being processed by HTMLDefinition
     * @type string
     */
    public $content_model;

    /**
     * Value of $child->type, used to determine which ChildDef to use,
     * used in combination with $content_model.
     * @warning This must be lowercase
     * @warning This is a temporary variable that is not available after
     *      being processed by HTMLDefinition
     * @type string
     */
    public $content_model_type;

    /**
     * Does the element have a content model (#PCDATA | Inline)*? This
     * is important for chameleon ins and del processing in
     * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't
     * have to worry about this one.
     * @type bool
     */
    public $descendants_are_inline = false;

    /**
     * List of the names of required attributes this element has.
     * Dynamically populated by HTMLPurifier_HTMLDefinition::getElement()
     * @type array
     */
    public $required_attr = array();

    /**
     * Lookup table of tags excluded from all descendants of this tag.
     * @type array
     * @note SGML permits exclusions for all descendants, but this is
     *       not possible with DTDs or XML Schemas. W3C has elected to
     *       use complicated compositions of content_models to simulate
     *       exclusion for children, but we go the simpler, SGML-style
     *       route of flat-out exclusions, which correctly apply to
     *       all descendants and not just children. Note that the XHTML
     *       Modularization Abstract Modules are blithely unaware of such
     *       distinctions.
     */
    public $excludes = array();

    /**
     * This tag is explicitly auto-closed by the following tags.
     * @type array
     */
    public $autoclose = array();

    /**
     * If a foreign element is found in this element, test if it is
     * allowed by this sub-element; if it is, instead of closing the
     * current element, place it inside this element.
     * @type string
     */
    public $wrap;

    /**
     * Whether or not this is a formatting element affected by the
     * "Active Formatting Elements" algorithm.
     * @type bool
     */
    public $formatting;

    /**
     * Low-level factory constructor for creating new standalone element defs
     */
    public static function create($content_model, $content_model_type, $attr)
    {
        $def = new HTMLPurifier_ElementDef();
        $def->content_model = $content_model;
        $def->content_model_type = $content_model_type;
        $def->attr = $attr;
        return $def;
    }

    /**
     * Merges the values of another element definition into this one.
     * Values from the new element def take precedence if a value is
     * not mergeable.
     * @param HTMLPurifier_ElementDef $def
     */
    public function mergeIn($def)
    {
        // later keys takes precedence
        foreach ($def->attr as $k => $v) {
            if ($k === 0) {
                // merge in the includes
                // sorry, no way to override an include
                foreach ($v as $v2) {
                    $this->attr[0][] = $v2;
                }
                continue;
            }
            if ($v === false) {
                if (isset($this->attr[$k])) {
                    unset($this->attr[$k]);
                }
                continue;
            }
            $this->attr[$k] = $v;
        }
        $this->_mergeAssocArray($this->excludes, $def->excludes);
        $this->attr_transform_pre = array_merge($this->attr_transform_pre, $def->attr_transform_pre);
        $this->attr_transform_post = array_merge($this->attr_transform_post, $def->attr_transform_post);

        if (!empty($def->content_model)) {
            $this->content_model =
                str_replace("#SUPER", $this->content_model, $def->content_model);
            $this->child = false;
        }
        if (!empty($def->content_model_type)) {
            $this->content_model_type = $def->content_model_type;
            $this->child = false;
        }
        if (!is_null($def->child)) {
            $this->child = $def->child;
        }
        if (!is_null($def->formatting)) {
            $this->formatting = $def->formatting;
        }
        if ($def->descendants_are_inline) {
            $this->descendants_are_inline = $def->descendants_are_inline;
        }
    }

    /**
     * Merges one array into another, removes values which equal false
     * @param $a1 Array by reference that is merged into
     * @param $a2 Array that merges into $a1
     */
    private function _mergeAssocArray(&$a1, $a2)
    {
        foreach ($a2 as $k => $v) {
            if ($v === false) {
                if (isset($a1[$k])) {
                    unset($a1[$k]);
                }
                continue;
            }
            $a1[$k] = $v;
        }
    }
}

// vim: et sw=4 sts=4