Add HTMLPurifier to extlib

HTMLPurifier defangs arbitrary submitted HTML. We're using it in the
OStatus plugin, but it may be valuable for other parts of the codebase
(I think OEmbed might benefit, for example).
This commit is contained in:
Evan Prodromou
2010-02-20 11:35:01 -05:00
parent ed45df045f
commit 81ea0f8117
322 changed files with 24205 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
<?php
class HTMLPurifier_DefinitionCache_Decorator extends HTMLPurifier_DefinitionCache
{
/**
* Cache object we are decorating
*/
public $cache;
public function __construct() {}
/**
* Lazy decorator function
* @param $cache Reference to cache object to decorate
*/
public function decorate(&$cache) {
$decorator = $this->copy();
// reference is necessary for mocks in PHP 4
$decorator->cache =& $cache;
$decorator->type = $cache->type;
return $decorator;
}
/**
* Cross-compatible clone substitute
*/
public function copy() {
return new HTMLPurifier_DefinitionCache_Decorator();
}
public function add($def, $config) {
return $this->cache->add($def, $config);
}
public function set($def, $config) {
return $this->cache->set($def, $config);
}
public function replace($def, $config) {
return $this->cache->replace($def, $config);
}
public function get($config) {
return $this->cache->get($config);
}
public function remove($config) {
return $this->cache->remove($config);
}
public function flush($config) {
return $this->cache->flush($config);
}
public function cleanup($config) {
return $this->cache->cleanup($config);
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,43 @@
<?php
/**
* Definition cache decorator class that cleans up the cache
* whenever there is a cache miss.
*/
class HTMLPurifier_DefinitionCache_Decorator_Cleanup extends
HTMLPurifier_DefinitionCache_Decorator
{
public $name = 'Cleanup';
public function copy() {
return new HTMLPurifier_DefinitionCache_Decorator_Cleanup();
}
public function add($def, $config) {
$status = parent::add($def, $config);
if (!$status) parent::cleanup($config);
return $status;
}
public function set($def, $config) {
$status = parent::set($def, $config);
if (!$status) parent::cleanup($config);
return $status;
}
public function replace($def, $config) {
$status = parent::replace($def, $config);
if (!$status) parent::cleanup($config);
return $status;
}
public function get($config) {
$ret = parent::get($config);
if (!$ret) parent::cleanup($config);
return $ret;
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,46 @@
<?php
/**
* Definition cache decorator class that saves all cache retrievals
* to PHP's memory; good for unit tests or circumstances where
* there are lots of configuration objects floating around.
*/
class HTMLPurifier_DefinitionCache_Decorator_Memory extends
HTMLPurifier_DefinitionCache_Decorator
{
protected $definitions;
public $name = 'Memory';
public function copy() {
return new HTMLPurifier_DefinitionCache_Decorator_Memory();
}
public function add($def, $config) {
$status = parent::add($def, $config);
if ($status) $this->definitions[$this->generateKey($config)] = $def;
return $status;
}
public function set($def, $config) {
$status = parent::set($def, $config);
if ($status) $this->definitions[$this->generateKey($config)] = $def;
return $status;
}
public function replace($def, $config) {
$status = parent::replace($def, $config);
if ($status) $this->definitions[$this->generateKey($config)] = $def;
return $status;
}
public function get($config) {
$key = $this->generateKey($config);
if (isset($this->definitions[$key])) return $this->definitions[$key];
$this->definitions[$key] = parent::get($config);
return $this->definitions[$key];
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,47 @@
<?php
require_once 'HTMLPurifier/DefinitionCache/Decorator.php';
/**
* Definition cache decorator template.
*/
class HTMLPurifier_DefinitionCache_Decorator_Template extends
HTMLPurifier_DefinitionCache_Decorator
{
var $name = 'Template'; // replace this
function copy() {
// replace class name with yours
return new HTMLPurifier_DefinitionCache_Decorator_Template();
}
// remove methods you don't need
function add($def, $config) {
return parent::add($def, $config);
}
function set($def, $config) {
return parent::set($def, $config);
}
function replace($def, $config) {
return parent::replace($def, $config);
}
function get($config) {
return parent::get($config);
}
function flush() {
return parent::flush();
}
function cleanup($config) {
return parent::cleanup($config);
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,39 @@
<?php
/**
* Null cache object to use when no caching is on.
*/
class HTMLPurifier_DefinitionCache_Null extends HTMLPurifier_DefinitionCache
{
public function add($def, $config) {
return false;
}
public function set($def, $config) {
return false;
}
public function replace($def, $config) {
return false;
}
public function remove($config) {
return false;
}
public function get($config) {
return false;
}
public function flush($config) {
return false;
}
public function cleanup($config) {
return false;
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,172 @@
<?php
class HTMLPurifier_DefinitionCache_Serializer extends
HTMLPurifier_DefinitionCache
{
public function add($def, $config) {
if (!$this->checkDefType($def)) return;
$file = $this->generateFilePath($config);
if (file_exists($file)) return false;
if (!$this->_prepareDir($config)) return false;
return $this->_write($file, serialize($def));
}
public function set($def, $config) {
if (!$this->checkDefType($def)) return;
$file = $this->generateFilePath($config);
if (!$this->_prepareDir($config)) return false;
return $this->_write($file, serialize($def));
}
public function replace($def, $config) {
if (!$this->checkDefType($def)) return;
$file = $this->generateFilePath($config);
if (!file_exists($file)) return false;
if (!$this->_prepareDir($config)) return false;
return $this->_write($file, serialize($def));
}
public function get($config) {
$file = $this->generateFilePath($config);
if (!file_exists($file)) return false;
return unserialize(file_get_contents($file));
}
public function remove($config) {
$file = $this->generateFilePath($config);
if (!file_exists($file)) return false;
return unlink($file);
}
public function flush($config) {
if (!$this->_prepareDir($config)) return false;
$dir = $this->generateDirectoryPath($config);
$dh = opendir($dir);
while (false !== ($filename = readdir($dh))) {
if (empty($filename)) continue;
if ($filename[0] === '.') continue;
unlink($dir . '/' . $filename);
}
}
public function cleanup($config) {
if (!$this->_prepareDir($config)) return false;
$dir = $this->generateDirectoryPath($config);
$dh = opendir($dir);
while (false !== ($filename = readdir($dh))) {
if (empty($filename)) continue;
if ($filename[0] === '.') continue;
$key = substr($filename, 0, strlen($filename) - 4);
if ($this->isOld($key, $config)) unlink($dir . '/' . $filename);
}
}
/**
* Generates the file path to the serial file corresponding to
* the configuration and definition name
* @todo Make protected
*/
public function generateFilePath($config) {
$key = $this->generateKey($config);
return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
}
/**
* Generates the path to the directory contain this cache's serial files
* @note No trailing slash
* @todo Make protected
*/
public function generateDirectoryPath($config) {
$base = $this->generateBaseDirectoryPath($config);
return $base . '/' . $this->type;
}
/**
* Generates path to base directory that contains all definition type
* serials
* @todo Make protected
*/
public function generateBaseDirectoryPath($config) {
$base = $config->get('Cache.SerializerPath');
$base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
return $base;
}
/**
* Convenience wrapper function for file_put_contents
* @param $file File name to write to
* @param $data Data to write into file
* @return Number of bytes written if success, or false if failure.
*/
private function _write($file, $data) {
return file_put_contents($file, $data);
}
/**
* Prepares the directory that this type stores the serials in
* @return True if successful
*/
private function _prepareDir($config) {
$directory = $this->generateDirectoryPath($config);
if (!is_dir($directory)) {
$base = $this->generateBaseDirectoryPath($config);
if (!is_dir($base)) {
trigger_error('Base directory '.$base.' does not exist,
please create or change using %Cache.SerializerPath',
E_USER_WARNING);
return false;
} elseif (!$this->_testPermissions($base)) {
return false;
}
$old = umask(0022); // disable group and world writes
mkdir($directory);
umask($old);
} elseif (!$this->_testPermissions($directory)) {
return false;
}
return true;
}
/**
* Tests permissions on a directory and throws out friendly
* error messages and attempts to chmod it itself if possible
*/
private function _testPermissions($dir) {
// early abort, if it is writable, everything is hunky-dory
if (is_writable($dir)) return true;
if (!is_dir($dir)) {
// generally, you'll want to handle this beforehand
// so a more specific error message can be given
trigger_error('Directory '.$dir.' does not exist',
E_USER_WARNING);
return false;
}
if (function_exists('posix_getuid')) {
// POSIX system, we can give more specific advice
if (fileowner($dir) === posix_getuid()) {
// we can chmod it ourselves
chmod($dir, 0755);
return true;
} elseif (filegroup($dir) === posix_getgid()) {
$chmod = '775';
} else {
// PHP's probably running as nobody, so we'll
// need to give global permissions
$chmod = '777';
}
trigger_error('Directory '.$dir.' not writable, '.
'please chmod to ' . $chmod,
E_USER_WARNING);
} else {
// generic error message
trigger_error('Directory '.$dir.' not writable, '.
'please alter file permissions',
E_USER_WARNING);
}
return false;
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,3 @@
This is a dummy file to prevent Git from ignoring this empty directory.
vim: et sw=4 sts=4