Task #2972 - Upgrading the HTMLPurifier library from 4.1.0 to its latest version 4.2.0.
parent
52b2fa923b
commit
82081609e9
@ -1 +1 @@ |
||||
4.1.0 |
||||
4.2.0 |
||||
@ -1,236 +1,253 @@ |
||||
<?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:param name="css" select="'styles/plain.css'"/> |
||||
<xsl:param name="title" select="'Configuration Documentation'"/> |
||||
|
||||
<xsl:variable name="typeLookup" select="document('../types.xml')/types" /> |
||||
<xsl:variable name="usageLookup" select="document('../usage.xml')/usage" /> |
||||
|
||||
<!-- Twiddle this variable to get the columns as even as possible --> |
||||
<xsl:variable name="maxNumberAdjust" select="2" /> |
||||
|
||||
<xsl:template match="/"> |
||||
<html lang="en" xml:lang="en"> |
||||
<head> |
||||
<title><xsl:value-of select="$title" /> - <xsl:value-of select="/configdoc/title" /></title> |
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> |
||||
<link rel="stylesheet" type="text/css" href="{$css}" /> |
||||
</head> |
||||
<body> |
||||
<div id="content"> |
||||
<div id="library"><xsl:value-of select="/configdoc/title" /></div> |
||||
<h1><xsl:value-of select="$title" /></h1> |
||||
<div id="tocContainer"> |
||||
<h2>Table of Contents</h2> |
||||
<ul id="toc"> |
||||
<xsl:apply-templates mode="toc"> |
||||
<xsl:with-param name="overflowNumber" select="round(count(/configdoc/namespace) div 2) + $maxNumberAdjust" /> |
||||
</xsl:apply-templates> |
||||
</ul> |
||||
</div> |
||||
<xsl:apply-templates /> |
||||
</div> |
||||
</body> |
||||
</html> |
||||
</xsl:template> |
||||
|
||||
<xsl:template match="title" mode="toc" /> |
||||
<xsl:template match="namespace" mode="toc"> |
||||
<xsl:param name="overflowNumber" /> |
||||
<xsl:variable name="number"><xsl:number level="single" /></xsl:variable> |
||||
<xsl:variable name="directiveNumber"><xsl:number level="any" count="directive" /></xsl:variable> |
||||
<xsl:if test="count(directive)>0"> |
||||
<li> |
||||
<!-- BEGIN multicolumn code --> |
||||
<xsl:if test="$number >= $overflowNumber"> |
||||
<xsl:attribute name="class">col-2</xsl:attribute> |
||||
</xsl:if> |
||||
<xsl:if test="$number = $overflowNumber"> |
||||
<xsl:attribute name="style">margin-top:-<xsl:value-of select="($number * 2 + $directiveNumber - 3) * 1.2" />em</xsl:attribute> |
||||
</xsl:if> |
||||
<!-- END multicolumn code --> |
||||
<a href="#{@id}"><xsl:value-of select="name" /></a> |
||||
<ul> |
||||
<xsl:apply-templates select="directive" mode="toc"> |
||||
<xsl:with-param name="overflowNumber" select="$overflowNumber" /> |
||||
</xsl:apply-templates> |
||||
</ul> |
||||
<xsl:if test="$number + 1 = $overflowNumber"> |
||||
<div class="col-l" /> |
||||
</xsl:if> |
||||
</li> |
||||
</xsl:if> |
||||
</xsl:template> |
||||
<xsl:template match="directive" mode="toc"> |
||||
<xsl:variable name="number"> |
||||
<xsl:number level="any" count="directive|namespace" /> |
||||
</xsl:variable> |
||||
<xsl:if test="not(deprecated)"> |
||||
<li> |
||||
<a href="#{@id}"><xsl:value-of select="name" /></a> |
||||
</li> |
||||
</xsl:if> |
||||
</xsl:template> |
||||
|
||||
<xsl:template match="title" /> |
||||
|
||||
<xsl:template match="namespace"> |
||||
<div class="namespace"> |
||||
<xsl:apply-templates /> |
||||
<xsl:if test="count(directive)=0"> |
||||
<p>No configuration directives defined for this namespace.</p> |
||||
</xsl:if> |
||||
</div> |
||||
</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 xmlns:xhtml="http://www.w3.org/1999/xhtml" select="xhtml:div/node()" /> |
||||
</div> |
||||
</xsl:template> |
||||
|
||||
<xsl:template match="directive"> |
||||
<div> |
||||
<xsl:attribute name="class"><!-- |
||||
-->directive<!-- |
||||
--><xsl:if test="deprecated"> deprecated</xsl:if><!-- |
||||
--></xsl:attribute> |
||||
<xsl:apply-templates> |
||||
<xsl:with-param name="id" select="@id" /> |
||||
</xsl:apply-templates> |
||||
</div> |
||||
</xsl:template> |
||||
<xsl:template match="directive/name"> |
||||
<xsl:param name="id" /> |
||||
<xsl:apply-templates select="../aliases/alias" mode="anchor" /> |
||||
<h3 id="{$id}"><xsl:value-of select="$id" /></h3> |
||||
</xsl:template> |
||||
<xsl:template match="alias" mode="anchor"> |
||||
<a id="{.}"></a> |
||||
</xsl:template> |
||||
|
||||
<!-- Do not pass through --> |
||||
<xsl:template match="alias"></xsl:template> |
||||
|
||||
<xsl:template match="directive/constraints"> |
||||
<xsl:param name="id" /> |
||||
<table class="constraints"> |
||||
<xsl:apply-templates /> |
||||
<xsl:if test="../aliases/alias"> |
||||
<xsl:apply-templates select="../aliases" mode="constraints" /> |
||||
</xsl:if> |
||||
<xsl:apply-templates select="$usageLookup/directive[@id=$id]" /> |
||||
</table> |
||||
</xsl:template> |
||||
<xsl:template match="directive/aliases" mode="constraints"> |
||||
<tr> |
||||
<th>Aliases</th> |
||||
<td> |
||||
<xsl:for-each select="alias"> |
||||
<xsl:if test="position()>1">, </xsl:if> |
||||
<xsl:value-of select="." /> |
||||
</xsl:for-each> |
||||
</td> |
||||
</tr> |
||||
</xsl:template> |
||||
<xsl:template match="directive/description"> |
||||
<div class="description"> |
||||
<xsl:copy-of xmlns:xhtml="http://www.w3.org/1999/xhtml" select="xhtml:div/node()" /> |
||||
</div> |
||||
</xsl:template> |
||||
<xsl:template match="directive/deprecated"> |
||||
<div class="deprecated-notice"> |
||||
<strong>Warning:</strong> |
||||
This directive was deprecated in version <xsl:value-of select="version" />. |
||||
<a href="#{use}">%<xsl:value-of select="use" /></a> should be used instead. |
||||
</div> |
||||
</xsl:template> |
||||
<xsl:template match="usage/directive"> |
||||
<tr> |
||||
<th>Used in</th> |
||||
<td> |
||||
<ul> |
||||
<xsl:apply-templates /> |
||||
</ul> |
||||
</td> |
||||
</tr> |
||||
</xsl:template> |
||||
<xsl:template match="usage/directive/file"> |
||||
<li> |
||||
<em><xsl:value-of select="@name" /></em> on line<xsl:if test="count(line)>1">s</xsl:if> |
||||
<xsl:text> </xsl:text> |
||||
<xsl:for-each select="line"> |
||||
<xsl:if test="position()>1">, </xsl:if> |
||||
<xsl:value-of select="." /> |
||||
</xsl:for-each> |
||||
</li> |
||||
</xsl:template> |
||||
|
||||
<xsl:template match="constraints/version"> |
||||
<tr> |
||||
<th>Version added</th> |
||||
<td><xsl:value-of select="." /></td> |
||||
</tr> |
||||
</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/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()>1">, </xsl:if> |
||||
"<xsl:value-of select="." />"<!-- |
||||
--></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:template match="constraints/external"> |
||||
<tr> |
||||
<th>External deps</th> |
||||
<td> |
||||
<ul> |
||||
<xsl:apply-templates /> |
||||
</ul> |
||||
</td> |
||||
</tr> |
||||
</xsl:template> |
||||
<xsl:template match="constraints/external/project"> |
||||
<li><xsl:value-of select="." /></li> |
||||
</xsl:template> |
||||
|
||||
</xsl:stylesheet> |
||||
|
||||
<!-- vim: et sw=4 sts=4 |
||||
--> |
||||
<?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:param name="css" select="'styles/plain.css'"/> |
||||
<xsl:param name="title" select="'Configuration Documentation'"/> |
||||
|
||||
<xsl:variable name="typeLookup" select="document('../types.xml')/types" /> |
||||
<xsl:variable name="usageLookup" select="document('../usage.xml')/usage" /> |
||||
|
||||
<!-- Twiddle this variable to get the columns as even as possible --> |
||||
<xsl:variable name="maxNumberAdjust" select="2" /> |
||||
|
||||
<xsl:template match="/"> |
||||
<html lang="en" xml:lang="en"> |
||||
<head> |
||||
<title><xsl:value-of select="$title" /> - <xsl:value-of select="/configdoc/title" /></title> |
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> |
||||
<link rel="stylesheet" type="text/css" href="{$css}" /> |
||||
</head> |
||||
<body> |
||||
<div id="content"> |
||||
<div id="library"><xsl:value-of select="/configdoc/title" /></div> |
||||
<h1><xsl:value-of select="$title" /></h1> |
||||
<div id="tocContainer"> |
||||
<h2>Table of Contents</h2> |
||||
<ul id="toc"> |
||||
<xsl:apply-templates mode="toc"> |
||||
<xsl:with-param name="overflowNumber" select="round(count(/configdoc/namespace) div 2) + $maxNumberAdjust" /> |
||||
</xsl:apply-templates> |
||||
</ul> |
||||
</div> |
||||
<div id="typesContainer"> |
||||
<h2>Types</h2> |
||||
<xsl:apply-templates select="$typeLookup" mode="types" /> |
||||
</div> |
||||
<xsl:apply-templates /> |
||||
</div> |
||||
</body> |
||||
</html> |
||||
</xsl:template> |
||||
|
||||
<xsl:template match="type" mode="types"> |
||||
<div class="type-block"> |
||||
<xsl:attribute name="id">type-<xsl:value-of select="@id" /></xsl:attribute> |
||||
<h3><code><xsl:value-of select="@id" /></code>: <xsl:value-of select="@name" /></h3> |
||||
<div class="type-description"> |
||||
<xsl:copy-of xmlns:xhtml="http://www.w3.org/1999/xhtml" select="xhtml:div/node()" /> |
||||
</div> |
||||
</div> |
||||
</xsl:template> |
||||
|
||||
<xsl:template match="title" mode="toc" /> |
||||
<xsl:template match="namespace" mode="toc"> |
||||
<xsl:param name="overflowNumber" /> |
||||
<xsl:variable name="number"><xsl:number level="single" /></xsl:variable> |
||||
<xsl:variable name="directiveNumber"><xsl:number level="any" count="directive" /></xsl:variable> |
||||
<xsl:if test="count(directive)>0"> |
||||
<li> |
||||
<!-- BEGIN multicolumn code --> |
||||
<xsl:if test="$number >= $overflowNumber"> |
||||
<xsl:attribute name="class">col-2</xsl:attribute> |
||||
</xsl:if> |
||||
<xsl:if test="$number = $overflowNumber"> |
||||
<xsl:attribute name="style">margin-top:-<xsl:value-of select="($number * 2 + $directiveNumber - 3) * 1.2" />em</xsl:attribute> |
||||
</xsl:if> |
||||
<!-- END multicolumn code --> |
||||
<a href="#{@id}"><xsl:value-of select="name" /></a> |
||||
<ul> |
||||
<xsl:apply-templates select="directive" mode="toc"> |
||||
<xsl:with-param name="overflowNumber" select="$overflowNumber" /> |
||||
</xsl:apply-templates> |
||||
</ul> |
||||
<xsl:if test="$number + 1 = $overflowNumber"> |
||||
<div class="col-l" /> |
||||
</xsl:if> |
||||
</li> |
||||
</xsl:if> |
||||
</xsl:template> |
||||
<xsl:template match="directive" mode="toc"> |
||||
<xsl:variable name="number"> |
||||
<xsl:number level="any" count="directive|namespace" /> |
||||
</xsl:variable> |
||||
<xsl:if test="not(deprecated)"> |
||||
<li> |
||||
<a href="#{@id}"><xsl:value-of select="name" /></a> |
||||
</li> |
||||
</xsl:if> |
||||
</xsl:template> |
||||
|
||||
<xsl:template match="title" /> |
||||
|
||||
<xsl:template match="namespace"> |
||||
<div class="namespace"> |
||||
<xsl:apply-templates /> |
||||
<xsl:if test="count(directive)=0"> |
||||
<p>No configuration directives defined for this namespace.</p> |
||||
</xsl:if> |
||||
</div> |
||||
</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 xmlns:xhtml="http://www.w3.org/1999/xhtml" select="xhtml:div/node()" /> |
||||
</div> |
||||
</xsl:template> |
||||
|
||||
<xsl:template match="directive"> |
||||
<div> |
||||
<xsl:attribute name="class"><!-- |
||||
-->directive<!-- |
||||
--><xsl:if test="deprecated"> deprecated</xsl:if><!-- |
||||
--></xsl:attribute> |
||||
<xsl:apply-templates> |
||||
<xsl:with-param name="id" select="@id" /> |
||||
</xsl:apply-templates> |
||||
</div> |
||||
</xsl:template> |
||||
<xsl:template match="directive/name"> |
||||
<xsl:param name="id" /> |
||||
<xsl:apply-templates select="../aliases/alias" mode="anchor" /> |
||||
<h3 id="{$id}"><xsl:value-of select="$id" /></h3> |
||||
</xsl:template> |
||||
<xsl:template match="alias" mode="anchor"> |
||||
<a id="{.}"></a> |
||||
</xsl:template> |
||||
|
||||
<!-- Do not pass through --> |
||||
<xsl:template match="alias"></xsl:template> |
||||
|
||||
<xsl:template match="directive/constraints"> |
||||
<xsl:param name="id" /> |
||||
<table class="constraints"> |
||||
<xsl:apply-templates /> |
||||
<xsl:if test="../aliases/alias"> |
||||
<xsl:apply-templates select="../aliases" mode="constraints" /> |
||||
</xsl:if> |
||||
<xsl:apply-templates select="$usageLookup/directive[@id=$id]" /> |
||||
</table> |
||||
</xsl:template> |
||||
<xsl:template match="directive/aliases" mode="constraints"> |
||||
<tr> |
||||
<th>Aliases</th> |
||||
<td> |
||||
<xsl:for-each select="alias"> |
||||
<xsl:if test="position()>1">, </xsl:if> |
||||
<xsl:value-of select="." /> |
||||
</xsl:for-each> |
||||
</td> |
||||
</tr> |
||||
</xsl:template> |
||||
<xsl:template match="directive/description"> |
||||
<div class="description"> |
||||
<xsl:copy-of xmlns:xhtml="http://www.w3.org/1999/xhtml" select="xhtml:div/node()" /> |
||||
</div> |
||||
</xsl:template> |
||||
<xsl:template match="directive/deprecated"> |
||||
<div class="deprecated-notice"> |
||||
<strong>Warning:</strong> |
||||
This directive was deprecated in version <xsl:value-of select="version" />. |
||||
<a href="#{use}">%<xsl:value-of select="use" /></a> should be used instead. |
||||
</div> |
||||
</xsl:template> |
||||
<xsl:template match="usage/directive"> |
||||
<tr> |
||||
<th>Used in</th> |
||||
<td> |
||||
<ul> |
||||
<xsl:apply-templates /> |
||||
</ul> |
||||
</td> |
||||
</tr> |
||||
</xsl:template> |
||||
<xsl:template match="usage/directive/file"> |
||||
<li> |
||||
<em><xsl:value-of select="@name" /></em> on line<xsl:if test="count(line)>1">s</xsl:if> |
||||
<xsl:text> </xsl:text> |
||||
<xsl:for-each select="line"> |
||||
<xsl:if test="position()>1">, </xsl:if> |
||||
<xsl:value-of select="." /> |
||||
</xsl:for-each> |
||||
</li> |
||||
</xsl:template> |
||||
|
||||
<xsl:template match="constraints/version"> |
||||
<tr> |
||||
<th>Version added</th> |
||||
<td><xsl:value-of select="." /></td> |
||||
</tr> |
||||
</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> |
||||
<a> |
||||
<xsl:attribute name="href">#type-<xsl:value-of select="$type" /></xsl:attribute> |
||||
<xsl:value-of select="$typeLookup/type[@id=$type]/@name" /> |
||||
<xsl:if test="@allow-null='yes'"> |
||||
(or null) |
||||
</xsl:if> |
||||
</a> |
||||
</td> |
||||
</tr> |
||||
</xsl:template> |
||||
<xsl:template match="constraints/allowed"> |
||||
<tr> |
||||
<th>Allowed values</th> |
||||
<td> |
||||
<xsl:for-each select="value"><!-- |
||||
--><xsl:if test="position()>1">, </xsl:if> |
||||
"<xsl:value-of select="." />"<!-- |
||||
--></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:template match="constraints/external"> |
||||
<tr> |
||||
<th>External deps</th> |
||||
<td> |
||||
<ul> |
||||
<xsl:apply-templates /> |
||||
</ul> |
||||
</td> |
||||
</tr> |
||||
</xsl:template> |
||||
<xsl:template match="constraints/external/project"> |
||||
<li><xsl:value-of select="." /></li> |
||||
</xsl:template> |
||||
|
||||
</xsl:stylesheet> |
||||
|
||||
<!-- vim: et sw=4 sts=4 |
||||
--> |
||||
|
||||
@ -1,17 +1,69 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<types> |
||||
<type id="string">String</type> |
||||
<type id="istring">Case-insensitive string</type> |
||||
<type id="text">Text</type> |
||||
<type id="itext">Case-insensitive text</type> |
||||
<type id="int">Integer</type> |
||||
<type id="float">Float</type> |
||||
<type id="bool">Boolean</type> |
||||
<type id="lookup">Lookup array</type> |
||||
<type id="list">Array list</type> |
||||
<type id="hash">Associative array</type> |
||||
<type id="mixed">Mixed</type> |
||||
</types> |
||||
|
||||
<!-- vim: et sw=4 sts=4 |
||||
--> |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<types> |
||||
<type id="string" name="String"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
A <a |
||||
href="http://docs.php.net/manual/en/language.types.string.php">sequence |
||||
of characters</a>. |
||||
</div></type> |
||||
<type id="istring" name="Case-insensitive string"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
A series of case-insensitive characters. Internally, upper-case |
||||
ASCII characters will be converted to lower-case. |
||||
</div></type> |
||||
<type id="text" name="Text"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
A series of characters that may contain newlines. Text tends to |
||||
indicate human-oriented text, as opposed to a machine format. |
||||
</div></type> |
||||
<type id="itext" name="Case-insensitive text"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
A series of case-insensitive characters that may contain newlines. |
||||
</div></type> |
||||
<type id="int" name="Integer"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
An <a |
||||
href="http://docs.php.net/manual/en/language.types.integer.php"> |
||||
integer</a>. You are alternatively permitted to pass a string of |
||||
digits instead, which will be cast to an integer using |
||||
<code>(int)</code>. |
||||
</div></type> |
||||
<type id="float" name="Float"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
A <a href="http://docs.php.net/manual/en/language.types.float.php"> |
||||
floating point number</a>. You are alternatively permitted to |
||||
pass a numeric string (as defined by <code>is_numeric()</code>), |
||||
which will be cast to a float using <code>(float)</code>. |
||||
</div></type> |
||||
<type id="bool" name="Boolean"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
A <a |
||||
href="http://docs.php.net/manual/en/language.types.boolean.php">boolean</a>. |
||||
You are alternatively permitted to pass an integer <code>0</code> or |
||||
<code>1</code> (other integers are not permitted) or a string |
||||
<code>"on"</code>, <code>"true"</code> or <code>"1"</code> for |
||||
<code>true</code>, and <code>"off"</code>, <code>"false"</code> or |
||||
<code>"0"</code> for <code>false</code>. |
||||
</div></type> |
||||
<type id="lookup" name="Lookup array"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
An array whose values are <code>true</code>, e.g. <code>array('key' |
||||
=> true, 'key2' => true)</code>. You are alternatively permitted |
||||
to pass an array list of the keys <code>array('key', 'key2')</code> |
||||
or a comma-separated string of keys <code>"key, key2"</code>. If |
||||
you pass an array list of values, ensure that your values are |
||||
strictly numerically indexed: <code>array('key1', 2 => |
||||
'key2')</code> will not do what you expect and emits a warning. |
||||
</div></type> |
||||
<type id="list" name="Array list"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
An array which has consecutive integer indexes, e.g. |
||||
<code>array('val1', 'val2')</code>. You are alternatively permitted |
||||
to pass a comma-separated string of keys <code>"val1, val2"</code>. |
||||
If your array is not in this form, <code>array_values</code> is run |
||||
on the array and a warning is emitted. |
||||
</div></type> |
||||
<type id="hash" name="Associative array"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
An array which is a mapping of keys to values, e.g. |
||||
<code>array('key1' => 'val1', 'key2' => 'val2')</code>. You are |
||||
alternatively permitted to pass a comma-separated string of |
||||
key-colon-value strings, e.g. <code>"key1: val1, key2: val2"</code>. |
||||
</div></type> |
||||
<type id="mixed" name="Mixed"><div xmlns="http://www.w3.org/1999/xhtml"> |
||||
An arbitrary PHP value of any type. |
||||
</div></type> |
||||
</types> |
||||
|
||||
<!-- vim: et sw=4 sts=4 |
||||
--> |
||||
|
||||
@ -1,210 +1,212 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @file |
||||
* This file was auto-generated by generate-includes.php and includes all of |
||||
* the core files required by HTML Purifier. Use this if performance is a |
||||
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS |
||||
* FILE, changes will be overwritten the next time the script is run. |
||||
* |
||||
* @version 4.1.0 |
||||
* |
||||
* @warning |
||||
* You must *not* include any other HTML Purifier files before this file, |
||||
* because 'require' not 'require_once' is used. |
||||
* |
||||
* @warning |
||||
* This file requires that the include path contains the HTML Purifier |
||||
* library directory; this is not auto-set. |
||||
*/ |
||||
|
||||
require 'HTMLPurifier.php'; |
||||
require 'HTMLPurifier/AttrCollections.php'; |
||||
require 'HTMLPurifier/AttrDef.php'; |
||||
require 'HTMLPurifier/AttrTransform.php'; |
||||
require 'HTMLPurifier/AttrTypes.php'; |
||||
require 'HTMLPurifier/AttrValidator.php'; |
||||
require 'HTMLPurifier/Bootstrap.php'; |
||||
require 'HTMLPurifier/Definition.php'; |
||||
require 'HTMLPurifier/CSSDefinition.php'; |
||||
require 'HTMLPurifier/ChildDef.php'; |
||||
require 'HTMLPurifier/Config.php'; |
||||
require 'HTMLPurifier/ConfigSchema.php'; |
||||
require 'HTMLPurifier/ContentSets.php'; |
||||
require 'HTMLPurifier/Context.php'; |
||||
require 'HTMLPurifier/DefinitionCache.php'; |
||||
require 'HTMLPurifier/DefinitionCacheFactory.php'; |
||||
require 'HTMLPurifier/Doctype.php'; |
||||
require 'HTMLPurifier/DoctypeRegistry.php'; |
||||
require 'HTMLPurifier/ElementDef.php'; |
||||
require 'HTMLPurifier/Encoder.php'; |
||||
require 'HTMLPurifier/EntityLookup.php'; |
||||
require 'HTMLPurifier/EntityParser.php'; |
||||
require 'HTMLPurifier/ErrorCollector.php'; |
||||
require 'HTMLPurifier/ErrorStruct.php'; |
||||
require 'HTMLPurifier/Exception.php'; |
||||
require 'HTMLPurifier/Filter.php'; |
||||
require 'HTMLPurifier/Generator.php'; |
||||
require 'HTMLPurifier/HTMLDefinition.php'; |
||||
require 'HTMLPurifier/HTMLModule.php'; |
||||
require 'HTMLPurifier/HTMLModuleManager.php'; |
||||
require 'HTMLPurifier/IDAccumulator.php'; |
||||
require 'HTMLPurifier/Injector.php'; |
||||
require 'HTMLPurifier/Language.php'; |
||||
require 'HTMLPurifier/LanguageFactory.php'; |
||||
require 'HTMLPurifier/Length.php'; |
||||
require 'HTMLPurifier/Lexer.php'; |
||||
require 'HTMLPurifier/PercentEncoder.php'; |
||||
require 'HTMLPurifier/PropertyList.php'; |
||||
require 'HTMLPurifier/PropertyListIterator.php'; |
||||
require 'HTMLPurifier/Strategy.php'; |
||||
require 'HTMLPurifier/StringHash.php'; |
||||
require 'HTMLPurifier/StringHashParser.php'; |
||||
require 'HTMLPurifier/TagTransform.php'; |
||||
require 'HTMLPurifier/Token.php'; |
||||
require 'HTMLPurifier/TokenFactory.php'; |
||||
require 'HTMLPurifier/URI.php'; |
||||
require 'HTMLPurifier/URIDefinition.php'; |
||||
require 'HTMLPurifier/URIFilter.php'; |
||||
require 'HTMLPurifier/URIParser.php'; |
||||
require 'HTMLPurifier/URIScheme.php'; |
||||
require 'HTMLPurifier/URISchemeRegistry.php'; |
||||
require 'HTMLPurifier/UnitConverter.php'; |
||||
require 'HTMLPurifier/VarParser.php'; |
||||
require 'HTMLPurifier/VarParserException.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS.php'; |
||||
require 'HTMLPurifier/AttrDef/Enum.php'; |
||||
require 'HTMLPurifier/AttrDef/Integer.php'; |
||||
require 'HTMLPurifier/AttrDef/Lang.php'; |
||||
require 'HTMLPurifier/AttrDef/Switch.php'; |
||||
require 'HTMLPurifier/AttrDef/Text.php'; |
||||
require 'HTMLPurifier/AttrDef/URI.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Number.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/AlphaValue.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Background.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Border.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Color.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Composite.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Filter.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Font.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/FontFamily.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/ImportantDecorator.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Length.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/ListStyle.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Multiple.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Percentage.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/URI.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Bool.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Class.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Color.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/ID.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Pixels.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Length.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/MultiLength.php'; |
||||
require 'HTMLPurifier/AttrDef/URI/Email.php'; |
||||
require 'HTMLPurifier/AttrDef/URI/Host.php'; |
||||
require 'HTMLPurifier/AttrDef/URI/IPv4.php'; |
||||
require 'HTMLPurifier/AttrDef/URI/IPv6.php'; |
||||
require 'HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php'; |
||||
require 'HTMLPurifier/AttrTransform/Background.php'; |
||||
require 'HTMLPurifier/AttrTransform/BdoDir.php'; |
||||
require 'HTMLPurifier/AttrTransform/BgColor.php'; |
||||
require 'HTMLPurifier/AttrTransform/BoolToCSS.php'; |
||||
require 'HTMLPurifier/AttrTransform/Border.php'; |
||||
require 'HTMLPurifier/AttrTransform/EnumToCSS.php'; |
||||
require 'HTMLPurifier/AttrTransform/ImgRequired.php'; |
||||
require 'HTMLPurifier/AttrTransform/ImgSpace.php'; |
||||
require 'HTMLPurifier/AttrTransform/Input.php'; |
||||
require 'HTMLPurifier/AttrTransform/Lang.php'; |
||||
require 'HTMLPurifier/AttrTransform/Length.php'; |
||||
require 'HTMLPurifier/AttrTransform/Name.php'; |
||||
require 'HTMLPurifier/AttrTransform/NameSync.php'; |
||||
require 'HTMLPurifier/AttrTransform/SafeEmbed.php'; |
||||
require 'HTMLPurifier/AttrTransform/SafeObject.php'; |
||||
require 'HTMLPurifier/AttrTransform/SafeParam.php'; |
||||
require 'HTMLPurifier/AttrTransform/ScriptRequired.php'; |
||||
require 'HTMLPurifier/AttrTransform/Textarea.php'; |
||||
require 'HTMLPurifier/ChildDef/Chameleon.php'; |
||||
require 'HTMLPurifier/ChildDef/Custom.php'; |
||||
require 'HTMLPurifier/ChildDef/Empty.php'; |
||||
require 'HTMLPurifier/ChildDef/Required.php'; |
||||
require 'HTMLPurifier/ChildDef/Optional.php'; |
||||
require 'HTMLPurifier/ChildDef/StrictBlockquote.php'; |
||||
require 'HTMLPurifier/ChildDef/Table.php'; |
||||
require 'HTMLPurifier/DefinitionCache/Decorator.php'; |
||||
require 'HTMLPurifier/DefinitionCache/Null.php'; |
||||
require 'HTMLPurifier/DefinitionCache/Serializer.php'; |
||||
require 'HTMLPurifier/DefinitionCache/Decorator/Cleanup.php'; |
||||
require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php'; |
||||
require 'HTMLPurifier/HTMLModule/Bdo.php'; |
||||
require 'HTMLPurifier/HTMLModule/CommonAttributes.php'; |
||||
require 'HTMLPurifier/HTMLModule/Edit.php'; |
||||
require 'HTMLPurifier/HTMLModule/Forms.php'; |
||||
require 'HTMLPurifier/HTMLModule/Hypertext.php'; |
||||
require 'HTMLPurifier/HTMLModule/Image.php'; |
||||
require 'HTMLPurifier/HTMLModule/Legacy.php'; |
||||
require 'HTMLPurifier/HTMLModule/List.php'; |
||||
require 'HTMLPurifier/HTMLModule/Name.php'; |
||||
require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php'; |
||||
require 'HTMLPurifier/HTMLModule/Object.php'; |
||||
require 'HTMLPurifier/HTMLModule/Presentation.php'; |
||||
require 'HTMLPurifier/HTMLModule/Proprietary.php'; |
||||
require 'HTMLPurifier/HTMLModule/Ruby.php'; |
||||
require 'HTMLPurifier/HTMLModule/SafeEmbed.php'; |
||||
require 'HTMLPurifier/HTMLModule/SafeObject.php'; |
||||
require 'HTMLPurifier/HTMLModule/Scripting.php'; |
||||
require 'HTMLPurifier/HTMLModule/StyleAttribute.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tables.php'; |
||||
require 'HTMLPurifier/HTMLModule/Target.php'; |
||||
require 'HTMLPurifier/HTMLModule/Text.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy.php'; |
||||
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/Name.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/Proprietary.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/Strict.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/Transitional.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/XHTML.php'; |
||||
require 'HTMLPurifier/Injector/AutoParagraph.php'; |
||||
require 'HTMLPurifier/Injector/DisplayLinkURI.php'; |
||||
require 'HTMLPurifier/Injector/Linkify.php'; |
||||
require 'HTMLPurifier/Injector/PurifierLinkify.php'; |
||||
require 'HTMLPurifier/Injector/RemoveEmpty.php'; |
||||
require 'HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php'; |
||||
require 'HTMLPurifier/Injector/SafeObject.php'; |
||||
require 'HTMLPurifier/Lexer/DOMLex.php'; |
||||
require 'HTMLPurifier/Lexer/DirectLex.php'; |
||||
require 'HTMLPurifier/Strategy/Composite.php'; |
||||
require 'HTMLPurifier/Strategy/Core.php'; |
||||
require 'HTMLPurifier/Strategy/FixNesting.php'; |
||||
require 'HTMLPurifier/Strategy/MakeWellFormed.php'; |
||||
require 'HTMLPurifier/Strategy/RemoveForeignElements.php'; |
||||
require 'HTMLPurifier/Strategy/ValidateAttributes.php'; |
||||
require 'HTMLPurifier/TagTransform/Font.php'; |
||||
require 'HTMLPurifier/TagTransform/Simple.php'; |
||||
require 'HTMLPurifier/Token/Comment.php'; |
||||
require 'HTMLPurifier/Token/Tag.php'; |
||||
require 'HTMLPurifier/Token/Empty.php'; |
||||
require 'HTMLPurifier/Token/End.php'; |
||||
require 'HTMLPurifier/Token/Start.php'; |
||||
require 'HTMLPurifier/Token/Text.php'; |
||||
require 'HTMLPurifier/URIFilter/DisableExternal.php'; |
||||
require 'HTMLPurifier/URIFilter/DisableExternalResources.php'; |
||||
require 'HTMLPurifier/URIFilter/HostBlacklist.php'; |
||||
require 'HTMLPurifier/URIFilter/MakeAbsolute.php'; |
||||
require 'HTMLPurifier/URIFilter/Munge.php'; |
||||
require 'HTMLPurifier/URIScheme/data.php'; |
||||
require 'HTMLPurifier/URIScheme/ftp.php'; |
||||
require 'HTMLPurifier/URIScheme/http.php'; |
||||
require 'HTMLPurifier/URIScheme/https.php'; |
||||
require 'HTMLPurifier/URIScheme/mailto.php'; |
||||
require 'HTMLPurifier/URIScheme/news.php'; |
||||
require 'HTMLPurifier/URIScheme/nntp.php'; |
||||
require 'HTMLPurifier/VarParser/Flexible.php'; |
||||
require 'HTMLPurifier/VarParser/Native.php'; |
||||
<?php |
||||
|
||||
/** |
||||
* @file |
||||
* This file was auto-generated by generate-includes.php and includes all of |
||||
* the core files required by HTML Purifier. Use this if performance is a |
||||
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS |
||||
* FILE, changes will be overwritten the next time the script is run. |
||||
* |
||||
* @version 4.2.0 |
||||
* |
||||
* @warning |
||||
* You must *not* include any other HTML Purifier files before this file, |
||||
* because 'require' not 'require_once' is used. |
||||
* |
||||
* @warning |
||||
* This file requires that the include path contains the HTML Purifier |
||||
* library directory; this is not auto-set. |
||||
*/ |
||||
|
||||
require 'HTMLPurifier.php'; |
||||
require 'HTMLPurifier/AttrCollections.php'; |
||||
require 'HTMLPurifier/AttrDef.php'; |
||||
require 'HTMLPurifier/AttrTransform.php'; |
||||
require 'HTMLPurifier/AttrTypes.php'; |
||||
require 'HTMLPurifier/AttrValidator.php'; |
||||
require 'HTMLPurifier/Bootstrap.php'; |
||||
require 'HTMLPurifier/Definition.php'; |
||||
require 'HTMLPurifier/CSSDefinition.php'; |
||||
require 'HTMLPurifier/ChildDef.php'; |
||||
require 'HTMLPurifier/Config.php'; |
||||
require 'HTMLPurifier/ConfigSchema.php'; |
||||
require 'HTMLPurifier/ContentSets.php'; |
||||
require 'HTMLPurifier/Context.php'; |
||||
require 'HTMLPurifier/DefinitionCache.php'; |
||||
require 'HTMLPurifier/DefinitionCacheFactory.php'; |
||||
require 'HTMLPurifier/Doctype.php'; |
||||
require 'HTMLPurifier/DoctypeRegistry.php'; |
||||
require 'HTMLPurifier/ElementDef.php'; |
||||
require 'HTMLPurifier/Encoder.php'; |
||||
require 'HTMLPurifier/EntityLookup.php'; |
||||
require 'HTMLPurifier/EntityParser.php'; |
||||
require 'HTMLPurifier/ErrorCollector.php'; |
||||
require 'HTMLPurifier/ErrorStruct.php'; |
||||
require 'HTMLPurifier/Exception.php'; |
||||
require 'HTMLPurifier/Filter.php'; |
||||
require 'HTMLPurifier/Generator.php'; |
||||
require 'HTMLPurifier/HTMLDefinition.php'; |
||||
require 'HTMLPurifier/HTMLModule.php'; |
||||
require 'HTMLPurifier/HTMLModuleManager.php'; |
||||
require 'HTMLPurifier/IDAccumulator.php'; |
||||
require 'HTMLPurifier/Injector.php'; |
||||
require 'HTMLPurifier/Language.php'; |
||||
require 'HTMLPurifier/LanguageFactory.php'; |
||||
require 'HTMLPurifier/Length.php'; |
||||
require 'HTMLPurifier/Lexer.php'; |
||||
require 'HTMLPurifier/PercentEncoder.php'; |
||||
require 'HTMLPurifier/PropertyList.php'; |
||||
require 'HTMLPurifier/PropertyListIterator.php'; |
||||
require 'HTMLPurifier/Strategy.php'; |
||||
require 'HTMLPurifier/StringHash.php'; |
||||
require 'HTMLPurifier/StringHashParser.php'; |
||||
require 'HTMLPurifier/TagTransform.php'; |
||||
require 'HTMLPurifier/Token.php'; |
||||
require 'HTMLPurifier/TokenFactory.php'; |
||||
require 'HTMLPurifier/URI.php'; |
||||
require 'HTMLPurifier/URIDefinition.php'; |
||||
require 'HTMLPurifier/URIFilter.php'; |
||||
require 'HTMLPurifier/URIParser.php'; |
||||
require 'HTMLPurifier/URIScheme.php'; |
||||
require 'HTMLPurifier/URISchemeRegistry.php'; |
||||
require 'HTMLPurifier/UnitConverter.php'; |
||||
require 'HTMLPurifier/VarParser.php'; |
||||
require 'HTMLPurifier/VarParserException.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS.php'; |
||||
require 'HTMLPurifier/AttrDef/Enum.php'; |
||||
require 'HTMLPurifier/AttrDef/Integer.php'; |
||||
require 'HTMLPurifier/AttrDef/Lang.php'; |
||||
require 'HTMLPurifier/AttrDef/Switch.php'; |
||||
require 'HTMLPurifier/AttrDef/Text.php'; |
||||
require 'HTMLPurifier/AttrDef/URI.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Number.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/AlphaValue.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Background.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Border.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Color.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Composite.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Filter.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Font.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/FontFamily.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/ImportantDecorator.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Length.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/ListStyle.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Multiple.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/Percentage.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php'; |
||||
require 'HTMLPurifier/AttrDef/CSS/URI.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Bool.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Class.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Color.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/ID.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Pixels.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/Length.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php'; |
||||
require 'HTMLPurifier/AttrDef/HTML/MultiLength.php'; |
||||
require 'HTMLPurifier/AttrDef/URI/Email.php'; |
||||
require 'HTMLPurifier/AttrDef/URI/Host.php'; |
||||
require 'HTMLPurifier/AttrDef/URI/IPv4.php'; |
||||
require 'HTMLPurifier/AttrDef/URI/IPv6.php'; |
||||
require 'HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php'; |
||||
require 'HTMLPurifier/AttrTransform/Background.php'; |
||||
require 'HTMLPurifier/AttrTransform/BdoDir.php'; |
||||
require 'HTMLPurifier/AttrTransform/BgColor.php'; |
||||
require 'HTMLPurifier/AttrTransform/BoolToCSS.php'; |
||||
require 'HTMLPurifier/AttrTransform/Border.php'; |
||||
require 'HTMLPurifier/AttrTransform/EnumToCSS.php'; |
||||
require 'HTMLPurifier/AttrTransform/ImgRequired.php'; |
||||
require 'HTMLPurifier/AttrTransform/ImgSpace.php'; |
||||
require 'HTMLPurifier/AttrTransform/Input.php'; |
||||
require 'HTMLPurifier/AttrTransform/Lang.php'; |
||||
require 'HTMLPurifier/AttrTransform/Length.php'; |
||||
require 'HTMLPurifier/AttrTransform/Name.php'; |
||||
require 'HTMLPurifier/AttrTransform/NameSync.php'; |
||||
require 'HTMLPurifier/AttrTransform/SafeEmbed.php'; |
||||
require 'HTMLPurifier/AttrTransform/SafeObject.php'; |
||||
require 'HTMLPurifier/AttrTransform/SafeParam.php'; |
||||
require 'HTMLPurifier/AttrTransform/ScriptRequired.php'; |
||||
require 'HTMLPurifier/AttrTransform/Textarea.php'; |
||||
require 'HTMLPurifier/ChildDef/Chameleon.php'; |
||||
require 'HTMLPurifier/ChildDef/Custom.php'; |
||||
require 'HTMLPurifier/ChildDef/Empty.php'; |
||||
require 'HTMLPurifier/ChildDef/Required.php'; |
||||
require 'HTMLPurifier/ChildDef/Optional.php'; |
||||
require 'HTMLPurifier/ChildDef/StrictBlockquote.php'; |
||||
require 'HTMLPurifier/ChildDef/Table.php'; |
||||
require 'HTMLPurifier/DefinitionCache/Decorator.php'; |
||||
require 'HTMLPurifier/DefinitionCache/Null.php'; |
||||
require 'HTMLPurifier/DefinitionCache/Serializer.php'; |
||||
require 'HTMLPurifier/DefinitionCache/Decorator/Cleanup.php'; |
||||
require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php'; |
||||
require 'HTMLPurifier/HTMLModule/Bdo.php'; |
||||
require 'HTMLPurifier/HTMLModule/CommonAttributes.php'; |
||||
require 'HTMLPurifier/HTMLModule/Edit.php'; |
||||
require 'HTMLPurifier/HTMLModule/Forms.php'; |
||||
require 'HTMLPurifier/HTMLModule/Hypertext.php'; |
||||
require 'HTMLPurifier/HTMLModule/Image.php'; |
||||
require 'HTMLPurifier/HTMLModule/Legacy.php'; |
||||
require 'HTMLPurifier/HTMLModule/List.php'; |
||||
require 'HTMLPurifier/HTMLModule/Name.php'; |
||||
require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php'; |
||||
require 'HTMLPurifier/HTMLModule/Object.php'; |
||||
require 'HTMLPurifier/HTMLModule/Presentation.php'; |
||||
require 'HTMLPurifier/HTMLModule/Proprietary.php'; |
||||
require 'HTMLPurifier/HTMLModule/Ruby.php'; |
||||
require 'HTMLPurifier/HTMLModule/SafeEmbed.php'; |
||||
require 'HTMLPurifier/HTMLModule/SafeObject.php'; |
||||
require 'HTMLPurifier/HTMLModule/Scripting.php'; |
||||
require 'HTMLPurifier/HTMLModule/StyleAttribute.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tables.php'; |
||||
require 'HTMLPurifier/HTMLModule/Target.php'; |
||||
require 'HTMLPurifier/HTMLModule/Text.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy.php'; |
||||
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/Name.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/Proprietary.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/Strict.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/Transitional.php'; |
||||
require 'HTMLPurifier/HTMLModule/Tidy/XHTML.php'; |
||||
require 'HTMLPurifier/Injector/AutoParagraph.php'; |
||||
require 'HTMLPurifier/Injector/DisplayLinkURI.php'; |
||||
require 'HTMLPurifier/Injector/Linkify.php'; |
||||
require 'HTMLPurifier/Injector/PurifierLinkify.php'; |
||||
require 'HTMLPurifier/Injector/RemoveEmpty.php'; |
||||
require 'HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php'; |
||||
require 'HTMLPurifier/Injector/SafeObject.php'; |
||||
require 'HTMLPurifier/Lexer/DOMLex.php'; |
||||
require 'HTMLPurifier/Lexer/DirectLex.php'; |
||||
require 'HTMLPurifier/Strategy/Composite.php'; |
||||
require 'HTMLPurifier/Strategy/Core.php'; |
||||
require 'HTMLPurifier/Strategy/FixNesting.php'; |
||||
require 'HTMLPurifier/Strategy/MakeWellFormed.php'; |
||||
require 'HTMLPurifier/Strategy/RemoveForeignElements.php'; |
||||
require 'HTMLPurifier/Strategy/ValidateAttributes.php'; |
||||
require 'HTMLPurifier/TagTransform/Font.php'; |
||||
require 'HTMLPurifier/TagTransform/Simple.php'; |
||||
require 'HTMLPurifier/Token/Comment.php'; |
||||
require 'HTMLPurifier/Token/Tag.php'; |
||||
require 'HTMLPurifier/Token/Empty.php'; |
||||
require 'HTMLPurifier/Token/End.php'; |
||||
require 'HTMLPurifier/Token/Start.php'; |
||||
require 'HTMLPurifier/Token/Text.php'; |
||||
require 'HTMLPurifier/URIFilter/DisableExternal.php'; |
||||
require 'HTMLPurifier/URIFilter/DisableExternalResources.php'; |
||||
require 'HTMLPurifier/URIFilter/DisableResources.php'; |
||||
require 'HTMLPurifier/URIFilter/HostBlacklist.php'; |
||||
require 'HTMLPurifier/URIFilter/MakeAbsolute.php'; |
||||
require 'HTMLPurifier/URIFilter/Munge.php'; |
||||
require 'HTMLPurifier/URIScheme/data.php'; |
||||
require 'HTMLPurifier/URIScheme/file.php'; |
||||
require 'HTMLPurifier/URIScheme/ftp.php'; |
||||
require 'HTMLPurifier/URIScheme/http.php'; |
||||
require 'HTMLPurifier/URIScheme/https.php'; |
||||
require 'HTMLPurifier/URIScheme/mailto.php'; |
||||
require 'HTMLPurifier/URIScheme/news.php'; |
||||
require 'HTMLPurifier/URIScheme/nntp.php'; |
||||
require 'HTMLPurifier/VarParser/Flexible.php'; |
||||
require 'HTMLPurifier/VarParser/Native.php'; |
||||
|
||||
@ -1,204 +1,206 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @file |
||||
* This file was auto-generated by generate-includes.php and includes all of |
||||
* the core files required by HTML Purifier. This is a convenience stub that |
||||
* includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT |
||||
* EDIT THIS FILE, changes will be overwritten the next time the script is run. |
||||
* |
||||
* Changes to include_path are not necessary. |
||||
*/ |
||||
|
||||
$__dir = dirname(__FILE__); |
||||
|
||||
require_once $__dir . '/HTMLPurifier.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrCollections.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTypes.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrValidator.php'; |
||||
require_once $__dir . '/HTMLPurifier/Bootstrap.php'; |
||||
require_once $__dir . '/HTMLPurifier/Definition.php'; |
||||
require_once $__dir . '/HTMLPurifier/CSSDefinition.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef.php'; |
||||
require_once $__dir . '/HTMLPurifier/Config.php'; |
||||
require_once $__dir . '/HTMLPurifier/ConfigSchema.php'; |
||||
require_once $__dir . '/HTMLPurifier/ContentSets.php'; |
||||
require_once $__dir . '/HTMLPurifier/Context.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCacheFactory.php'; |
||||
require_once $__dir . '/HTMLPurifier/Doctype.php'; |
||||
require_once $__dir . '/HTMLPurifier/DoctypeRegistry.php'; |
||||
require_once $__dir . '/HTMLPurifier/ElementDef.php'; |
||||
require_once $__dir . '/HTMLPurifier/Encoder.php'; |
||||
require_once $__dir . '/HTMLPurifier/EntityLookup.php'; |
||||
require_once $__dir . '/HTMLPurifier/EntityParser.php'; |
||||
require_once $__dir . '/HTMLPurifier/ErrorCollector.php'; |
||||
require_once $__dir . '/HTMLPurifier/ErrorStruct.php'; |
||||
require_once $__dir . '/HTMLPurifier/Exception.php'; |
||||
require_once $__dir . '/HTMLPurifier/Filter.php'; |
||||
require_once $__dir . '/HTMLPurifier/Generator.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLDefinition.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModuleManager.php'; |
||||
require_once $__dir . '/HTMLPurifier/IDAccumulator.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector.php'; |
||||
require_once $__dir . '/HTMLPurifier/Language.php'; |
||||
require_once $__dir . '/HTMLPurifier/LanguageFactory.php'; |
||||
require_once $__dir . '/HTMLPurifier/Length.php'; |
||||
require_once $__dir . '/HTMLPurifier/Lexer.php'; |
||||
require_once $__dir . '/HTMLPurifier/PercentEncoder.php'; |
||||
require_once $__dir . '/HTMLPurifier/PropertyList.php'; |
||||
require_once $__dir . '/HTMLPurifier/PropertyListIterator.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy.php'; |
||||
require_once $__dir . '/HTMLPurifier/StringHash.php'; |
||||
require_once $__dir . '/HTMLPurifier/StringHashParser.php'; |
||||
require_once $__dir . '/HTMLPurifier/TagTransform.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token.php'; |
||||
require_once $__dir . '/HTMLPurifier/TokenFactory.php'; |
||||
require_once $__dir . '/HTMLPurifier/URI.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIDefinition.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIParser.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme.php'; |
||||
require_once $__dir . '/HTMLPurifier/URISchemeRegistry.php'; |
||||
require_once $__dir . '/HTMLPurifier/UnitConverter.php'; |
||||
require_once $__dir . '/HTMLPurifier/VarParser.php'; |
||||
require_once $__dir . '/HTMLPurifier/VarParserException.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/Enum.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/Integer.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/Lang.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/Switch.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/Text.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Number.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/AlphaValue.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Background.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Border.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Color.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Composite.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Filter.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Font.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/FontFamily.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Length.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ListStyle.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Multiple.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Percentage.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Nmtokens.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Class.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Color.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/FrameTarget.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ID.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Pixels.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Length.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/LinkTypes.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/MultiLength.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Host.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv4.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv6.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Background.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/BdoDir.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/BgColor.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/BoolToCSS.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Border.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Input.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/NameSync.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Required.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Optional.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/StrictBlockquote.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Table.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache/Null.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache/Serializer.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Memory.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Forms.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/List.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Name.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Proprietary.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Ruby.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeEmbed.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeObject.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Scripting.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Name.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Proprietary.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Strict.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Transitional.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTML.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/AutoParagraph.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/DisplayLinkURI.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/Linkify.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/RemoveEmpty.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/SafeObject.php'; |
||||
require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php'; |
||||
require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/Composite.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/Core.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/FixNesting.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/MakeWellFormed.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/RemoveForeignElements.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/ValidateAttributes.php'; |
||||
require_once $__dir . '/HTMLPurifier/TagTransform/Font.php'; |
||||
require_once $__dir . '/HTMLPurifier/TagTransform/Simple.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/Comment.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/Tag.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/Empty.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/End.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/Start.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/Text.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/data.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/http.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/https.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/news.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php'; |
||||
require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php'; |
||||
require_once $__dir . '/HTMLPurifier/VarParser/Native.php'; |
||||
<?php |
||||
|
||||
/** |
||||
* @file |
||||
* This file was auto-generated by generate-includes.php and includes all of |
||||
* the core files required by HTML Purifier. This is a convenience stub that |
||||
* includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT |
||||
* EDIT THIS FILE, changes will be overwritten the next time the script is run. |
||||
* |
||||
* Changes to include_path are not necessary. |
||||
*/ |
||||
|
||||
$__dir = dirname(__FILE__); |
||||
|
||||
require_once $__dir . '/HTMLPurifier.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrCollections.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTypes.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrValidator.php'; |
||||
require_once $__dir . '/HTMLPurifier/Bootstrap.php'; |
||||
require_once $__dir . '/HTMLPurifier/Definition.php'; |
||||
require_once $__dir . '/HTMLPurifier/CSSDefinition.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef.php'; |
||||
require_once $__dir . '/HTMLPurifier/Config.php'; |
||||
require_once $__dir . '/HTMLPurifier/ConfigSchema.php'; |
||||
require_once $__dir . '/HTMLPurifier/ContentSets.php'; |
||||
require_once $__dir . '/HTMLPurifier/Context.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCacheFactory.php'; |
||||
require_once $__dir . '/HTMLPurifier/Doctype.php'; |
||||
require_once $__dir . '/HTMLPurifier/DoctypeRegistry.php'; |
||||
require_once $__dir . '/HTMLPurifier/ElementDef.php'; |
||||
require_once $__dir . '/HTMLPurifier/Encoder.php'; |
||||
require_once $__dir . '/HTMLPurifier/EntityLookup.php'; |
||||
require_once $__dir . '/HTMLPurifier/EntityParser.php'; |
||||
require_once $__dir . '/HTMLPurifier/ErrorCollector.php'; |
||||
require_once $__dir . '/HTMLPurifier/ErrorStruct.php'; |
||||
require_once $__dir . '/HTMLPurifier/Exception.php'; |
||||
require_once $__dir . '/HTMLPurifier/Filter.php'; |
||||
require_once $__dir . '/HTMLPurifier/Generator.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLDefinition.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModuleManager.php'; |
||||
require_once $__dir . '/HTMLPurifier/IDAccumulator.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector.php'; |
||||
require_once $__dir . '/HTMLPurifier/Language.php'; |
||||
require_once $__dir . '/HTMLPurifier/LanguageFactory.php'; |
||||
require_once $__dir . '/HTMLPurifier/Length.php'; |
||||
require_once $__dir . '/HTMLPurifier/Lexer.php'; |
||||
require_once $__dir . '/HTMLPurifier/PercentEncoder.php'; |
||||
require_once $__dir . '/HTMLPurifier/PropertyList.php'; |
||||
require_once $__dir . '/HTMLPurifier/PropertyListIterator.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy.php'; |
||||
require_once $__dir . '/HTMLPurifier/StringHash.php'; |
||||
require_once $__dir . '/HTMLPurifier/StringHashParser.php'; |
||||
require_once $__dir . '/HTMLPurifier/TagTransform.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token.php'; |
||||
require_once $__dir . '/HTMLPurifier/TokenFactory.php'; |
||||
require_once $__dir . '/HTMLPurifier/URI.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIDefinition.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIParser.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme.php'; |
||||
require_once $__dir . '/HTMLPurifier/URISchemeRegistry.php'; |
||||
require_once $__dir . '/HTMLPurifier/UnitConverter.php'; |
||||
require_once $__dir . '/HTMLPurifier/VarParser.php'; |
||||
require_once $__dir . '/HTMLPurifier/VarParserException.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/Enum.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/Integer.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/Lang.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/Switch.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/Text.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Number.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/AlphaValue.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Background.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Border.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Color.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Composite.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Filter.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Font.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/FontFamily.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Length.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ListStyle.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Multiple.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Percentage.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Nmtokens.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Class.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Color.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/FrameTarget.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ID.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Pixels.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Length.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/LinkTypes.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/MultiLength.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Host.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv4.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv6.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Background.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/BdoDir.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/BgColor.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/BoolToCSS.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Border.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Input.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/NameSync.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php'; |
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Required.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Optional.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/StrictBlockquote.php'; |
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Table.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache/Null.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache/Serializer.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php'; |
||||
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Memory.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Forms.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/List.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Name.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Proprietary.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Ruby.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeEmbed.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeObject.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Scripting.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Name.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Proprietary.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Strict.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Transitional.php'; |
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTML.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/AutoParagraph.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/DisplayLinkURI.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/Linkify.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/RemoveEmpty.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php'; |
||||
require_once $__dir . '/HTMLPurifier/Injector/SafeObject.php'; |
||||
require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php'; |
||||
require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/Composite.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/Core.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/FixNesting.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/MakeWellFormed.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/RemoveForeignElements.php'; |
||||
require_once $__dir . '/HTMLPurifier/Strategy/ValidateAttributes.php'; |
||||
require_once $__dir . '/HTMLPurifier/TagTransform/Font.php'; |
||||
require_once $__dir . '/HTMLPurifier/TagTransform/Simple.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/Comment.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/Tag.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/Empty.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/End.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/Start.php'; |
||||
require_once $__dir . '/HTMLPurifier/Token/Text.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/DisableResources.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/data.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/file.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/http.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/https.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/news.php'; |
||||
require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php'; |
||||
require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php'; |
||||
require_once $__dir . '/HTMLPurifier/VarParser/Native.php'; |
||||
|
||||
@ -1,87 +1,123 @@ |
||||
<?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. |
||||
*/ |
||||
|
||||
abstract class HTMLPurifier_AttrDef |
||||
{ |
||||
|
||||
/** |
||||
* Tells us whether or not an HTML attribute is minimized. Has no |
||||
* meaning in other contexts. |
||||
*/ |
||||
public $minimized = false; |
||||
|
||||
/** |
||||
* Tells us whether or not an HTML attribute is required. Has no |
||||
* meaning in other contexts |
||||
*/ |
||||
public $required = false; |
||||
|
||||
/** |
||||
* Validates and cleans passed string according to a definition. |
||||
* |
||||
* @param $string String to be validated and cleaned. |
||||
* @param $config Mandatory HTMLPurifier_Config object. |
||||
* @param $context Mandatory HTMLPurifier_AttrContext object. |
||||
*/ |
||||
abstract public function validate($string, $config, $context); |
||||
|
||||
/** |
||||
* 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. However, note that we are NOT necessarily |
||||
* parsing XML, thus, this behavior may still be correct. We |
||||
* assume that newlines have been normalized. |
||||
*/ |
||||
public function parseCDATA($string) { |
||||
$string = trim($string); |
||||
$string = str_replace(array("\n", "\t", "\r"), ' ', $string); |
||||
return $string; |
||||
} |
||||
|
||||
/** |
||||
* Factory method for creating this class from a string. |
||||
* @param $string String construction info |
||||
* @return Created AttrDef object corresponding to $string |
||||
*/ |
||||
public function make($string) { |
||||
// default implementation, return a flyweight of this object. |
||||
// If $string has an effect on the returned object (i.e. you |
||||
// need to overload this method), it is best |
||||
// to clone or instantiate new copies. (Instantiation is safer.) |
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work |
||||
* properly. THIS IS A HACK! |
||||
*/ |
||||
protected function mungeRgb($string) { |
||||
return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string); |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?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. |
||||
*/ |
||||
|
||||
abstract class HTMLPurifier_AttrDef |
||||
{ |
||||
|
||||
/** |
||||
* Tells us whether or not an HTML attribute is minimized. Has no |
||||
* meaning in other contexts. |
||||
*/ |
||||
public $minimized = false; |
||||
|
||||
/** |
||||
* Tells us whether or not an HTML attribute is required. Has no |
||||
* meaning in other contexts |
||||
*/ |
||||
public $required = false; |
||||
|
||||
/** |
||||
* Validates and cleans passed string according to a definition. |
||||
* |
||||
* @param $string String to be validated and cleaned. |
||||
* @param $config Mandatory HTMLPurifier_Config object. |
||||
* @param $context Mandatory HTMLPurifier_AttrContext object. |
||||
*/ |
||||
abstract public function validate($string, $config, $context); |
||||
|
||||
/** |
||||
* 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. However, note that we are NOT necessarily |
||||
* parsing XML, thus, this behavior may still be correct. We |
||||
* assume that newlines have been normalized. |
||||
*/ |
||||
public function parseCDATA($string) { |
||||
$string = trim($string); |
||||
$string = str_replace(array("\n", "\t", "\r"), ' ', $string); |
||||
return $string; |
||||
} |
||||
|
||||
/** |
||||
* Factory method for creating this class from a string. |
||||
* @param $string String construction info |
||||
* @return Created AttrDef object corresponding to $string |
||||
*/ |
||||
public function make($string) { |
||||
// default implementation, return a flyweight of this object. |
||||
// If $string has an effect on the returned object (i.e. you |
||||
// need to overload this method), it is best |
||||
// to clone or instantiate new copies. (Instantiation is safer.) |
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work |
||||
* properly. THIS IS A HACK! |
||||
*/ |
||||
protected function mungeRgb($string) { |
||||
return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string); |
||||
} |
||||
|
||||
/** |
||||
* Parses a possibly escaped CSS string and returns the "pure" |
||||
* version of it. |
||||
*/ |
||||
protected function expandCSSEscape($string) { |
||||
// flexibly parse it |
||||
$ret = ''; |
||||
for ($i = 0, $c = strlen($string); $i < $c; $i++) { |
||||
if ($string[$i] === '\\') { |
||||
$i++; |
||||
if ($i >= $c) { |
||||
$ret .= '\\'; |
||||
break; |
||||
} |
||||
if (ctype_xdigit($string[$i])) { |
||||
$code = $string[$i]; |
||||
for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) { |
||||
if (!ctype_xdigit($string[$i])) break; |
||||
$code .= $string[$i]; |
||||
} |
||||
// We have to be extremely careful when adding |
||||
// new characters, to make sure we're not breaking |
||||
// the encoding. |
||||
$char = HTMLPurifier_Encoder::unichr(hexdec($code)); |
||||
if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue; |
||||
$ret .= $char; |
||||
if ($i < $c && trim($string[$i]) !== '') $i--; |
||||
continue; |
||||
} |
||||
if ($string[$i] === "\n") continue; |
||||
} |
||||
$ret .= $string[$i]; |
||||
} |
||||
return $ret; |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,126 +1,133 @@ |
||||
<?php |
||||
|
||||
/* W3C says: |
||||
[ // adjective and number must be in correct order, even if |
||||
// you could switch them without introducing ambiguity. |
||||
// some browsers support that syntax |
||||
[ |
||||
<percentage> | <length> | left | center | right |
||||
] |
||||
[ |
||||
<percentage> | <length> | top | center | bottom |
||||
]? |
||||
] | |
||||
[ // this signifies that the vertical and horizontal adjectives |
||||
// can be arbitrarily ordered, however, there can only be two, |
||||
// one of each, or none at all |
||||
[ |
||||
left | center | right |
||||
] || |
||||
[ |
||||
top | center | bottom |
||||
] |
||||
] |
||||
top, left = 0% |
||||
center, (none) = 50% |
||||
bottom, right = 100% |
||||
*/ |
||||
|
||||
/* QuirksMode says: |
||||
keyword + length/percentage must be ordered correctly, as per W3C |
||||
|
||||
Internet Explorer and Opera, however, support arbitrary ordering. We |
||||
should fix it up. |
||||
|
||||
Minor issue though, not strictly necessary. |
||||
*/ |
||||
|
||||
// control freaks may appreciate the ability to convert these to |
||||
// percentages or something, but it's not necessary |
||||
|
||||
/** |
||||
* Validates the value of background-position. |
||||
*/ |
||||
class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef |
||||
{ |
||||
|
||||
protected $length; |
||||
protected $percentage; |
||||
|
||||
public function __construct() { |
||||
$this->length = new HTMLPurifier_AttrDef_CSS_Length(); |
||||
$this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage(); |
||||
} |
||||
|
||||
public function validate($string, $config, $context) { |
||||
$string = $this->parseCDATA($string); |
||||
$bits = explode(' ', $string); |
||||
|
||||
$keywords = array(); |
||||
$keywords['h'] = false; // left, right |
||||
$keywords['v'] = false; // top, bottom |
||||
$keywords['c'] = false; // center |
||||
$measures = array(); |
||||
|
||||
$i = 0; |
||||
|
||||
$lookup = array( |
||||
'top' => 'v', |
||||
'bottom' => 'v', |
||||
'left' => 'h', |
||||
'right' => 'h', |
||||
'center' => 'c' |
||||
); |
||||
|
||||
foreach ($bits as $bit) { |
||||
if ($bit === '') continue; |
||||
|
||||
// test for keyword |
||||
$lbit = ctype_lower($bit) ? $bit : strtolower($bit); |
||||
if (isset($lookup[$lbit])) { |
||||
$status = $lookup[$lbit]; |
||||
$keywords[$status] = $lbit; |
||||
$i++; |
||||
} |
||||
|
||||
// test for length |
||||
$r = $this->length->validate($bit, $config, $context); |
||||
if ($r !== false) { |
||||
$measures[] = $r; |
||||
$i++; |
||||
} |
||||
|
||||
// test for percentage |
||||
$r = $this->percentage->validate($bit, $config, $context); |
||||
if ($r !== false) { |
||||
$measures[] = $r; |
||||
$i++; |
||||
} |
||||
|
||||
} |
||||
|
||||
if (!$i) return false; // no valid values were caught |
||||
|
||||
|
||||
$ret = array(); |
||||
|
||||
// first keyword |
||||
if ($keywords['h']) $ret[] = $keywords['h']; |
||||
elseif (count($measures)) $ret[] = array_shift($measures); |
||||
elseif ($keywords['c']) { |
||||
$ret[] = $keywords['c']; |
||||
$keywords['c'] = false; // prevent re-use: center = center center |
||||
} |
||||
|
||||
if ($keywords['v']) $ret[] = $keywords['v']; |
||||
elseif (count($measures)) $ret[] = array_shift($measures); |
||||
elseif ($keywords['c']) $ret[] = $keywords['c']; |
||||
|
||||
if (empty($ret)) return false; |
||||
return implode(' ', $ret); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/* W3C says: |
||||
[ // adjective and number must be in correct order, even if |
||||
// you could switch them without introducing ambiguity. |
||||
// some browsers support that syntax |
||||
[ |
||||
<percentage> | <length> | left | center | right |
||||
] |
||||
[ |
||||
<percentage> | <length> | top | center | bottom |
||||
]? |
||||
] | |
||||
[ // this signifies that the vertical and horizontal adjectives |
||||
// can be arbitrarily ordered, however, there can only be two, |
||||
// one of each, or none at all |
||||
[ |
||||
left | center | right |
||||
] || |
||||
[ |
||||
top | center | bottom |
||||
] |
||||
] |
||||
top, left = 0% |
||||
center, (none) = 50% |
||||
bottom, right = 100% |
||||
*/ |
||||
|
||||
/* QuirksMode says: |
||||
keyword + length/percentage must be ordered correctly, as per W3C |
||||
|
||||
Internet Explorer and Opera, however, support arbitrary ordering. We |
||||
should fix it up. |
||||
|
||||
Minor issue though, not strictly necessary. |
||||
*/ |
||||
|
||||
// control freaks may appreciate the ability to convert these to |
||||
// percentages or something, but it's not necessary |
||||
|
||||
/** |
||||
* Validates the value of background-position. |
||||
*/ |
||||
class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef |
||||
{ |
||||
|
||||
protected $length; |
||||
protected $percentage; |
||||
|
||||
public function __construct() { |
||||
$this->length = new HTMLPurifier_AttrDef_CSS_Length(); |
||||
$this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage(); |
||||
} |
||||
|
||||
public function validate($string, $config, $context) { |
||||
$string = $this->parseCDATA($string); |
||||
$bits = explode(' ', $string); |
||||
|
||||
$keywords = array(); |
||||
$keywords['h'] = false; // left, right |
||||
$keywords['v'] = false; // top, bottom |
||||
$keywords['ch'] = false; // center (first word) |
||||
$keywords['cv'] = false; // center (second word) |
||||
$measures = array(); |
||||
|
||||
$i = 0; |
||||
|
||||
$lookup = array( |
||||
'top' => 'v', |
||||
'bottom' => 'v', |
||||
'left' => 'h', |
||||
'right' => 'h', |
||||
'center' => 'c' |
||||
); |
||||
|
||||
foreach ($bits as $bit) { |
||||
if ($bit === '') continue; |
||||
|
||||
// test for keyword |
||||
$lbit = ctype_lower($bit) ? $bit : strtolower($bit); |
||||
if (isset($lookup[$lbit])) { |
||||
$status = $lookup[$lbit]; |
||||
if ($status == 'c') { |
||||
if ($i == 0) { |
||||
$status = 'ch'; |
||||
} else { |
||||
$status = 'cv'; |
||||
} |
||||
} |
||||
$keywords[$status] = $lbit; |
||||
$i++; |
||||
} |
||||
|
||||
// test for length |
||||
$r = $this->length->validate($bit, $config, $context); |
||||
if ($r !== false) { |
||||
$measures[] = $r; |
||||
$i++; |
||||
} |
||||
|
||||
// test for percentage |
||||
$r = $this->percentage->validate($bit, $config, $context); |
||||
if ($r !== false) { |
||||
$measures[] = $r; |
||||
$i++; |
||||
} |
||||
|
||||
} |
||||
|
||||
if (!$i) return false; // no valid values were caught |
||||
|
||||
$ret = array(); |
||||
|
||||
// first keyword |
||||
if ($keywords['h']) $ret[] = $keywords['h']; |
||||
elseif ($keywords['ch']) { |
||||
$ret[] = $keywords['ch']; |
||||
$keywords['cv'] = false; // prevent re-use: center = center center |
||||
} |
||||
elseif (count($measures)) $ret[] = array_shift($measures); |
||||
|
||||
if ($keywords['v']) $ret[] = $keywords['v']; |
||||
elseif ($keywords['cv']) $ret[] = $keywords['cv']; |
||||
elseif (count($measures)) $ret[] = array_shift($measures); |
||||
|
||||
if (empty($ret)) return false; |
||||
return implode(' ', $ret); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,90 +1,72 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Validates a font family list according to CSS spec |
||||
* @todo whitelisting allowed fonts would be nice |
||||
*/ |
||||
class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef |
||||
{ |
||||
|
||||
public function validate($string, $config, $context) { |
||||
static $generic_names = array( |
||||
'serif' => true, |
||||
'sans-serif' => true, |
||||
'monospace' => true, |
||||
'fantasy' => true, |
||||
'cursive' => true |
||||
); |
||||
|
||||
// assume that no font names contain commas in them |
||||
$fonts = explode(',', $string); |
||||
$final = ''; |
||||
foreach($fonts as $font) { |
||||
$font = trim($font); |
||||
if ($font === '') continue; |
||||
// match a generic name |
||||
if (isset($generic_names[$font])) { |
||||
$final .= $font . ', '; |
||||
continue; |
||||
} |
||||
// match a quoted name |
||||
if ($font[0] === '"' || $font[0] === "'") { |
||||
$length = strlen($font); |
||||
if ($length <= 2) continue; |
||||
$quote = $font[0]; |
||||
if ($font[$length - 1] !== $quote) continue; |
||||
$font = substr($font, 1, $length - 2); |
||||
|
||||
$new_font = ''; |
||||
for ($i = 0, $c = strlen($font); $i < $c; $i++) { |
||||
if ($font[$i] === '\\') { |
||||
$i++; |
||||
if ($i >= $c) { |
||||
$new_font .= '\\'; |
||||
break; |
||||
} |
||||
if (ctype_xdigit($font[$i])) { |
||||
$code = $font[$i]; |
||||
for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) { |
||||
if (!ctype_xdigit($font[$i])) break; |
||||
$code .= $font[$i]; |
||||
} |
||||
// We have to be extremely careful when adding |
||||
// new characters, to make sure we're not breaking |
||||
// the encoding. |
||||
$char = HTMLPurifier_Encoder::unichr(hexdec($code)); |
||||
if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue; |
||||
$new_font .= $char; |
||||
if ($i < $c && trim($font[$i]) !== '') $i--; |
||||
continue; |
||||
} |
||||
if ($font[$i] === "\n") continue; |
||||
} |
||||
$new_font .= $font[$i]; |
||||
} |
||||
|
||||
$font = $new_font; |
||||
} |
||||
// $font is a pure representation of the font name |
||||
|
||||
if (ctype_alnum($font) && $font !== '') { |
||||
// very simple font, allow it in unharmed |
||||
$final .= $font . ', '; |
||||
continue; |
||||
} |
||||
|
||||
// complicated font, requires quoting |
||||
|
||||
// armor single quotes and new lines |
||||
$font = str_replace("\\", "\\\\", $font); |
||||
$font = str_replace("'", "\\'", $font); |
||||
$final .= "'$font', "; |
||||
} |
||||
$final = rtrim($final, ', '); |
||||
if ($final === '') return false; |
||||
return $final; |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Validates a font family list according to CSS spec |
||||
* @todo whitelisting allowed fonts would be nice |
||||
*/ |
||||
class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef |
||||
{ |
||||
|
||||
public function validate($string, $config, $context) { |
||||
static $generic_names = array( |
||||
'serif' => true, |
||||
'sans-serif' => true, |
||||
'monospace' => true, |
||||
'fantasy' => true, |
||||
'cursive' => true |
||||
); |
||||
|
||||
// assume that no font names contain commas in them |
||||
$fonts = explode(',', $string); |
||||
$final = ''; |
||||
foreach($fonts as $font) { |
||||
$font = trim($font); |
||||
if ($font === '') continue; |
||||
// match a generic name |
||||
if (isset($generic_names[$font])) { |
||||
$final .= $font . ', '; |
||||
continue; |
||||
} |
||||
// match a quoted name |
||||
if ($font[0] === '"' || $font[0] === "'") { |
||||
$length = strlen($font); |
||||
if ($length <= 2) continue; |
||||
$quote = $font[0]; |
||||
if ($font[$length - 1] !== $quote) continue; |
||||
$font = substr($font, 1, $length - 2); |
||||
} |
||||
|
||||
$font = $this->expandCSSEscape($font); |
||||
|
||||
// $font is a pure representation of the font name |
||||
|
||||
if (ctype_alnum($font) && $font !== '') { |
||||
// very simple font, allow it in unharmed |
||||
$final .= $font . ', '; |
||||
continue; |
||||
} |
||||
|
||||
// bugger out on whitespace. form feed (0C) really |
||||
// shouldn't show up regardless |
||||
$font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font); |
||||
|
||||
// These ugly transforms don't pose a security |
||||
// risk (as \\ and \" might). We could try to be clever and |
||||
// use single-quote wrapping when there is a double quote |
||||
// present, but I have choosen not to implement that. |
||||
// (warning: this code relies on the selection of quotation |
||||
// mark below) |
||||
$font = str_replace('\\', '\\5C ', $font); |
||||
$font = str_replace('"', '\\22 ', $font); |
||||
|
||||
// complicated font, requires quoting |
||||
$final .= "\"$font\", "; // note that this will later get turned into " |
||||
} |
||||
$final = rtrim($final, ', '); |
||||
if ($final === '') return false; |
||||
return $final; |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,56 +1,52 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Validates a URI in CSS syntax, which uses url('http://example.com') |
||||
* @note While theoretically speaking a URI in a CSS document could |
||||
* be non-embedded, as of CSS2 there is no such usage so we're |
||||
* generalizing it. This may need to be changed in the future. |
||||
* @warning Since HTMLPurifier_AttrDef_CSS blindly uses semicolons as |
||||
* the separator, you cannot put a literal semicolon in |
||||
* in the URI. Try percent encoding it, in that case. |
||||
*/ |
||||
class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI |
||||
{ |
||||
|
||||
public function __construct() { |
||||
parent::__construct(true); // always embedded |
||||
} |
||||
|
||||
public function validate($uri_string, $config, $context) { |
||||
// parse the URI out of the string and then pass it onto |
||||
// the parent object |
||||
|
||||
$uri_string = $this->parseCDATA($uri_string); |
||||
if (strpos($uri_string, 'url(') !== 0) return false; |
||||
$uri_string = substr($uri_string, 4); |
||||
$new_length = strlen($uri_string) - 1; |
||||
if ($uri_string[$new_length] != ')') return false; |
||||
$uri = trim(substr($uri_string, 0, $new_length)); |
||||
|
||||
if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) { |
||||
$quote = $uri[0]; |
||||
$new_length = strlen($uri) - 1; |
||||
if ($uri[$new_length] !== $quote) return false; |
||||
$uri = substr($uri, 1, $new_length - 1); |
||||
} |
||||
|
||||
$keys = array( '(', ')', ',', ' ', '"', "'"); |
||||
$values = array('\\(', '\\)', '\\,', '\\ ', '\\"', "\\'"); |
||||
$uri = str_replace($values, $keys, $uri); |
||||
|
||||
$result = parent::validate($uri, $config, $context); |
||||
|
||||
if ($result === false) return false; |
||||
|
||||
// escape necessary characters according to CSS spec |
||||
// except for the comma, none of these should appear in the |
||||
// URI at all |
||||
$result = str_replace($keys, $values, $result); |
||||
|
||||
return "url('$result')"; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Validates a URI in CSS syntax, which uses url('http://example.com') |
||||
* @note While theoretically speaking a URI in a CSS document could |
||||
* be non-embedded, as of CSS2 there is no such usage so we're |
||||
* generalizing it. This may need to be changed in the future. |
||||
* @warning Since HTMLPurifier_AttrDef_CSS blindly uses semicolons as |
||||
* the separator, you cannot put a literal semicolon in |
||||
* in the URI. Try percent encoding it, in that case. |
||||
*/ |
||||
class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI |
||||
{ |
||||
|
||||
public function __construct() { |
||||
parent::__construct(true); // always embedded |
||||
} |
||||
|
||||
public function validate($uri_string, $config, $context) { |
||||
// parse the URI out of the string and then pass it onto |
||||
// the parent object |
||||
|
||||
$uri_string = $this->parseCDATA($uri_string); |
||||
if (strpos($uri_string, 'url(') !== 0) return false; |
||||
$uri_string = substr($uri_string, 4); |
||||
$new_length = strlen($uri_string) - 1; |
||||
if ($uri_string[$new_length] != ')') return false; |
||||
$uri = trim(substr($uri_string, 0, $new_length)); |
||||
|
||||
if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) { |
||||
$quote = $uri[0]; |
||||
$new_length = strlen($uri) - 1; |
||||
if ($uri[$new_length] !== $quote) return false; |
||||
$uri = substr($uri, 1, $new_length - 1); |
||||
} |
||||
|
||||
$uri = $this->expandCSSEscape($uri); |
||||
|
||||
$result = parent::validate($uri, $config, $context); |
||||
|
||||
if ($result === false) return false; |
||||
|
||||
// extra sanity check; should have been done by URI |
||||
$result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result); |
||||
|
||||
return "url(\"$result\")"; |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,56 +1,63 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Validates name/value pairs in param tags to be used in safe objects. This |
||||
* will only allow name values it recognizes, and pre-fill certain attributes |
||||
* with required values. |
||||
* |
||||
* @note |
||||
* This class only supports Flash. In the future, Quicktime support |
||||
* may be added. |
||||
* |
||||
* @warning |
||||
* This class expects an injector to add the necessary parameters tags. |
||||
*/ |
||||
class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform |
||||
{ |
||||
public $name = "SafeParam"; |
||||
private $uri; |
||||
|
||||
public function __construct() { |
||||
$this->uri = new HTMLPurifier_AttrDef_URI(true); // embedded |
||||
} |
||||
|
||||
public function transform($attr, $config, $context) { |
||||
// If we add support for other objects, we'll need to alter the |
||||
// transforms. |
||||
switch ($attr['name']) { |
||||
// application/x-shockwave-flash |
||||
// Keep this synchronized with Injector/SafeObject.php |
||||
case 'allowScriptAccess': |
||||
$attr['value'] = 'never'; |
||||
break; |
||||
case 'allowNetworking': |
||||
$attr['value'] = 'internal'; |
||||
break; |
||||
case 'wmode': |
||||
$attr['value'] = 'window'; |
||||
break; |
||||
case 'movie': |
||||
case 'src': |
||||
$attr['name'] = "movie"; |
||||
$attr['value'] = $this->uri->validate($attr['value'], $config, $context); |
||||
break; |
||||
case 'flashvars': |
||||
// we're going to allow arbitrary inputs to the SWF, on |
||||
// the reasoning that it could only hack the SWF, not us. |
||||
break; |
||||
// add other cases to support other param name/value pairs |
||||
default: |
||||
$attr['name'] = $attr['value'] = null; |
||||
} |
||||
return $attr; |
||||
} |
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Validates name/value pairs in param tags to be used in safe objects. This |
||||
* will only allow name values it recognizes, and pre-fill certain attributes |
||||
* with required values. |
||||
* |
||||
* @note |
||||
* This class only supports Flash. In the future, Quicktime support |
||||
* may be added. |
||||
* |
||||
* @warning |
||||
* This class expects an injector to add the necessary parameters tags. |
||||
*/ |
||||
class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform |
||||
{ |
||||
public $name = "SafeParam"; |
||||
private $uri; |
||||
|
||||
public function __construct() { |
||||
$this->uri = new HTMLPurifier_AttrDef_URI(true); // embedded |
||||
} |
||||
|
||||
public function transform($attr, $config, $context) { |
||||
// If we add support for other objects, we'll need to alter the |
||||
// transforms. |
||||
switch ($attr['name']) { |
||||
// application/x-shockwave-flash |
||||
// Keep this synchronized with Injector/SafeObject.php |
||||
case 'allowScriptAccess': |
||||
$attr['value'] = 'never'; |
||||
break; |
||||
case 'allowNetworking': |
||||
$attr['value'] = 'internal'; |
||||
break; |
||||
case 'allowFullScreen': |
||||
if ($config->get('HTML.FlashAllowFullScreen')) { |
||||
$attr['value'] = ($attr['value'] == 'true') ? 'true' : 'false'; |
||||
} else { |
||||
$attr['value'] = 'false'; |
||||
} |
||||
break; |
||||
case 'wmode': |
||||
$attr['value'] = 'window'; |
||||
break; |
||||
case 'movie': |
||||
case 'src': |
||||
$attr['name'] = "movie"; |
||||
$attr['value'] = $this->uri->validate($attr['value'], $config, $context); |
||||
break; |
||||
case 'flashvars': |
||||
// we're going to allow arbitrary inputs to the SWF, on |
||||
// the reasoning that it could only hack the SWF, not us. |
||||
break; |
||||
// add other cases to support other param name/value pairs |
||||
default: |
||||
$attr['name'] = $attr['value'] = null; |
||||
} |
||||
return $attr; |
||||
} |
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,292 +1,301 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Defines allowed CSS attributes and what their values are. |
||||
* @see HTMLPurifier_HTMLDefinition |
||||
*/ |
||||
class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition |
||||
{ |
||||
|
||||
public $type = 'CSS'; |
||||
|
||||
/** |
||||
* Assoc array of attribute name to definition object. |
||||
*/ |
||||
public $info = array(); |
||||
|
||||
/** |
||||
* Constructs the info array. The meat of this class. |
||||
*/ |
||||
protected function doSetup($config) { |
||||
|
||||
$this->info['text-align'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('left', 'right', 'center', 'justify'), false); |
||||
|
||||
$border_style = |
||||
$this->info['border-bottom-style'] = |
||||
$this->info['border-right-style'] = |
||||
$this->info['border-left-style'] = |
||||
$this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', |
||||
'groove', 'ridge', 'inset', 'outset'), false); |
||||
|
||||
$this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style); |
||||
|
||||
$this->info['clear'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('none', 'left', 'right', 'both'), false); |
||||
$this->info['float'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('none', 'left', 'right'), false); |
||||
$this->info['font-style'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('normal', 'italic', 'oblique'), false); |
||||
$this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('normal', 'small-caps'), false); |
||||
|
||||
$uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite( |
||||
array( |
||||
new HTMLPurifier_AttrDef_Enum(array('none')), |
||||
new HTMLPurifier_AttrDef_CSS_URI() |
||||
) |
||||
); |
||||
|
||||
$this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('inside', 'outside'), false); |
||||
$this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('disc', 'circle', 'square', 'decimal', 'lower-roman', |
||||
'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false); |
||||
$this->info['list-style-image'] = $uri_or_none; |
||||
|
||||
$this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config); |
||||
|
||||
$this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('capitalize', 'uppercase', 'lowercase', 'none'), false); |
||||
$this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
|
||||
$this->info['background-image'] = $uri_or_none; |
||||
$this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('repeat', 'repeat-x', 'repeat-y', 'no-repeat') |
||||
); |
||||
$this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('scroll', 'fixed') |
||||
); |
||||
$this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition(); |
||||
|
||||
$border_color = |
||||
$this->info['border-top-color'] = |
||||
$this->info['border-bottom-color'] = |
||||
$this->info['border-left-color'] = |
||||
$this->info['border-right-color'] = |
||||
$this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('transparent')), |
||||
new HTMLPurifier_AttrDef_CSS_Color() |
||||
)); |
||||
|
||||
$this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config); |
||||
|
||||
$this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color); |
||||
|
||||
$border_width = |
||||
$this->info['border-top-width'] = |
||||
$this->info['border-bottom-width'] = |
||||
$this->info['border-left-width'] = |
||||
$this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')), |
||||
new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative |
||||
)); |
||||
|
||||
$this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width); |
||||
|
||||
$this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('normal')), |
||||
new HTMLPurifier_AttrDef_CSS_Length() |
||||
)); |
||||
|
||||
$this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('normal')), |
||||
new HTMLPurifier_AttrDef_CSS_Length() |
||||
)); |
||||
|
||||
$this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small', |
||||
'small', 'medium', 'large', 'x-large', 'xx-large', |
||||
'larger', 'smaller')), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage(), |
||||
new HTMLPurifier_AttrDef_CSS_Length() |
||||
)); |
||||
|
||||
$this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('normal')), |
||||
new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives |
||||
new HTMLPurifier_AttrDef_CSS_Length('0'), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage(true) |
||||
)); |
||||
|
||||
$margin = |
||||
$this->info['margin-top'] = |
||||
$this->info['margin-bottom'] = |
||||
$this->info['margin-left'] = |
||||
$this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_CSS_Length(), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage(), |
||||
new HTMLPurifier_AttrDef_Enum(array('auto')) |
||||
)); |
||||
|
||||
$this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin); |
||||
|
||||
// non-negative |
||||
$padding = |
||||
$this->info['padding-top'] = |
||||
$this->info['padding-bottom'] = |
||||
$this->info['padding-left'] = |
||||
$this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_CSS_Length('0'), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage(true) |
||||
)); |
||||
|
||||
$this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding); |
||||
|
||||
$this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_CSS_Length(), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage() |
||||
)); |
||||
|
||||
$trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_CSS_Length('0'), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage(true), |
||||
new HTMLPurifier_AttrDef_Enum(array('auto')) |
||||
)); |
||||
$max = $config->get('CSS.MaxImgLength'); |
||||
|
||||
$this->info['width'] = |
||||
$this->info['height'] = |
||||
$max === null ? |
||||
$trusted_wh : |
||||
new HTMLPurifier_AttrDef_Switch('img', |
||||
// For img tags: |
||||
new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_CSS_Length('0', $max), |
||||
new HTMLPurifier_AttrDef_Enum(array('auto')) |
||||
)), |
||||
// For everyone else: |
||||
$trusted_wh |
||||
); |
||||
|
||||
$this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration(); |
||||
|
||||
$this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily(); |
||||
|
||||
// this could use specialized code |
||||
$this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum( |
||||
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_CSS_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_CSS_Border($config); |
||||
|
||||
$this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array( |
||||
'collapse', 'separate')); |
||||
|
||||
$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_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super', |
||||
'top', 'text-top', 'middle', 'bottom', 'text-bottom')), |
||||
new HTMLPurifier_AttrDef_CSS_Length(), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage() |
||||
)); |
||||
|
||||
$this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2); |
||||
|
||||
// partial support |
||||
$this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap')); |
||||
|
||||
if ($config->get('CSS.Proprietary')) { |
||||
$this->doSetupProprietary($config); |
||||
} |
||||
|
||||
if ($config->get('CSS.AllowTricky')) { |
||||
$this->doSetupTricky($config); |
||||
} |
||||
|
||||
$allow_important = $config->get('CSS.AllowImportant'); |
||||
// wrap all attr-defs with decorator that handles !important |
||||
foreach ($this->info as $k => $v) { |
||||
$this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important); |
||||
} |
||||
|
||||
$this->setupConfigStuff($config); |
||||
} |
||||
|
||||
protected function doSetupProprietary($config) { |
||||
// Internet Explorer only scrollbar colors |
||||
$this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
$this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
$this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
$this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
$this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
$this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
|
||||
// technically not proprietary, but CSS3, and no one supports it |
||||
$this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
||||
$this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
||||
$this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
||||
|
||||
// only opacity, for now |
||||
$this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter(); |
||||
|
||||
} |
||||
|
||||
protected function doSetupTricky($config) { |
||||
$this->info['display'] = new HTMLPurifier_AttrDef_Enum(array( |
||||
'inline', 'block', 'list-item', 'run-in', 'compact', |
||||
'marker', 'table', 'inline-table', 'table-row-group', |
||||
'table-header-group', 'table-footer-group', 'table-row', |
||||
'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none' |
||||
)); |
||||
$this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array( |
||||
'visible', 'hidden', 'collapse' |
||||
)); |
||||
$this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll')); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Performs extra config-based processing. Based off of |
||||
* HTMLPurifier_HTMLDefinition. |
||||
* @todo Refactor duplicate elements into common class (probably using |
||||
* composition, not inheritance). |
||||
*/ |
||||
protected function setupConfigStuff($config) { |
||||
|
||||
// setup allowed elements |
||||
$support = "(for information on implementing this, see the ". |
||||
"support forums) "; |
||||
$allowed_attributes = $config->get('CSS.AllowedProperties'); |
||||
if ($allowed_attributes !== null) { |
||||
foreach ($this->info as $name => $d) { |
||||
if(!isset($allowed_attributes[$name])) unset($this->info[$name]); |
||||
unset($allowed_attributes[$name]); |
||||
} |
||||
// emit errors |
||||
foreach ($allowed_attributes as $name => $d) { |
||||
// :TODO: Is this htmlspecialchars() call really necessary? |
||||
$name = htmlspecialchars($name); |
||||
trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Defines allowed CSS attributes and what their values are. |
||||
* @see HTMLPurifier_HTMLDefinition |
||||
*/ |
||||
class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition |
||||
{ |
||||
|
||||
public $type = 'CSS'; |
||||
|
||||
/** |
||||
* Assoc array of attribute name to definition object. |
||||
*/ |
||||
public $info = array(); |
||||
|
||||
/** |
||||
* Constructs the info array. The meat of this class. |
||||
*/ |
||||
protected function doSetup($config) { |
||||
|
||||
$this->info['text-align'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('left', 'right', 'center', 'justify'), false); |
||||
|
||||
$border_style = |
||||
$this->info['border-bottom-style'] = |
||||
$this->info['border-right-style'] = |
||||
$this->info['border-left-style'] = |
||||
$this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', |
||||
'groove', 'ridge', 'inset', 'outset'), false); |
||||
|
||||
$this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style); |
||||
|
||||
$this->info['clear'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('none', 'left', 'right', 'both'), false); |
||||
$this->info['float'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('none', 'left', 'right'), false); |
||||
$this->info['font-style'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('normal', 'italic', 'oblique'), false); |
||||
$this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('normal', 'small-caps'), false); |
||||
|
||||
$uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite( |
||||
array( |
||||
new HTMLPurifier_AttrDef_Enum(array('none')), |
||||
new HTMLPurifier_AttrDef_CSS_URI() |
||||
) |
||||
); |
||||
|
||||
$this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('inside', 'outside'), false); |
||||
$this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('disc', 'circle', 'square', 'decimal', 'lower-roman', |
||||
'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false); |
||||
$this->info['list-style-image'] = $uri_or_none; |
||||
|
||||
$this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config); |
||||
|
||||
$this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('capitalize', 'uppercase', 'lowercase', 'none'), false); |
||||
$this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
|
||||
$this->info['background-image'] = $uri_or_none; |
||||
$this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('repeat', 'repeat-x', 'repeat-y', 'no-repeat') |
||||
); |
||||
$this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum( |
||||
array('scroll', 'fixed') |
||||
); |
||||
$this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition(); |
||||
|
||||
$border_color = |
||||
$this->info['border-top-color'] = |
||||
$this->info['border-bottom-color'] = |
||||
$this->info['border-left-color'] = |
||||
$this->info['border-right-color'] = |
||||
$this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('transparent')), |
||||
new HTMLPurifier_AttrDef_CSS_Color() |
||||
)); |
||||
|
||||
$this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config); |
||||
|
||||
$this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color); |
||||
|
||||
$border_width = |
||||
$this->info['border-top-width'] = |
||||
$this->info['border-bottom-width'] = |
||||
$this->info['border-left-width'] = |
||||
$this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')), |
||||
new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative |
||||
)); |
||||
|
||||
$this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width); |
||||
|
||||
$this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('normal')), |
||||
new HTMLPurifier_AttrDef_CSS_Length() |
||||
)); |
||||
|
||||
$this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('normal')), |
||||
new HTMLPurifier_AttrDef_CSS_Length() |
||||
)); |
||||
|
||||
$this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small', |
||||
'small', 'medium', 'large', 'x-large', 'xx-large', |
||||
'larger', 'smaller')), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage(), |
||||
new HTMLPurifier_AttrDef_CSS_Length() |
||||
)); |
||||
|
||||
$this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('normal')), |
||||
new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives |
||||
new HTMLPurifier_AttrDef_CSS_Length('0'), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage(true) |
||||
)); |
||||
|
||||
$margin = |
||||
$this->info['margin-top'] = |
||||
$this->info['margin-bottom'] = |
||||
$this->info['margin-left'] = |
||||
$this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_CSS_Length(), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage(), |
||||
new HTMLPurifier_AttrDef_Enum(array('auto')) |
||||
)); |
||||
|
||||
$this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin); |
||||
|
||||
// non-negative |
||||
$padding = |
||||
$this->info['padding-top'] = |
||||
$this->info['padding-bottom'] = |
||||
$this->info['padding-left'] = |
||||
$this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_CSS_Length('0'), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage(true) |
||||
)); |
||||
|
||||
$this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding); |
||||
|
||||
$this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_CSS_Length(), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage() |
||||
)); |
||||
|
||||
$trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_CSS_Length('0'), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage(true), |
||||
new HTMLPurifier_AttrDef_Enum(array('auto')) |
||||
)); |
||||
$max = $config->get('CSS.MaxImgLength'); |
||||
|
||||
$this->info['width'] = |
||||
$this->info['height'] = |
||||
$max === null ? |
||||
$trusted_wh : |
||||
new HTMLPurifier_AttrDef_Switch('img', |
||||
// For img tags: |
||||
new HTMLPurifier_AttrDef_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_CSS_Length('0', $max), |
||||
new HTMLPurifier_AttrDef_Enum(array('auto')) |
||||
)), |
||||
// For everyone else: |
||||
$trusted_wh |
||||
); |
||||
|
||||
$this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration(); |
||||
|
||||
$this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily(); |
||||
|
||||
// this could use specialized code |
||||
$this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum( |
||||
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_CSS_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_CSS_Border($config); |
||||
|
||||
$this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array( |
||||
'collapse', 'separate')); |
||||
|
||||
$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_CSS_Composite(array( |
||||
new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super', |
||||
'top', 'text-top', 'middle', 'bottom', 'text-bottom')), |
||||
new HTMLPurifier_AttrDef_CSS_Length(), |
||||
new HTMLPurifier_AttrDef_CSS_Percentage() |
||||
)); |
||||
|
||||
$this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2); |
||||
|
||||
// partial support |
||||
$this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap')); |
||||
|
||||
if ($config->get('CSS.Proprietary')) { |
||||
$this->doSetupProprietary($config); |
||||
} |
||||
|
||||
if ($config->get('CSS.AllowTricky')) { |
||||
$this->doSetupTricky($config); |
||||
} |
||||
|
||||
$allow_important = $config->get('CSS.AllowImportant'); |
||||
// wrap all attr-defs with decorator that handles !important |
||||
foreach ($this->info as $k => $v) { |
||||
$this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important); |
||||
} |
||||
|
||||
$this->setupConfigStuff($config); |
||||
} |
||||
|
||||
protected function doSetupProprietary($config) { |
||||
// Internet Explorer only scrollbar colors |
||||
$this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
$this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
$this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
$this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
$this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
$this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
||||
|
||||
// technically not proprietary, but CSS3, and no one supports it |
||||
$this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
||||
$this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
||||
$this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
||||
|
||||
// only opacity, for now |
||||
$this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter(); |
||||
|
||||
} |
||||
|
||||
protected function doSetupTricky($config) { |
||||
$this->info['display'] = new HTMLPurifier_AttrDef_Enum(array( |
||||
'inline', 'block', 'list-item', 'run-in', 'compact', |
||||
'marker', 'table', 'inline-table', 'table-row-group', |
||||
'table-header-group', 'table-footer-group', 'table-row', |
||||
'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none' |
||||
)); |
||||
$this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array( |
||||
'visible', 'hidden', 'collapse' |
||||
)); |
||||
$this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll')); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Performs extra config-based processing. Based off of |
||||
* HTMLPurifier_HTMLDefinition. |
||||
* @todo Refactor duplicate elements into common class (probably using |
||||
* composition, not inheritance). |
||||
*/ |
||||
protected function setupConfigStuff($config) { |
||||
|
||||
// setup allowed elements |
||||
$support = "(for information on implementing this, see the ". |
||||
"support forums) "; |
||||
$allowed_properties = $config->get('CSS.AllowedProperties'); |
||||
if ($allowed_properties !== null) { |
||||
foreach ($this->info as $name => $d) { |
||||
if(!isset($allowed_properties[$name])) unset($this->info[$name]); |
||||
unset($allowed_properties[$name]); |
||||
} |
||||
// emit errors |
||||
foreach ($allowed_properties as $name => $d) { |
||||
// :TODO: Is this htmlspecialchars() call really necessary? |
||||
$name = htmlspecialchars($name); |
||||
trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); |
||||
} |
||||
} |
||||
|
||||
$forbidden_properties = $config->get('CSS.ForbiddenProperties'); |
||||
if ($forbidden_properties !== null) { |
||||
foreach ($this->info as $name => $d) { |
||||
if (isset($forbidden_properties[$name])) { |
||||
unset($this->info[$name]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -0,0 +1,13 @@ |
||||
CSS.ForbiddenProperties |
||||
TYPE: lookup |
||||
VERSION: 4.2.0 |
||||
DEFAULT: array() |
||||
--DESCRIPTION-- |
||||
<p> |
||||
This is the logical inverse of %CSS.AllowedProperties, and it will |
||||
override that directive or any other directive. If possible, |
||||
%CSS.AllowedProperties is recommended over this directive, |
||||
because it can sometimes be difficult to tell whether or not you've |
||||
forbidden all of the CSS properties you truly would like to disallow. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
@ -0,0 +1,11 @@ |
||||
Core.NormalizeNewlines |
||||
TYPE: bool |
||||
VERSION: 4.2.0 |
||||
DEFAULT: true |
||||
--DESCRIPTION-- |
||||
<p> |
||||
Whether or not to normalize newlines to the operating |
||||
system default. When <code>false</code>, HTML Purifier |
||||
will attempt to preserve mixed newline files. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
@ -0,0 +1,11 @@ |
||||
Core.RemoveProcessingInstructions |
||||
TYPE: bool |
||||
VERSION: 4.2.0 |
||||
DEFAULT: false |
||||
--DESCRIPTION-- |
||||
Instead of escaping processing instructions in the form <code><? ... |
||||
?></code>, remove it out-right. This may be useful if the HTML |
||||
you are validating contains XML processing instruction gunk, however, |
||||
it can also be user-unfriendly for people attempting to post PHP |
||||
snippets. |
||||
--# vim: et sw=4 sts=4 |
||||
@ -1,11 +1,16 @@ |
||||
Filter.YouTube |
||||
TYPE: bool |
||||
VERSION: 3.1.0 |
||||
DEFAULT: false |
||||
--DESCRIPTION-- |
||||
<p> |
||||
This directive enables YouTube video embedding in HTML Purifier. Check |
||||
<a href="http://htmlpurifier.org/docs/enduser-youtube.html">this document |
||||
on embedding videos</a> for more information on what this filter does. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
Filter.YouTube |
||||
TYPE: bool |
||||
VERSION: 3.1.0 |
||||
DEFAULT: false |
||||
--DESCRIPTION-- |
||||
<p> |
||||
<strong>Warning:</strong> Deprecated in favor of %HTML.SafeObject and |
||||
%Output.FlashCompat (turn both on to allow YouTube videos and other |
||||
Flash content). |
||||
</p> |
||||
<p> |
||||
This directive enables YouTube video embedding in HTML Purifier. Check |
||||
<a href="http://htmlpurifier.org/docs/enduser-youtube.html">this document |
||||
on embedding videos</a> for more information on what this filter does. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
|
||||
@ -1,22 +1,25 @@ |
||||
HTML.Allowed |
||||
TYPE: itext/null |
||||
VERSION: 2.0.0 |
||||
DEFAULT: NULL |
||||
--DESCRIPTION-- |
||||
|
||||
<p> |
||||
This is a convenience directive that rolls the functionality of |
||||
%HTML.AllowedElements and %HTML.AllowedAttributes into one directive. |
||||
Specify elements and attributes that are allowed using: |
||||
<code>element1[attr1|attr2],element2...</code>. You can also use |
||||
newlines instead of commas to separate elements. |
||||
</p> |
||||
<p> |
||||
<strong>Warning</strong>: |
||||
All of the constraints on the component directives are still enforced. |
||||
The syntax is a <em>subset</em> of TinyMCE's <code>valid_elements</code> |
||||
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. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
HTML.Allowed |
||||
TYPE: itext/null |
||||
VERSION: 2.0.0 |
||||
DEFAULT: NULL |
||||
--DESCRIPTION-- |
||||
|
||||
<p> |
||||
This is a preferred convenience directive that combines |
||||
%HTML.AllowedElements and %HTML.AllowedAttributes. |
||||
Specify elements and attributes that are allowed using: |
||||
<code>element1[attr1|attr2],element2...</code>. For example, |
||||
if you would like to only allow paragraphs and links, specify |
||||
<code>a[href],p</code>. You can specify attributes that apply |
||||
to all elements using an asterisk, e.g. <code>*[lang]</code>. |
||||
You can also use newlines instead of commas to separate elements. |
||||
</p> |
||||
<p> |
||||
<strong>Warning</strong>: |
||||
All of the constraints on the component directives are still enforced. |
||||
The syntax is a <em>subset</em> of TinyMCE's <code>valid_elements</code> |
||||
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. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
|
||||
@ -1,18 +1,23 @@ |
||||
HTML.AllowedElements |
||||
TYPE: lookup/null |
||||
VERSION: 1.3.0 |
||||
DEFAULT: NULL |
||||
--DESCRIPTION-- |
||||
<p> |
||||
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. |
||||
</p> |
||||
<p> |
||||
<strong>Warning:</strong> If another directive conflicts with the |
||||
elements here, <em>that</em> directive will win and override. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
HTML.AllowedElements |
||||
TYPE: lookup/null |
||||
VERSION: 1.3.0 |
||||
DEFAULT: NULL |
||||
--DESCRIPTION-- |
||||
<p> |
||||
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. |
||||
</p> |
||||
<p> |
||||
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 |
||||
<a href="http://htmlpurifier.org/docs/enduser-customize.html">advanced customization features.</a> |
||||
</p> |
||||
<p> |
||||
<strong>Warning:</strong> If another directive conflicts with the |
||||
elements here, <em>that</em> directive will win and override. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
|
||||
@ -0,0 +1,11 @@ |
||||
HTML.FlashAllowFullScreen |
||||
TYPE: bool |
||||
VERSION: 4.2.0 |
||||
DEFAULT: false |
||||
--DESCRIPTION-- |
||||
<p> |
||||
Whether or not to permit embedded Flash content from |
||||
%HTML.SafeObject to expand to the full screen. Corresponds to |
||||
the <code>allowFullScreen</code> parameter. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
@ -1,17 +1,17 @@ |
||||
URI.AllowedSchemes |
||||
TYPE: lookup |
||||
--DEFAULT-- |
||||
array ( |
||||
'http' => true, |
||||
'https' => true, |
||||
'mailto' => true, |
||||
'ftp' => true, |
||||
'nntp' => true, |
||||
'news' => 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 <code>data</code> URI scheme, but it is not |
||||
enabled by default. |
||||
--# vim: et sw=4 sts=4 |
||||
URI.AllowedSchemes |
||||
TYPE: lookup |
||||
--DEFAULT-- |
||||
array ( |
||||
'http' => true, |
||||
'https' => true, |
||||
'mailto' => true, |
||||
'ftp' => true, |
||||
'nntp' => true, |
||||
'news' => 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 <code>data</code> and <code>file</code> |
||||
URI schemes, but they are not enabled by default. |
||||
--# vim: et sw=4 sts=4 |
||||
|
||||
@ -1,12 +1,15 @@ |
||||
URI.DisableResources |
||||
TYPE: bool |
||||
VERSION: 1.3.0 |
||||
DEFAULT: false |
||||
--DESCRIPTION-- |
||||
|
||||
<p> |
||||
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. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
URI.DisableResources |
||||
TYPE: bool |
||||
VERSION: 4.2.0 |
||||
DEFAULT: false |
||||
--DESCRIPTION-- |
||||
<p> |
||||
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. |
||||
</p> |
||||
<p> |
||||
<em>Note:</em> While this directive has been available since 1.3.0, |
||||
it didn't actually start doing anything until 4.2.0. |
||||
</p> |
||||
--# vim: et sw=4 sts=4 |
||||
|
||||
@ -1,224 +1,229 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Generates HTML from tokens. |
||||
* @todo Refactor interface so that configuration/context is determined |
||||
* upon instantiation, no need for messy generateFromTokens() calls |
||||
* @todo Make some of the more internal functions protected, and have |
||||
* unit tests work around that |
||||
*/ |
||||
class HTMLPurifier_Generator |
||||
{ |
||||
|
||||
/** |
||||
* Whether or not generator should produce XML output |
||||
*/ |
||||
private $_xhtml = true; |
||||
|
||||
/** |
||||
* :HACK: Whether or not generator should comment the insides of <script> tags |
||||
*/ |
||||
private $_scriptFix = false; |
||||
|
||||
/** |
||||
* Cache of HTMLDefinition during HTML output to determine whether or |
||||
* not attributes should be minimized. |
||||
*/ |
||||
private $_def; |
||||
|
||||
/** |
||||
* Cache of %Output.SortAttr |
||||
*/ |
||||
private $_sortAttr; |
||||
|
||||
/** |
||||
* Cache of %Output.FlashCompat |
||||
*/ |
||||
private $_flashCompat; |
||||
|
||||
/** |
||||
* Stack for keeping track of object information when outputting IE |
||||
* compatibility code. |
||||
*/ |
||||
private $_flashStack = array(); |
||||
|
||||
/** |
||||
* Configuration for the generator |
||||
*/ |
||||
protected $config; |
||||
|
||||
/** |
||||
* @param $config Instance of HTMLPurifier_Config |
||||
* @param $context Instance of HTMLPurifier_Context |
||||
*/ |
||||
public function __construct($config, $context) { |
||||
$this->config = $config; |
||||
$this->_scriptFix = $config->get('Output.CommentScriptContents'); |
||||
$this->_sortAttr = $config->get('Output.SortAttr'); |
||||
$this->_flashCompat = $config->get('Output.FlashCompat'); |
||||
$this->_def = $config->getHTMLDefinition(); |
||||
$this->_xhtml = $this->_def->doctype->xml; |
||||
} |
||||
|
||||
/** |
||||
* Generates HTML from an array of tokens. |
||||
* @param $tokens Array of HTMLPurifier_Token |
||||
* @param $config HTMLPurifier_Config object |
||||
* @return Generated HTML |
||||
*/ |
||||
public function generateFromTokens($tokens) { |
||||
if (!$tokens) return ''; |
||||
|
||||
// Basic algorithm |
||||
$html = ''; |
||||
for ($i = 0, $size = count($tokens); $i < $size; $i++) { |
||||
if ($this->_scriptFix && $tokens[$i]->name === 'script' |
||||
&& $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) { |
||||
// script special case |
||||
// the contents of the script block must be ONE token |
||||
// for this to work. |
||||
$html .= $this->generateFromToken($tokens[$i++]); |
||||
$html .= $this->generateScriptFromToken($tokens[$i++]); |
||||
} |
||||
$html .= $this->generateFromToken($tokens[$i]); |
||||
} |
||||
|
||||
// Tidy cleanup |
||||
if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) { |
||||
$tidy = new Tidy; |
||||
$tidy->parseString($html, array( |
||||
'indent'=> true, |
||||
'output-xhtml' => $this->_xhtml, |
||||
'show-body-only' => true, |
||||
'indent-spaces' => 2, |
||||
'wrap' => 68, |
||||
), 'utf8'); |
||||
$tidy->cleanRepair(); |
||||
$html = (string) $tidy; // explicit cast necessary |
||||
} |
||||
|
||||
// Normalize newlines to system defined value |
||||
$nl = $this->config->get('Output.Newline'); |
||||
if ($nl === null) $nl = PHP_EOL; |
||||
if ($nl !== "\n") $html = str_replace("\n", $nl, $html); |
||||
return $html; |
||||
} |
||||
|
||||
/** |
||||
* Generates HTML from a single token. |
||||
* @param $token HTMLPurifier_Token object. |
||||
* @return Generated HTML |
||||
*/ |
||||
public function generateFromToken($token) { |
||||
if (!$token instanceof HTMLPurifier_Token) { |
||||
trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING); |
||||
return ''; |
||||
|
||||
} elseif ($token instanceof HTMLPurifier_Token_Start) { |
||||
$attr = $this->generateAttributes($token->attr, $token->name); |
||||
if ($this->_flashCompat) { |
||||
if ($token->name == "object") { |
||||
$flash = new stdclass(); |
||||
$flash->attr = $token->attr; |
||||
$flash->param = array(); |
||||
$this->_flashStack[] = $flash; |
||||
} |
||||
} |
||||
return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>'; |
||||
|
||||
} elseif ($token instanceof HTMLPurifier_Token_End) { |
||||
$_extra = ''; |
||||
if ($this->_flashCompat) { |
||||
if ($token->name == "object" && !empty($this->_flashStack)) { |
||||
$flash = array_pop($this->_flashStack); |
||||
$compat_token = new HTMLPurifier_Token_Empty("embed"); |
||||
foreach ($flash->attr as $name => $val) { |
||||
if ($name == "classid") continue; |
||||
if ($name == "type") continue; |
||||
if ($name == "data") $name = "src"; |
||||
$compat_token->attr[$name] = $val; |
||||
} |
||||
foreach ($flash->param as $name => $val) { |
||||
if ($name == "movie") $name = "src"; |
||||
$compat_token->attr[$name] = $val; |
||||
} |
||||
$_extra = "<!--[if IE]>".$this->generateFromToken($compat_token)."<![endif]-->"; |
||||
} |
||||
} |
||||
return $_extra . '</' . $token->name . '>'; |
||||
|
||||
} elseif ($token instanceof HTMLPurifier_Token_Empty) { |
||||
if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) { |
||||
$this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value']; |
||||
} |
||||
$attr = $this->generateAttributes($token->attr, $token->name); |
||||
return '<' . $token->name . ($attr ? ' ' : '') . $attr . |
||||
( $this->_xhtml ? ' /': '' ) // <br /> v. <br> |
||||
. '>'; |
||||
|
||||
} elseif ($token instanceof HTMLPurifier_Token_Text) { |
||||
return $this->escape($token->data, ENT_NOQUOTES); |
||||
|
||||
} elseif ($token instanceof HTMLPurifier_Token_Comment) { |
||||
return '<!--' . $token->data . '-->'; |
||||
} else { |
||||
return ''; |
||||
|
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Special case processor for the contents of script tags |
||||
* @warning This runs into problems if there's already a literal |
||||
* --> somewhere inside the script contents. |
||||
*/ |
||||
public function generateScriptFromToken($token) { |
||||
if (!$token instanceof HTMLPurifier_Token_Text) return $this->generateFromToken($token); |
||||
// Thanks <http://lachy.id.au/log/2005/05/script-comments> |
||||
$data = preg_replace('#//\s*$#', '', $token->data); |
||||
return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>'; |
||||
} |
||||
|
||||
/** |
||||
* Generates attribute declarations from attribute array. |
||||
* @note This does not include the leading or trailing space. |
||||
* @param $assoc_array_of_attributes Attribute array |
||||
* @param $element Name of element attributes are for, used to check |
||||
* attribute minimization. |
||||
* @return Generate HTML fragment for insertion. |
||||
*/ |
||||
public function generateAttributes($assoc_array_of_attributes, $element = false) { |
||||
$html = ''; |
||||
if ($this->_sortAttr) ksort($assoc_array_of_attributes); |
||||
foreach ($assoc_array_of_attributes as $key => $value) { |
||||
if (!$this->_xhtml) { |
||||
// Remove namespaced attributes |
||||
if (strpos($key, ':') !== false) continue; |
||||
// Check if we should minimize the attribute: val="val" -> val |
||||
if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) { |
||||
$html .= $key . ' '; |
||||
continue; |
||||
} |
||||
} |
||||
$html .= $key.'="'.$this->escape($value).'" '; |
||||
} |
||||
return rtrim($html); |
||||
} |
||||
|
||||
/** |
||||
* Escapes raw text data. |
||||
* @todo This really ought to be protected, but until we have a facility |
||||
* for properly generating HTML here w/o using tokens, it stays |
||||
* public. |
||||
* @param $string String data to escape for HTML. |
||||
* @param $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is |
||||
* permissible for non-attribute output. |
||||
* @return String escaped data. |
||||
*/ |
||||
public function escape($string, $quote = ENT_COMPAT) { |
||||
return htmlspecialchars($string, $quote, 'UTF-8'); |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Generates HTML from tokens. |
||||
* @todo Refactor interface so that configuration/context is determined |
||||
* upon instantiation, no need for messy generateFromTokens() calls |
||||
* @todo Make some of the more internal functions protected, and have |
||||
* unit tests work around that |
||||
*/ |
||||
class HTMLPurifier_Generator |
||||
{ |
||||
|
||||
/** |
||||
* Whether or not generator should produce XML output |
||||
*/ |
||||
private $_xhtml = true; |
||||
|
||||
/** |
||||
* :HACK: Whether or not generator should comment the insides of <script> tags |
||||
*/ |
||||
private $_scriptFix = false; |
||||
|
||||
/** |
||||
* Cache of HTMLDefinition during HTML output to determine whether or |
||||
* not attributes should be minimized. |
||||
*/ |
||||
private $_def; |
||||
|
||||
/** |
||||
* Cache of %Output.SortAttr |
||||
*/ |
||||
private $_sortAttr; |
||||
|
||||
/** |
||||
* Cache of %Output.FlashCompat |
||||
*/ |
||||
private $_flashCompat; |
||||
|
||||
/** |
||||
* Stack for keeping track of object information when outputting IE |
||||
* compatibility code. |
||||
*/ |
||||
private $_flashStack = array(); |
||||
|
||||
/** |
||||
* Configuration for the generator |
||||
*/ |
||||
protected $config; |
||||
|
||||
/** |
||||
* @param $config Instance of HTMLPurifier_Config |
||||
* @param $context Instance of HTMLPurifier_Context |
||||
*/ |
||||
public function __construct($config, $context) { |
||||
$this->config = $config; |
||||
$this->_scriptFix = $config->get('Output.CommentScriptContents'); |
||||
$this->_sortAttr = $config->get('Output.SortAttr'); |
||||
$this->_flashCompat = $config->get('Output.FlashCompat'); |
||||
$this->_def = $config->getHTMLDefinition(); |
||||
$this->_xhtml = $this->_def->doctype->xml; |
||||
} |
||||
|
||||
/** |
||||
* Generates HTML from an array of tokens. |
||||
* @param $tokens Array of HTMLPurifier_Token |
||||
* @param $config HTMLPurifier_Config object |
||||
* @return Generated HTML |
||||
*/ |
||||
public function generateFromTokens($tokens) { |
||||
if (!$tokens) return ''; |
||||
|
||||
// Basic algorithm |
||||
$html = ''; |
||||
for ($i = 0, $size = count($tokens); $i < $size; $i++) { |
||||
if ($this->_scriptFix && $tokens[$i]->name === 'script' |
||||
&& $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) { |
||||
// script special case |
||||
// the contents of the script block must be ONE token |
||||
// for this to work. |
||||
$html .= $this->generateFromToken($tokens[$i++]); |
||||
$html .= $this->generateScriptFromToken($tokens[$i++]); |
||||
} |
||||
$html .= $this->generateFromToken($tokens[$i]); |
||||
} |
||||
|
||||
// Tidy cleanup |
||||
if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) { |
||||
$tidy = new Tidy; |
||||
$tidy->parseString($html, array( |
||||
'indent'=> true, |
||||
'output-xhtml' => $this->_xhtml, |
||||
'show-body-only' => true, |
||||
'indent-spaces' => 2, |
||||
'wrap' => 68, |
||||
), 'utf8'); |
||||
$tidy->cleanRepair(); |
||||
$html = (string) $tidy; // explicit cast necessary |
||||
} |
||||
|
||||
// Normalize newlines to system defined value |
||||
if ($this->config->get('Core.NormalizeNewlines')) { |
||||
$nl = $this->config->get('Output.Newline'); |
||||
if ($nl === null) $nl = PHP_EOL; |
||||
if ($nl !== "\n") $html = str_replace("\n", $nl, $html); |
||||
} |
||||
return $html; |
||||
} |
||||
|
||||
/** |
||||
* Generates HTML from a single token. |
||||
* @param $token HTMLPurifier_Token object. |
||||
* @return Generated HTML |
||||
*/ |
||||
public function generateFromToken($token) { |
||||
if (!$token instanceof HTMLPurifier_Token) { |
||||
trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING); |
||||
return ''; |
||||
|
||||
} elseif ($token instanceof HTMLPurifier_Token_Start) { |
||||
$attr = $this->generateAttributes($token->attr, $token->name); |
||||
if ($this->_flashCompat) { |
||||
if ($token->name == "object") { |
||||
$flash = new stdclass(); |
||||
$flash->attr = $token->attr; |
||||
$flash->param = array(); |
||||
$this->_flashStack[] = $flash; |
||||
} |
||||
} |
||||
return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>'; |
||||
|
||||
} elseif ($token instanceof HTMLPurifier_Token_End) { |
||||
$_extra = ''; |
||||
if ($this->_flashCompat) { |
||||
if ($token->name == "object" && !empty($this->_flashStack)) { |
||||
$flash = array_pop($this->_flashStack); |
||||
$compat_token = new HTMLPurifier_Token_Empty("embed"); |
||||
foreach ($flash->attr as $name => $val) { |
||||
if ($name == "classid") continue; |
||||
if ($name == "type") continue; |
||||
if ($name == "data") $name = "src"; |
||||
$compat_token->attr[$name] = $val; |
||||
} |
||||
foreach ($flash->param as $name => $val) { |
||||
if ($name == "movie") $name = "src"; |
||||
$compat_token->attr[$name] = $val; |
||||
} |
||||
$_extra = "<!--[if IE]>".$this->generateFromToken($compat_token)."<![endif]-->"; |
||||
} |
||||
} |
||||
return $_extra . '</' . $token->name . '>'; |
||||
|
||||
} elseif ($token instanceof HTMLPurifier_Token_Empty) { |
||||
if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) { |
||||
$this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value']; |
||||
} |
||||
$attr = $this->generateAttributes($token->attr, $token->name); |
||||
return '<' . $token->name . ($attr ? ' ' : '') . $attr . |
||||
( $this->_xhtml ? ' /': '' ) // <br /> v. <br> |
||||
. '>'; |
||||
|
||||
} elseif ($token instanceof HTMLPurifier_Token_Text) { |
||||
return $this->escape($token->data, ENT_NOQUOTES); |
||||
|
||||
} elseif ($token instanceof HTMLPurifier_Token_Comment) { |
||||
return '<!--' . $token->data . '-->'; |
||||
} else { |
||||
return ''; |
||||
|
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Special case processor for the contents of script tags |
||||
* @warning This runs into problems if there's already a literal |
||||
* --> somewhere inside the script contents. |
||||
*/ |
||||
public function generateScriptFromToken($token) { |
||||
if (!$token instanceof HTMLPurifier_Token_Text) return $this->generateFromToken($token); |
||||
// Thanks <http://lachy.id.au/log/2005/05/script-comments> |
||||
$data = preg_replace('#//\s*$#', '', $token->data); |
||||
return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>'; |
||||
} |
||||
|
||||
/** |
||||
* Generates attribute declarations from attribute array. |
||||
* @note This does not include the leading or trailing space. |
||||
* @param $assoc_array_of_attributes Attribute array |
||||
* @param $element Name of element attributes are for, used to check |
||||
* attribute minimization. |
||||
* @return Generate HTML fragment for insertion. |
||||
*/ |
||||
public function generateAttributes($assoc_array_of_attributes, $element = false) { |
||||
$html = ''; |
||||
if ($this->_sortAttr) ksort($assoc_array_of_attributes); |
||||
foreach ($assoc_array_of_attributes as $key => $value) { |
||||
if (!$this->_xhtml) { |
||||
// Remove namespaced attributes |
||||
if (strpos($key, ':') !== false) continue; |
||||
// Check if we should minimize the attribute: val="val" -> val |
||||
if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) { |
||||
$html .= $key . ' '; |
||||
continue; |
||||
} |
||||
} |
||||
$html .= $key.'="'.$this->escape($value).'" '; |
||||
} |
||||
return rtrim($html); |
||||
} |
||||
|
||||
/** |
||||
* Escapes raw text data. |
||||
* @todo This really ought to be protected, but until we have a facility |
||||
* for properly generating HTML here w/o using tokens, it stays |
||||
* public. |
||||
* @param $string String data to escape for HTML. |
||||
* @param $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is |
||||
* permissible for non-attribute output. |
||||
* @return String escaped data. |
||||
*/ |
||||
public function escape($string, $quote = null) { |
||||
// Workaround for APC bug on Mac Leopard reported by sidepodcast |
||||
// http://htmlpurifier.org/phorum/read.php?3,4823,4846 |
||||
if ($quote === null) $quote = ENT_COMPAT; |
||||
return htmlspecialchars($string, $quote, 'UTF-8'); |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,420 +1,425 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Definition of the purified HTML that describes allowed children, |
||||
* attributes, and many other things. |
||||
* |
||||
* Conventions: |
||||
* |
||||
* All member variables that are prefixed with info |
||||
* (including the main $info array) are used by HTML Purifier internals |
||||
* and should not be directly edited when customizing the HTMLDefinition. |
||||
* They can usually be set via configuration directives or custom |
||||
* modules. |
||||
* |
||||
* On the other hand, member variables without the info prefix are used |
||||
* internally by the HTMLDefinition and MUST NOT be used by other HTML |
||||
* Purifier internals. Many of them, however, are public, and may be |
||||
* edited by userspace code to tweak the behavior of HTMLDefinition. |
||||
* |
||||
* @note This class is inspected by Printer_HTMLDefinition; please |
||||
* update that class if things here change. |
||||
* |
||||
* @warning Directives that change this object's structure must be in |
||||
* the HTML or Attr namespace! |
||||
*/ |
||||
class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition |
||||
{ |
||||
|
||||
// FULLY-PUBLIC VARIABLES --------------------------------------------- |
||||
|
||||
/** |
||||
* Associative array of element names to HTMLPurifier_ElementDef |
||||
*/ |
||||
public $info = array(); |
||||
|
||||
/** |
||||
* Associative array of global attribute name to attribute definition. |
||||
*/ |
||||
public $info_global_attr = array(); |
||||
|
||||
/** |
||||
* String name of parent element HTML will be going into. |
||||
*/ |
||||
public $info_parent = 'div'; |
||||
|
||||
/** |
||||
* Definition for parent element, allows parent element to be a |
||||
* tag that's not allowed inside the HTML fragment. |
||||
*/ |
||||
public $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 $info_block_wrapper = 'p'; |
||||
|
||||
/** |
||||
* Associative array of deprecated tag name to HTMLPurifier_TagTransform |
||||
*/ |
||||
public $info_tag_transform = array(); |
||||
|
||||
/** |
||||
* Indexed list of HTMLPurifier_AttrTransform to be performed before validation. |
||||
*/ |
||||
public $info_attr_transform_pre = array(); |
||||
|
||||
/** |
||||
* Indexed list of HTMLPurifier_AttrTransform to be performed after validation. |
||||
*/ |
||||
public $info_attr_transform_post = array(); |
||||
|
||||
/** |
||||
* Nested lookup array of content set name (Block, Inline) to |
||||
* element name to whether or not it belongs in that content set. |
||||
*/ |
||||
public $info_content_sets = array(); |
||||
|
||||
/** |
||||
* Indexed list of HTMLPurifier_Injector to be used. |
||||
*/ |
||||
public $info_injector = array(); |
||||
|
||||
/** |
||||
* Doctype object |
||||
*/ |
||||
public $doctype; |
||||
|
||||
|
||||
|
||||
// RAW CUSTOMIZATION STUFF -------------------------------------------- |
||||
|
||||
/** |
||||
* Adds a custom attribute to a pre-existing element |
||||
* @note This is strictly convenience, and does not have a corresponding |
||||
* method in HTMLPurifier_HTMLModule |
||||
* @param $element_name String element name to add attribute to |
||||
* @param $attr_name String name of attribute |
||||
* @param $def Attribute definition, can be string or object, see |
||||
* HTMLPurifier_AttrTypes for details |
||||
*/ |
||||
public function addAttribute($element_name, $attr_name, $def) { |
||||
$module = $this->getAnonymousModule(); |
||||
if (!isset($module->info[$element_name])) { |
||||
$element = $module->addBlankElement($element_name); |
||||
} else { |
||||
$element = $module->info[$element_name]; |
||||
} |
||||
$element->attr[$attr_name] = $def; |
||||
} |
||||
|
||||
/** |
||||
* Adds a custom element to your HTML definition |
||||
* @note See HTMLPurifier_HTMLModule::addElement for detailed |
||||
* parameter and return value descriptions. |
||||
*/ |
||||
public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) { |
||||
$module = $this->getAnonymousModule(); |
||||
// assume that if the user is calling this, the element |
||||
// is safe. This may not be a good idea |
||||
$element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes); |
||||
return $element; |
||||
} |
||||
|
||||
/** |
||||
* Adds a blank element to your HTML definition, for overriding |
||||
* existing behavior |
||||
* @note See HTMLPurifier_HTMLModule::addBlankElement for detailed |
||||
* parameter and return value descriptions. |
||||
*/ |
||||
public function addBlankElement($element_name) { |
||||
$module = $this->getAnonymousModule(); |
||||
$element = $module->addBlankElement($element_name); |
||||
return $element; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves a reference to the anonymous module, so you can |
||||
* bust out advanced features without having to make your own |
||||
* module. |
||||
*/ |
||||
public function getAnonymousModule() { |
||||
if (!$this->_anonModule) { |
||||
$this->_anonModule = new HTMLPurifier_HTMLModule(); |
||||
$this->_anonModule->name = 'Anonymous'; |
||||
} |
||||
return $this->_anonModule; |
||||
} |
||||
|
||||
private $_anonModule; |
||||
|
||||
|
||||
// PUBLIC BUT INTERNAL VARIABLES -------------------------------------- |
||||
|
||||
public $type = 'HTML'; |
||||
public $manager; /**< Instance of HTMLPurifier_HTMLModuleManager */ |
||||
|
||||
/** |
||||
* Performs low-cost, preliminary initialization. |
||||
*/ |
||||
public function __construct() { |
||||
$this->manager = new HTMLPurifier_HTMLModuleManager(); |
||||
} |
||||
|
||||
protected function doSetup($config) { |
||||
$this->processModules($config); |
||||
$this->setupConfigStuff($config); |
||||
unset($this->manager); |
||||
|
||||
// cleanup some of the element definitions |
||||
foreach ($this->info as $k => $v) { |
||||
unset($this->info[$k]->content_model); |
||||
unset($this->info[$k]->content_model_type); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Extract out the information from the manager |
||||
*/ |
||||
protected function processModules($config) { |
||||
|
||||
if ($this->_anonModule) { |
||||
// for user specific changes |
||||
// this is late-loaded so we don't have to deal with PHP4 |
||||
// reference wonky-ness |
||||
$this->manager->addModule($this->_anonModule); |
||||
unset($this->_anonModule); |
||||
} |
||||
|
||||
$this->manager->setup($config); |
||||
$this->doctype = $this->manager->doctype; |
||||
|
||||
foreach ($this->manager->modules as $module) { |
||||
foreach($module->info_tag_transform as $k => $v) { |
||||
if ($v === false) unset($this->info_tag_transform[$k]); |
||||
else $this->info_tag_transform[$k] = $v; |
||||
} |
||||
foreach($module->info_attr_transform_pre as $k => $v) { |
||||
if ($v === false) unset($this->info_attr_transform_pre[$k]); |
||||
else $this->info_attr_transform_pre[$k] = $v; |
||||
} |
||||
foreach($module->info_attr_transform_post as $k => $v) { |
||||
if ($v === false) unset($this->info_attr_transform_post[$k]); |
||||
else $this->info_attr_transform_post[$k] = $v; |
||||
} |
||||
foreach ($module->info_injector as $k => $v) { |
||||
if ($v === false) unset($this->info_injector[$k]); |
||||
else $this->info_injector[$k] = $v; |
||||
} |
||||
} |
||||
|
||||
$this->info = $this->manager->getElements(); |
||||
$this->info_content_sets = $this->manager->contentSets->lookup; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Sets up stuff based on config. We need a better way of doing this. |
||||
*/ |
||||
protected function setupConfigStuff($config) { |
||||
|
||||
$block_wrapper = $config->get('HTML.BlockWrapper'); |
||||
if (isset($this->info_content_sets['Block'][$block_wrapper])) { |
||||
$this->info_block_wrapper = $block_wrapper; |
||||
} else { |
||||
trigger_error('Cannot use non-block element as block wrapper', |
||||
E_USER_ERROR); |
||||
} |
||||
|
||||
$parent = $config->get('HTML.Parent'); |
||||
$def = $this->manager->getElement($parent, true); |
||||
if ($def) { |
||||
$this->info_parent = $parent; |
||||
$this->info_parent_def = $def; |
||||
} else { |
||||
trigger_error('Cannot use unrecognized element as parent', |
||||
E_USER_ERROR); |
||||
$this->info_parent_def = $this->manager->getElement($this->info_parent, true); |
||||
} |
||||
|
||||
// support template text |
||||
$support = "(for information on implementing this, see the ". |
||||
"support forums) "; |
||||
|
||||
// setup allowed elements ----------------------------------------- |
||||
|
||||
$allowed_elements = $config->get('HTML.AllowedElements'); |
||||
$allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early |
||||
|
||||
if (!is_array($allowed_elements) && !is_array($allowed_attributes)) { |
||||
$allowed = $config->get('HTML.Allowed'); |
||||
if (is_string($allowed)) { |
||||
list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed); |
||||
} |
||||
} |
||||
|
||||
if (is_array($allowed_elements)) { |
||||
foreach ($this->info as $name => $d) { |
||||
if(!isset($allowed_elements[$name])) unset($this->info[$name]); |
||||
unset($allowed_elements[$name]); |
||||
} |
||||
// emit errors |
||||
foreach ($allowed_elements as $element => $d) { |
||||
$element = htmlspecialchars($element); // PHP doesn't escape errors, be careful! |
||||
trigger_error("Element '$element' is not supported $support", E_USER_WARNING); |
||||
} |
||||
} |
||||
|
||||
// setup allowed attributes --------------------------------------- |
||||
|
||||
$allowed_attributes_mutable = $allowed_attributes; // by copy! |
||||
if (is_array($allowed_attributes)) { |
||||
|
||||
// This actually doesn't do anything, since we went away from |
||||
// global attributes. It's possible that userland code uses |
||||
// it, but HTMLModuleManager doesn't! |
||||
foreach ($this->info_global_attr as $attr => $x) { |
||||
$keys = array($attr, "*@$attr", "*.$attr"); |
||||
$delete = true; |
||||
foreach ($keys as $key) { |
||||
if ($delete && isset($allowed_attributes[$key])) { |
||||
$delete = false; |
||||
} |
||||
if (isset($allowed_attributes_mutable[$key])) { |
||||
unset($allowed_attributes_mutable[$key]); |
||||
} |
||||
} |
||||
if ($delete) unset($this->info_global_attr[$attr]); |
||||
} |
||||
|
||||
foreach ($this->info as $tag => $info) { |
||||
foreach ($info->attr as $attr => $x) { |
||||
$keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr"); |
||||
$delete = true; |
||||
foreach ($keys as $key) { |
||||
if ($delete && isset($allowed_attributes[$key])) { |
||||
$delete = false; |
||||
} |
||||
if (isset($allowed_attributes_mutable[$key])) { |
||||
unset($allowed_attributes_mutable[$key]); |
||||
} |
||||
} |
||||
if ($delete) unset($this->info[$tag]->attr[$attr]); |
||||
} |
||||
} |
||||
// emit errors |
||||
foreach ($allowed_attributes_mutable as $elattr => $d) { |
||||
$bits = preg_split('/[.@]/', $elattr, 2); |
||||
$c = count($bits); |
||||
switch ($c) { |
||||
case 2: |
||||
if ($bits[0] !== '*') { |
||||
$element = htmlspecialchars($bits[0]); |
||||
$attribute = htmlspecialchars($bits[1]); |
||||
if (!isset($this->info[$element])) { |
||||
trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support"); |
||||
} else { |
||||
trigger_error("Attribute '$attribute' in element '$element' not supported $support", |
||||
E_USER_WARNING); |
||||
} |
||||
break; |
||||
} |
||||
// otherwise fall through |
||||
case 1: |
||||
$attribute = htmlspecialchars($bits[0]); |
||||
trigger_error("Global attribute '$attribute' is not ". |
||||
"supported in any elements $support", |
||||
E_USER_WARNING); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// setup forbidden elements --------------------------------------- |
||||
|
||||
$forbidden_elements = $config->get('HTML.ForbiddenElements'); |
||||
$forbidden_attributes = $config->get('HTML.ForbiddenAttributes'); |
||||
|
||||
foreach ($this->info as $tag => $info) { |
||||
if (isset($forbidden_elements[$tag])) { |
||||
unset($this->info[$tag]); |
||||
continue; |
||||
} |
||||
foreach ($info->attr as $attr => $x) { |
||||
if ( |
||||
isset($forbidden_attributes["$tag@$attr"]) || |
||||
isset($forbidden_attributes["*@$attr"]) || |
||||
isset($forbidden_attributes[$attr]) |
||||
) { |
||||
unset($this->info[$tag]->attr[$attr]); |
||||
continue; |
||||
} // this segment might get removed eventually |
||||
elseif (isset($forbidden_attributes["$tag.$attr"])) { |
||||
// $tag.$attr are not user supplied, so no worries! |
||||
trigger_error("Error with $tag.$attr: tag.attr syntax not supported for HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING); |
||||
} |
||||
} |
||||
} |
||||
foreach ($forbidden_attributes as $key => $v) { |
||||
if (strlen($key) < 2) continue; |
||||
if ($key[0] != '*') continue; |
||||
if ($key[1] == '.') { |
||||
trigger_error("Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING); |
||||
} |
||||
} |
||||
|
||||
// setup injectors ----------------------------------------------------- |
||||
foreach ($this->info_injector as $i => $injector) { |
||||
if ($injector->checkNeeded($config) !== false) { |
||||
// remove injector that does not have it's required |
||||
// elements/attributes present, and is thus not needed. |
||||
unset($this->info_injector[$i]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Parses a TinyMCE-flavored Allowed Elements and Attributes list into |
||||
* separate lists for processing. Format is element[attr1|attr2],element2... |
||||
* @warning Although it's largely drawn from TinyMCE's implementation, |
||||
* it is different, and you'll probably have to modify your lists |
||||
* @param $list String list to parse |
||||
* @param array($allowed_elements, $allowed_attributes) |
||||
* @todo Give this its own class, probably static interface |
||||
*/ |
||||
public function parseTinyMCEAllowedList($list) { |
||||
|
||||
$list = str_replace(array(' ', "\t"), '', $list); |
||||
|
||||
$elements = array(); |
||||
$attributes = array(); |
||||
|
||||
$chunks = preg_split('/(,|[\n\r]+)/', $list); |
||||
foreach ($chunks as $chunk) { |
||||
if (empty($chunk)) continue; |
||||
// remove TinyMCE element control characters |
||||
if (!strpos($chunk, '[')) { |
||||
$element = $chunk; |
||||
$attr = false; |
||||
} else { |
||||
list($element, $attr) = explode('[', $chunk); |
||||
} |
||||
if ($element !== '*') $elements[$element] = true; |
||||
if (!$attr) continue; |
||||
$attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ] |
||||
$attr = explode('|', $attr); |
||||
foreach ($attr as $key) { |
||||
$attributes["$element.$key"] = true; |
||||
} |
||||
} |
||||
|
||||
return array($elements, $attributes); |
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Definition of the purified HTML that describes allowed children, |
||||
* attributes, and many other things. |
||||
* |
||||
* Conventions: |
||||
* |
||||
* All member variables that are prefixed with info |
||||
* (including the main $info array) are used by HTML Purifier internals |
||||
* and should not be directly edited when customizing the HTMLDefinition. |
||||
* They can usually be set via configuration directives or custom |
||||
* modules. |
||||
* |
||||
* On the other hand, member variables without the info prefix are used |
||||
* internally by the HTMLDefinition and MUST NOT be used by other HTML |
||||
* Purifier internals. Many of them, however, are public, and may be |
||||
* edited by userspace code to tweak the behavior of HTMLDefinition. |
||||
* |
||||
* @note This class is inspected by Printer_HTMLDefinition; please |
||||
* update that class if things here change. |
||||
* |
||||
* @warning Directives that change this object's structure must be in |
||||
* the HTML or Attr namespace! |
||||
*/ |
||||
class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition |
||||
{ |
||||
|
||||
// FULLY-PUBLIC VARIABLES --------------------------------------------- |
||||
|
||||
/** |
||||
* Associative array of element names to HTMLPurifier_ElementDef |
||||
*/ |
||||
public $info = array(); |
||||
|
||||
/** |
||||
* Associative array of global attribute name to attribute definition. |
||||
*/ |
||||
public $info_global_attr = array(); |
||||
|
||||
/** |
||||
* String name of parent element HTML will be going into. |
||||
*/ |
||||
public $info_parent = 'div'; |
||||
|
||||
/** |
||||
* Definition for parent element, allows parent element to be a |
||||
* tag that's not allowed inside the HTML fragment. |
||||
*/ |
||||
public $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 $info_block_wrapper = 'p'; |
||||
|
||||
/** |
||||
* Associative array of deprecated tag name to HTMLPurifier_TagTransform |
||||
*/ |
||||
public $info_tag_transform = array(); |
||||
|
||||
/** |
||||
* Indexed list of HTMLPurifier_AttrTransform to be performed before validation. |
||||
*/ |
||||
public $info_attr_transform_pre = array(); |
||||
|
||||
/** |
||||
* Indexed list of HTMLPurifier_AttrTransform to be performed after validation. |
||||
*/ |
||||
public $info_attr_transform_post = array(); |
||||
|
||||
/** |
||||
* Nested lookup array of content set name (Block, Inline) to |
||||
* element name to whether or not it belongs in that content set. |
||||
*/ |
||||
public $info_content_sets = array(); |
||||
|
||||
/** |
||||
* Indexed list of HTMLPurifier_Injector to be used. |
||||
*/ |
||||
public $info_injector = array(); |
||||
|
||||
/** |
||||
* Doctype object |
||||
*/ |
||||
public $doctype; |
||||
|
||||
|
||||
|
||||
// RAW CUSTOMIZATION STUFF -------------------------------------------- |
||||
|
||||
/** |
||||
* Adds a custom attribute to a pre-existing element |
||||
* @note This is strictly convenience, and does not have a corresponding |
||||
* method in HTMLPurifier_HTMLModule |
||||
* @param $element_name String element name to add attribute to |
||||
* @param $attr_name String name of attribute |
||||
* @param $def Attribute definition, can be string or object, see |
||||
* HTMLPurifier_AttrTypes for details |
||||
*/ |
||||
public function addAttribute($element_name, $attr_name, $def) { |
||||
$module = $this->getAnonymousModule(); |
||||
if (!isset($module->info[$element_name])) { |
||||
$element = $module->addBlankElement($element_name); |
||||
} else { |
||||
$element = $module->info[$element_name]; |
||||
} |
||||
$element->attr[$attr_name] = $def; |
||||
} |
||||
|
||||
/** |
||||
* Adds a custom element to your HTML definition |
||||
* @note See HTMLPurifier_HTMLModule::addElement for detailed |
||||
* parameter and return value descriptions. |
||||
*/ |
||||
public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) { |
||||
$module = $this->getAnonymousModule(); |
||||
// assume that if the user is calling this, the element |
||||
// is safe. This may not be a good idea |
||||
$element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes); |
||||
return $element; |
||||
} |
||||
|
||||
/** |
||||
* Adds a blank element to your HTML definition, for overriding |
||||
* existing behavior |
||||
* @note See HTMLPurifier_HTMLModule::addBlankElement for detailed |
||||
* parameter and return value descriptions. |
||||
*/ |
||||
public function addBlankElement($element_name) { |
||||
$module = $this->getAnonymousModule(); |
||||
$element = $module->addBlankElement($element_name); |
||||
return $element; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves a reference to the anonymous module, so you can |
||||
* bust out advanced features without having to make your own |
||||
* module. |
||||
*/ |
||||
public function getAnonymousModule() { |
||||
if (!$this->_anonModule) { |
||||
$this->_anonModule = new HTMLPurifier_HTMLModule(); |
||||
$this->_anonModule->name = 'Anonymous'; |
||||
} |
||||
return $this->_anonModule; |
||||
} |
||||
|
||||
private $_anonModule; |
||||
|
||||
|
||||
// PUBLIC BUT INTERNAL VARIABLES -------------------------------------- |
||||
|
||||
public $type = 'HTML'; |
||||
public $manager; /**< Instance of HTMLPurifier_HTMLModuleManager */ |
||||
|
||||
/** |
||||
* Performs low-cost, preliminary initialization. |
||||
*/ |
||||
public function __construct() { |
||||
$this->manager = new HTMLPurifier_HTMLModuleManager(); |
||||
} |
||||
|
||||
protected function doSetup($config) { |
||||
$this->processModules($config); |
||||
$this->setupConfigStuff($config); |
||||
unset($this->manager); |
||||
|
||||
// cleanup some of the element definitions |
||||
foreach ($this->info as $k => $v) { |
||||
unset($this->info[$k]->content_model); |
||||
unset($this->info[$k]->content_model_type); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Extract out the information from the manager |
||||
*/ |
||||
protected function processModules($config) { |
||||
|
||||
if ($this->_anonModule) { |
||||
// for user specific changes |
||||
// this is late-loaded so we don't have to deal with PHP4 |
||||
// reference wonky-ness |
||||
$this->manager->addModule($this->_anonModule); |
||||
unset($this->_anonModule); |
||||
} |
||||
|
||||
$this->manager->setup($config); |
||||
$this->doctype = $this->manager->doctype; |
||||
|
||||
foreach ($this->manager->modules as $module) { |
||||
foreach($module->info_tag_transform as $k => $v) { |
||||
if ($v === false) unset($this->info_tag_transform[$k]); |
||||
else $this->info_tag_transform[$k] = $v; |
||||
} |
||||
foreach($module->info_attr_transform_pre as $k => $v) { |
||||
if ($v === false) unset($this->info_attr_transform_pre[$k]); |
||||
else $this->info_attr_transform_pre[$k] = $v; |
||||
} |
||||
foreach($module->info_attr_transform_post as $k => $v) { |
||||
if ($v === false) unset($this->info_attr_transform_post[$k]); |
||||
else $this->info_attr_transform_post[$k] = $v; |
||||
} |
||||
foreach ($module->info_injector as $k => $v) { |
||||
if ($v === false) unset($this->info_injector[$k]); |
||||
else $this->info_injector[$k] = $v; |
||||
} |
||||
} |
||||
|
||||
$this->info = $this->manager->getElements(); |
||||
$this->info_content_sets = $this->manager->contentSets->lookup; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Sets up stuff based on config. We need a better way of doing this. |
||||
*/ |
||||
protected function setupConfigStuff($config) { |
||||
|
||||
$block_wrapper = $config->get('HTML.BlockWrapper'); |
||||
if (isset($this->info_content_sets['Block'][$block_wrapper])) { |
||||
$this->info_block_wrapper = $block_wrapper; |
||||
} else { |
||||
trigger_error('Cannot use non-block element as block wrapper', |
||||
E_USER_ERROR); |
||||
} |
||||
|
||||
$parent = $config->get('HTML.Parent'); |
||||
$def = $this->manager->getElement($parent, true); |
||||
if ($def) { |
||||
$this->info_parent = $parent; |
||||
$this->info_parent_def = $def; |
||||
} else { |
||||
trigger_error('Cannot use unrecognized element as parent', |
||||
E_USER_ERROR); |
||||
$this->info_parent_def = $this->manager->getElement($this->info_parent, true); |
||||
} |
||||
|
||||
// support template text |
||||
$support = "(for information on implementing this, see the ". |
||||
"support forums) "; |
||||
|
||||
// setup allowed elements ----------------------------------------- |
||||
|
||||
$allowed_elements = $config->get('HTML.AllowedElements'); |
||||
$allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early |
||||
|
||||
if (!is_array($allowed_elements) && !is_array($allowed_attributes)) { |
||||
$allowed = $config->get('HTML.Allowed'); |
||||
if (is_string($allowed)) { |
||||
list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed); |
||||
} |
||||
} |
||||
|
||||
if (is_array($allowed_elements)) { |
||||
foreach ($this->info as $name => $d) { |
||||
if(!isset($allowed_elements[$name])) unset($this->info[$name]); |
||||
unset($allowed_elements[$name]); |
||||
} |
||||
// emit errors |
||||
foreach ($allowed_elements as $element => $d) { |
||||
$element = htmlspecialchars($element); // PHP doesn't escape errors, be careful! |
||||
trigger_error("Element '$element' is not supported $support", E_USER_WARNING); |
||||
} |
||||
} |
||||
|
||||
// setup allowed attributes --------------------------------------- |
||||
|
||||
$allowed_attributes_mutable = $allowed_attributes; // by copy! |
||||
if (is_array($allowed_attributes)) { |
||||
|
||||
// This actually doesn't do anything, since we went away from |
||||
// global attributes. It's possible that userland code uses |
||||
// it, but HTMLModuleManager doesn't! |
||||
foreach ($this->info_global_attr as $attr => $x) { |
||||
$keys = array($attr, "*@$attr", "*.$attr"); |
||||
$delete = true; |
||||
foreach ($keys as $key) { |
||||
if ($delete && isset($allowed_attributes[$key])) { |
||||
$delete = false; |
||||
} |
||||
if (isset($allowed_attributes_mutable[$key])) { |
||||
unset($allowed_attributes_mutable[$key]); |
||||
} |
||||
} |
||||
if ($delete) unset($this->info_global_attr[$attr]); |
||||
} |
||||
|
||||
foreach ($this->info as $tag => $info) { |
||||
foreach ($info->attr as $attr => $x) { |
||||
$keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr"); |
||||
$delete = true; |
||||
foreach ($keys as $key) { |
||||
if ($delete && isset($allowed_attributes[$key])) { |
||||
$delete = false; |
||||
} |
||||
if (isset($allowed_attributes_mutable[$key])) { |
||||
unset($allowed_attributes_mutable[$key]); |
||||
} |
||||
} |
||||
if ($delete) { |
||||
if ($this->info[$tag]->attr[$attr]->required) { |
||||
trigger_error("Required attribute '$attr' in element '$tag' was not allowed, which means '$tag' will not be allowed either", E_USER_WARNING); |
||||
} |
||||
unset($this->info[$tag]->attr[$attr]); |
||||
} |
||||
} |
||||
} |
||||
// emit errors |
||||
foreach ($allowed_attributes_mutable as $elattr => $d) { |
||||
$bits = preg_split('/[.@]/', $elattr, 2); |
||||
$c = count($bits); |
||||
switch ($c) { |
||||
case 2: |
||||
if ($bits[0] !== '*') { |
||||
$element = htmlspecialchars($bits[0]); |
||||
$attribute = htmlspecialchars($bits[1]); |
||||
if (!isset($this->info[$element])) { |
||||
trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support"); |
||||
} else { |
||||
trigger_error("Attribute '$attribute' in element '$element' not supported $support", |
||||
E_USER_WARNING); |
||||
} |
||||
break; |
||||
} |
||||
// otherwise fall through |
||||
case 1: |
||||
$attribute = htmlspecialchars($bits[0]); |
||||
trigger_error("Global attribute '$attribute' is not ". |
||||
"supported in any elements $support", |
||||
E_USER_WARNING); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// setup forbidden elements --------------------------------------- |
||||
|
||||
$forbidden_elements = $config->get('HTML.ForbiddenElements'); |
||||
$forbidden_attributes = $config->get('HTML.ForbiddenAttributes'); |
||||
|
||||
foreach ($this->info as $tag => $info) { |
||||
if (isset($forbidden_elements[$tag])) { |
||||
unset($this->info[$tag]); |
||||
continue; |
||||
} |
||||
foreach ($info->attr as $attr => $x) { |
||||
if ( |
||||
isset($forbidden_attributes["$tag@$attr"]) || |
||||
isset($forbidden_attributes["*@$attr"]) || |
||||
isset($forbidden_attributes[$attr]) |
||||
) { |
||||
unset($this->info[$tag]->attr[$attr]); |
||||
continue; |
||||
} // this segment might get removed eventually |
||||
elseif (isset($forbidden_attributes["$tag.$attr"])) { |
||||
// $tag.$attr are not user supplied, so no worries! |
||||
trigger_error("Error with $tag.$attr: tag.attr syntax not supported for HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING); |
||||
} |
||||
} |
||||
} |
||||
foreach ($forbidden_attributes as $key => $v) { |
||||
if (strlen($key) < 2) continue; |
||||
if ($key[0] != '*') continue; |
||||
if ($key[1] == '.') { |
||||
trigger_error("Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING); |
||||
} |
||||
} |
||||
|
||||
// setup injectors ----------------------------------------------------- |
||||
foreach ($this->info_injector as $i => $injector) { |
||||
if ($injector->checkNeeded($config) !== false) { |
||||
// remove injector that does not have it's required |
||||
// elements/attributes present, and is thus not needed. |
||||
unset($this->info_injector[$i]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Parses a TinyMCE-flavored Allowed Elements and Attributes list into |
||||
* separate lists for processing. Format is element[attr1|attr2],element2... |
||||
* @warning Although it's largely drawn from TinyMCE's implementation, |
||||
* it is different, and you'll probably have to modify your lists |
||||
* @param $list String list to parse |
||||
* @param array($allowed_elements, $allowed_attributes) |
||||
* @todo Give this its own class, probably static interface |
||||
*/ |
||||
public function parseTinyMCEAllowedList($list) { |
||||
|
||||
$list = str_replace(array(' ', "\t"), '', $list); |
||||
|
||||
$elements = array(); |
||||
$attributes = array(); |
||||
|
||||
$chunks = preg_split('/(,|[\n\r]+)/', $list); |
||||
foreach ($chunks as $chunk) { |
||||
if (empty($chunk)) continue; |
||||
// remove TinyMCE element control characters |
||||
if (!strpos($chunk, '[')) { |
||||
$element = $chunk; |
||||
$attr = false; |
||||
} else { |
||||
list($element, $attr) = explode('[', $chunk); |
||||
} |
||||
if ($element !== '*') $elements[$element] = true; |
||||
if (!$attr) continue; |
||||
$attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ] |
||||
$attr = explode('|', $attr); |
||||
foreach ($attr as $key) { |
||||
$attributes["$element.$key"] = true; |
||||
} |
||||
} |
||||
|
||||
return array($elements, $attributes); |
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,90 +1,91 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Adds important param elements to inside of object in order to make |
||||
* things safe. |
||||
*/ |
||||
class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector |
||||
{ |
||||
public $name = 'SafeObject'; |
||||
public $needed = array('object', 'param'); |
||||
|
||||
protected $objectStack = array(); |
||||
protected $paramStack = array(); |
||||
|
||||
// Keep this synchronized with AttrTransform/SafeParam.php |
||||
protected $addParam = array( |
||||
'allowScriptAccess' => 'never', |
||||
'allowNetworking' => 'internal', |
||||
); |
||||
protected $allowedParam = array( |
||||
'wmode' => true, |
||||
'movie' => true, |
||||
'flashvars' => true, |
||||
'src' => true, |
||||
); |
||||
|
||||
public function prepare($config, $context) { |
||||
parent::prepare($config, $context); |
||||
} |
||||
|
||||
public function handleElement(&$token) { |
||||
if ($token->name == 'object') { |
||||
$this->objectStack[] = $token; |
||||
$this->paramStack[] = array(); |
||||
$new = array($token); |
||||
foreach ($this->addParam as $name => $value) { |
||||
$new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value)); |
||||
} |
||||
$token = $new; |
||||
} elseif ($token->name == 'param') { |
||||
$nest = count($this->currentNesting) - 1; |
||||
if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') { |
||||
$i = count($this->objectStack) - 1; |
||||
if (!isset($token->attr['name'])) { |
||||
$token = false; |
||||
return; |
||||
} |
||||
$n = $token->attr['name']; |
||||
// We need this fix because YouTube doesn't supply a data |
||||
// attribute, which we need if a type is specified. This is |
||||
// *very* Flash specific. |
||||
if (!isset($this->objectStack[$i]->attr['data']) && |
||||
($token->attr['name'] == 'movie' || $token->attr['name'] == 'src')) { |
||||
$this->objectStack[$i]->attr['data'] = $token->attr['value']; |
||||
} |
||||
// Check if the parameter is the correct value but has not |
||||
// already been added |
||||
if ( |
||||
!isset($this->paramStack[$i][$n]) && |
||||
isset($this->addParam[$n]) && |
||||
$token->attr['name'] === $this->addParam[$n] |
||||
) { |
||||
// keep token, and add to param stack |
||||
$this->paramStack[$i][$n] = true; |
||||
} elseif (isset($this->allowedParam[$n])) { |
||||
// keep token, don't do anything to it |
||||
// (could possibly check for duplicates here) |
||||
} else { |
||||
$token = false; |
||||
} |
||||
} else { |
||||
// not directly inside an object, DENY! |
||||
$token = false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public function handleEnd(&$token) { |
||||
// This is the WRONG way of handling the object and param stacks; |
||||
// we should be inserting them directly on the relevant object tokens |
||||
// so that the global stack handling handles it. |
||||
if ($token->name == 'object') { |
||||
array_pop($this->objectStack); |
||||
array_pop($this->paramStack); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Adds important param elements to inside of object in order to make |
||||
* things safe. |
||||
*/ |
||||
class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector |
||||
{ |
||||
public $name = 'SafeObject'; |
||||
public $needed = array('object', 'param'); |
||||
|
||||
protected $objectStack = array(); |
||||
protected $paramStack = array(); |
||||
|
||||
// Keep this synchronized with AttrTransform/SafeParam.php |
||||
protected $addParam = array( |
||||
'allowScriptAccess' => 'never', |
||||
'allowNetworking' => 'internal', |
||||
); |
||||
protected $allowedParam = array( |
||||
'wmode' => true, |
||||
'movie' => true, |
||||
'flashvars' => true, |
||||
'src' => true, |
||||
'allowFullScreen' => true, // if omitted, assume to be 'false' |
||||
); |
||||
|
||||
public function prepare($config, $context) { |
||||
parent::prepare($config, $context); |
||||
} |
||||
|
||||
public function handleElement(&$token) { |
||||
if ($token->name == 'object') { |
||||
$this->objectStack[] = $token; |
||||
$this->paramStack[] = array(); |
||||
$new = array($token); |
||||
foreach ($this->addParam as $name => $value) { |
||||
$new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value)); |
||||
} |
||||
$token = $new; |
||||
} elseif ($token->name == 'param') { |
||||
$nest = count($this->currentNesting) - 1; |
||||
if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') { |
||||
$i = count($this->objectStack) - 1; |
||||
if (!isset($token->attr['name'])) { |
||||
$token = false; |
||||
return; |
||||
} |
||||
$n = $token->attr['name']; |
||||
// We need this fix because YouTube doesn't supply a data |
||||
// attribute, which we need if a type is specified. This is |
||||
// *very* Flash specific. |
||||
if (!isset($this->objectStack[$i]->attr['data']) && |
||||
($token->attr['name'] == 'movie' || $token->attr['name'] == 'src')) { |
||||
$this->objectStack[$i]->attr['data'] = $token->attr['value']; |
||||
} |
||||
// Check if the parameter is the correct value but has not |
||||
// already been added |
||||
if ( |
||||
!isset($this->paramStack[$i][$n]) && |
||||
isset($this->addParam[$n]) && |
||||
$token->attr['name'] === $this->addParam[$n] |
||||
) { |
||||
// keep token, and add to param stack |
||||
$this->paramStack[$i][$n] = true; |
||||
} elseif (isset($this->allowedParam[$n])) { |
||||
// keep token, don't do anything to it |
||||
// (could possibly check for duplicates here) |
||||
} else { |
||||
$token = false; |
||||
} |
||||
} else { |
||||
// not directly inside an object, DENY! |
||||
$token = false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public function handleEnd(&$token) { |
||||
// This is the WRONG way of handling the object and param stacks; |
||||
// we should be inserting them directly on the relevant object tokens |
||||
// so that the global stack handling handles it. |
||||
if ($token->name == 'object') { |
||||
array_pop($this->objectStack); |
||||
array_pop($this->paramStack); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,62 +1,63 @@ |
||||
<?php |
||||
|
||||
$fallback = false; |
||||
|
||||
$messages = array( |
||||
|
||||
'HTMLPurifier' => 'HTML Purifier', |
||||
|
||||
// for unit testing purposes |
||||
'LanguageFactoryTest: Pizza' => 'Pizza', |
||||
'LanguageTest: List' => '$1', |
||||
'LanguageTest: Hash' => '$1.Keys; $1.Values', |
||||
|
||||
'Item separator' => ', ', |
||||
'Item separator last' => ' and ', // non-Harvard style |
||||
|
||||
'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.', |
||||
'ErrorCollector: At line' => ' at line $line', |
||||
'ErrorCollector: Incidental errors' => 'Incidental errors', |
||||
|
||||
'Lexer: Unclosed comment' => 'Unclosed comment', |
||||
'Lexer: Unescaped lt' => 'Unescaped less-than sign (<) should be <', |
||||
'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped', |
||||
'Lexer: Missing attribute key' => 'Attribute declaration has no key', |
||||
'Lexer: Missing end quote' => 'Attribute declaration has no end quote', |
||||
|
||||
'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized', |
||||
'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1', |
||||
'Strategy_RemoveForeignElements: Foreign element to text' => 'Unrecognized $CurrentToken.Serialized tag converted to text', |
||||
'Strategy_RemoveForeignElements: Foreign element removed' => 'Unrecognized $CurrentToken.Serialized tag removed', |
||||
'Strategy_RemoveForeignElements: Comment removed' => 'Comment containing "$CurrentToken.Data" removed', |
||||
'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed', |
||||
'Strategy_RemoveForeignElements: Token removed to end' => 'Tags and text starting from $1 element where removed to end', |
||||
'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' => 'Trailing hyphen(s) in comment removed', |
||||
'Strategy_RemoveForeignElements: Hyphens in comment collapsed' => 'Double hyphens in comments are not allowed, and were collapsed into single hyphens', |
||||
|
||||
'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed', |
||||
'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text', |
||||
'Strategy_MakeWellFormed: Tag auto closed' => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact', |
||||
'Strategy_MakeWellFormed: Tag carryover' => '$1.Compact started on line $1.Line auto-continued into $CurrentToken.Compact', |
||||
'Strategy_MakeWellFormed: Stray end tag removed' => 'Stray $CurrentToken.Serialized tag removed', |
||||
'Strategy_MakeWellFormed: Stray end tag to text' => 'Stray $CurrentToken.Serialized tag converted to text', |
||||
'Strategy_MakeWellFormed: Tag closed by element end' => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized', |
||||
'Strategy_MakeWellFormed: Tag closed by document end' => '$1.Compact tag started on line $1.Line closed by end of document', |
||||
|
||||
'Strategy_FixNesting: Node removed' => '$CurrentToken.Compact node removed', |
||||
'Strategy_FixNesting: Node excluded' => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element', |
||||
'Strategy_FixNesting: Node reorganized' => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model', |
||||
'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed', |
||||
|
||||
'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys', |
||||
'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed', |
||||
|
||||
); |
||||
|
||||
$errorNames = array( |
||||
E_ERROR => 'Error', |
||||
E_WARNING => 'Warning', |
||||
E_NOTICE => 'Notice' |
||||
); |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
$fallback = false; |
||||
|
||||
$messages = array( |
||||
|
||||
'HTMLPurifier' => 'HTML Purifier', |
||||
|
||||
// for unit testing purposes |
||||
'LanguageFactoryTest: Pizza' => 'Pizza', |
||||
'LanguageTest: List' => '$1', |
||||
'LanguageTest: Hash' => '$1.Keys; $1.Values', |
||||
|
||||
'Item separator' => ', ', |
||||
'Item separator last' => ' and ', // non-Harvard style |
||||
|
||||
'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.', |
||||
'ErrorCollector: At line' => ' at line $line', |
||||
'ErrorCollector: Incidental errors' => 'Incidental errors', |
||||
|
||||
'Lexer: Unclosed comment' => 'Unclosed comment', |
||||
'Lexer: Unescaped lt' => 'Unescaped less-than sign (<) should be <', |
||||
'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped', |
||||
'Lexer: Missing attribute key' => 'Attribute declaration has no key', |
||||
'Lexer: Missing end quote' => 'Attribute declaration has no end quote', |
||||
'Lexer: Extracted body' => 'Removed document metadata tags', |
||||
|
||||
'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized', |
||||
'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1', |
||||
'Strategy_RemoveForeignElements: Foreign element to text' => 'Unrecognized $CurrentToken.Serialized tag converted to text', |
||||
'Strategy_RemoveForeignElements: Foreign element removed' => 'Unrecognized $CurrentToken.Serialized tag removed', |
||||
'Strategy_RemoveForeignElements: Comment removed' => 'Comment containing "$CurrentToken.Data" removed', |
||||
'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed', |
||||
'Strategy_RemoveForeignElements: Token removed to end' => 'Tags and text starting from $1 element where removed to end', |
||||
'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' => 'Trailing hyphen(s) in comment removed', |
||||
'Strategy_RemoveForeignElements: Hyphens in comment collapsed' => 'Double hyphens in comments are not allowed, and were collapsed into single hyphens', |
||||
|
||||
'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed', |
||||
'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text', |
||||
'Strategy_MakeWellFormed: Tag auto closed' => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact', |
||||
'Strategy_MakeWellFormed: Tag carryover' => '$1.Compact started on line $1.Line auto-continued into $CurrentToken.Compact', |
||||
'Strategy_MakeWellFormed: Stray end tag removed' => 'Stray $CurrentToken.Serialized tag removed', |
||||
'Strategy_MakeWellFormed: Stray end tag to text' => 'Stray $CurrentToken.Serialized tag converted to text', |
||||
'Strategy_MakeWellFormed: Tag closed by element end' => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized', |
||||
'Strategy_MakeWellFormed: Tag closed by document end' => '$1.Compact tag started on line $1.Line closed by end of document', |
||||
|
||||
'Strategy_FixNesting: Node removed' => '$CurrentToken.Compact node removed', |
||||
'Strategy_FixNesting: Node excluded' => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element', |
||||
'Strategy_FixNesting: Node reorganized' => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model', |
||||
'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed', |
||||
|
||||
'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys', |
||||
'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed', |
||||
|
||||
); |
||||
|
||||
$errorNames = array( |
||||
E_ERROR => 'Error', |
||||
E_WARNING => 'Warning', |
||||
E_NOTICE => 'Notice' |
||||
); |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,298 +1,326 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Forgivingly lexes HTML (SGML-style) markup into tokens. |
||||
* |
||||
* A lexer parses a string of SGML-style markup and converts them into |
||||
* corresponding tokens. It doesn't check for well-formedness, although its |
||||
* internal mechanism may make this automatic (such as the case of |
||||
* HTMLPurifier_Lexer_DOMLex). There are several implementations to choose |
||||
* from. |
||||
* |
||||
* A lexer is HTML-oriented: it might work with XML, but it's not |
||||
* recommended, as we adhere to a subset of the specification for optimization |
||||
* reasons. This might change in the future. Also, most tokenizers are not |
||||
* expected to handle DTDs or PIs. |
||||
* |
||||
* This class should not be directly instantiated, but you may use create() to |
||||
* retrieve a default copy of the lexer. Being a supertype, this class |
||||
* does not actually define any implementation, but offers commonly used |
||||
* convenience functions for subclasses. |
||||
* |
||||
* @note The unit tests will instantiate this class for testing purposes, as |
||||
* many of the utility functions require a class to be instantiated. |
||||
* This means that, even though this class is not runnable, it will |
||||
* not be declared abstract. |
||||
* |
||||
* @par |
||||
* |
||||
* @note |
||||
* We use tokens rather than create a DOM representation because DOM would: |
||||
* |
||||
* @par |
||||
* -# Require more processing and memory to create, |
||||
* -# Is not streamable, and |
||||
* -# Has the entire document structure (html and body not needed). |
||||
* |
||||
* @par |
||||
* However, DOM is helpful in that it makes it easy to move around nodes |
||||
* without a lot of lookaheads to see when a tag is closed. This is a |
||||
* limitation of the token system and some workarounds would be nice. |
||||
*/ |
||||
class HTMLPurifier_Lexer |
||||
{ |
||||
|
||||
/** |
||||
* Whether or not this lexer implements line-number/column-number tracking. |
||||
* If it does, set to true. |
||||
*/ |
||||
public $tracksLineNumbers = false; |
||||
|
||||
// -- STATIC ---------------------------------------------------------- |
||||
|
||||
/** |
||||
* Retrieves or sets the default Lexer as a Prototype Factory. |
||||
* |
||||
* By default HTMLPurifier_Lexer_DOMLex will be returned. There are |
||||
* a few exceptions involving special features that only DirectLex |
||||
* implements. |
||||
* |
||||
* @note The behavior of this class has changed, rather than accepting |
||||
* a prototype object, it now accepts a configuration object. |
||||
* To specify your own prototype, set %Core.LexerImpl to it. |
||||
* This change in behavior de-singletonizes the lexer object. |
||||
* |
||||
* @param $config Instance of HTMLPurifier_Config |
||||
* @return Concrete lexer. |
||||
*/ |
||||
public static function create($config) { |
||||
|
||||
if (!($config instanceof HTMLPurifier_Config)) { |
||||
$lexer = $config; |
||||
trigger_error("Passing a prototype to |
||||
HTMLPurifier_Lexer::create() is deprecated, please instead |
||||
use %Core.LexerImpl", E_USER_WARNING); |
||||
} else { |
||||
$lexer = $config->get('Core.LexerImpl'); |
||||
} |
||||
|
||||
$needs_tracking = |
||||
$config->get('Core.MaintainLineNumbers') || |
||||
$config->get('Core.CollectErrors'); |
||||
|
||||
$inst = null; |
||||
if (is_object($lexer)) { |
||||
$inst = $lexer; |
||||
} else { |
||||
|
||||
if (is_null($lexer)) { do { |
||||
// auto-detection algorithm |
||||
|
||||
if ($needs_tracking) { |
||||
$lexer = 'DirectLex'; |
||||
break; |
||||
} |
||||
|
||||
if ( |
||||
class_exists('DOMDocument') && |
||||
method_exists('DOMDocument', 'loadHTML') && |
||||
!extension_loaded('domxml') |
||||
) { |
||||
// check for DOM support, because while it's part of the |
||||
// core, it can be disabled compile time. Also, the PECL |
||||
// domxml extension overrides the default DOM, and is evil |
||||
// and nasty and we shan't bother to support it |
||||
$lexer = 'DOMLex'; |
||||
} else { |
||||
$lexer = 'DirectLex'; |
||||
} |
||||
|
||||
} while(0); } // do..while so we can break |
||||
|
||||
// instantiate recognized string names |
||||
switch ($lexer) { |
||||
case 'DOMLex': |
||||
$inst = new HTMLPurifier_Lexer_DOMLex(); |
||||
break; |
||||
case 'DirectLex': |
||||
$inst = new HTMLPurifier_Lexer_DirectLex(); |
||||
break; |
||||
case 'PH5P': |
||||
$inst = new HTMLPurifier_Lexer_PH5P(); |
||||
break; |
||||
default: |
||||
throw new HTMLPurifier_Exception("Cannot instantiate unrecognized Lexer type " . htmlspecialchars($lexer)); |
||||
} |
||||
} |
||||
|
||||
if (!$inst) throw new HTMLPurifier_Exception('No lexer was instantiated'); |
||||
|
||||
// once PHP DOM implements native line numbers, or we |
||||
// hack out something using XSLT, remove this stipulation |
||||
if ($needs_tracking && !$inst->tracksLineNumbers) { |
||||
throw new HTMLPurifier_Exception('Cannot use lexer that does not support line numbers with Core.MaintainLineNumbers or Core.CollectErrors (use DirectLex instead)'); |
||||
} |
||||
|
||||
return $inst; |
||||
|
||||
} |
||||
|
||||
// -- CONVENIENCE MEMBERS --------------------------------------------- |
||||
|
||||
public function __construct() { |
||||
$this->_entity_parser = new HTMLPurifier_EntityParser(); |
||||
} |
||||
|
||||
/** |
||||
* Most common entity to raw value conversion table for special entities. |
||||
*/ |
||||
protected $_special_entity2str = |
||||
array( |
||||
'"' => '"', |
||||
'&' => '&', |
||||
'<' => '<', |
||||
'>' => '>', |
||||
''' => "'", |
||||
''' => "'", |
||||
''' => "'" |
||||
); |
||||
|
||||
/** |
||||
* 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. |
||||
*/ |
||||
public 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, '&'); |
||||
$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; |
||||
} |
||||
|
||||
/** |
||||
* Lexes an HTML string into tokens. |
||||
* |
||||
* @param $string String HTML. |
||||
* @return HTMLPurifier_Token array representation of HTML. |
||||
*/ |
||||
public function tokenizeHTML($string, $config, $context) { |
||||
trigger_error('Call to abstract class', E_USER_ERROR); |
||||
} |
||||
|
||||
/** |
||||
* Translates CDATA sections into regular sections (through escaping). |
||||
* |
||||
* @param $string HTML string to process. |
||||
* @returns HTML with CDATA sections escaped. |
||||
*/ |
||||
protected static function escapeCDATA($string) { |
||||
return preg_replace_callback( |
||||
'/<!\[CDATA\[(.+?)\]\]>/s', |
||||
array('HTMLPurifier_Lexer', 'CDATACallback'), |
||||
$string |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Special CDATA case that is especially convoluted for <script> |
||||
*/ |
||||
protected static function escapeCommentedCDATA($string) { |
||||
return preg_replace_callback( |
||||
'#<!--//--><!\[CDATA\[//><!--(.+?)//--><!\]\]>#s', |
||||
array('HTMLPurifier_Lexer', 'CDATACallback'), |
||||
$string |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Callback function for escapeCDATA() that does the work. |
||||
* |
||||
* @warning Though this is public in order to let the callback happen, |
||||
* calling it directly is not recommended. |
||||
* @params $matches PCRE matches array, with index 0 the entire match |
||||
* and 1 the inside of the CDATA section. |
||||
* @returns Escaped internals of the CDATA section. |
||||
*/ |
||||
protected static function CDATACallback($matches) { |
||||
// not exactly sure why the character set is needed, but whatever |
||||
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. |
||||
* @todo Consider making protected |
||||
*/ |
||||
public function normalize($html, $config, $context) { |
||||
|
||||
// normalize newlines to \n |
||||
$html = str_replace("\r\n", "\n", $html); |
||||
$html = str_replace("\r", "\n", $html); |
||||
|
||||
if ($config->get('HTML.Trusted')) { |
||||
// escape convoluted CDATA |
||||
$html = $this->escapeCommentedCDATA($html); |
||||
} |
||||
|
||||
// escape CDATA |
||||
$html = $this->escapeCDATA($html); |
||||
|
||||
// extract body from document if applicable |
||||
if ($config->get('Core.ConvertDocumentToFragment')) { |
||||
$html = $this->extractBody($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 = HTMLPurifier_Encoder::cleanUTF8($html); |
||||
|
||||
return $html; |
||||
} |
||||
|
||||
/** |
||||
* Takes a string of HTML (fragment or document) and returns the content |
||||
* @todo Consider making protected |
||||
*/ |
||||
public function extractBody($html) { |
||||
$matches = array(); |
||||
$result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches); |
||||
if ($result) { |
||||
return $matches[1]; |
||||
} else { |
||||
return $html; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Forgivingly lexes HTML (SGML-style) markup into tokens. |
||||
* |
||||
* A lexer parses a string of SGML-style markup and converts them into |
||||
* corresponding tokens. It doesn't check for well-formedness, although its |
||||
* internal mechanism may make this automatic (such as the case of |
||||
* HTMLPurifier_Lexer_DOMLex). There are several implementations to choose |
||||
* from. |
||||
* |
||||
* A lexer is HTML-oriented: it might work with XML, but it's not |
||||
* recommended, as we adhere to a subset of the specification for optimization |
||||
* reasons. This might change in the future. Also, most tokenizers are not |
||||
* expected to handle DTDs or PIs. |
||||
* |
||||
* This class should not be directly instantiated, but you may use create() to |
||||
* retrieve a default copy of the lexer. Being a supertype, this class |
||||
* does not actually define any implementation, but offers commonly used |
||||
* convenience functions for subclasses. |
||||
* |
||||
* @note The unit tests will instantiate this class for testing purposes, as |
||||
* many of the utility functions require a class to be instantiated. |
||||
* This means that, even though this class is not runnable, it will |
||||
* not be declared abstract. |
||||
* |
||||
* @par |
||||
* |
||||
* @note |
||||
* We use tokens rather than create a DOM representation because DOM would: |
||||
* |
||||
* @par |
||||
* -# Require more processing and memory to create, |
||||
* -# Is not streamable, and |
||||
* -# Has the entire document structure (html and body not needed). |
||||
* |
||||
* @par |
||||
* However, DOM is helpful in that it makes it easy to move around nodes |
||||
* without a lot of lookaheads to see when a tag is closed. This is a |
||||
* limitation of the token system and some workarounds would be nice. |
||||
*/ |
||||
class HTMLPurifier_Lexer |
||||
{ |
||||
|
||||
/** |
||||
* Whether or not this lexer implements line-number/column-number tracking. |
||||
* If it does, set to true. |
||||
*/ |
||||
public $tracksLineNumbers = false; |
||||
|
||||
// -- STATIC ---------------------------------------------------------- |
||||
|
||||
/** |
||||
* Retrieves or sets the default Lexer as a Prototype Factory. |
||||
* |
||||
* By default HTMLPurifier_Lexer_DOMLex will be returned. There are |
||||
* a few exceptions involving special features that only DirectLex |
||||
* implements. |
||||
* |
||||
* @note The behavior of this class has changed, rather than accepting |
||||
* a prototype object, it now accepts a configuration object. |
||||
* To specify your own prototype, set %Core.LexerImpl to it. |
||||
* This change in behavior de-singletonizes the lexer object. |
||||
* |
||||
* @param $config Instance of HTMLPurifier_Config |
||||
* @return Concrete lexer. |
||||
*/ |
||||
public static function create($config) { |
||||
|
||||
if (!($config instanceof HTMLPurifier_Config)) { |
||||
$lexer = $config; |
||||
trigger_error("Passing a prototype to |
||||
HTMLPurifier_Lexer::create() is deprecated, please instead |
||||
use %Core.LexerImpl", E_USER_WARNING); |
||||
} else { |
||||
$lexer = $config->get('Core.LexerImpl'); |
||||
} |
||||
|
||||
$needs_tracking = |
||||
$config->get('Core.MaintainLineNumbers') || |
||||
$config->get('Core.CollectErrors'); |
||||
|
||||
$inst = null; |
||||
if (is_object($lexer)) { |
||||
$inst = $lexer; |
||||
} else { |
||||
|
||||
if (is_null($lexer)) { do { |
||||
// auto-detection algorithm |
||||
|
||||
if ($needs_tracking) { |
||||
$lexer = 'DirectLex'; |
||||
break; |
||||
} |
||||
|
||||
if ( |
||||
class_exists('DOMDocument') && |
||||
method_exists('DOMDocument', 'loadHTML') && |
||||
!extension_loaded('domxml') |
||||
) { |
||||
// check for DOM support, because while it's part of the |
||||
// core, it can be disabled compile time. Also, the PECL |
||||
// domxml extension overrides the default DOM, and is evil |
||||
// and nasty and we shan't bother to support it |
||||
$lexer = 'DOMLex'; |
||||
} else { |
||||
$lexer = 'DirectLex'; |
||||
} |
||||
|
||||
} while(0); } // do..while so we can break |
||||
|
||||
// instantiate recognized string names |
||||
switch ($lexer) { |
||||
case 'DOMLex': |
||||
$inst = new HTMLPurifier_Lexer_DOMLex(); |
||||
break; |
||||
case 'DirectLex': |
||||
$inst = new HTMLPurifier_Lexer_DirectLex(); |
||||
break; |
||||
case 'PH5P': |
||||
$inst = new HTMLPurifier_Lexer_PH5P(); |
||||
break; |
||||
default: |
||||
throw new HTMLPurifier_Exception("Cannot instantiate unrecognized Lexer type " . htmlspecialchars($lexer)); |
||||
} |
||||
} |
||||
|
||||
if (!$inst) throw new HTMLPurifier_Exception('No lexer was instantiated'); |
||||
|
||||
// once PHP DOM implements native line numbers, or we |
||||
// hack out something using XSLT, remove this stipulation |
||||
if ($needs_tracking && !$inst->tracksLineNumbers) { |
||||
throw new HTMLPurifier_Exception('Cannot use lexer that does not support line numbers with Core.MaintainLineNumbers or Core.CollectErrors (use DirectLex instead)'); |
||||
} |
||||
|
||||
return $inst; |
||||
|
||||
} |
||||
|
||||
// -- CONVENIENCE MEMBERS --------------------------------------------- |
||||
|
||||
public function __construct() { |
||||
$this->_entity_parser = new HTMLPurifier_EntityParser(); |
||||
} |
||||
|
||||
/** |
||||
* Most common entity to raw value conversion table for special entities. |
||||
*/ |
||||
protected $_special_entity2str = |
||||
array( |
||||
'"' => '"', |
||||
'&' => '&', |
||||
'<' => '<', |
||||
'>' => '>', |
||||
''' => "'", |
||||
''' => "'", |
||||
''' => "'" |
||||
); |
||||
|
||||
/** |
||||
* 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. |
||||
*/ |
||||
public 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, '&'); |
||||
$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; |
||||
} |
||||
|
||||
/** |
||||
* Lexes an HTML string into tokens. |
||||
* |
||||
* @param $string String HTML. |
||||
* @return HTMLPurifier_Token array representation of HTML. |
||||
*/ |
||||
public function tokenizeHTML($string, $config, $context) { |
||||
trigger_error('Call to abstract class', E_USER_ERROR); |
||||
} |
||||
|
||||
/** |
||||
* Translates CDATA sections into regular sections (through escaping). |
||||
* |
||||
* @param $string HTML string to process. |
||||
* @returns HTML with CDATA sections escaped. |
||||
*/ |
||||
protected static function escapeCDATA($string) { |
||||
return preg_replace_callback( |
||||
'/<!\[CDATA\[(.+?)\]\]>/s', |
||||
array('HTMLPurifier_Lexer', 'CDATACallback'), |
||||
$string |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Special CDATA case that is especially convoluted for <script> |
||||
*/ |
||||
protected static function escapeCommentedCDATA($string) { |
||||
return preg_replace_callback( |
||||
'#<!--//--><!\[CDATA\[//><!--(.+?)//--><!\]\]>#s', |
||||
array('HTMLPurifier_Lexer', 'CDATACallback'), |
||||
$string |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Special Internet Explorer conditional comments should be removed. |
||||
*/ |
||||
protected static function removeIEConditional($string) { |
||||
return preg_replace( |
||||
'#<!--\[if [^>]+\]>.*<!\[endif\]-->#si', // probably should generalize for all strings |
||||
'', |
||||
$string |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Callback function for escapeCDATA() that does the work. |
||||
* |
||||
* @warning Though this is public in order to let the callback happen, |
||||
* calling it directly is not recommended. |
||||
* @params $matches PCRE matches array, with index 0 the entire match |
||||
* and 1 the inside of the CDATA section. |
||||
* @returns Escaped internals of the CDATA section. |
||||
*/ |
||||
protected static function CDATACallback($matches) { |
||||
// not exactly sure why the character set is needed, but whatever |
||||
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. |
||||
* @todo Consider making protected |
||||
*/ |
||||
public function normalize($html, $config, $context) { |
||||
|
||||
// normalize newlines to \n |
||||
if ($config->get('Core.NormalizeNewlines')) { |
||||
$html = str_replace("\r\n", "\n", $html); |
||||
$html = str_replace("\r", "\n", $html); |
||||
} |
||||
|
||||
if ($config->get('HTML.Trusted')) { |
||||
// escape convoluted CDATA |
||||
$html = $this->escapeCommentedCDATA($html); |
||||
} |
||||
|
||||
$html = $this->removeIEConditional($html); |
||||
|
||||
// escape CDATA |
||||
$html = $this->escapeCDATA($html); |
||||
|
||||
// extract body from document if applicable |
||||
if ($config->get('Core.ConvertDocumentToFragment')) { |
||||
$e = false; |
||||
if ($config->get('Core.CollectErrors')) { |
||||
$e =& $context->get('ErrorCollector'); |
||||
} |
||||
$new_html = $this->extractBody($html); |
||||
if ($e && $new_html != $html) { |
||||
$e->send(E_WARNING, 'Lexer: Extracted body'); |
||||
} |
||||
$html = $new_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 = HTMLPurifier_Encoder::cleanUTF8($html); |
||||
|
||||
// if processing instructions are to removed, remove them now |
||||
if ($config->get('Core.RemoveProcessingInstructions')) { |
||||
$html = preg_replace('#<\?.+?\?>#s', '', $html); |
||||
} |
||||
|
||||
return $html; |
||||
} |
||||
|
||||
/** |
||||
* Takes a string of HTML (fragment or document) and returns the content |
||||
* @todo Consider making protected |
||||
*/ |
||||
public function extractBody($html) { |
||||
$matches = array(); |
||||
$result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches); |
||||
if ($result) { |
||||
return $matches[1]; |
||||
} else { |
||||
return $html; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,490 +1,490 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Our in-house implementation of a parser. |
||||
* |
||||
* A pure PHP parser, DirectLex has absolutely no dependencies, making |
||||
* it a reasonably good default for PHP4. Written with efficiency in mind, |
||||
* it can be four times faster than HTMLPurifier_Lexer_PEARSax3, although it |
||||
* pales in comparison to HTMLPurifier_Lexer_DOMLex. |
||||
* |
||||
* @todo Reread XML spec and document differences. |
||||
*/ |
||||
class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer |
||||
{ |
||||
|
||||
public $tracksLineNumbers = true; |
||||
|
||||
/** |
||||
* Whitespace characters for str(c)spn. |
||||
*/ |
||||
protected $_whitespace = "\x20\x09\x0D\x0A"; |
||||
|
||||
/** |
||||
* Callback function for script CDATA fudge |
||||
* @param $matches, in form of array(opening tag, contents, closing tag) |
||||
*/ |
||||
protected function scriptCallback($matches) { |
||||
return $matches[1] . htmlspecialchars($matches[2], ENT_COMPAT, 'UTF-8') . $matches[3]; |
||||
} |
||||
|
||||
public function tokenizeHTML($html, $config, $context) { |
||||
|
||||
// special normalization for script tags without any armor |
||||
// our "armor" heurstic is a < sign any number of whitespaces after |
||||
// the first script tag |
||||
if ($config->get('HTML.Trusted')) { |
||||
$html = preg_replace_callback('#(<script[^>]*>)(\s*[^<].+?)(</script>)#si', |
||||
array($this, 'scriptCallback'), $html); |
||||
} |
||||
|
||||
$html = $this->normalize($html, $config, $context); |
||||
|
||||
$cursor = 0; // our location in the text |
||||
$inside_tag = false; // whether or not we're parsing the inside of a tag |
||||
$array = array(); // result array |
||||
|
||||
// This is also treated to mean maintain *column* numbers too |
||||
$maintain_line_numbers = $config->get('Core.MaintainLineNumbers'); |
||||
|
||||
if ($maintain_line_numbers === null) { |
||||
// automatically determine line numbering by checking |
||||
// if error collection is on |
||||
$maintain_line_numbers = $config->get('Core.CollectErrors'); |
||||
} |
||||
|
||||
if ($maintain_line_numbers) { |
||||
$current_line = 1; |
||||
$current_col = 0; |
||||
$length = strlen($html); |
||||
} else { |
||||
$current_line = false; |
||||
$current_col = false; |
||||
$length = false; |
||||
} |
||||
$context->register('CurrentLine', $current_line); |
||||
$context->register('CurrentCol', $current_col); |
||||
$nl = "\n"; |
||||
// how often to manually recalculate. This will ALWAYS be right, |
||||
// but it's pretty wasteful. Set to 0 to turn off |
||||
$synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval'); |
||||
|
||||
$e = false; |
||||
if ($config->get('Core.CollectErrors')) { |
||||
$e =& $context->get('ErrorCollector'); |
||||
} |
||||
|
||||
// for testing synchronization |
||||
$loops = 0; |
||||
|
||||
while(++$loops) { |
||||
|
||||
// $cursor is either at the start of a token, or inside of |
||||
// a tag (i.e. there was a < immediately before it), as indicated |
||||
// by $inside_tag |
||||
|
||||
if ($maintain_line_numbers) { |
||||
|
||||
// $rcursor, however, is always at the start of a token. |
||||
$rcursor = $cursor - (int) $inside_tag; |
||||
|
||||
// Column number is cheap, so we calculate it every round. |
||||
// We're interested at the *end* of the newline string, so |
||||
// we need to add strlen($nl) == 1 to $nl_pos before subtracting it |
||||
// from our "rcursor" position. |
||||
$nl_pos = strrpos($html, $nl, $rcursor - $length); |
||||
$current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1); |
||||
|
||||
// recalculate lines |
||||
if ( |
||||
$synchronize_interval && // synchronization is on |
||||
$cursor > 0 && // cursor is further than zero |
||||
$loops % $synchronize_interval === 0 // time to synchronize! |
||||
) { |
||||
$current_line = 1 + $this->substrCount($html, $nl, 0, $cursor); |
||||
} |
||||
|
||||
} |
||||
|
||||
$position_next_lt = strpos($html, '<', $cursor); |
||||
$position_next_gt = strpos($html, '>', $cursor); |
||||
|
||||
// triggers on "<b>asdf</b>" but not "asdf <b></b>" |
||||
// special case to set up context |
||||
if ($position_next_lt === $cursor) { |
||||
$inside_tag = true; |
||||
$cursor++; |
||||
} |
||||
|
||||
if (!$inside_tag && $position_next_lt !== false) { |
||||
// We are not inside tag and there still is another tag to parse |
||||
$token = new |
||||
HTMLPurifier_Token_Text( |
||||
$this->parseData( |
||||
substr( |
||||
$html, $cursor, $position_next_lt - $cursor |
||||
) |
||||
) |
||||
); |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor); |
||||
} |
||||
$array[] = $token; |
||||
$cursor = $position_next_lt + 1; |
||||
$inside_tag = true; |
||||
continue; |
||||
} elseif (!$inside_tag) { |
||||
// We are not inside tag but there are no more tags |
||||
// If we're already at the end, break |
||||
if ($cursor === strlen($html)) break; |
||||
// Create Text of rest of string |
||||
$token = new |
||||
HTMLPurifier_Token_Text( |
||||
$this->parseData( |
||||
substr( |
||||
$html, $cursor |
||||
) |
||||
) |
||||
); |
||||
if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); |
||||
$array[] = $token; |
||||
break; |
||||
} elseif ($inside_tag && $position_next_gt !== false) { |
||||
// We are in tag and it is well formed |
||||
// Grab the internals of the tag |
||||
$strlen_segment = $position_next_gt - $cursor; |
||||
|
||||
if ($strlen_segment < 1) { |
||||
// there's nothing to process! |
||||
$token = new HTMLPurifier_Token_Text('<'); |
||||
$cursor++; |
||||
continue; |
||||
} |
||||
|
||||
$segment = substr($html, $cursor, $strlen_segment); |
||||
|
||||
if ($segment === false) { |
||||
// somehow, we attempted to access beyond the end of |
||||
// the string, defense-in-depth, reported by Nate Abele |
||||
break; |
||||
} |
||||
|
||||
// Check if it's a comment |
||||
if ( |
||||
substr($segment, 0, 3) === '!--' |
||||
) { |
||||
// re-determine segment length, looking for --> |
||||
$position_comment_end = strpos($html, '-->', $cursor); |
||||
if ($position_comment_end === false) { |
||||
// uh oh, we have a comment that extends to |
||||
// infinity. Can't be helped: set comment |
||||
// end position to end of string |
||||
if ($e) $e->send(E_WARNING, 'Lexer: Unclosed comment'); |
||||
$position_comment_end = strlen($html); |
||||
$end = true; |
||||
} else { |
||||
$end = false; |
||||
} |
||||
$strlen_segment = $position_comment_end - $cursor; |
||||
$segment = substr($html, $cursor, $strlen_segment); |
||||
$token = new |
||||
HTMLPurifier_Token_Comment( |
||||
substr( |
||||
$segment, 3, $strlen_segment - 3 |
||||
) |
||||
); |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment); |
||||
} |
||||
$array[] = $token; |
||||
$cursor = $end ? $position_comment_end : $position_comment_end + 3; |
||||
$inside_tag = false; |
||||
continue; |
||||
} |
||||
|
||||
// Check if it's an end tag |
||||
$is_end_tag = (strpos($segment,'/') === 0); |
||||
if ($is_end_tag) { |
||||
$type = substr($segment, 1); |
||||
$token = new HTMLPurifier_Token_End($type); |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); |
||||
} |
||||
$array[] = $token; |
||||
$inside_tag = false; |
||||
$cursor = $position_next_gt + 1; |
||||
continue; |
||||
} |
||||
|
||||
// Check leading character is alnum, if not, we may |
||||
// have accidently grabbed an emoticon. Translate into |
||||
// text and go our merry way |
||||
if (!ctype_alpha($segment[0])) { |
||||
// XML: $segment[0] !== '_' && $segment[0] !== ':' |
||||
if ($e) $e->send(E_NOTICE, 'Lexer: Unescaped lt'); |
||||
$token = new HTMLPurifier_Token_Text('<'); |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); |
||||
} |
||||
$array[] = $token; |
||||
$inside_tag = false; |
||||
continue; |
||||
} |
||||
|
||||
// Check if it is explicitly self closing, if so, remove |
||||
// trailing slash. Remember, we could have a tag like <br>, so |
||||
// any later token processing scripts must convert improperly |
||||
// classified EmptyTags from StartTags. |
||||
$is_self_closing = (strrpos($segment,'/') === $strlen_segment-1); |
||||
if ($is_self_closing) { |
||||
$strlen_segment--; |
||||
$segment = substr($segment, 0, $strlen_segment); |
||||
} |
||||
|
||||
// Check if there are any attributes |
||||
$position_first_space = strcspn($segment, $this->_whitespace); |
||||
|
||||
if ($position_first_space >= $strlen_segment) { |
||||
if ($is_self_closing) { |
||||
$token = new HTMLPurifier_Token_Empty($segment); |
||||
} else { |
||||
$token = new HTMLPurifier_Token_Start($segment); |
||||
} |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); |
||||
} |
||||
$array[] = $token; |
||||
$inside_tag = false; |
||||
$cursor = $position_next_gt + 1; |
||||
continue; |
||||
} |
||||
|
||||
// Grab out all the data |
||||
$type = substr($segment, 0, $position_first_space); |
||||
$attribute_string = |
||||
trim( |
||||
substr( |
||||
$segment, $position_first_space |
||||
) |
||||
); |
||||
if ($attribute_string) { |
||||
$attr = $this->parseAttributeString( |
||||
$attribute_string |
||||
, $config, $context |
||||
); |
||||
} else { |
||||
$attr = array(); |
||||
} |
||||
|
||||
if ($is_self_closing) { |
||||
$token = new HTMLPurifier_Token_Empty($type, $attr); |
||||
} else { |
||||
$token = new HTMLPurifier_Token_Start($type, $attr); |
||||
} |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); |
||||
} |
||||
$array[] = $token; |
||||
$cursor = $position_next_gt + 1; |
||||
$inside_tag = false; |
||||
continue; |
||||
} else { |
||||
// inside tag, but there's no ending > sign |
||||
if ($e) $e->send(E_WARNING, 'Lexer: Missing gt'); |
||||
$token = new |
||||
HTMLPurifier_Token_Text( |
||||
'<' . |
||||
$this->parseData( |
||||
substr($html, $cursor) |
||||
) |
||||
); |
||||
if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); |
||||
// no cursor scroll? Hmm... |
||||
$array[] = $token; |
||||
break; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
$context->destroy('CurrentLine'); |
||||
$context->destroy('CurrentCol'); |
||||
return $array; |
||||
} |
||||
|
||||
/** |
||||
* PHP 5.0.x compatible substr_count that implements offset and length |
||||
*/ |
||||
protected function substrCount($haystack, $needle, $offset, $length) { |
||||
static $oldVersion; |
||||
if ($oldVersion === null) { |
||||
$oldVersion = version_compare(PHP_VERSION, '5.1', '<'); |
||||
} |
||||
if ($oldVersion) { |
||||
$haystack = substr($haystack, $offset, $length); |
||||
return substr_count($haystack, $needle); |
||||
} else { |
||||
return substr_count($haystack, $needle, $offset, $length); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Takes the inside of an HTML tag and makes an assoc array of attributes. |
||||
* |
||||
* @param $string Inside of tag excluding name. |
||||
* @returns Assoc array of attributes. |
||||
*/ |
||||
public function parseAttributeString($string, $config, $context) { |
||||
$string = (string) $string; // quick typecast |
||||
|
||||
if ($string == '') return array(); // no attributes |
||||
|
||||
$e = false; |
||||
if ($config->get('Core.CollectErrors')) { |
||||
$e =& $context->get('ErrorCollector'); |
||||
} |
||||
|
||||
// let's see if we can abort as quickly as possible |
||||
// one equal sign, no spaces => one attribute |
||||
$num_equal = substr_count($string, '='); |
||||
$has_space = strpos($string, ' '); |
||||
if ($num_equal === 0 && !$has_space) { |
||||
// bool attribute |
||||
return array($string => $string); |
||||
} elseif ($num_equal === 1 && !$has_space) { |
||||
// only one attribute |
||||
list($key, $quoted_value) = explode('=', $string); |
||||
$quoted_value = trim($quoted_value); |
||||
if (!$key) { |
||||
if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); |
||||
return array(); |
||||
} |
||||
if (!$quoted_value) return array($key => ''); |
||||
$first_char = @$quoted_value[0]; |
||||
$last_char = @$quoted_value[strlen($quoted_value)-1]; |
||||
|
||||
$same_quote = ($first_char == $last_char); |
||||
$open_quote = ($first_char == '"' || $first_char == "'"); |
||||
|
||||
if ( $same_quote && $open_quote) { |
||||
// well behaved |
||||
$value = substr($quoted_value, 1, strlen($quoted_value) - 2); |
||||
} else { |
||||
// not well behaved |
||||
if ($open_quote) { |
||||
if ($e) $e->send(E_ERROR, 'Lexer: Missing end quote'); |
||||
$value = substr($quoted_value, 1); |
||||
} else { |
||||
$value = $quoted_value; |
||||
} |
||||
} |
||||
if ($value === false) $value = ''; |
||||
return array($key => $value); |
||||
} |
||||
|
||||
// setup loop environment |
||||
$array = array(); // return assoc array of attributes |
||||
$cursor = 0; // current position in string (moves forward) |
||||
$size = strlen($string); // size of the string (stays the same) |
||||
|
||||
// if we have unquoted attributes, the parser expects a terminating |
||||
// space, so let's guarantee that there's always a terminating space. |
||||
$string .= ' '; |
||||
|
||||
while(true) { |
||||
|
||||
if ($cursor >= $size) { |
||||
break; |
||||
} |
||||
|
||||
$cursor += ($value = strspn($string, $this->_whitespace, $cursor)); |
||||
// grab the key |
||||
|
||||
$key_begin = $cursor; //we're currently at the start of the key |
||||
|
||||
// scroll past all characters that are the key (not whitespace or =) |
||||
$cursor += strcspn($string, $this->_whitespace . '=', $cursor); |
||||
|
||||
$key_end = $cursor; // now at the end of the key |
||||
|
||||
$key = substr($string, $key_begin, $key_end - $key_begin); |
||||
|
||||
if (!$key) { |
||||
if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); |
||||
$cursor += strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop |
||||
continue; // empty key |
||||
} |
||||
|
||||
// scroll past all whitespace |
||||
$cursor += strspn($string, $this->_whitespace, $cursor); |
||||
|
||||
if ($cursor >= $size) { |
||||
$array[$key] = $key; |
||||
break; |
||||
} |
||||
|
||||
// if the next character is an equal sign, we've got a regular |
||||
// pair, otherwise, it's a bool attribute |
||||
$first_char = @$string[$cursor]; |
||||
|
||||
if ($first_char == '=') { |
||||
// key="value" |
||||
|
||||
$cursor++; |
||||
$cursor += strspn($string, $this->_whitespace, $cursor); |
||||
|
||||
if ($cursor === false) { |
||||
$array[$key] = ''; |
||||
break; |
||||
} |
||||
|
||||
// we might be in front of a quote right now |
||||
|
||||
$char = @$string[$cursor]; |
||||
|
||||
if ($char == '"' || $char == "'") { |
||||
// it's quoted, end bound is $char |
||||
$cursor++; |
||||
$value_begin = $cursor; |
||||
$cursor = strpos($string, $char, $cursor); |
||||
$value_end = $cursor; |
||||
} else { |
||||
// it's not quoted, end bound is whitespace |
||||
$value_begin = $cursor; |
||||
$cursor += strcspn($string, $this->_whitespace, $cursor); |
||||
$value_end = $cursor; |
||||
} |
||||
|
||||
// we reached a premature end |
||||
if ($cursor === false) { |
||||
$cursor = $size; |
||||
$value_end = $cursor; |
||||
} |
||||
|
||||
$value = substr($string, $value_begin, $value_end - $value_begin); |
||||
if ($value === false) $value = ''; |
||||
$array[$key] = $this->parseData($value); |
||||
$cursor++; |
||||
|
||||
} else { |
||||
// boolattr |
||||
if ($key !== '') { |
||||
$array[$key] = $key; |
||||
} else { |
||||
// purely theoretical |
||||
if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); |
||||
} |
||||
|
||||
} |
||||
} |
||||
return $array; |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Our in-house implementation of a parser. |
||||
* |
||||
* A pure PHP parser, DirectLex has absolutely no dependencies, making |
||||
* it a reasonably good default for PHP4. Written with efficiency in mind, |
||||
* it can be four times faster than HTMLPurifier_Lexer_PEARSax3, although it |
||||
* pales in comparison to HTMLPurifier_Lexer_DOMLex. |
||||
* |
||||
* @todo Reread XML spec and document differences. |
||||
*/ |
||||
class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer |
||||
{ |
||||
|
||||
public $tracksLineNumbers = true; |
||||
|
||||
/** |
||||
* Whitespace characters for str(c)spn. |
||||
*/ |
||||
protected $_whitespace = "\x20\x09\x0D\x0A"; |
||||
|
||||
/** |
||||
* Callback function for script CDATA fudge |
||||
* @param $matches, in form of array(opening tag, contents, closing tag) |
||||
*/ |
||||
protected function scriptCallback($matches) { |
||||
return $matches[1] . htmlspecialchars($matches[2], ENT_COMPAT, 'UTF-8') . $matches[3]; |
||||
} |
||||
|
||||
public function tokenizeHTML($html, $config, $context) { |
||||
|
||||
// special normalization for script tags without any armor |
||||
// our "armor" heurstic is a < sign any number of whitespaces after |
||||
// the first script tag |
||||
if ($config->get('HTML.Trusted')) { |
||||
$html = preg_replace_callback('#(<script[^>]*>)(\s*[^<].+?)(</script>)#si', |
||||
array($this, 'scriptCallback'), $html); |
||||
} |
||||
|
||||
$html = $this->normalize($html, $config, $context); |
||||
|
||||
$cursor = 0; // our location in the text |
||||
$inside_tag = false; // whether or not we're parsing the inside of a tag |
||||
$array = array(); // result array |
||||
|
||||
// This is also treated to mean maintain *column* numbers too |
||||
$maintain_line_numbers = $config->get('Core.MaintainLineNumbers'); |
||||
|
||||
if ($maintain_line_numbers === null) { |
||||
// automatically determine line numbering by checking |
||||
// if error collection is on |
||||
$maintain_line_numbers = $config->get('Core.CollectErrors'); |
||||
} |
||||
|
||||
if ($maintain_line_numbers) { |
||||
$current_line = 1; |
||||
$current_col = 0; |
||||
$length = strlen($html); |
||||
} else { |
||||
$current_line = false; |
||||
$current_col = false; |
||||
$length = false; |
||||
} |
||||
$context->register('CurrentLine', $current_line); |
||||
$context->register('CurrentCol', $current_col); |
||||
$nl = "\n"; |
||||
// how often to manually recalculate. This will ALWAYS be right, |
||||
// but it's pretty wasteful. Set to 0 to turn off |
||||
$synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval'); |
||||
|
||||
$e = false; |
||||
if ($config->get('Core.CollectErrors')) { |
||||
$e =& $context->get('ErrorCollector'); |
||||
} |
||||
|
||||
// for testing synchronization |
||||
$loops = 0; |
||||
|
||||
while(++$loops) { |
||||
|
||||
// $cursor is either at the start of a token, or inside of |
||||
// a tag (i.e. there was a < immediately before it), as indicated |
||||
// by $inside_tag |
||||
|
||||
if ($maintain_line_numbers) { |
||||
|
||||
// $rcursor, however, is always at the start of a token. |
||||
$rcursor = $cursor - (int) $inside_tag; |
||||
|
||||
// Column number is cheap, so we calculate it every round. |
||||
// We're interested at the *end* of the newline string, so |
||||
// we need to add strlen($nl) == 1 to $nl_pos before subtracting it |
||||
// from our "rcursor" position. |
||||
$nl_pos = strrpos($html, $nl, $rcursor - $length); |
||||
$current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1); |
||||
|
||||
// recalculate lines |
||||
if ( |
||||
$synchronize_interval && // synchronization is on |
||||
$cursor > 0 && // cursor is further than zero |
||||
$loops % $synchronize_interval === 0 // time to synchronize! |
||||
) { |
||||
$current_line = 1 + $this->substrCount($html, $nl, 0, $cursor); |
||||
} |
||||
|
||||
} |
||||
|
||||
$position_next_lt = strpos($html, '<', $cursor); |
||||
$position_next_gt = strpos($html, '>', $cursor); |
||||
|
||||
// triggers on "<b>asdf</b>" but not "asdf <b></b>" |
||||
// special case to set up context |
||||
if ($position_next_lt === $cursor) { |
||||
$inside_tag = true; |
||||
$cursor++; |
||||
} |
||||
|
||||
if (!$inside_tag && $position_next_lt !== false) { |
||||
// We are not inside tag and there still is another tag to parse |
||||
$token = new |
||||
HTMLPurifier_Token_Text( |
||||
$this->parseData( |
||||
substr( |
||||
$html, $cursor, $position_next_lt - $cursor |
||||
) |
||||
) |
||||
); |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor); |
||||
} |
||||
$array[] = $token; |
||||
$cursor = $position_next_lt + 1; |
||||
$inside_tag = true; |
||||
continue; |
||||
} elseif (!$inside_tag) { |
||||
// We are not inside tag but there are no more tags |
||||
// If we're already at the end, break |
||||
if ($cursor === strlen($html)) break; |
||||
// Create Text of rest of string |
||||
$token = new |
||||
HTMLPurifier_Token_Text( |
||||
$this->parseData( |
||||
substr( |
||||
$html, $cursor |
||||
) |
||||
) |
||||
); |
||||
if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); |
||||
$array[] = $token; |
||||
break; |
||||
} elseif ($inside_tag && $position_next_gt !== false) { |
||||
// We are in tag and it is well formed |
||||
// Grab the internals of the tag |
||||
$strlen_segment = $position_next_gt - $cursor; |
||||
|
||||
if ($strlen_segment < 1) { |
||||
// there's nothing to process! |
||||
$token = new HTMLPurifier_Token_Text('<'); |
||||
$cursor++; |
||||
continue; |
||||
} |
||||
|
||||
$segment = substr($html, $cursor, $strlen_segment); |
||||
|
||||
if ($segment === false) { |
||||
// somehow, we attempted to access beyond the end of |
||||
// the string, defense-in-depth, reported by Nate Abele |
||||
break; |
||||
} |
||||
|
||||
// Check if it's a comment |
||||
if ( |
||||
substr($segment, 0, 3) === '!--' |
||||
) { |
||||
// re-determine segment length, looking for --> |
||||
$position_comment_end = strpos($html, '-->', $cursor); |
||||
if ($position_comment_end === false) { |
||||
// uh oh, we have a comment that extends to |
||||
// infinity. Can't be helped: set comment |
||||
// end position to end of string |
||||
if ($e) $e->send(E_WARNING, 'Lexer: Unclosed comment'); |
||||
$position_comment_end = strlen($html); |
||||
$end = true; |
||||
} else { |
||||
$end = false; |
||||
} |
||||
$strlen_segment = $position_comment_end - $cursor; |
||||
$segment = substr($html, $cursor, $strlen_segment); |
||||
$token = new |
||||
HTMLPurifier_Token_Comment( |
||||
substr( |
||||
$segment, 3, $strlen_segment - 3 |
||||
) |
||||
); |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment); |
||||
} |
||||
$array[] = $token; |
||||
$cursor = $end ? $position_comment_end : $position_comment_end + 3; |
||||
$inside_tag = false; |
||||
continue; |
||||
} |
||||
|
||||
// Check if it's an end tag |
||||
$is_end_tag = (strpos($segment,'/') === 0); |
||||
if ($is_end_tag) { |
||||
$type = substr($segment, 1); |
||||
$token = new HTMLPurifier_Token_End($type); |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); |
||||
} |
||||
$array[] = $token; |
||||
$inside_tag = false; |
||||
$cursor = $position_next_gt + 1; |
||||
continue; |
||||
} |
||||
|
||||
// Check leading character is alnum, if not, we may |
||||
// have accidently grabbed an emoticon. Translate into |
||||
// text and go our merry way |
||||
if (!ctype_alpha($segment[0])) { |
||||
// XML: $segment[0] !== '_' && $segment[0] !== ':' |
||||
if ($e) $e->send(E_NOTICE, 'Lexer: Unescaped lt'); |
||||
$token = new HTMLPurifier_Token_Text('<'); |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); |
||||
} |
||||
$array[] = $token; |
||||
$inside_tag = false; |
||||
continue; |
||||
} |
||||
|
||||
// Check if it is explicitly self closing, if so, remove |
||||
// trailing slash. Remember, we could have a tag like <br>, so |
||||
// any later token processing scripts must convert improperly |
||||
// classified EmptyTags from StartTags. |
||||
$is_self_closing = (strrpos($segment,'/') === $strlen_segment-1); |
||||
if ($is_self_closing) { |
||||
$strlen_segment--; |
||||
$segment = substr($segment, 0, $strlen_segment); |
||||
} |
||||
|
||||
// Check if there are any attributes |
||||
$position_first_space = strcspn($segment, $this->_whitespace); |
||||
|
||||
if ($position_first_space >= $strlen_segment) { |
||||
if ($is_self_closing) { |
||||
$token = new HTMLPurifier_Token_Empty($segment); |
||||
} else { |
||||
$token = new HTMLPurifier_Token_Start($segment); |
||||
} |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); |
||||
} |
||||
$array[] = $token; |
||||
$inside_tag = false; |
||||
$cursor = $position_next_gt + 1; |
||||
continue; |
||||
} |
||||
|
||||
// Grab out all the data |
||||
$type = substr($segment, 0, $position_first_space); |
||||
$attribute_string = |
||||
trim( |
||||
substr( |
||||
$segment, $position_first_space |
||||
) |
||||
); |
||||
if ($attribute_string) { |
||||
$attr = $this->parseAttributeString( |
||||
$attribute_string |
||||
, $config, $context |
||||
); |
||||
} else { |
||||
$attr = array(); |
||||
} |
||||
|
||||
if ($is_self_closing) { |
||||
$token = new HTMLPurifier_Token_Empty($type, $attr); |
||||
} else { |
||||
$token = new HTMLPurifier_Token_Start($type, $attr); |
||||
} |
||||
if ($maintain_line_numbers) { |
||||
$token->rawPosition($current_line, $current_col); |
||||
$current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); |
||||
} |
||||
$array[] = $token; |
||||
$cursor = $position_next_gt + 1; |
||||
$inside_tag = false; |
||||
continue; |
||||
} else { |
||||
// inside tag, but there's no ending > sign |
||||
if ($e) $e->send(E_WARNING, 'Lexer: Missing gt'); |
||||
$token = new |
||||
HTMLPurifier_Token_Text( |
||||
'<' . |
||||
$this->parseData( |
||||
substr($html, $cursor) |
||||
) |
||||
); |
||||
if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); |
||||
// no cursor scroll? Hmm... |
||||
$array[] = $token; |
||||
break; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
$context->destroy('CurrentLine'); |
||||
$context->destroy('CurrentCol'); |
||||
return $array; |
||||
} |
||||
|
||||
/** |
||||
* PHP 5.0.x compatible substr_count that implements offset and length |
||||
*/ |
||||
protected function substrCount($haystack, $needle, $offset, $length) { |
||||
static $oldVersion; |
||||
if ($oldVersion === null) { |
||||
$oldVersion = version_compare(PHP_VERSION, '5.1', '<'); |
||||
} |
||||
if ($oldVersion) { |
||||
$haystack = substr($haystack, $offset, $length); |
||||
return substr_count($haystack, $needle); |
||||
} else { |
||||
return substr_count($haystack, $needle, $offset, $length); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Takes the inside of an HTML tag and makes an assoc array of attributes. |
||||
* |
||||
* @param $string Inside of tag excluding name. |
||||
* @returns Assoc array of attributes. |
||||
*/ |
||||
public function parseAttributeString($string, $config, $context) { |
||||
$string = (string) $string; // quick typecast |
||||
|
||||
if ($string == '') return array(); // no attributes |
||||
|
||||
$e = false; |
||||
if ($config->get('Core.CollectErrors')) { |
||||
$e =& $context->get('ErrorCollector'); |
||||
} |
||||
|
||||
// let's see if we can abort as quickly as possible |
||||
// one equal sign, no spaces => one attribute |
||||
$num_equal = substr_count($string, '='); |
||||
$has_space = strpos($string, ' '); |
||||
if ($num_equal === 0 && !$has_space) { |
||||
// bool attribute |
||||
return array($string => $string); |
||||
} elseif ($num_equal === 1 && !$has_space) { |
||||
// only one attribute |
||||
list($key, $quoted_value) = explode('=', $string); |
||||
$quoted_value = trim($quoted_value); |
||||
if (!$key) { |
||||
if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); |
||||
return array(); |
||||
} |
||||
if (!$quoted_value) return array($key => ''); |
||||
$first_char = @$quoted_value[0]; |
||||
$last_char = @$quoted_value[strlen($quoted_value)-1]; |
||||
|
||||
$same_quote = ($first_char == $last_char); |
||||
$open_quote = ($first_char == '"' || $first_char == "'"); |
||||
|
||||
if ( $same_quote && $open_quote) { |
||||
// well behaved |
||||
$value = substr($quoted_value, 1, strlen($quoted_value) - 2); |
||||
} else { |
||||
// not well behaved |
||||
if ($open_quote) { |
||||
if ($e) $e->send(E_ERROR, 'Lexer: Missing end quote'); |
||||
$value = substr($quoted_value, 1); |
||||
} else { |
||||
$value = $quoted_value; |
||||
} |
||||
} |
||||
if ($value === false) $value = ''; |
||||
return array($key => $this->parseData($value)); |
||||
} |
||||
|
||||
// setup loop environment |
||||
$array = array(); // return assoc array of attributes |
||||
$cursor = 0; // current position in string (moves forward) |
||||
$size = strlen($string); // size of the string (stays the same) |
||||
|
||||
// if we have unquoted attributes, the parser expects a terminating |
||||
// space, so let's guarantee that there's always a terminating space. |
||||
$string .= ' '; |
||||
|
||||
while(true) { |
||||
|
||||
if ($cursor >= $size) { |
||||
break; |
||||
} |
||||
|
||||
$cursor += ($value = strspn($string, $this->_whitespace, $cursor)); |
||||
// grab the key |
||||
|
||||
$key_begin = $cursor; //we're currently at the start of the key |
||||
|
||||
// scroll past all characters that are the key (not whitespace or =) |
||||
$cursor += strcspn($string, $this->_whitespace . '=', $cursor); |
||||
|
||||
$key_end = $cursor; // now at the end of the key |
||||
|
||||
$key = substr($string, $key_begin, $key_end - $key_begin); |
||||
|
||||
if (!$key) { |
||||
if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); |
||||
$cursor += strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop |
||||
continue; // empty key |
||||
} |
||||
|
||||
// scroll past all whitespace |
||||
$cursor += strspn($string, $this->_whitespace, $cursor); |
||||
|
||||
if ($cursor >= $size) { |
||||
$array[$key] = $key; |
||||
break; |
||||
} |
||||
|
||||
// if the next character is an equal sign, we've got a regular |
||||
// pair, otherwise, it's a bool attribute |
||||
$first_char = @$string[$cursor]; |
||||
|
||||
if ($first_char == '=') { |
||||
// key="value" |
||||
|
||||
$cursor++; |
||||
$cursor += strspn($string, $this->_whitespace, $cursor); |
||||
|
||||
if ($cursor === false) { |
||||
$array[$key] = ''; |
||||
break; |
||||
} |
||||
|
||||
// we might be in front of a quote right now |
||||
|
||||
$char = @$string[$cursor]; |
||||
|
||||
if ($char == '"' || $char == "'") { |
||||
// it's quoted, end bound is $char |
||||
$cursor++; |
||||
$value_begin = $cursor; |
||||
$cursor = strpos($string, $char, $cursor); |
||||
$value_end = $cursor; |
||||
} else { |
||||
// it's not quoted, end bound is whitespace |
||||
$value_begin = $cursor; |
||||
$cursor += strcspn($string, $this->_whitespace, $cursor); |
||||
$value_end = $cursor; |
||||
} |
||||
|
||||
// we reached a premature end |
||||
if ($cursor === false) { |
||||
$cursor = $size; |
||||
$value_end = $cursor; |
||||
} |
||||
|
||||
$value = substr($string, $value_begin, $value_end - $value_begin); |
||||
if ($value === false) $value = ''; |
||||
$array[$key] = $this->parseData($value); |
||||
$cursor++; |
||||
|
||||
} else { |
||||
// boolattr |
||||
if ($key !== '') { |
||||
$array[$key] = $key; |
||||
} else { |
||||
// purely theoretical |
||||
if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); |
||||
} |
||||
|
||||
} |
||||
} |
||||
return $array; |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,471 +1,475 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Takes tokens makes them well-formed (balance end tags, etc.) |
||||
*/ |
||||
class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy |
||||
{ |
||||
|
||||
/** |
||||
* Array stream of tokens being processed. |
||||
*/ |
||||
protected $tokens; |
||||
|
||||
/** |
||||
* Current index in $tokens. |
||||
*/ |
||||
protected $t; |
||||
|
||||
/** |
||||
* Current nesting of elements. |
||||
*/ |
||||
protected $stack; |
||||
|
||||
/** |
||||
* Injectors active in this stream processing. |
||||
*/ |
||||
protected $injectors; |
||||
|
||||
/** |
||||
* Current instance of HTMLPurifier_Config. |
||||
*/ |
||||
protected $config; |
||||
|
||||
/** |
||||
* Current instance of HTMLPurifier_Context. |
||||
*/ |
||||
protected $context; |
||||
|
||||
public function execute($tokens, $config, $context) { |
||||
|
||||
$definition = $config->getHTMLDefinition(); |
||||
|
||||
// local variables |
||||
$generator = new HTMLPurifier_Generator($config, $context); |
||||
$escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); |
||||
$e = $context->get('ErrorCollector', true); |
||||
$t = false; // token index |
||||
$i = false; // injector index |
||||
$token = false; // the current token |
||||
$reprocess = false; // whether or not to reprocess the same token |
||||
$stack = array(); |
||||
|
||||
// member variables |
||||
$this->stack =& $stack; |
||||
$this->t =& $t; |
||||
$this->tokens =& $tokens; |
||||
$this->config = $config; |
||||
$this->context = $context; |
||||
|
||||
// context variables |
||||
$context->register('CurrentNesting', $stack); |
||||
$context->register('InputIndex', $t); |
||||
$context->register('InputTokens', $tokens); |
||||
$context->register('CurrentToken', $token); |
||||
|
||||
// -- begin INJECTOR -- |
||||
|
||||
$this->injectors = array(); |
||||
|
||||
$injectors = $config->getBatch('AutoFormat'); |
||||
$def_injectors = $definition->info_injector; |
||||
$custom_injectors = $injectors['Custom']; |
||||
unset($injectors['Custom']); // special case |
||||
foreach ($injectors as $injector => $b) { |
||||
// XXX: Fix with a legitimate lookup table of enabled filters |
||||
if (strpos($injector, '.') !== false) continue; |
||||
$injector = "HTMLPurifier_Injector_$injector"; |
||||
if (!$b) continue; |
||||
$this->injectors[] = new $injector; |
||||
} |
||||
foreach ($def_injectors as $injector) { |
||||
// assumed to be objects |
||||
$this->injectors[] = $injector; |
||||
} |
||||
foreach ($custom_injectors as $injector) { |
||||
if (!$injector) continue; |
||||
if (is_string($injector)) { |
||||
$injector = "HTMLPurifier_Injector_$injector"; |
||||
$injector = new $injector; |
||||
} |
||||
$this->injectors[] = $injector; |
||||
} |
||||
|
||||
// give the injectors references to the definition and context |
||||
// variables for performance reasons |
||||
foreach ($this->injectors as $ix => $injector) { |
||||
$error = $injector->prepare($config, $context); |
||||
if (!$error) continue; |
||||
array_splice($this->injectors, $ix, 1); // rm the injector |
||||
trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING); |
||||
} |
||||
|
||||
// -- end INJECTOR -- |
||||
|
||||
// a note on punting: |
||||
// In order to reduce code duplication, whenever some code needs |
||||
// to make HTML changes in order to make things "correct", the |
||||
// new HTML gets sent through the purifier, regardless of its |
||||
// status. This means that if we add a start token, because it |
||||
// was totally necessary, we don't have to update nesting; we just |
||||
// punt ($reprocess = true; continue;) and it does that for us. |
||||
|
||||
// isset is in loop because $tokens size changes during loop exec |
||||
for ( |
||||
$t = 0; |
||||
$t == 0 || isset($tokens[$t - 1]); |
||||
// only increment if we don't need to reprocess |
||||
$reprocess ? $reprocess = false : $t++ |
||||
) { |
||||
|
||||
// check for a rewind |
||||
if (is_int($i) && $i >= 0) { |
||||
// possibility: disable rewinding if the current token has a |
||||
// rewind set on it already. This would offer protection from |
||||
// infinite loop, but might hinder some advanced rewinding. |
||||
$rewind_to = $this->injectors[$i]->getRewind(); |
||||
if (is_int($rewind_to) && $rewind_to < $t) { |
||||
if ($rewind_to < 0) $rewind_to = 0; |
||||
while ($t > $rewind_to) { |
||||
$t--; |
||||
$prev = $tokens[$t]; |
||||
// indicate that other injectors should not process this token, |
||||
// but we need to reprocess it |
||||
unset($prev->skip[$i]); |
||||
$prev->rewind = $i; |
||||
if ($prev instanceof HTMLPurifier_Token_Start) array_pop($this->stack); |
||||
elseif ($prev instanceof HTMLPurifier_Token_End) $this->stack[] = $prev->start; |
||||
} |
||||
} |
||||
$i = false; |
||||
} |
||||
|
||||
// handle case of document end |
||||
if (!isset($tokens[$t])) { |
||||
// kill processing if stack is empty |
||||
if (empty($this->stack)) break; |
||||
|
||||
// peek |
||||
$top_nesting = array_pop($this->stack); |
||||
$this->stack[] = $top_nesting; |
||||
|
||||
// send error |
||||
if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) { |
||||
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting); |
||||
} |
||||
|
||||
// append, don't splice, since this is the end |
||||
$tokens[] = new HTMLPurifier_Token_End($top_nesting->name); |
||||
|
||||
// punt! |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
|
||||
$token = $tokens[$t]; |
||||
|
||||
//echo '<br>'; printTokens($tokens, $t); printTokens($this->stack); |
||||
|
||||
// quick-check: if it's not a tag, no need to process |
||||
if (empty($token->is_tag)) { |
||||
if ($token instanceof HTMLPurifier_Token_Text) { |
||||
foreach ($this->injectors as $i => $injector) { |
||||
if (isset($token->skip[$i])) continue; |
||||
if ($token->rewind !== null && $token->rewind !== $i) continue; |
||||
$injector->handleText($token); |
||||
$this->processToken($token, $i); |
||||
$reprocess = true; |
||||
break; |
||||
} |
||||
} |
||||
// another possibility is a comment |
||||
continue; |
||||
} |
||||
|
||||
if (isset($definition->info[$token->name])) { |
||||
$type = $definition->info[$token->name]->child->type; |
||||
} else { |
||||
$type = false; // Type is unknown, treat accordingly |
||||
} |
||||
|
||||
// quick tag checks: anything that's *not* an end tag |
||||
$ok = false; |
||||
if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) { |
||||
// claims to be a start tag but is empty |
||||
$token = new HTMLPurifier_Token_Empty($token->name, $token->attr); |
||||
$ok = true; |
||||
} elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) { |
||||
// claims to be empty but really is a start tag |
||||
$this->swap(new HTMLPurifier_Token_End($token->name)); |
||||
$this->insertBefore(new HTMLPurifier_Token_Start($token->name, $token->attr)); |
||||
// punt (since we had to modify the input stream in a non-trivial way) |
||||
$reprocess = true; |
||||
continue; |
||||
} elseif ($token instanceof HTMLPurifier_Token_Empty) { |
||||
// real empty token |
||||
$ok = true; |
||||
} elseif ($token instanceof HTMLPurifier_Token_Start) { |
||||
// start tag |
||||
|
||||
// ...unless they also have to close their parent |
||||
if (!empty($this->stack)) { |
||||
|
||||
$parent = array_pop($this->stack); |
||||
$this->stack[] = $parent; |
||||
|
||||
if (isset($definition->info[$parent->name])) { |
||||
$elements = $definition->info[$parent->name]->child->getAllowedElements($config); |
||||
$autoclose = !isset($elements[$token->name]); |
||||
} else { |
||||
$autoclose = false; |
||||
} |
||||
|
||||
if ($autoclose && $definition->info[$token->name]->wrap) { |
||||
// check if this is actually a wrap (mmm wraps!) |
||||
$wrapname = $definition->info[$token->name]->wrap; |
||||
$wrapdef = $definition->info[$wrapname]; |
||||
$elements = $wrapdef->child->getAllowedElements($config); |
||||
if (isset($elements[$token->name])) { |
||||
$newtoken = new HTMLPurifier_Token_Start($wrapname); |
||||
$this->insertBefore($newtoken); |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
$carryover = false; |
||||
if ($autoclose && $definition->info[$parent->name]->formatting) { |
||||
$carryover = true; |
||||
} |
||||
|
||||
if ($autoclose) { |
||||
// errors need to be updated |
||||
$new_token = new HTMLPurifier_Token_End($parent->name); |
||||
$new_token->start = $parent; |
||||
if ($carryover) { |
||||
$element = clone $parent; |
||||
$element->armor['MakeWellFormed_TagClosedError'] = true; |
||||
$element->carryover = true; |
||||
$this->processToken(array($new_token, $token, $element)); |
||||
} else { |
||||
$this->insertBefore($new_token); |
||||
} |
||||
if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) { |
||||
if (!$carryover) { |
||||
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent); |
||||
} else { |
||||
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent); |
||||
} |
||||
} |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
|
||||
} |
||||
$ok = true; |
||||
} |
||||
|
||||
if ($ok) { |
||||
foreach ($this->injectors as $i => $injector) { |
||||
if (isset($token->skip[$i])) continue; |
||||
if ($token->rewind !== null && $token->rewind !== $i) continue; |
||||
$injector->handleElement($token); |
||||
$this->processToken($token, $i); |
||||
$reprocess = true; |
||||
break; |
||||
} |
||||
if (!$reprocess) { |
||||
// ah, nothing interesting happened; do normal processing |
||||
$this->swap($token); |
||||
if ($token instanceof HTMLPurifier_Token_Start) { |
||||
$this->stack[] = $token; |
||||
} elseif ($token instanceof HTMLPurifier_Token_End) { |
||||
throw new HTMLPurifier_Exception('Improper handling of end tag in start code; possible error in MakeWellFormed'); |
||||
} |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
// sanity check: we should be dealing with a closing tag |
||||
if (!$token instanceof HTMLPurifier_Token_End) { |
||||
throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier'); |
||||
} |
||||
|
||||
// make sure that we have something open |
||||
if (empty($this->stack)) { |
||||
if ($escape_invalid_tags) { |
||||
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); |
||||
$this->swap(new HTMLPurifier_Token_Text( |
||||
$generator->generateFromToken($token) |
||||
)); |
||||
} else { |
||||
$this->remove(); |
||||
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); |
||||
} |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
|
||||
// first, check for the simplest case: everything closes neatly. |
||||
// Eventually, everything passes through here; if there are problems |
||||
// we modify the input stream accordingly and then punt, so that |
||||
// the tokens get processed again. |
||||
$current_parent = array_pop($this->stack); |
||||
if ($current_parent->name == $token->name) { |
||||
$token->start = $current_parent; |
||||
foreach ($this->injectors as $i => $injector) { |
||||
if (isset($token->skip[$i])) continue; |
||||
if ($token->rewind !== null && $token->rewind !== $i) continue; |
||||
$injector->handleEnd($token); |
||||
$this->processToken($token, $i); |
||||
$this->stack[] = $current_parent; |
||||
$reprocess = true; |
||||
break; |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
// okay, so we're trying to close the wrong tag |
||||
|
||||
// undo the pop previous pop |
||||
$this->stack[] = $current_parent; |
||||
|
||||
// scroll back the entire nest, trying to find our tag. |
||||
// (feature could be to specify how far you'd like to go) |
||||
$size = count($this->stack); |
||||
// -2 because -1 is the last element, but we already checked that |
||||
$skipped_tags = false; |
||||
for ($j = $size - 2; $j >= 0; $j--) { |
||||
if ($this->stack[$j]->name == $token->name) { |
||||
$skipped_tags = array_slice($this->stack, $j); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// we didn't find the tag, so remove |
||||
if ($skipped_tags === false) { |
||||
if ($escape_invalid_tags) { |
||||
$this->swap(new HTMLPurifier_Token_Text( |
||||
$generator->generateFromToken($token) |
||||
)); |
||||
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); |
||||
} else { |
||||
$this->remove(); |
||||
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); |
||||
} |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
|
||||
// do errors, in REVERSE $j order: a,b,c with </a></b></c> |
||||
$c = count($skipped_tags); |
||||
if ($e) { |
||||
for ($j = $c - 1; $j > 0; $j--) { |
||||
// notice we exclude $j == 0, i.e. the current ending tag, from |
||||
// the errors... |
||||
if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) { |
||||
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// insert tags, in FORWARD $j order: c,b,a with </a></b></c> |
||||
$replace = array($token); |
||||
for ($j = 1; $j < $c; $j++) { |
||||
// ...as well as from the insertions |
||||
$new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name); |
||||
$new_token->start = $skipped_tags[$j]; |
||||
array_unshift($replace, $new_token); |
||||
if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) { |
||||
$element = clone $skipped_tags[$j]; |
||||
$element->carryover = true; |
||||
$element->armor['MakeWellFormed_TagClosedError'] = true; |
||||
$replace[] = $element; |
||||
} |
||||
} |
||||
$this->processToken($replace); |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
|
||||
$context->destroy('CurrentNesting'); |
||||
$context->destroy('InputTokens'); |
||||
$context->destroy('InputIndex'); |
||||
$context->destroy('CurrentToken'); |
||||
|
||||
unset($this->injectors, $this->stack, $this->tokens, $this->t); |
||||
return $tokens; |
||||
} |
||||
|
||||
/** |
||||
* Processes arbitrary token values for complicated substitution patterns. |
||||
* In general: |
||||
* |
||||
* If $token is an array, it is a list of tokens to substitute for the |
||||
* current token. These tokens then get individually processed. If there |
||||
* is a leading integer in the list, that integer determines how many |
||||
* tokens from the stream should be removed. |
||||
* |
||||
* If $token is a regular token, it is swapped with the current token. |
||||
* |
||||
* If $token is false, the current token is deleted. |
||||
* |
||||
* If $token is an integer, that number of tokens (with the first token |
||||
* being the current one) will be deleted. |
||||
* |
||||
* @param $token Token substitution value |
||||
* @param $injector Injector that performed the substitution; default is if |
||||
* this is not an injector related operation. |
||||
*/ |
||||
protected function processToken($token, $injector = -1) { |
||||
|
||||
// normalize forms of token |
||||
if (is_object($token)) $token = array(1, $token); |
||||
if (is_int($token)) $token = array($token); |
||||
if ($token === false) $token = array(1); |
||||
if (!is_array($token)) throw new HTMLPurifier_Exception('Invalid token type from injector'); |
||||
if (!is_int($token[0])) array_unshift($token, 1); |
||||
if ($token[0] === 0) throw new HTMLPurifier_Exception('Deleting zero tokens is not valid'); |
||||
|
||||
// $token is now an array with the following form: |
||||
// array(number nodes to delete, new node 1, new node 2, ...) |
||||
|
||||
$delete = array_shift($token); |
||||
$old = array_splice($this->tokens, $this->t, $delete, $token); |
||||
|
||||
if ($injector > -1) { |
||||
// determine appropriate skips |
||||
$oldskip = isset($old[0]) ? $old[0]->skip : array(); |
||||
foreach ($token as $object) { |
||||
$object->skip = $oldskip; |
||||
$object->skip[$injector] = true; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Inserts a token before the current token. Cursor now points to this token |
||||
*/ |
||||
private function insertBefore($token) { |
||||
array_splice($this->tokens, $this->t, 0, array($token)); |
||||
} |
||||
|
||||
/** |
||||
* Removes current token. Cursor now points to new token occupying previously |
||||
* occupied space. |
||||
*/ |
||||
private function remove() { |
||||
array_splice($this->tokens, $this->t, 1); |
||||
} |
||||
|
||||
/** |
||||
* Swap current token with new token. Cursor points to new token (no change). |
||||
*/ |
||||
private function swap($token) { |
||||
$this->tokens[$this->t] = $token; |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Takes tokens makes them well-formed (balance end tags, etc.) |
||||
*/ |
||||
class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy |
||||
{ |
||||
|
||||
/** |
||||
* Array stream of tokens being processed. |
||||
*/ |
||||
protected $tokens; |
||||
|
||||
/** |
||||
* Current index in $tokens. |
||||
*/ |
||||
protected $t; |
||||
|
||||
/** |
||||
* Current nesting of elements. |
||||
*/ |
||||
protected $stack; |
||||
|
||||
/** |
||||
* Injectors active in this stream processing. |
||||
*/ |
||||
protected $injectors; |
||||
|
||||
/** |
||||
* Current instance of HTMLPurifier_Config. |
||||
*/ |
||||
protected $config; |
||||
|
||||
/** |
||||
* Current instance of HTMLPurifier_Context. |
||||
*/ |
||||
protected $context; |
||||
|
||||
public function execute($tokens, $config, $context) { |
||||
|
||||
$definition = $config->getHTMLDefinition(); |
||||
|
||||
// local variables |
||||
$generator = new HTMLPurifier_Generator($config, $context); |
||||
$escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); |
||||
$e = $context->get('ErrorCollector', true); |
||||
$t = false; // token index |
||||
$i = false; // injector index |
||||
$token = false; // the current token |
||||
$reprocess = false; // whether or not to reprocess the same token |
||||
$stack = array(); |
||||
|
||||
// member variables |
||||
$this->stack =& $stack; |
||||
$this->t =& $t; |
||||
$this->tokens =& $tokens; |
||||
$this->config = $config; |
||||
$this->context = $context; |
||||
|
||||
// context variables |
||||
$context->register('CurrentNesting', $stack); |
||||
$context->register('InputIndex', $t); |
||||
$context->register('InputTokens', $tokens); |
||||
$context->register('CurrentToken', $token); |
||||
|
||||
// -- begin INJECTOR -- |
||||
|
||||
$this->injectors = array(); |
||||
|
||||
$injectors = $config->getBatch('AutoFormat'); |
||||
$def_injectors = $definition->info_injector; |
||||
$custom_injectors = $injectors['Custom']; |
||||
unset($injectors['Custom']); // special case |
||||
foreach ($injectors as $injector => $b) { |
||||
// XXX: Fix with a legitimate lookup table of enabled filters |
||||
if (strpos($injector, '.') !== false) continue; |
||||
$injector = "HTMLPurifier_Injector_$injector"; |
||||
if (!$b) continue; |
||||
$this->injectors[] = new $injector; |
||||
} |
||||
foreach ($def_injectors as $injector) { |
||||
// assumed to be objects |
||||
$this->injectors[] = $injector; |
||||
} |
||||
foreach ($custom_injectors as $injector) { |
||||
if (!$injector) continue; |
||||
if (is_string($injector)) { |
||||
$injector = "HTMLPurifier_Injector_$injector"; |
||||
$injector = new $injector; |
||||
} |
||||
$this->injectors[] = $injector; |
||||
} |
||||
|
||||
// give the injectors references to the definition and context |
||||
// variables for performance reasons |
||||
foreach ($this->injectors as $ix => $injector) { |
||||
$error = $injector->prepare($config, $context); |
||||
if (!$error) continue; |
||||
array_splice($this->injectors, $ix, 1); // rm the injector |
||||
trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING); |
||||
} |
||||
|
||||
// -- end INJECTOR -- |
||||
|
||||
// a note on punting: |
||||
// In order to reduce code duplication, whenever some code needs |
||||
// to make HTML changes in order to make things "correct", the |
||||
// new HTML gets sent through the purifier, regardless of its |
||||
// status. This means that if we add a start token, because it |
||||
// was totally necessary, we don't have to update nesting; we just |
||||
// punt ($reprocess = true; continue;) and it does that for us. |
||||
|
||||
// isset is in loop because $tokens size changes during loop exec |
||||
for ( |
||||
$t = 0; |
||||
$t == 0 || isset($tokens[$t - 1]); |
||||
// only increment if we don't need to reprocess |
||||
$reprocess ? $reprocess = false : $t++ |
||||
) { |
||||
|
||||
// check for a rewind |
||||
if (is_int($i) && $i >= 0) { |
||||
// possibility: disable rewinding if the current token has a |
||||
// rewind set on it already. This would offer protection from |
||||
// infinite loop, but might hinder some advanced rewinding. |
||||
$rewind_to = $this->injectors[$i]->getRewind(); |
||||
if (is_int($rewind_to) && $rewind_to < $t) { |
||||
if ($rewind_to < 0) $rewind_to = 0; |
||||
while ($t > $rewind_to) { |
||||
$t--; |
||||
$prev = $tokens[$t]; |
||||
// indicate that other injectors should not process this token, |
||||
// but we need to reprocess it |
||||
unset($prev->skip[$i]); |
||||
$prev->rewind = $i; |
||||
if ($prev instanceof HTMLPurifier_Token_Start) array_pop($this->stack); |
||||
elseif ($prev instanceof HTMLPurifier_Token_End) $this->stack[] = $prev->start; |
||||
} |
||||
} |
||||
$i = false; |
||||
} |
||||
|
||||
// handle case of document end |
||||
if (!isset($tokens[$t])) { |
||||
// kill processing if stack is empty |
||||
if (empty($this->stack)) break; |
||||
|
||||
// peek |
||||
$top_nesting = array_pop($this->stack); |
||||
$this->stack[] = $top_nesting; |
||||
|
||||
// send error |
||||
if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) { |
||||
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting); |
||||
} |
||||
|
||||
// append, don't splice, since this is the end |
||||
$tokens[] = new HTMLPurifier_Token_End($top_nesting->name); |
||||
|
||||
// punt! |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
|
||||
$token = $tokens[$t]; |
||||
|
||||
//echo '<br>'; printTokens($tokens, $t); printTokens($this->stack); |
||||
//flush(); |
||||
|
||||
// quick-check: if it's not a tag, no need to process |
||||
if (empty($token->is_tag)) { |
||||
if ($token instanceof HTMLPurifier_Token_Text) { |
||||
foreach ($this->injectors as $i => $injector) { |
||||
if (isset($token->skip[$i])) continue; |
||||
if ($token->rewind !== null && $token->rewind !== $i) continue; |
||||
$injector->handleText($token); |
||||
$this->processToken($token, $i); |
||||
$reprocess = true; |
||||
break; |
||||
} |
||||
} |
||||
// another possibility is a comment |
||||
continue; |
||||
} |
||||
|
||||
if (isset($definition->info[$token->name])) { |
||||
$type = $definition->info[$token->name]->child->type; |
||||
} else { |
||||
$type = false; // Type is unknown, treat accordingly |
||||
} |
||||
|
||||
// quick tag checks: anything that's *not* an end tag |
||||
$ok = false; |
||||
if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) { |
||||
// claims to be a start tag but is empty |
||||
$token = new HTMLPurifier_Token_Empty($token->name, $token->attr); |
||||
$ok = true; |
||||
} elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) { |
||||
// claims to be empty but really is a start tag |
||||
$this->swap(new HTMLPurifier_Token_End($token->name)); |
||||
$this->insertBefore(new HTMLPurifier_Token_Start($token->name, $token->attr)); |
||||
// punt (since we had to modify the input stream in a non-trivial way) |
||||
$reprocess = true; |
||||
continue; |
||||
} elseif ($token instanceof HTMLPurifier_Token_Empty) { |
||||
// real empty token |
||||
$ok = true; |
||||
} elseif ($token instanceof HTMLPurifier_Token_Start) { |
||||
// start tag |
||||
|
||||
// ...unless they also have to close their parent |
||||
if (!empty($this->stack)) { |
||||
|
||||
$parent = array_pop($this->stack); |
||||
$this->stack[] = $parent; |
||||
|
||||
if (isset($definition->info[$parent->name])) { |
||||
$elements = $definition->info[$parent->name]->child->getAllowedElements($config); |
||||
$autoclose = !isset($elements[$token->name]); |
||||
} else { |
||||
$autoclose = false; |
||||
} |
||||
|
||||
if ($autoclose && $definition->info[$token->name]->wrap) { |
||||
// Check if an element can be wrapped by another |
||||
// element to make it valid in a context (for |
||||
// example, <ul><ul> needs a <li> in between) |
||||
$wrapname = $definition->info[$token->name]->wrap; |
||||
$wrapdef = $definition->info[$wrapname]; |
||||
$elements = $wrapdef->child->getAllowedElements($config); |
||||
$parent_elements = $definition->info[$parent->name]->child->getAllowedElements($config); |
||||
if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) { |
||||
$newtoken = new HTMLPurifier_Token_Start($wrapname); |
||||
$this->insertBefore($newtoken); |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
$carryover = false; |
||||
if ($autoclose && $definition->info[$parent->name]->formatting) { |
||||
$carryover = true; |
||||
} |
||||
|
||||
if ($autoclose) { |
||||
// errors need to be updated |
||||
$new_token = new HTMLPurifier_Token_End($parent->name); |
||||
$new_token->start = $parent; |
||||
if ($carryover) { |
||||
$element = clone $parent; |
||||
$element->armor['MakeWellFormed_TagClosedError'] = true; |
||||
$element->carryover = true; |
||||
$this->processToken(array($new_token, $token, $element)); |
||||
} else { |
||||
$this->insertBefore($new_token); |
||||
} |
||||
if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) { |
||||
if (!$carryover) { |
||||
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent); |
||||
} else { |
||||
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent); |
||||
} |
||||
} |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
|
||||
} |
||||
$ok = true; |
||||
} |
||||
|
||||
if ($ok) { |
||||
foreach ($this->injectors as $i => $injector) { |
||||
if (isset($token->skip[$i])) continue; |
||||
if ($token->rewind !== null && $token->rewind !== $i) continue; |
||||
$injector->handleElement($token); |
||||
$this->processToken($token, $i); |
||||
$reprocess = true; |
||||
break; |
||||
} |
||||
if (!$reprocess) { |
||||
// ah, nothing interesting happened; do normal processing |
||||
$this->swap($token); |
||||
if ($token instanceof HTMLPurifier_Token_Start) { |
||||
$this->stack[] = $token; |
||||
} elseif ($token instanceof HTMLPurifier_Token_End) { |
||||
throw new HTMLPurifier_Exception('Improper handling of end tag in start code; possible error in MakeWellFormed'); |
||||
} |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
// sanity check: we should be dealing with a closing tag |
||||
if (!$token instanceof HTMLPurifier_Token_End) { |
||||
throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier'); |
||||
} |
||||
|
||||
// make sure that we have something open |
||||
if (empty($this->stack)) { |
||||
if ($escape_invalid_tags) { |
||||
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); |
||||
$this->swap(new HTMLPurifier_Token_Text( |
||||
$generator->generateFromToken($token) |
||||
)); |
||||
} else { |
||||
$this->remove(); |
||||
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); |
||||
} |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
|
||||
// first, check for the simplest case: everything closes neatly. |
||||
// Eventually, everything passes through here; if there are problems |
||||
// we modify the input stream accordingly and then punt, so that |
||||
// the tokens get processed again. |
||||
$current_parent = array_pop($this->stack); |
||||
if ($current_parent->name == $token->name) { |
||||
$token->start = $current_parent; |
||||
foreach ($this->injectors as $i => $injector) { |
||||
if (isset($token->skip[$i])) continue; |
||||
if ($token->rewind !== null && $token->rewind !== $i) continue; |
||||
$injector->handleEnd($token); |
||||
$this->processToken($token, $i); |
||||
$this->stack[] = $current_parent; |
||||
$reprocess = true; |
||||
break; |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
// okay, so we're trying to close the wrong tag |
||||
|
||||
// undo the pop previous pop |
||||
$this->stack[] = $current_parent; |
||||
|
||||
// scroll back the entire nest, trying to find our tag. |
||||
// (feature could be to specify how far you'd like to go) |
||||
$size = count($this->stack); |
||||
// -2 because -1 is the last element, but we already checked that |
||||
$skipped_tags = false; |
||||
for ($j = $size - 2; $j >= 0; $j--) { |
||||
if ($this->stack[$j]->name == $token->name) { |
||||
$skipped_tags = array_slice($this->stack, $j); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// we didn't find the tag, so remove |
||||
if ($skipped_tags === false) { |
||||
if ($escape_invalid_tags) { |
||||
$this->swap(new HTMLPurifier_Token_Text( |
||||
$generator->generateFromToken($token) |
||||
)); |
||||
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); |
||||
} else { |
||||
$this->remove(); |
||||
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); |
||||
} |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
|
||||
// do errors, in REVERSE $j order: a,b,c with </a></b></c> |
||||
$c = count($skipped_tags); |
||||
if ($e) { |
||||
for ($j = $c - 1; $j > 0; $j--) { |
||||
// notice we exclude $j == 0, i.e. the current ending tag, from |
||||
// the errors... |
||||
if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) { |
||||
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// insert tags, in FORWARD $j order: c,b,a with </a></b></c> |
||||
$replace = array($token); |
||||
for ($j = 1; $j < $c; $j++) { |
||||
// ...as well as from the insertions |
||||
$new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name); |
||||
$new_token->start = $skipped_tags[$j]; |
||||
array_unshift($replace, $new_token); |
||||
if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) { |
||||
$element = clone $skipped_tags[$j]; |
||||
$element->carryover = true; |
||||
$element->armor['MakeWellFormed_TagClosedError'] = true; |
||||
$replace[] = $element; |
||||
} |
||||
} |
||||
$this->processToken($replace); |
||||
$reprocess = true; |
||||
continue; |
||||
} |
||||
|
||||
$context->destroy('CurrentNesting'); |
||||
$context->destroy('InputTokens'); |
||||
$context->destroy('InputIndex'); |
||||
$context->destroy('CurrentToken'); |
||||
|
||||
unset($this->injectors, $this->stack, $this->tokens, $this->t); |
||||
return $tokens; |
||||
} |
||||
|
||||
/** |
||||
* Processes arbitrary token values for complicated substitution patterns. |
||||
* In general: |
||||
* |
||||
* If $token is an array, it is a list of tokens to substitute for the |
||||
* current token. These tokens then get individually processed. If there |
||||
* is a leading integer in the list, that integer determines how many |
||||
* tokens from the stream should be removed. |
||||
* |
||||
* If $token is a regular token, it is swapped with the current token. |
||||
* |
||||
* If $token is false, the current token is deleted. |
||||
* |
||||
* If $token is an integer, that number of tokens (with the first token |
||||
* being the current one) will be deleted. |
||||
* |
||||
* @param $token Token substitution value |
||||
* @param $injector Injector that performed the substitution; default is if |
||||
* this is not an injector related operation. |
||||
*/ |
||||
protected function processToken($token, $injector = -1) { |
||||
|
||||
// normalize forms of token |
||||
if (is_object($token)) $token = array(1, $token); |
||||
if (is_int($token)) $token = array($token); |
||||
if ($token === false) $token = array(1); |
||||
if (!is_array($token)) throw new HTMLPurifier_Exception('Invalid token type from injector'); |
||||
if (!is_int($token[0])) array_unshift($token, 1); |
||||
if ($token[0] === 0) throw new HTMLPurifier_Exception('Deleting zero tokens is not valid'); |
||||
|
||||
// $token is now an array with the following form: |
||||
// array(number nodes to delete, new node 1, new node 2, ...) |
||||
|
||||
$delete = array_shift($token); |
||||
$old = array_splice($this->tokens, $this->t, $delete, $token); |
||||
|
||||
if ($injector > -1) { |
||||
// determine appropriate skips |
||||
$oldskip = isset($old[0]) ? $old[0]->skip : array(); |
||||
foreach ($token as $object) { |
||||
$object->skip = $oldskip; |
||||
$object->skip[$injector] = true; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Inserts a token before the current token. Cursor now points to this token |
||||
*/ |
||||
private function insertBefore($token) { |
||||
array_splice($this->tokens, $this->t, 0, array($token)); |
||||
} |
||||
|
||||
/** |
||||
* Removes current token. Cursor now points to new token occupying previously |
||||
* occupied space. |
||||
*/ |
||||
private function remove() { |
||||
array_splice($this->tokens, $this->t, 1); |
||||
} |
||||
|
||||
/** |
||||
* Swap current token with new token. Cursor points to new token (no change). |
||||
*/ |
||||
private function swap($token) { |
||||
$this->tokens[$this->t] = $token; |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
class HTMLPurifier_URIFilter_DisableResources extends HTMLPurifier_URIFilter |
||||
{ |
||||
public $name = 'DisableResources'; |
||||
public function filter(&$uri, $config, $context) { |
||||
return !$context->get('EmbeddedURI', true); |
||||
} |
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
@ -0,0 +1,26 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Validates file as defined by RFC 1630 and RFC 1738. |
||||
*/ |
||||
class HTMLPurifier_URIScheme_file extends HTMLPurifier_URIScheme { |
||||
|
||||
// Generally file:// URLs are not accessible from most |
||||
// machines, so placing them as an img src is incorrect. |
||||
public $browsable = false; |
||||
|
||||
public function validate(&$uri, $config, $context) { |
||||
parent::validate($uri, $config, $context); |
||||
// Authentication method is not supported |
||||
$uri->userinfo = null; |
||||
// file:// makes no provisions for accessing the resource |
||||
$uri->port = null; |
||||
// While it seems to work on Firefox, the querystring has |
||||
// no possible effect and is thus stripped. |
||||
$uri->query = null; |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
@ -1,96 +1,103 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* Performs safe variable parsing based on types which can be used by |
||||
* users. This may not be able to represent all possible data inputs, |
||||
* however. |
||||
*/ |
||||
class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser |
||||
{ |
||||
|
||||
protected function parseImplementation($var, $type, $allow_null) { |
||||
if ($allow_null && $var === null) return null; |
||||
switch ($type) { |
||||
// Note: if code "breaks" from the switch, it triggers a generic |
||||
// exception to be thrown. Specific errors can be specifically |
||||
// done here. |
||||
case self::MIXED : |
||||
case self::ISTRING : |
||||
case self::STRING : |
||||
case self::TEXT : |
||||
case self::ITEXT : |
||||
return $var; |
||||
case self::INT : |
||||
if (is_string($var) && ctype_digit($var)) $var = (int) $var; |
||||
return $var; |
||||
case self::FLOAT : |
||||
if ((is_string($var) && is_numeric($var)) || is_int($var)) $var = (float) $var; |
||||
return $var; |
||||
case self::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 { |
||||
throw new HTMLPurifier_VarParserException("Unrecognized value '$var' for $type"); |
||||
} |
||||
} |
||||
return $var; |
||||
case self::ALIST : |
||||
case self::HASH : |
||||
case self::LOOKUP : |
||||
if (is_string($var)) { |
||||
// special case: technically, this is an array with |
||||
// a single empty string item, but having an empty |
||||
// array is more intuitive |
||||
if ($var == '') return array(); |
||||
if (strpos($var, "\n") === false && strpos($var, "\r") === false) { |
||||
// simplistic string to array method that only works |
||||
// for simple lists of tag names or alphanumeric characters |
||||
$var = explode(',',$var); |
||||
} else { |
||||
$var = preg_split('/(,|[\n\r]+)/', $var); |
||||
} |
||||
// remove spaces |
||||
foreach ($var as $i => $j) $var[$i] = trim($j); |
||||
if ($type === self::HASH) { |
||||
// key:value,key2:value2 |
||||
$nvar = array(); |
||||
foreach ($var as $keypair) { |
||||
$c = explode(':', $keypair, 2); |
||||
if (!isset($c[1])) continue; |
||||
$nvar[$c[0]] = $c[1]; |
||||
} |
||||
$var = $nvar; |
||||
} |
||||
} |
||||
if (!is_array($var)) break; |
||||
$keys = array_keys($var); |
||||
if ($keys === array_keys($keys)) { |
||||
if ($type == self::ALIST) return $var; |
||||
elseif ($type == self::LOOKUP) { |
||||
$new = array(); |
||||
foreach ($var as $key) { |
||||
$new[$key] = true; |
||||
} |
||||
return $new; |
||||
} else break; |
||||
} |
||||
if ($type === self::LOOKUP) { |
||||
foreach ($var as $key => $value) { |
||||
$var[$key] = true; |
||||
} |
||||
} |
||||
return $var; |
||||
default: |
||||
$this->errorInconsistent(__CLASS__, $type); |
||||
} |
||||
$this->errorGeneric($var, $type); |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
<?php |
||||
|
||||
/** |
||||
* Performs safe variable parsing based on types which can be used by |
||||
* users. This may not be able to represent all possible data inputs, |
||||
* however. |
||||
*/ |
||||
class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser |
||||
{ |
||||
|
||||
protected function parseImplementation($var, $type, $allow_null) { |
||||
if ($allow_null && $var === null) return null; |
||||
switch ($type) { |
||||
// Note: if code "breaks" from the switch, it triggers a generic |
||||
// exception to be thrown. Specific errors can be specifically |
||||
// done here. |
||||
case self::MIXED : |
||||
case self::ISTRING : |
||||
case self::STRING : |
||||
case self::TEXT : |
||||
case self::ITEXT : |
||||
return $var; |
||||
case self::INT : |
||||
if (is_string($var) && ctype_digit($var)) $var = (int) $var; |
||||
return $var; |
||||
case self::FLOAT : |
||||
if ((is_string($var) && is_numeric($var)) || is_int($var)) $var = (float) $var; |
||||
return $var; |
||||
case self::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 { |
||||
throw new HTMLPurifier_VarParserException("Unrecognized value '$var' for $type"); |
||||
} |
||||
} |
||||
return $var; |
||||
case self::ALIST : |
||||
case self::HASH : |
||||
case self::LOOKUP : |
||||
if (is_string($var)) { |
||||
// special case: technically, this is an array with |
||||
// a single empty string item, but having an empty |
||||
// array is more intuitive |
||||
if ($var == '') return array(); |
||||
if (strpos($var, "\n") === false && strpos($var, "\r") === false) { |
||||
// simplistic string to array method that only works |
||||
// for simple lists of tag names or alphanumeric characters |
||||
$var = explode(',',$var); |
||||
} else { |
||||
$var = preg_split('/(,|[\n\r]+)/', $var); |
||||
} |
||||
// remove spaces |
||||
foreach ($var as $i => $j) $var[$i] = trim($j); |
||||
if ($type === self::HASH) { |
||||
// key:value,key2:value2 |
||||
$nvar = array(); |
||||
foreach ($var as $keypair) { |
||||
$c = explode(':', $keypair, 2); |
||||
if (!isset($c[1])) continue; |
||||
$nvar[trim($c[0])] = trim($c[1]); |
||||
} |
||||
$var = $nvar; |
||||
} |
||||
} |
||||
if (!is_array($var)) break; |
||||
$keys = array_keys($var); |
||||
if ($keys === array_keys($keys)) { |
||||
if ($type == self::ALIST) return $var; |
||||
elseif ($type == self::LOOKUP) { |
||||
$new = array(); |
||||
foreach ($var as $key) { |
||||
$new[$key] = true; |
||||
} |
||||
return $new; |
||||
} else break; |
||||
} |
||||
if ($type === self::ALIST) { |
||||
trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING); |
||||
return array_values($var); |
||||
} |
||||
if ($type === self::LOOKUP) { |
||||
foreach ($var as $key => $value) { |
||||
if ($value !== true) { |
||||
trigger_error("Lookup array has non-true value at key '$key'; maybe your input array was not indexed numerically", E_USER_WARNING); |
||||
} |
||||
$var[$key] = true; |
||||
} |
||||
} |
||||
return $var; |
||||
default: |
||||
$this->errorInconsistent(__CLASS__, $type); |
||||
} |
||||
$this->errorGeneric($var, $type); |
||||
} |
||||
|
||||
} |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,30 +1,29 @@ |
||||
#!/usr/bin/php |
||||
<?php |
||||
|
||||
chdir(dirname(__FILE__)); |
||||
require_once 'common.php'; |
||||
assertCli(); |
||||
|
||||
/** |
||||
* @file |
||||
* Runs all generation/flush cache scripts to ensure that somewhat volatile |
||||
* generated files are up-to-date. |
||||
*/ |
||||
|
||||
function e($cmd) { |
||||
echo "\$ $cmd\n"; |
||||
passthru($cmd, $status); |
||||
echo "\n"; |
||||
if ($status) exit($status); |
||||
} |
||||
|
||||
$php = $_SERVER['argv'][1]; |
||||
if (!$php) $php = 'php'; |
||||
|
||||
e($php . ' generate-includes.php'); |
||||
e($php . ' generate-schema-cache.php'); |
||||
e($php . ' flush-definition-cache.php'); |
||||
e($php . ' generate-standalone.php'); |
||||
e($php . ' config-scanner.php'); |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
#!/usr/bin/php |
||||
<?php |
||||
|
||||
chdir(dirname(__FILE__)); |
||||
require_once 'common.php'; |
||||
assertCli(); |
||||
|
||||
/** |
||||
* @file |
||||
* Runs all generation/flush cache scripts to ensure that somewhat volatile |
||||
* generated files are up-to-date. |
||||
*/ |
||||
|
||||
function e($cmd) { |
||||
echo "\$ $cmd\n"; |
||||
passthru($cmd, $status); |
||||
echo "\n"; |
||||
if ($status) exit($status); |
||||
} |
||||
|
||||
$php = empty($_SERVER['argv'][1]) ? 'php' : $_SERVER['argv'][1]; |
||||
|
||||
e($php . ' generate-includes.php'); |
||||
e($php . ' generate-schema-cache.php'); |
||||
e($php . ' flush-definition-cache.php'); |
||||
e($php . ' generate-standalone.php'); |
||||
e($php . ' config-scanner.php'); |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
@ -1,189 +1,190 @@ |
||||
#!/usr/bin/php |
||||
<?php |
||||
|
||||
chdir(dirname(__FILE__)); |
||||
require_once 'common.php'; |
||||
require_once '../tests/path2class.func.php'; |
||||
require_once '../library/HTMLPurifier/Bootstrap.php'; |
||||
assertCli(); |
||||
|
||||
/** |
||||
* @file |
||||
* Generates an include stub for users who do not want to use the autoloader. |
||||
* When new files are added to HTML Purifier's main codebase, this file should |
||||
* be called. |
||||
*/ |
||||
|
||||
chdir(dirname(__FILE__) . '/../library/'); |
||||
$FS = new FSTools(); |
||||
|
||||
$exclude_dirs = array( |
||||
'HTMLPurifier/Language/', |
||||
'HTMLPurifier/ConfigSchema/', |
||||
'HTMLPurifier/Filter/', |
||||
'HTMLPurifier/Printer/', |
||||
/* These should be excluded, but need to have ConfigSchema support first |
||||
|
||||
*/ |
||||
); |
||||
$exclude_files = array( |
||||
'HTMLPurifier/Lexer/PEARSax3.php', |
||||
'HTMLPurifier/Lexer/PH5P.php', |
||||
'HTMLPurifier/Printer.php', |
||||
); |
||||
|
||||
// Determine what files need to be included: |
||||
echo 'Scanning for files... '; |
||||
$raw_files = $FS->globr('.', '*.php'); |
||||
if (!$raw_files) throw new Exception('Did not find any PHP source files'); |
||||
$files = array(); |
||||
foreach ($raw_files as $file) { |
||||
$file = substr($file, 2); // rm leading './' |
||||
if (strncmp('standalone/', $file, 11) === 0) continue; // rm generated files |
||||
if (substr_count($file, '.') > 1) continue; // rm meta files |
||||
$ok = true; |
||||
foreach ($exclude_dirs as $dir) { |
||||
if (strncmp($dir, $file, strlen($dir)) === 0) { |
||||
$ok = false; |
||||
break; |
||||
} |
||||
} |
||||
if (!$ok) continue; // rm excluded directories |
||||
if (in_array($file, $exclude_files)) continue; // rm excluded files |
||||
$files[] = $file; |
||||
} |
||||
echo "done!\n"; |
||||
|
||||
// Reorder list so that dependencies are included first: |
||||
|
||||
/** |
||||
* Returns a lookup array of dependencies for a file. |
||||
* |
||||
* @note This function expects that format $name extends $parent on one line |
||||
* |
||||
* @param $file |
||||
* File to check dependencies of. |
||||
* @return |
||||
* Lookup array of files the file is dependent on, sorted accordingly. |
||||
*/ |
||||
function get_dependency_lookup($file) { |
||||
static $cache = array(); |
||||
if (isset($cache[$file])) return $cache[$file]; |
||||
if (!file_exists($file)) { |
||||
echo "File doesn't exist: $file\n"; |
||||
return array(); |
||||
} |
||||
$fh = fopen($file, 'r'); |
||||
$deps = array(); |
||||
while (!feof($fh)) { |
||||
$line = fgets($fh); |
||||
if (strncmp('class', $line, 5) === 0) { |
||||
// The implementation here is fragile and will break if we attempt |
||||
// to use interfaces. Beware! |
||||
list(, $parent) = explode(' extends ', trim($line, ' {'."\n\r"), 2); |
||||
if (empty($parent)) break; |
||||
$dep_file = HTMLPurifier_Bootstrap::getPath($parent); |
||||
if (!$dep_file) break; |
||||
$deps[$dep_file] = true; |
||||
break; |
||||
} |
||||
} |
||||
fclose($fh); |
||||
foreach (array_keys($deps) as $file) { |
||||
// Extra dependencies must come *before* base dependencies |
||||
$deps = get_dependency_lookup($file) + $deps; |
||||
} |
||||
$cache[$file] = $deps; |
||||
return $deps; |
||||
} |
||||
|
||||
/** |
||||
* Sorts files based on dependencies. This function is lazy and will not |
||||
* group files with dependencies together; it will merely ensure that a file |
||||
* is never included before its dependencies are. |
||||
* |
||||
* @param $files |
||||
* Files array to sort. |
||||
* @return |
||||
* Sorted array ($files is not modified by reference!) |
||||
*/ |
||||
function dep_sort($files) { |
||||
$ret = array(); |
||||
$cache = array(); |
||||
foreach ($files as $file) { |
||||
if (isset($cache[$file])) continue; |
||||
$deps = get_dependency_lookup($file); |
||||
foreach (array_keys($deps) as $dep) { |
||||
if (!isset($cache[$dep])) { |
||||
$ret[] = $dep; |
||||
$cache[$dep] = true; |
||||
} |
||||
} |
||||
$cache[$file] = true; |
||||
$ret[] = $file; |
||||
} |
||||
return $ret; |
||||
} |
||||
|
||||
$files = dep_sort($files); |
||||
|
||||
// Build the actual include stub: |
||||
|
||||
$version = trim(file_get_contents('../VERSION')); |
||||
|
||||
// stub |
||||
$php = "<?php |
||||
|
||||
/** |
||||
* @file |
||||
* This file was auto-generated by generate-includes.php and includes all of |
||||
* the core files required by HTML Purifier. Use this if performance is a |
||||
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS |
||||
* FILE, changes will be overwritten the next time the script is run. |
||||
* |
||||
* @version $version |
||||
* |
||||
* @warning |
||||
* You must *not* include any other HTML Purifier files before this file, |
||||
* because 'require' not 'require_once' is used. |
||||
* |
||||
* @warning |
||||
* This file requires that the include path contains the HTML Purifier |
||||
* library directory; this is not auto-set. |
||||
*/ |
||||
|
||||
"; |
||||
|
||||
foreach ($files as $file) { |
||||
$php .= "require '$file';" . PHP_EOL; |
||||
} |
||||
|
||||
echo "Writing HTMLPurifier.includes.php... "; |
||||
file_put_contents('HTMLPurifier.includes.php', $php); |
||||
echo "done!\n"; |
||||
|
||||
$php = "<?php |
||||
|
||||
/** |
||||
* @file |
||||
* This file was auto-generated by generate-includes.php and includes all of |
||||
* the core files required by HTML Purifier. This is a convenience stub that |
||||
* includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT |
||||
* EDIT THIS FILE, changes will be overwritten the next time the script is run. |
||||
* |
||||
* Changes to include_path are not necessary. |
||||
*/ |
||||
|
||||
\$__dir = dirname(__FILE__); |
||||
|
||||
"; |
||||
|
||||
foreach ($files as $file) { |
||||
$php .= "require_once \$__dir . '/$file';" . PHP_EOL; |
||||
} |
||||
|
||||
echo "Writing HTMLPurifier.safe-includes.php... "; |
||||
file_put_contents('HTMLPurifier.safe-includes.php', $php); |
||||
echo "done!\n"; |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
#!/usr/bin/php |
||||
<?php |
||||
|
||||
chdir(dirname(__FILE__)); |
||||
require_once 'common.php'; |
||||
require_once '../tests/path2class.func.php'; |
||||
require_once '../library/HTMLPurifier/Bootstrap.php'; |
||||
assertCli(); |
||||
|
||||
/** |
||||
* @file |
||||
* Generates an include stub for users who do not want to use the autoloader. |
||||
* When new files are added to HTML Purifier's main codebase, this file should |
||||
* be called. |
||||
*/ |
||||
|
||||
chdir(dirname(__FILE__) . '/../library/'); |
||||
$FS = new FSTools(); |
||||
|
||||
$exclude_dirs = array( |
||||
'HTMLPurifier/Language/', |
||||
'HTMLPurifier/ConfigSchema/', |
||||
'HTMLPurifier/Filter/', |
||||
'HTMLPurifier/Printer/', |
||||
/* These should be excluded, but need to have ConfigSchema support first |
||||
|
||||
*/ |
||||
); |
||||
$exclude_files = array( |
||||
'HTMLPurifier/Lexer/PEARSax3.php', |
||||
'HTMLPurifier/Lexer/PH5P.php', |
||||
'HTMLPurifier/Printer.php', |
||||
); |
||||
|
||||
// Determine what files need to be included: |
||||
echo 'Scanning for files... '; |
||||
$raw_files = $FS->globr('.', '*.php'); |
||||
if (!$raw_files) throw new Exception('Did not find any PHP source files'); |
||||
$files = array(); |
||||
foreach ($raw_files as $file) { |
||||
$file = substr($file, 2); // rm leading './' |
||||
if (strncmp('standalone/', $file, 11) === 0) continue; // rm generated files |
||||
if (substr_count($file, '.') > 1) continue; // rm meta files |
||||
$ok = true; |
||||
foreach ($exclude_dirs as $dir) { |
||||
if (strncmp($dir, $file, strlen($dir)) === 0) { |
||||
$ok = false; |
||||
break; |
||||
} |
||||
} |
||||
if (!$ok) continue; // rm excluded directories |
||||
if (in_array($file, $exclude_files)) continue; // rm excluded files |
||||
$files[] = $file; |
||||
} |
||||
echo "done!\n"; |
||||
|
||||
// Reorder list so that dependencies are included first: |
||||
|
||||
/** |
||||
* Returns a lookup array of dependencies for a file. |
||||
* |
||||
* @note This function expects that format $name extends $parent on one line |
||||
* |
||||
* @param $file |
||||
* File to check dependencies of. |
||||
* @return |
||||
* Lookup array of files the file is dependent on, sorted accordingly. |
||||
*/ |
||||
function get_dependency_lookup($file) { |
||||
static $cache = array(); |
||||
if (isset($cache[$file])) return $cache[$file]; |
||||
if (!file_exists($file)) { |
||||
echo "File doesn't exist: $file\n"; |
||||
return array(); |
||||
} |
||||
$fh = fopen($file, 'r'); |
||||
$deps = array(); |
||||
while (!feof($fh)) { |
||||
$line = fgets($fh); |
||||
if (strncmp('class', $line, 5) === 0) { |
||||
// The implementation here is fragile and will break if we attempt |
||||
// to use interfaces. Beware! |
||||
$arr = explode(' extends ', trim($line, ' {'."\n\r"), 2); |
||||
if (count($arr) < 2) break; |
||||
$parent = $arr[1]; |
||||
$dep_file = HTMLPurifier_Bootstrap::getPath($parent); |
||||
if (!$dep_file) break; |
||||
$deps[$dep_file] = true; |
||||
break; |
||||
} |
||||
} |
||||
fclose($fh); |
||||
foreach (array_keys($deps) as $file) { |
||||
// Extra dependencies must come *before* base dependencies |
||||
$deps = get_dependency_lookup($file) + $deps; |
||||
} |
||||
$cache[$file] = $deps; |
||||
return $deps; |
||||
} |
||||
|
||||
/** |
||||
* Sorts files based on dependencies. This function is lazy and will not |
||||
* group files with dependencies together; it will merely ensure that a file |
||||
* is never included before its dependencies are. |
||||
* |
||||
* @param $files |
||||
* Files array to sort. |
||||
* @return |
||||
* Sorted array ($files is not modified by reference!) |
||||
*/ |
||||
function dep_sort($files) { |
||||
$ret = array(); |
||||
$cache = array(); |
||||
foreach ($files as $file) { |
||||
if (isset($cache[$file])) continue; |
||||
$deps = get_dependency_lookup($file); |
||||
foreach (array_keys($deps) as $dep) { |
||||
if (!isset($cache[$dep])) { |
||||
$ret[] = $dep; |
||||
$cache[$dep] = true; |
||||
} |
||||
} |
||||
$cache[$file] = true; |
||||
$ret[] = $file; |
||||
} |
||||
return $ret; |
||||
} |
||||
|
||||
$files = dep_sort($files); |
||||
|
||||
// Build the actual include stub: |
||||
|
||||
$version = trim(file_get_contents('../VERSION')); |
||||
|
||||
// stub |
||||
$php = "<?php |
||||
|
||||
/** |
||||
* @file |
||||
* This file was auto-generated by generate-includes.php and includes all of |
||||
* the core files required by HTML Purifier. Use this if performance is a |
||||
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS |
||||
* FILE, changes will be overwritten the next time the script is run. |
||||
* |
||||
* @version $version |
||||
* |
||||
* @warning |
||||
* You must *not* include any other HTML Purifier files before this file, |
||||
* because 'require' not 'require_once' is used. |
||||
* |
||||
* @warning |
||||
* This file requires that the include path contains the HTML Purifier |
||||
* library directory; this is not auto-set. |
||||
*/ |
||||
|
||||
"; |
||||
|
||||
foreach ($files as $file) { |
||||
$php .= "require '$file';" . PHP_EOL; |
||||
} |
||||
|
||||
echo "Writing HTMLPurifier.includes.php... "; |
||||
file_put_contents('HTMLPurifier.includes.php', $php); |
||||
echo "done!\n"; |
||||
|
||||
$php = "<?php |
||||
|
||||
/** |
||||
* @file |
||||
* This file was auto-generated by generate-includes.php and includes all of |
||||
* the core files required by HTML Purifier. This is a convenience stub that |
||||
* includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT |
||||
* EDIT THIS FILE, changes will be overwritten the next time the script is run. |
||||
* |
||||
* Changes to include_path are not necessary. |
||||
*/ |
||||
|
||||
\$__dir = dirname(__FILE__); |
||||
|
||||
"; |
||||
|
||||
foreach ($files as $file) { |
||||
$php .= "require_once \$__dir . '/$file';" . PHP_EOL; |
||||
} |
||||
|
||||
echo "Writing HTMLPurifier.safe-includes.php... "; |
||||
file_put_contents('HTMLPurifier.safe-includes.php', $php); |
||||
echo "done!\n"; |
||||
|
||||
// vim: et sw=4 sts=4 |
||||
|
||||
Loading…
Reference in new issue