vendor: Upgrade HTMLPurifier to version 4.7.0

refs #10044
This commit is contained in:
Eric Lippmann 2015-09-01 12:57:57 +02:00
parent 6556059afd
commit d4669c7832
25 changed files with 105 additions and 61 deletions

View File

@ -44,7 +44,7 @@ class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef
*/ */
public function validate($string, $config, $context) public function validate($string, $config, $context)
{ {
$string = $this->parseCDATA($string); $string = $this->mungeRgb($this->parseCDATA($string));
if ($string === '') { if ($string === '') {
return false; return false;
} }

View File

@ -32,9 +32,6 @@ class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef
*/ */
public function validate($string, $config, $context) public function validate($string, $config, $context)
{ {
if (empty($string)) {
return false;
}
return $this->name; return $this->name;
} }

View File

@ -350,8 +350,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
$this->info['scrollbar-highlight-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(); $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
// technically not proprietary, but CSS3, and no one supports it // vendor specific prefixes of opacity
$this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
$this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
$this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
@ -404,6 +403,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
array('visible', 'hidden', 'collapse') array('visible', 'hidden', 'collapse')
); );
$this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll')); $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
$this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
} }
/** /**

View File

@ -21,7 +21,7 @@ class HTMLPurifier_Config
* HTML Purifier's version * HTML Purifier's version
* @type string * @type string
*/ */
public $version = '4.6.0'; public $version = '4.7.0';
/** /**
* Whether or not to automatically finalize * Whether or not to automatically finalize
@ -646,16 +646,25 @@ class HTMLPurifier_Config
return $this->getDefinition($name, true, true); return $this->getDefinition($name, true, true);
} }
/**
* @return HTMLPurifier_HTMLDefinition
*/
public function maybeGetRawHTMLDefinition() public function maybeGetRawHTMLDefinition()
{ {
return $this->getDefinition('HTML', true, true); return $this->getDefinition('HTML', true, true);
} }
/**
* @return HTMLPurifier_CSSDefinition
*/
public function maybeGetRawCSSDefinition() public function maybeGetRawCSSDefinition()
{ {
return $this->getDefinition('CSS', true, true); return $this->getDefinition('CSS', true, true);
} }
/**
* @return HTMLPurifier_URIDefinition
*/
public function maybeGetRawURIDefinition() public function maybeGetRawURIDefinition()
{ {
return $this->getDefinition('URI', true, true); return $this->getDefinition('URI', true, true);

View File

@ -0,0 +1,14 @@
AutoFormat.RemoveEmpty.Predicate
TYPE: hash
VERSION: 4.7.0
DEFAULT: array('colgroup' => array(), 'th' => array(), 'td' => array(), 'iframe' => array('src'))
--DESCRIPTION--
<p>
Given that an element has no contents, it will be removed by default, unless
this predicate dictates otherwise. The predicate can either be an associative
map from tag name to list of attributes that must be present for the element
to be considered preserved: thus, the default always preserves <code>colgroup</code>,
<code>th</code> and <code>td</code>, and also <code>iframe</code> if it
has a <code>src</code>.
</p>
--# vim: et sw=4 sts=4

View File

@ -4,6 +4,6 @@ VERSION: 2.0.1
DEFAULT: NULL DEFAULT: NULL
--DESCRIPTION-- --DESCRIPTION--
A custom doctype for power-users who defined there own document A custom doctype for power-users who defined their own document
type. This directive only applies when %HTML.Doctype is blank. type. This directive only applies when %HTML.Doctype is blank.
--# vim: et sw=4 sts=4 --# vim: et sw=4 sts=4

View File

@ -219,9 +219,15 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
} elseif (!$this->_testPermissions($base, $chmod)) { } elseif (!$this->_testPermissions($base, $chmod)) {
return false; return false;
} }
$old = umask(0000);
mkdir($directory, $chmod); mkdir($directory, $chmod);
umask($old); if (!$this->_testPermissions($directory, $chmod)) {
trigger_error(
'Base directory ' . $base . ' does not exist,
please create or change using %Cache.SerializerPath',
E_USER_WARNING
);
return false;
}
} elseif (!$this->_testPermissions($directory, $chmod)) { } elseif (!$this->_testPermissions($directory, $chmod)) {
return false; return false;
} }

View File

@ -17,7 +17,7 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
public function preFilter($html, $config, $context) public function preFilter($html, $config, $context)
{ {
$pre_regex = '#<object[^>]+>.+?' . $pre_regex = '#<object[^>]+>.+?' .
'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s'; '(?:http:)?//www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s';
$pre_replace = '<span class="youtube-embed">\1</span>'; $pre_replace = '<span class="youtube-embed">\1</span>';
return preg_replace($pre_regex, $pre_replace, $html); return preg_replace($pre_regex, $pre_replace, $html);
} }
@ -51,10 +51,10 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
{ {
$url = $this->armorUrl($matches[1]); $url = $this->armorUrl($matches[1]);
return '<object width="425" height="350" type="application/x-shockwave-flash" ' . return '<object width="425" height="350" type="application/x-shockwave-flash" ' .
'data="http://www.youtube.com/' . $url . '">' . 'data="//www.youtube.com/' . $url . '">' .
'<param name="movie" value="http://www.youtube.com/' . $url . '"></param>' . '<param name="movie" value="//www.youtube.com/' . $url . '"></param>' .
'<!--[if IE]>' . '<!--[if IE]>' .
'<embed src="http://www.youtube.com/' . $url . '"' . '<embed src="//www.youtube.com/' . $url . '"' .
'type="application/x-shockwave-flash"' . 'type="application/x-shockwave-flash"' .
'wmode="transparent" width="425" height="350" />' . 'wmode="transparent" width="425" height="350" />' .
'<![endif]-->' . '<![endif]-->' .

View File

@ -1,4 +1,4 @@
<?php <?php
if (!defined('HTMLPURIFIER_PREFIX')) { if (!defined('HTMLPURIFIER_PREFIX')) {
define('HTMLPURIFIER_PREFIX', __DIR__); define('HTMLPURIFIER_PREFIX', dirname(__FILE__));
} }

View File

@ -7,7 +7,7 @@
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
* FILE, changes will be overwritten the next time the script is run. * FILE, changes will be overwritten the next time the script is run.
* *
* @version 4.6.0 * @version 4.7.0
* *
* @warning * @warning
* You must *not* include any other HTML Purifier files before this file, * You must *not* include any other HTML Purifier files before this file,

View File

@ -19,7 +19,7 @@
*/ */
/* /*
HTML Purifier 4.6.0 - Standards Compliant HTML Filtering HTML Purifier 4.7.0 - Standards Compliant HTML Filtering
Copyright (C) 2006-2008 Edward Z. Yang Copyright (C) 2006-2008 Edward Z. Yang
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
@ -58,12 +58,12 @@ class HTMLPurifier
* Version of HTML Purifier. * Version of HTML Purifier.
* @type string * @type string
*/ */
public $version = '4.6.0'; public $version = '4.7.0';
/** /**
* Constant with version of HTML Purifier. * Constant with version of HTML Purifier.
*/ */
const VERSION = '4.6.0'; const VERSION = '4.7.0';
/** /**
* Global configuration object. * Global configuration object.

View File

@ -44,7 +44,7 @@ class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef
*/ */
public function validate($string, $config, $context) public function validate($string, $config, $context)
{ {
$string = $this->parseCDATA($string); $string = $this->mungeRgb($this->parseCDATA($string));
if ($string === '') { if ($string === '') {
return false; return false;
} }

View File

@ -32,9 +32,6 @@ class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef
*/ */
public function validate($string, $config, $context) public function validate($string, $config, $context)
{ {
if (empty($string)) {
return false;
}
return $this->name; return $this->name;
} }

View File

@ -350,8 +350,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
$this->info['scrollbar-highlight-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(); $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
// technically not proprietary, but CSS3, and no one supports it // vendor specific prefixes of opacity
$this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
$this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
$this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
@ -404,6 +403,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
array('visible', 'hidden', 'collapse') array('visible', 'hidden', 'collapse')
); );
$this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll')); $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
$this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
} }
/** /**

View File

@ -21,7 +21,7 @@ class HTMLPurifier_Config
* HTML Purifier's version * HTML Purifier's version
* @type string * @type string
*/ */
public $version = '4.6.0'; public $version = '4.7.0';
/** /**
* Whether or not to automatically finalize * Whether or not to automatically finalize
@ -646,16 +646,25 @@ class HTMLPurifier_Config
return $this->getDefinition($name, true, true); return $this->getDefinition($name, true, true);
} }
/**
* @return HTMLPurifier_HTMLDefinition
*/
public function maybeGetRawHTMLDefinition() public function maybeGetRawHTMLDefinition()
{ {
return $this->getDefinition('HTML', true, true); return $this->getDefinition('HTML', true, true);
} }
/**
* @return HTMLPurifier_CSSDefinition
*/
public function maybeGetRawCSSDefinition() public function maybeGetRawCSSDefinition()
{ {
return $this->getDefinition('CSS', true, true); return $this->getDefinition('CSS', true, true);
} }
/**
* @return HTMLPurifier_URIDefinition
*/
public function maybeGetRawURIDefinition() public function maybeGetRawURIDefinition()
{ {
return $this->getDefinition('URI', true, true); return $this->getDefinition('URI', true, true);

View File

@ -219,9 +219,15 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
} elseif (!$this->_testPermissions($base, $chmod)) { } elseif (!$this->_testPermissions($base, $chmod)) {
return false; return false;
} }
$old = umask(0000);
mkdir($directory, $chmod); mkdir($directory, $chmod);
umask($old); if (!$this->_testPermissions($directory, $chmod)) {
trigger_error(
'Base directory ' . $base . ' does not exist,
please create or change using %Cache.SerializerPath',
E_USER_WARNING
);
return false;
}
} elseif (!$this->_testPermissions($directory, $chmod)) { } elseif (!$this->_testPermissions($directory, $chmod)) {
return false; return false;
} }

View File

@ -17,7 +17,7 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
public function preFilter($html, $config, $context) public function preFilter($html, $config, $context)
{ {
$pre_regex = '#<object[^>]+>.+?' . $pre_regex = '#<object[^>]+>.+?' .
'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s'; '(?:http:)?//www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s';
$pre_replace = '<span class="youtube-embed">\1</span>'; $pre_replace = '<span class="youtube-embed">\1</span>';
return preg_replace($pre_regex, $pre_replace, $html); return preg_replace($pre_regex, $pre_replace, $html);
} }
@ -51,10 +51,10 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
{ {
$url = $this->armorUrl($matches[1]); $url = $this->armorUrl($matches[1]);
return '<object width="425" height="350" type="application/x-shockwave-flash" ' . return '<object width="425" height="350" type="application/x-shockwave-flash" ' .
'data="http://www.youtube.com/' . $url . '">' . 'data="//www.youtube.com/' . $url . '">' .
'<param name="movie" value="http://www.youtube.com/' . $url . '"></param>' . '<param name="movie" value="//www.youtube.com/' . $url . '"></param>' .
'<!--[if IE]>' . '<!--[if IE]>' .
'<embed src="http://www.youtube.com/' . $url . '"' . '<embed src="//www.youtube.com/' . $url . '"' .
'type="application/x-shockwave-flash"' . 'type="application/x-shockwave-flash"' .
'wmode="transparent" width="425" height="350" />' . 'wmode="transparent" width="425" height="350" />' .
'<![endif]-->' . '<![endif]-->' .

View File

@ -28,10 +28,10 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
private $removeNbspExceptions; private $removeNbspExceptions;
/** /**
* Cached contents of %AutoFormat.RemoveEmpty.Predicate
* @type array * @type array
* TODO: make me configurable
*/ */
private $_exclude = array('colgroup' => 1, 'th' => 1, 'td' => 1, 'iframe' => 1); private $exclude;
/** /**
* @param HTMLPurifier_Config $config * @param HTMLPurifier_Config $config
@ -45,6 +45,7 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
$this->context = $context; $this->context = $context;
$this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp'); $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
$this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions'); $this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
$this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
$this->attrValidator = new HTMLPurifier_AttrValidator(); $this->attrValidator = new HTMLPurifier_AttrValidator();
} }
@ -75,11 +76,15 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
break; break;
} }
if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) { if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {
if (isset($this->_exclude[$token->name])) {
return;
}
$this->attrValidator->validateToken($token, $this->config, $this->context); $this->attrValidator->validateToken($token, $this->config, $this->context);
$token->armor['ValidateAttributes'] = true; $token->armor['ValidateAttributes'] = true;
if (isset($this->exclude[$token->name])) {
$r = true;
foreach ($this->exclude[$token->name] as $elem) {
if (!isset($token->attr[$elem])) $r = false;
}
if ($r) return;
}
if (isset($token->attr['id']) || isset($token->attr['name'])) { if (isset($token->attr['id']) || isset($token->attr['name'])) {
return; return;
} }

View File

@ -75,8 +75,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
$tokens = array(); $tokens = array();
$this->tokenizeDOM( $this->tokenizeDOM(
$doc->getElementsByTagName('html')->item(0)-> // <html> $doc->getElementsByTagName('html')->item(0)-> // <html>
getElementsByTagName('body')->item(0)-> // <body> getElementsByTagName('body')->item(0), // <body>
getElementsByTagName('div')->item(0), // <div>
$tokens $tokens
); );
return $tokens; return $tokens;
@ -272,7 +271,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
$ret .= '<html><head>'; $ret .= '<html><head>';
$ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'; $ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
// No protection if $html contains a stray </div>! // No protection if $html contains a stray </div>!
$ret .= '</head><body><div>' . $html . '</div></body></html>'; $ret .= '</head><body>' . $html . '</body></html>';
return $ret; return $ret;
} }
} }

View File

@ -34,8 +34,7 @@ class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex
$tokens = array(); $tokens = array();
$this->tokenizeDOM( $this->tokenizeDOM(
$doc->getElementsByTagName('html')->item(0)-> // <html> $doc->getElementsByTagName('html')->item(0)-> // <html>
getElementsByTagName('body')->item(0)-> // <body> getElementsByTagName('body')->item(0) // <body>
getElementsByTagName('div')->item(0) // <div>
, ,
$tokens $tokens
); );

View File

@ -28,10 +28,10 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
private $removeNbspExceptions; private $removeNbspExceptions;
/** /**
* Cached contents of %AutoFormat.RemoveEmpty.Predicate
* @type array * @type array
* TODO: make me configurable
*/ */
private $_exclude = array('colgroup' => 1, 'th' => 1, 'td' => 1, 'iframe' => 1); private $exclude;
/** /**
* @param HTMLPurifier_Config $config * @param HTMLPurifier_Config $config
@ -45,6 +45,7 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
$this->context = $context; $this->context = $context;
$this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp'); $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
$this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions'); $this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
$this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
$this->attrValidator = new HTMLPurifier_AttrValidator(); $this->attrValidator = new HTMLPurifier_AttrValidator();
} }
@ -75,11 +76,15 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
break; break;
} }
if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) { if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {
if (isset($this->_exclude[$token->name])) {
return;
}
$this->attrValidator->validateToken($token, $this->config, $this->context); $this->attrValidator->validateToken($token, $this->config, $this->context);
$token->armor['ValidateAttributes'] = true; $token->armor['ValidateAttributes'] = true;
if (isset($this->exclude[$token->name])) {
$r = true;
foreach ($this->exclude[$token->name] as $elem) {
if (!isset($token->attr[$elem])) $r = false;
}
if ($r) return;
}
if (isset($token->attr['id']) || isset($token->attr['name'])) { if (isset($token->attr['id']) || isset($token->attr['name'])) {
return; return;
} }

View File

@ -75,8 +75,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
$tokens = array(); $tokens = array();
$this->tokenizeDOM( $this->tokenizeDOM(
$doc->getElementsByTagName('html')->item(0)-> // <html> $doc->getElementsByTagName('html')->item(0)-> // <html>
getElementsByTagName('body')->item(0)-> // <body> getElementsByTagName('body')->item(0), // <body>
getElementsByTagName('div')->item(0), // <div>
$tokens $tokens
); );
return $tokens; return $tokens;
@ -272,7 +271,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
$ret .= '<html><head>'; $ret .= '<html><head>';
$ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'; $ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
// No protection if $html contains a stray </div>! // No protection if $html contains a stray </div>!
$ret .= '</head><body><div>' . $html . '</div></body></html>'; $ret .= '</head><body>' . $html . '</body></html>';
return $ret; return $ret;
} }
} }

View File

@ -34,8 +34,7 @@ class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex
$tokens = array(); $tokens = array();
$this->tokenizeDOM( $this->tokenizeDOM(
$doc->getElementsByTagName('html')->item(0)-> // <html> $doc->getElementsByTagName('html')->item(0)-> // <html>
getElementsByTagName('body')->item(0)-> // <body> getElementsByTagName('body')->item(0) // <body>
getElementsByTagName('div')->item(0) // <div>
, ,
$tokens $tokens
); );

View File

@ -1,5 +1,5 @@
curl https://codeload.github.com/ezyang/htmlpurifier/tar.gz/v4.6.0 -o htmlpurifier-4.6.0.tar.gz curl https://codeload.github.com/ezyang/htmlpurifier/tar.gz/v4.7.0 -o htmlpurifier-4.7.0.tar.gz
tar xzf htmlpurifier-4.6.0.tar.gz --strip-components 1 htmlpurifier-4.6.0/LICENSE tar xzf htmlpurifier-4.7.0.tar.gz --strip-components 1 htmlpurifier-4.7.0/LICENSE
tar xzf htmlpurifier-4.6.0.tar.gz --strip-components 2 htmlpurifier-4.6.0/library/*.php tar xzf htmlpurifier-4.7.0.tar.gz --strip-components 2 htmlpurifier-4.7.0/library/*.php
tar xzf htmlpurifier-4.6.0.tar.gz --strip-components 3 htmlpurifier-4.6.0/library/HTMLPurifier/* tar xzf htmlpurifier-4.7.0.tar.gz --strip-components 3 htmlpurifier-4.7.0/library/HTMLPurifier/*
rm htmlpurifier-4.6.0.tar.gz rm htmlpurifier-4.7.0.tar.gz