f试ssefahah可能卡卡卡马卡卡计算机视觉事实上事实上飒飒飒gj◐◐◐◐hfkttkgdgkmcuocucocucoc顶顶顶顶顶顶顶顶顶顶顶顶uco朵朵可可放2巍峨2555555666369🌹 �����ѩ�����һ����dddf2qevf2fv69666 ? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!htmlpurifier/library/HTMLPurifier/PercentEncoder.php 0000644 00000006755 15266554155 0016640 0 ustar 00 preserve[$i] = true; } for ($i = 65; $i <= 90; $i++) { // upper-case $this->preserve[$i] = true; } for ($i = 97; $i <= 122; $i++) { // lower-case $this->preserve[$i] = true; } $this->preserve[45] = true; // Dash - $this->preserve[46] = true; // Period . $this->preserve[95] = true; // Underscore _ $this->preserve[126]= true; // Tilde ~ // extra letters not to escape if ($preserve !== false) { for ($i = 0, $c = strlen($preserve); $i < $c; $i++) { $this->preserve[ord($preserve[$i])] = true; } } } /** * Our replacement for urlencode, it encodes all non-reserved characters, * as well as any extra characters that were instructed to be preserved. * @note * Assumes that the string has already been normalized, making any * and all percent escape sequences valid. Percents will not be * re-escaped, regardless of their status in $preserve * @param string $string String to be encoded * @return string Encoded string. */ public function encode($string) { $ret = ''; for ($i = 0, $c = strlen($string); $i < $c; $i++) { if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])])) { $ret .= '%' . sprintf('%02X', $int); } else { $ret .= $string[$i]; } } return $ret; } /** * Fix up percent-encoding by decoding unreserved characters and normalizing. * @warning This function is affected by $preserve, even though the * usual desired behavior is for this not to preserve those * characters. Be careful when reusing instances of PercentEncoder! * @param string $string String to normalize * @return string */ public function normalize($string) { if ($string == '') { return ''; } $parts = explode('%', $string); $ret = array_shift($parts); foreach ($parts as $part) { $length = strlen($part); if ($length < 2) { $ret .= '%25' . $part; continue; } $encoding = substr($part, 0, 2); $text = substr($part, 2); if (!ctype_xdigit($encoding)) { $ret .= '%25' . $part; continue; } $int = hexdec($encoding); if (isset($this->preserve[$int])) { $ret .= chr($int) . $text; continue; } $encoding = strtoupper($encoding); $ret .= '%' . $encoding . $text; } return $ret; } } // vim: et sw=4 sts=4 htmlpurifier/library/HTMLPurifier/URIDefinition.php 0000644 00000006546 15266554155 0016406 0 ustar 00 registerFilter(new HTMLPurifier_URIFilter_DisableExternal()); $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources()); $this->registerFilter(new HTMLPurifier_URIFilter_DisableResources()); $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist()); $this->registerFilter(new HTMLPurifier_URIFilter_SafeIframe()); $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute()); $this->registerFilter(new HTMLPurifier_URIFilter_Munge()); } public function registerFilter($filter) { $this->registeredFilters[$filter->name] = $filter; } public function addFilter($filter, $config) { $r = $filter->prepare($config); if ($r === false) return; // null is ok, for backwards compat if ($filter->post) { $this->postFilters[$filter->name] = $filter; } else { $this->filters[$filter->name] = $filter; } } protected function doSetup($config) { $this->setupMemberVariables($config); $this->setupFilters($config); } protected function setupFilters($config) { foreach ($this->registeredFilters as $name => $filter) { if ($filter->always_load) { $this->addFilter($filter, $config); } else { $conf = $config->get('URI.' . $name); if ($conf !== false && $conf !== null) { $this->addFilter($filter, $config); } } } unset($this->registeredFilters); } protected function setupMemberVariables($config) { $this->host = $config->get('URI.Host'); $base_uri = $config->get('URI.Base'); if (!is_null($base_uri)) { $parser = new HTMLPurifier_URIParser(); $this->base = $parser->parse($base_uri); $this->defaultScheme = $this->base->scheme; if (is_null($this->host)) $this->host = $this->base->host; } if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme'); } public function getDefaultScheme($config, $context) { return HTMLPurifier_URISchemeRegistry::instance()->getScheme($this->defaultScheme, $config, $context); } public function filter(&$uri, $config, $context) { foreach ($this->filters as $name => $f) { $result = $f->filter($uri, $config, $context); if (!$result) return false; } return true; } public function postFilter(&$uri, $config, $context) { foreach ($this->postFilters as $name => $f) { $result = $f->filter($uri, $config, $context); if (!$result) return false; } return true; } } // vim: et sw=4 sts=4 htmlpurifier/library/HTMLPurifier/Exception.php 0000644 00000000261 15266554155 0015660 0 ustar 00 get('URI.AllowedSchemes'); if (!$config->get('URI.OverrideAllowedSchemes') && !isset($allowed_schemes[$scheme]) ) { return; } if (isset($this->schemes[$scheme])) { return $this->schemes[$scheme]; } if (!isset($allowed_schemes[$scheme])) { return; } $class = 'HTMLPurifier_URIScheme_' . $scheme; if (!class_exists($class)) { return; } $this->schemes[$scheme] = new $class(); return $this->schemes[$scheme]; } /** * Registers a custom scheme to the cache, bypassing reflection. * @param string $scheme Scheme name * @param HTMLPurifier_URIScheme $scheme_obj */ public function register($scheme, $scheme_obj) { $this->schemes[$scheme] = $scheme_obj; } } // vim: et sw=4 sts=4 htmlpurifier/library/HTMLPurifier/ErrorCollector.php 0000644 00000016711 15266554155 0016671 0 ustar 00 locale =& $context->get('Locale'); $this->context = $context; $this->_current =& $this->_stacks[0]; $this->errors =& $this->_stacks[0]; } /** * Sends an error message to the collector for later use * @param int $severity Error severity, PHP error style (don't use E_USER_) * @param string $msg Error message text */ public function send($severity, $msg) { $args = array(); if (func_num_args() > 2) { $args = func_get_args(); array_shift($args); unset($args[0]); } $token = $this->context->get('CurrentToken', true); $line = $token ? $token->line : $this->context->get('CurrentLine', true); $col = $token ? $token->col : $this->context->get('CurrentCol', true); $attr = $this->context->get('CurrentAttr', true); // perform special substitutions, also add custom parameters $subst = array(); if (!is_null($token)) { $args['CurrentToken'] = $token; } if (!is_null($attr)) { $subst['$CurrentAttr.Name'] = $attr; if (isset($token->attr[$attr])) { $subst['$CurrentAttr.Value'] = $token->attr[$attr]; } } if (empty($args)) { $msg = $this->locale->getMessage($msg); } else { $msg = $this->locale->formatMessage($msg, $args); } if (!empty($subst)) { $msg = strtr($msg, $subst); } // (numerically indexed) $error = array( self::LINENO => $line, self::SEVERITY => $severity, self::MESSAGE => $msg, self::CHILDREN => array() ); $this->_current[] = $error; // NEW CODE BELOW ... // Top-level errors are either: // TOKEN type, if $value is set appropriately, or // "syntax" type, if $value is null $new_struct = new HTMLPurifier_ErrorStruct(); $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN; if ($token) { $new_struct->value = clone $token; } if (is_int($line) && is_int($col)) { if (isset($this->lines[$line][$col])) { $struct = $this->lines[$line][$col]; } else { $struct = $this->lines[$line][$col] = $new_struct; } // These ksorts may present a performance problem ksort($this->lines[$line], SORT_NUMERIC); } else { if (isset($this->lines[-1])) { $struct = $this->lines[-1]; } else { $struct = $this->lines[-1] = $new_struct; } } ksort($this->lines, SORT_NUMERIC); // Now, check if we need to operate on a lower structure if (!empty($attr)) { $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr); if (!$struct->value) { $struct->value = array($attr, 'PUT VALUE HERE'); } } if (!empty($cssprop)) { $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop); if (!$struct->value) { // if we tokenize CSS this might be a little more difficult to do $struct->value = array($cssprop, 'PUT VALUE HERE'); } } // Ok, structs are all setup, now time to register the error $struct->addError($severity, $msg); } /** * Retrieves raw error data for custom formatter to use */ public function getRaw() { return $this->errors; } /** * Default HTML formatting implementation for error messages * @param HTMLPurifier_Config $config Configuration, vital for HTML output nature * @param array $errors Errors array to display; used for recursion. * @return string */ public function getHTMLFormatted($config, $errors = null) { $ret = array(); $this->generator = new HTMLPurifier_Generator($config, $this->context); if ($errors === null) { $errors = $this->errors; } // 'At line' message needs to be removed // generation code for new structure goes here. It needs to be recursive. foreach ($this->lines as $line => $col_array) { if ($line == -1) { continue; } foreach ($col_array as $col => $struct) { $this->_renderStruct($ret, $struct, $line, $col); } } if (isset($this->lines[-1])) { $this->_renderStruct($ret, $this->lines[-1]); } if (empty($errors)) { return '
' . $this->locale->getMessage('ErrorCollector: No errors') . '
'; } else { return '';
//$string .= '';
//$string .= '';
$ret[] = $string;
}
foreach ($current->children as $array) {
$context[] = $current;
$stack = array_merge($stack, array_reverse($array, true));
for ($i = count($array); $i > 0; $i--) {
$context_stack[] = $context;
}
}
}
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/StringHashParser.php 0000644 00000007071 15266554155 0017157 0 ustar 00 'DefaultKeyValue',
* 'KEY' => 'Value',
* 'KEY2' => 'Value2',
* 'MULTILINE-KEY' => "Multiline\nvalue.\n",
* )
*
* We use this as an easy to use file-format for configuration schema
* files, but the class itself is usage agnostic.
*
* You can use ---- to forcibly terminate parsing of a single string-hash;
* this marker is used in multi string-hashes to delimit boundaries.
*/
class HTMLPurifier_StringHashParser
{
/**
* @type string
*/
public $default = 'ID';
/**
* Parses a file that contains a single string-hash.
* @param string $file
* @return array
*/
public function parseFile($file)
{
if (!file_exists($file)) {
return false;
}
$fh = fopen($file, 'r');
if (!$fh) {
return false;
}
$ret = $this->parseHandle($fh);
fclose($fh);
return $ret;
}
/**
* Parses a file that contains multiple string-hashes delimited by '----'
* @param string $file
* @return array
*/
public function parseMultiFile($file)
{
if (!file_exists($file)) {
return false;
}
$ret = array();
$fh = fopen($file, 'r');
if (!$fh) {
return false;
}
while (!feof($fh)) {
$ret[] = $this->parseHandle($fh);
}
fclose($fh);
return $ret;
}
/**
* Internal parser that acepts a file handle.
* @note While it's possible to simulate in-memory parsing by using
* custom stream wrappers, if such a use-case arises we should
* factor out the file handle into its own class.
* @param resource $fh File handle with pointer at start of valid string-hash
* block.
* @return array
*/
protected function parseHandle($fh)
{
$state = false;
$single = false;
$ret = array();
do {
$line = fgets($fh);
if ($line === false) {
break;
}
$line = rtrim($line, "\n\r");
if (!$state && $line === '') {
continue;
}
if ($line === '----') {
break;
}
if (strncmp('--#', $line, 3) === 0) {
// Comment
continue;
} elseif (strncmp('--', $line, 2) === 0) {
// Multiline declaration
$state = trim($line, '- ');
if (!isset($ret[$state])) {
$ret[$state] = '';
}
continue;
} elseif (!$state) {
$single = true;
if (strpos($line, ':') !== false) {
// Single-line declaration
list($state, $line) = explode(':', $line, 2);
$line = trim($line);
} else {
// Use default declaration
$state = $this->default;
}
}
if ($single) {
$ret[$state] = $line;
$single = false;
$state = false;
} else {
$ret[$state] .= "$line\n";
}
} while (!feof($fh));
return $ret;
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/AttrDef.php 0000644 00000012113 15266554155 0015252 0 ustar 00 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 String construction info
* @return HTMLPurifier_AttrDef 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!
* @param string $string a CSS colour definition
* @return string
*/
protected function mungeRgb($string)
{
$p = '\s*(\d+(\.\d+)?([%]?))\s*';
if (preg_match('/(rgba|hsla)\(/', $string)) {
return preg_replace('/(rgba|hsla)\('.$p.','.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8,\11)', $string);
}
return preg_replace('/(rgb|hsl)\('.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8)', $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
htmlpurifier/library/HTMLPurifier/IDAccumulator.php 0000644 00000003157 15266554155 0016425 0 ustar 00 load($config->get('Attr.IDBlacklist'));
return $id_accumulator;
}
/**
* Add an ID to the lookup table.
* @param string $id ID to be added.
* @return bool status, true if success, false if there's a dupe
*/
public function add($id)
{
if (isset($this->ids[$id])) {
return false;
}
return $this->ids[$id] = true;
}
/**
* Load a list of IDs into the lookup table
* @param $array_of_ids Array of IDs to load
* @note This function doesn't care about duplicates
*/
public function load($array_of_ids)
{
foreach ($array_of_ids as $id) {
$this->ids[$id] = true;
}
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/URI.php 0000644 00000024570 15266554155 0014372 0 ustar 00 scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme);
$this->userinfo = $userinfo;
$this->host = $host;
$this->port = is_null($port) ? $port : (int)$port;
$this->path = $path;
$this->query = $query;
$this->fragment = $fragment;
}
/**
* Retrieves a scheme object corresponding to the URI's scheme/default
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return HTMLPurifier_URIScheme Scheme object appropriate for validating this URI
*/
public function getSchemeObj($config, $context)
{
$registry = HTMLPurifier_URISchemeRegistry::instance();
if ($this->scheme !== null) {
$scheme_obj = $registry->getScheme($this->scheme, $config, $context);
if (!$scheme_obj) {
return false;
} // invalid scheme, clean it out
} else {
// no scheme: retrieve the default one
$def = $config->getDefinition('URI');
$scheme_obj = $def->getDefaultScheme($config, $context);
if (!$scheme_obj) {
if ($def->defaultScheme !== null) {
// something funky happened to the default scheme object
trigger_error(
'Default scheme object "' . $def->defaultScheme . '" was not readable',
E_USER_WARNING
);
} // suppress error if it's null
return false;
}
}
return $scheme_obj;
}
/**
* Generic validation method applicable for all schemes. May modify
* this URI in order to get it into a compliant form.
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool True if validation/filtering succeeds, false if failure
*/
public function validate($config, $context)
{
// ABNF definitions from RFC 3986
$chars_sub_delims = $config->get('URI.AllowedSymbols');
$chars_gen_delims = ':/?#[]@';
$chars_pchar = $chars_sub_delims . ':@';
// validate host
if (!is_null($this->host)) {
$host_def = new HTMLPurifier_AttrDef_URI_Host();
$this->host = $host_def->validate($this->host, $config, $context);
if ($this->host === false) {
$this->host = null;
}
}
// validate scheme
// NOTE: It's not appropriate to check whether or not this
// scheme is in our registry, since a URIFilter may convert a
// URI that we don't allow into one we do. So instead, we just
// check if the scheme can be dropped because there is no host
// and it is our default scheme.
if (!is_null($this->scheme) && is_null($this->host) || $this->host === '') {
// support for relative paths is pretty abysmal when the
// scheme is present, so axe it when possible
$def = $config->getDefinition('URI');
if ($def->defaultScheme === $this->scheme) {
$this->scheme = null;
}
}
// validate username
if (!is_null($this->userinfo)) {
$encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':');
$this->userinfo = $encoder->encode($this->userinfo);
}
// validate port
if (!is_null($this->port)) {
if ($this->port < 1 || $this->port > 65535) {
$this->port = null;
}
}
// validate path
$segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/');
if (!is_null($this->host)) { // this catches $this->host === ''
// path-abempty (hier and relative)
// http://www.example.com/my/path
// //www.example.com/my/path (looks odd, but works, and
// recognized by most browsers)
// (this set is valid or invalid on a scheme by scheme
// basis, so we'll deal with it later)
// file:///my/path
// ///my/path
$this->path = $segments_encoder->encode($this->path);
} elseif ($this->path !== '') {
if ($this->path[0] === '/') {
// path-absolute (hier and relative)
// http:/my/path
// /my/path
if (strlen($this->path) >= 2 && $this->path[1] === '/') {
// This could happen if both the host gets stripped
// out
// http://my/path
// //my/path
$this->path = '';
} else {
$this->path = $segments_encoder->encode($this->path);
}
} elseif (!is_null($this->scheme)) {
// path-rootless (hier)
// http:my/path
// Short circuit evaluation means we don't need to check nz
$this->path = $segments_encoder->encode($this->path);
} else {
// path-noscheme (relative)
// my/path
// (once again, not checking nz)
$segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@');
$c = strpos($this->path, '/');
if ($c !== false) {
$this->path =
$segment_nc_encoder->encode(substr($this->path, 0, $c)) .
$segments_encoder->encode(substr($this->path, $c));
} else {
$this->path = $segment_nc_encoder->encode($this->path);
}
}
} else {
// path-empty (hier and relative)
$this->path = ''; // just to be safe
}
// qf = query and fragment
$qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?');
if (!is_null($this->query)) {
$this->query = $qf_encoder->encode($this->query);
}
if (!is_null($this->fragment)) {
$this->fragment = $qf_encoder->encode($this->fragment);
}
return true;
}
/**
* Convert URI back to string
* @return string URI appropriate for output
*/
public function toString()
{
// reconstruct authority
$authority = null;
// there is a rendering difference between a null authority
// (http:foo-bar) and an empty string authority
// (http:///foo-bar).
if (!is_null($this->host)) {
$authority = '';
if (!is_null($this->userinfo)) {
$authority .= $this->userinfo . '@';
}
$authority .= $this->host;
if (!is_null($this->port)) {
$authority .= ':' . $this->port;
}
}
// Reconstruct the result
// One might wonder about parsing quirks from browsers after
// this reconstruction. Unfortunately, parsing behavior depends
// on what *scheme* was employed (file:///foo is handled *very*
// differently than http:///foo), so unfortunately we have to
// defer to the schemes to do the right thing.
$result = '';
if (!is_null($this->scheme)) {
$result .= $this->scheme . ':';
}
if (!is_null($authority)) {
$result .= '//' . $authority;
}
$result .= $this->path;
if (!is_null($this->query)) {
$result .= '?' . $this->query;
}
if (!is_null($this->fragment)) {
$result .= '#' . $this->fragment;
}
return $result;
}
/**
* Returns true if this URL might be considered a 'local' URL given
* the current context. This is true when the host is null, or
* when it matches the host supplied to the configuration.
*
* Note that this does not do any scheme checking, so it is mostly
* only appropriate for metadata that doesn't care about protocol
* security. isBenign is probably what you actually want.
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool
*/
public function isLocal($config, $context)
{
if ($this->host === null) {
return true;
}
$uri_def = $config->getDefinition('URI');
if ($uri_def->host === $this->host) {
return true;
}
return false;
}
/**
* Returns true if this URL should be considered a 'benign' URL,
* that is:
*
* - It is a local URL (isLocal), and
* - It has a equal or better level of security
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool
*/
public function isBenign($config, $context)
{
if (!$this->isLocal($config, $context)) {
return false;
}
$scheme_obj = $this->getSchemeObj($config, $context);
if (!$scheme_obj) {
return false;
} // conservative approach
$current_scheme_obj = $config->getDefinition('URI')->getDefaultScheme($config, $context);
if ($current_scheme_obj->secure) {
if (!$scheme_obj->secure) {
return false;
}
}
return true;
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php 0000644 00000024373 15266554155 0020134 0 ustar 00 config =& $config;
$this->def = $config->getHTMLDefinition();
$ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
$ret .= $this->renderDoctype();
$ret .= $this->renderEnvironment();
$ret .= $this->renderContentSets();
$ret .= $this->renderInfo();
$ret .= $this->end('div');
return $ret;
}
/**
* Renders the Doctype table
* @return string
*/
protected function renderDoctype()
{
$doctype = $this->def->doctype;
$ret = '';
$ret .= $this->start('table');
$ret .= $this->element('caption', 'Doctype');
$ret .= $this->row('Name', $doctype->name);
$ret .= $this->row('XML', $doctype->xml ? 'Yes' : 'No');
$ret .= $this->row('Default Modules', implode(', ', $doctype->modules));
$ret .= $this->row('Default Tidy Modules', implode(', ', $doctype->tidyModules));
$ret .= $this->end('table');
return $ret;
}
/**
* Renders environment table, which is miscellaneous info
* @return string
*/
protected function renderEnvironment()
{
$def = $this->def;
$ret = '';
$ret .= $this->start('table');
$ret .= $this->element('caption', 'Environment');
$ret .= $this->row('Parent of fragment', $def->info_parent);
$ret .= $this->renderChildren($def->info_parent_def->child);
$ret .= $this->row('Block wrap name', $def->info_block_wrapper);
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Global attributes');
$ret .= $this->element('td', $this->listifyAttr($def->info_global_attr), null, 0);
$ret .= $this->end('tr');
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Tag transforms');
$list = array();
foreach ($def->info_tag_transform as $old => $new) {
$new = $this->getClass($new, 'TagTransform_');
$list[] = "<$old> with $new";
}
$ret .= $this->element('td', $this->listify($list));
$ret .= $this->end('tr');
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Pre-AttrTransform');
$ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre));
$ret .= $this->end('tr');
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Post-AttrTransform');
$ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post));
$ret .= $this->end('tr');
$ret .= $this->end('table');
return $ret;
}
/**
* Renders the Content Sets table
* @return string
*/
protected function renderContentSets()
{
$ret = '';
$ret .= $this->start('table');
$ret .= $this->element('caption', 'Content Sets');
foreach ($this->def->info_content_sets as $name => $lookup) {
$ret .= $this->heavyHeader($name);
$ret .= $this->start('tr');
$ret .= $this->element('td', $this->listifyTagLookup($lookup));
$ret .= $this->end('tr');
}
$ret .= $this->end('table');
return $ret;
}
/**
* Renders the Elements ($info) table
* @return string
*/
protected function renderInfo()
{
$ret = '';
$ret .= $this->start('table');
$ret .= $this->element('caption', 'Elements ($info)');
ksort($this->def->info);
$ret .= $this->heavyHeader('Allowed tags', 2);
$ret .= $this->start('tr');
$ret .= $this->element('td', $this->listifyTagLookup($this->def->info), array('colspan' => 2));
$ret .= $this->end('tr');
foreach ($this->def->info as $name => $def) {
$ret .= $this->start('tr');
$ret .= $this->element('th', "<$name>", array('class' => 'heavy', 'colspan' => 2));
$ret .= $this->end('tr');
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Inline content');
$ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No');
$ret .= $this->end('tr');
if (!empty($def->excludes)) {
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Excludes');
$ret .= $this->element('td', $this->listifyTagLookup($def->excludes));
$ret .= $this->end('tr');
}
if (!empty($def->attr_transform_pre)) {
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Pre-AttrTransform');
$ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre));
$ret .= $this->end('tr');
}
if (!empty($def->attr_transform_post)) {
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Post-AttrTransform');
$ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post));
$ret .= $this->end('tr');
}
if (!empty($def->auto_close)) {
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Auto closed by');
$ret .= $this->element('td', $this->listifyTagLookup($def->auto_close));
$ret .= $this->end('tr');
}
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Allowed attributes');
$ret .= $this->element('td', $this->listifyAttr($def->attr), array(), 0);
$ret .= $this->end('tr');
if (!empty($def->required_attr)) {
$ret .= $this->row('Required attributes', $this->listify($def->required_attr));
}
$ret .= $this->renderChildren($def->child);
}
$ret .= $this->end('table');
return $ret;
}
/**
* Renders a row describing the allowed children of an element
* @param HTMLPurifier_ChildDef $def HTMLPurifier_ChildDef of pertinent element
* @return string
*/
protected function renderChildren($def)
{
$context = new HTMLPurifier_Context();
$ret = '';
$ret .= $this->start('tr');
$elements = array();
$attr = array();
if (isset($def->elements)) {
if ($def->type == 'strictblockquote') {
$def->validateChildren(array(), $this->config, $context);
}
$elements = $def->elements;
}
if ($def->type == 'chameleon') {
$attr['rowspan'] = 2;
} elseif ($def->type == 'empty') {
$elements = array();
} elseif ($def->type == 'table') {
$elements = array_flip(
array(
'col',
'caption',
'colgroup',
'thead',
'tfoot',
'tbody',
'tr'
)
);
}
$ret .= $this->element('th', 'Allowed children', $attr);
if ($def->type == 'chameleon') {
$ret .= $this->element(
'td',
'Block: ' .
$this->escape($this->listifyTagLookup($def->block->elements)),
null,
0
);
$ret .= $this->end('tr');
$ret .= $this->start('tr');
$ret .= $this->element(
'td',
'Inline: ' .
$this->escape($this->listifyTagLookup($def->inline->elements)),
null,
0
);
} elseif ($def->type == 'custom') {
$ret .= $this->element(
'td',
'' . ucfirst($def->type) . ': ' .
$def->dtd_regex
);
} else {
$ret .= $this->element(
'td',
'' . ucfirst($def->type) . ': ' .
$this->escape($this->listifyTagLookup($elements)),
null,
0
);
}
$ret .= $this->end('tr');
return $ret;
}
/**
* Listifies a tag lookup table.
* @param array $array Tag lookup array in form of array('tagname' => true)
* @return string
*/
protected function listifyTagLookup($array)
{
ksort($array);
$list = array();
foreach ($array as $name => $discard) {
if ($name !== '#PCDATA' && !isset($this->def->info[$name])) {
continue;
}
$list[] = $name;
}
return $this->listify($list);
}
/**
* Listifies a list of objects by retrieving class names and internal state
* @param array $array List of objects
* @return string
* @todo Also add information about internal state
*/
protected function listifyObjectList($array)
{
ksort($array);
$list = array();
foreach ($array as $obj) {
$list[] = $this->getClass($obj, 'AttrTransform_');
}
return $this->listify($list);
}
/**
* Listifies a hash of attributes to AttrDef classes
* @param array $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef)
* @return string
*/
protected function listifyAttr($array)
{
ksort($array);
$list = array();
foreach ($array as $name => $obj) {
if ($obj === false) {
continue;
}
$list[] = "$name = " . $this->getClass($obj, 'AttrDef_') . '';
}
return $this->listify($list);
}
/**
* Creates a heavy header row
* @param string $text
* @param int $num
* @return string
*/
protected function heavyHeader($text, $num = 1)
{
$ret = '';
$ret .= $this->start('tr');
$ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy'));
$ret .= $this->end('tr');
return $ret;
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php 0000644 00000035056 15266554155 0017410 0 ustar 00 docURL = $doc_url;
$this->name = $name;
$this->compress = $compress;
// initialize sub-printers
$this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default();
$this->fields[HTMLPurifier_VarParser::C_BOOL] = new HTMLPurifier_Printer_ConfigForm_bool();
}
/**
* Sets default column and row size for textareas in sub-printers
* @param $cols Integer columns of textarea, null to use default
* @param $rows Integer rows of textarea, null to use default
*/
public function setTextareaDimensions($cols = null, $rows = null)
{
if ($cols) {
$this->fields['default']->cols = $cols;
}
if ($rows) {
$this->fields['default']->rows = $rows;
}
}
/**
* Retrieves styling, in case it is not accessible by webserver
*/
public static function getCSS()
{
return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css');
}
/**
* Retrieves JavaScript, in case it is not accessible by webserver
*/
public static function getJavaScript()
{
return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js');
}
/**
* Returns HTML output for a configuration form
* @param HTMLPurifier_Config|array $config Configuration object of current form state, or an array
* where [0] has an HTML namespace and [1] is being rendered.
* @param array|bool $allowed Optional namespace(s) and directives to restrict form to.
* @param bool $render_controls
* @return string
*/
public function render($config, $allowed = true, $render_controls = true)
{
if (is_array($config) && isset($config[0])) {
$gen_config = $config[0];
$config = $config[1];
} else {
$gen_config = $config;
}
$this->config = $config;
$this->genConfig = $gen_config;
$this->prepareGenerator($gen_config);
$allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def);
$all = array();
foreach ($allowed as $key) {
list($ns, $directive) = $key;
$all[$ns][$directive] = $config->get($ns . '.' . $directive);
}
$ret = '';
$ret .= $this->start('table', array('class' => 'hp-config'));
$ret .= $this->start('thead');
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Directive', array('class' => 'hp-directive'));
$ret .= $this->element('th', 'Value', array('class' => 'hp-value'));
$ret .= $this->end('tr');
$ret .= $this->end('thead');
foreach ($all as $ns => $directives) {
$ret .= $this->renderNamespace($ns, $directives);
}
if ($render_controls) {
$ret .= $this->start('tbody');
$ret .= $this->start('tr');
$ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls'));
$ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit'));
$ret .= '[Reset]';
$ret .= $this->end('td');
$ret .= $this->end('tr');
$ret .= $this->end('tbody');
}
$ret .= $this->end('table');
return $ret;
}
/**
* Renders a single namespace
* @param $ns String namespace name
* @param array $directives array of directives to values
* @return string
*/
protected function renderNamespace($ns, $directives)
{
$ret = '';
$ret .= $this->start('tbody', array('class' => 'namespace'));
$ret .= $this->start('tr');
$ret .= $this->element('th', $ns, array('colspan' => 2));
$ret .= $this->end('tr');
$ret .= $this->end('tbody');
$ret .= $this->start('tbody');
foreach ($directives as $directive => $value) {
$ret .= $this->start('tr');
$ret .= $this->start('th');
if ($this->docURL) {
$url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL);
$ret .= $this->start('a', array('href' => $url));
}
$attr = array('for' => "{$this->name}:$ns.$directive");
// crop directive name if it's too long
if (!$this->compress || (strlen($directive) < $this->compress)) {
$directive_disp = $directive;
} else {
$directive_disp = substr($directive, 0, $this->compress - 2) . '...';
$attr['title'] = $directive;
}
$ret .= $this->element(
'label',
$directive_disp,
// component printers must create an element with this id
$attr
);
if ($this->docURL) {
$ret .= $this->end('a');
}
$ret .= $this->end('th');
$ret .= $this->start('td');
$def = $this->config->def->info["$ns.$directive"];
if (is_int($def)) {
$allow_null = $def < 0;
$type = abs($def);
} else {
$type = $def->type;
$allow_null = isset($def->allow_null);
}
if (!isset($this->fields[$type])) {
$type = 0;
} // default
$type_obj = $this->fields[$type];
if ($allow_null) {
$type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);
}
$ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config));
$ret .= $this->end('td');
$ret .= $this->end('tr');
}
$ret .= $this->end('tbody');
return $ret;
}
}
/**
* Printer decorator for directives that accept null
*/
class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
{
/**
* Printer being decorated
* @type HTMLPurifier_Printer
*/
protected $obj;
/**
* @param HTMLPurifier_Printer $obj Printer to decorate
*/
public function __construct($obj)
{
parent::__construct();
$this->obj = $obj;
}
/**
* @param string $ns
* @param string $directive
* @param string $value
* @param string $name
* @param HTMLPurifier_Config|array $config
* @return string
*/
public function render($ns, $directive, $value, $name, $config)
{
if (is_array($config) && isset($config[0])) {
$gen_config = $config[0];
$config = $config[1];
} else {
$gen_config = $config;
}
$this->prepareGenerator($gen_config);
$ret = '';
$ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive"));
$ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
$ret .= $this->text(' Null/Disabled');
$ret .= $this->end('label');
$attr = array(
'type' => 'checkbox',
'value' => '1',
'class' => 'null-toggle',
'name' => "$name" . "[Null_$ns.$directive]",
'id' => "$name:Null_$ns.$directive",
'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!!
);
if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) {
// modify inline javascript slightly
$attr['onclick'] =
"toggleWriteability('$name:Yes_$ns.$directive',checked);" .
"toggleWriteability('$name:No_$ns.$directive',checked)";
}
if ($value === null) {
$attr['checked'] = 'checked';
}
$ret .= $this->elementEmpty('input', $attr);
$ret .= $this->text(' or ');
$ret .= $this->elementEmpty('br');
$ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config));
return $ret;
}
}
/**
* Swiss-army knife configuration form field printer
*/
class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer
{
/**
* @type int
*/
public $cols = 18;
/**
* @type int
*/
public $rows = 5;
/**
* @param string $ns
* @param string $directive
* @param string $value
* @param string $name
* @param HTMLPurifier_Config|array $config
* @return string
*/
public function render($ns, $directive, $value, $name, $config)
{
if (is_array($config) && isset($config[0])) {
$gen_config = $config[0];
$config = $config[1];
} else {
$gen_config = $config;
}
$this->prepareGenerator($gen_config);
// this should probably be split up a little
$ret = '';
$def = $config->def->info["$ns.$directive"];
if (is_int($def)) {
$type = abs($def);
} else {
$type = $def->type;
}
if (is_array($value)) {
switch ($type) {
case HTMLPurifier_VarParser::LOOKUP:
$array = $value;
$value = array();
foreach ($array as $val => $b) {
$value[] = $val;
}
//TODO does this need a break?
case HTMLPurifier_VarParser::ALIST:
$value = implode(PHP_EOL, $value);
break;
case HTMLPurifier_VarParser::HASH:
$nvalue = '';
foreach ($value as $i => $v) {
if (is_array($v)) {
// HACK
$v = implode(";", $v);
}
$nvalue .= "$i:$v" . PHP_EOL;
}
$value = $nvalue;
break;
default:
$value = '';
}
}
if ($type === HTMLPurifier_VarParser::C_MIXED) {
return 'Not supported';
$value = serialize($value);
}
$attr = array(
'name' => "$name" . "[$ns.$directive]",
'id' => "$name:$ns.$directive"
);
if ($value === null) {
$attr['disabled'] = 'disabled';
}
if (isset($def->allowed)) {
$ret .= $this->start('select', $attr);
foreach ($def->allowed as $val => $b) {
$attr = array();
if ($value == $val) {
$attr['selected'] = 'selected';
}
$ret .= $this->element('option', $val, $attr);
}
$ret .= $this->end('select');
} elseif ($type === HTMLPurifier_VarParser::TEXT ||
$type === HTMLPurifier_VarParser::ITEXT ||
$type === HTMLPurifier_VarParser::ALIST ||
$type === HTMLPurifier_VarParser::HASH ||
$type === HTMLPurifier_VarParser::LOOKUP) {
$attr['cols'] = $this->cols;
$attr['rows'] = $this->rows;
$ret .= $this->start('textarea', $attr);
$ret .= $this->text($value);
$ret .= $this->end('textarea');
} else {
$attr['value'] = $value;
$attr['type'] = 'text';
$ret .= $this->elementEmpty('input', $attr);
}
return $ret;
}
}
/**
* Bool form field printer
*/
class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer
{
/**
* @param string $ns
* @param string $directive
* @param string $value
* @param string $name
* @param HTMLPurifier_Config|array $config
* @return string
*/
public function render($ns, $directive, $value, $name, $config)
{
if (is_array($config) && isset($config[0])) {
$gen_config = $config[0];
$config = $config[1];
} else {
$gen_config = $config;
}
$this->prepareGenerator($gen_config);
$ret = '';
$ret .= $this->start('div', array('id' => "$name:$ns.$directive"));
$ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive"));
$ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
$ret .= $this->text(' Yes');
$ret .= $this->end('label');
$attr = array(
'type' => 'radio',
'name' => "$name" . "[$ns.$directive]",
'id' => "$name:Yes_$ns.$directive",
'value' => '1'
);
if ($value === true) {
$attr['checked'] = 'checked';
}
if ($value === null) {
$attr['disabled'] = 'disabled';
}
$ret .= $this->elementEmpty('input', $attr);
$ret .= $this->start('label', array('for' => "$name:No_$ns.$directive"));
$ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
$ret .= $this->text(' No');
$ret .= $this->end('label');
$attr = array(
'type' => 'radio',
'name' => "$name" . "[$ns.$directive]",
'id' => "$name:No_$ns.$directive",
'value' => '0'
);
if ($value === false) {
$attr['checked'] = 'checked';
}
if ($value === null) {
$attr['disabled'] = 'disabled';
}
$ret .= $this->elementEmpty('input', $attr);
$ret .= $this->end('div');
return $ret;
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js 0000644 00000000216 15266554155 0017223 0 ustar 00 function toggleWriteability(id_of_patient, checked) {
document.getElementById(id_of_patient).disabled = checked;
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php 0000644 00000002252 15266554155 0020010 0 ustar 00 def = $config->getCSSDefinition();
$ret = '';
$ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
$ret .= $this->start('table');
$ret .= $this->element('caption', 'Properties ($info)');
$ret .= $this->start('thead');
$ret .= $this->start('tr');
$ret .= $this->element('th', 'Property', array('class' => 'heavy'));
$ret .= $this->element('th', 'Definition', array('class' => 'heavy', 'style' => 'width:auto;'));
$ret .= $this->end('tr');
$ret .= $this->end('thead');
ksort($this->def->info);
foreach ($this->def->info as $property => $obj) {
$name = $this->getClass($obj, 'AttrDef_');
$ret .= $this->row($property, $name);
}
$ret .= $this->end('table');
$ret .= $this->end('div');
return $ret;
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css 0000644 00000000455 15266554155 0017404 0 ustar 00
.hp-config {}
.hp-config tbody th {text-align:right; padding-right:0.5em;}
.hp-config thead, .hp-config .namespace {background:#3C578C; color:#FFF;}
.hp-config .namespace th {text-align:center;}
.hp-config .verbose {display:none;}
.hp-config .controls {text-align:center;}
/* vim: et sw=4 sts=4 */
htmlpurifier/library/HTMLPurifier/ContentSets.php 0000644 00000012744 15266554155 0016204 0 ustar 00 true) indexed by name.
* @type array
* @note This is in HTMLPurifier_HTMLDefinition->info_content_sets
*/
public $lookup = array();
/**
* Synchronized list of defined content sets (keys of info).
* @type array
*/
protected $keys = array();
/**
* Synchronized list of defined content values (values of info).
* @type array
*/
protected $values = array();
/**
* Merges in module's content sets, expands identifiers in the content
* sets and populates the keys, values and lookup member variables.
* @param HTMLPurifier_HTMLModule[] $modules List of HTMLPurifier_HTMLModule
*/
public function __construct($modules)
{
if (!is_array($modules)) {
$modules = array($modules);
}
// populate content_sets based on module hints
// sorry, no way of overloading
foreach ($modules as $module) {
foreach ($module->content_sets as $key => $value) {
$temp = $this->convertToLookup($value);
if (isset($this->lookup[$key])) {
// add it into the existing content set
$this->lookup[$key] = array_merge($this->lookup[$key], $temp);
} else {
$this->lookup[$key] = $temp;
}
}
}
$old_lookup = false;
while ($old_lookup !== $this->lookup) {
$old_lookup = $this->lookup;
foreach ($this->lookup as $i => $set) {
$add = array();
foreach ($set as $element => $x) {
if (isset($this->lookup[$element])) {
$add += $this->lookup[$element];
unset($this->lookup[$i][$element]);
}
}
$this->lookup[$i] += $add;
}
}
foreach ($this->lookup as $key => $lookup) {
$this->info[$key] = implode(' | ', array_keys($lookup));
}
$this->keys = array_keys($this->info);
$this->values = array_values($this->info);
}
/**
* Accepts a definition; generates and assigns a ChildDef for it
* @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef reference
* @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
*/
public function generateChildDef(&$def, $module)
{
if (!empty($def->child)) { // already done!
return;
}
$content_model = $def->content_model;
if (is_string($content_model)) {
// Assume that $this->keys is alphanumeric
$def->content_model = preg_replace_callback(
'/\b(' . implode('|', $this->keys) . ')\b/',
array($this, 'generateChildDefCallback'),
$content_model
);
//$def->content_model = str_replace(
// $this->keys, $this->values, $content_model);
}
$def->child = $this->getChildDef($def, $module);
}
public function generateChildDefCallback($matches)
{
return $this->info[$matches[0]];
}
/**
* Instantiates a ChildDef based on content_model and content_model_type
* member variables in HTMLPurifier_ElementDef
* @note This will also defer to modules for custom HTMLPurifier_ChildDef
* subclasses that need content set expansion
* @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef to have ChildDef extracted
* @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
* @return HTMLPurifier_ChildDef corresponding to ElementDef
*/
public function getChildDef($def, $module)
{
$value = $def->content_model;
if (is_object($value)) {
trigger_error(
'Literal object child definitions should be stored in '.
'ElementDef->child not ElementDef->content_model',
E_USER_NOTICE
);
return $value;
}
switch ($def->content_model_type) {
case 'required':
return new HTMLPurifier_ChildDef_Required($value);
case 'optional':
return new HTMLPurifier_ChildDef_Optional($value);
case 'empty':
return new HTMLPurifier_ChildDef_Empty();
case 'custom':
return new HTMLPurifier_ChildDef_Custom($value);
}
// defer to its module
$return = false;
if ($module->defines_child_def) { // save a func call
$return = $module->getChildDef($def);
}
if ($return !== false) {
return $return;
}
throw new Exception(
'Could not determine which ChildDef class to instantiate',
E_USER_ERROR
);
}
/**
* Converts a string list of elements separated by pipes into
* a lookup array.
* @param string $string List of elements
* @return array Lookup array of elements
*/
protected function convertToLookup($string)
{
$array = explode('|', str_replace(' ', '', $string));
$ret = array();
foreach ($array as $k) {
$ret[$k] = true;
}
return $ret;
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php 0000644 00000002404 15266554155 0020576 0 ustar 00 getDefinition('URI')->host;
if ($our_host !== null) {
$this->ourHostParts = array_reverse(explode('.', $our_host));
}
}
/**
* @param HTMLPurifier_URI $uri Reference
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool
*/
public function filter(&$uri, $config, $context)
{
if (is_null($uri->host)) {
return true;
}
if ($this->ourHostParts === false) {
return false;
}
$host_parts = array_reverse(explode('.', $uri->host));
foreach ($this->ourHostParts as $i => $x) {
if (!isset($host_parts[$i])) {
return false;
}
if ($host_parts[$i] != $this->ourHostParts[$i]) {
return false;
}
}
return true;
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php 0000644 00000003467 15266554155 0017544 0 ustar 00 regexp = $config->get('URI.SafeIframeRegexp');
return true;
}
/**
* @param HTMLPurifier_URI $uri
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool
*/
public function filter(&$uri, $config, $context)
{
// check if filter not applicable
if (!$config->get('HTML.SafeIframe')) {
return true;
}
// check if the filter should actually trigger
if (!$context->get('EmbeddedURI', true)) {
return true;
}
$token = $context->get('CurrentToken', true);
if (!($token && $token->name == 'iframe')) {
return true;
}
// check if we actually have some whitelists enabled
if ($this->regexp !== null) {
return preg_match($this->regexp, $uri->toString());
}
// check if the host is in a whitelist for safe iframe hosts
$safeHosts = $config->get('URI.SafeIframeHosts');
return $safeHosts !== null && isset($safeHosts[$uri->host]);
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php 0000644 00000011541 15266554155 0020106 0 ustar 00 getDefinition('URI');
$this->base = $def->base;
if (is_null($this->base)) {
trigger_error(
'URI.MakeAbsolute is being ignored due to lack of ' .
'value for URI.Base configuration',
E_USER_WARNING
);
return false;
}
$this->base->fragment = null; // fragment is invalid for base URI
$stack = explode('/', $this->base->path);
array_pop($stack); // discard last segment
$stack = $this->_collapseStack($stack); // do pre-parsing
$this->basePathStack = $stack;
return true;
}
/**
* @param HTMLPurifier_URI $uri
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool
*/
public function filter(&$uri, $config, $context)
{
if (is_null($this->base)) {
return true;
} // abort early
if ($uri->path === '' && is_null($uri->scheme) &&
is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) {
// reference to current document
$uri = clone $this->base;
return true;
}
if (!is_null($uri->scheme)) {
// absolute URI already: don't change
if (!is_null($uri->host)) {
return true;
}
$scheme_obj = $uri->getSchemeObj($config, $context);
if (!$scheme_obj) {
// scheme not recognized
return false;
}
if (!$scheme_obj->hierarchical) {
// non-hierarchal URI with explicit scheme, don't change
return true;
}
// special case: had a scheme but always is hierarchical and had no authority
}
if (!is_null($uri->host)) {
// network path, don't bother
return true;
}
if ($uri->path === '') {
$uri->path = $this->base->path;
} elseif ($uri->path[0] !== '/') {
// relative path, needs more complicated processing
$stack = explode('/', $uri->path);
$new_stack = array_merge($this->basePathStack, $stack);
if ($new_stack[0] !== '' && !is_null($this->base->host)) {
array_unshift($new_stack, '');
}
$new_stack = $this->_collapseStack($new_stack);
$uri->path = implode('/', $new_stack);
} else {
// absolute path, but still we should collapse
$uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path)));
}
// re-combine
$uri->scheme = $this->base->scheme;
if (is_null($uri->userinfo)) {
$uri->userinfo = $this->base->userinfo;
}
if (is_null($uri->host)) {
$uri->host = $this->base->host;
}
if (is_null($uri->port)) {
$uri->port = $this->base->port;
}
return true;
}
/**
* Resolve dots and double-dots in a path stack
* @param array $stack
* @return array
*/
private function _collapseStack($stack)
{
$result = array();
$is_folder = false;
for ($i = 0; isset($stack[$i]); $i++) {
$is_folder = false;
// absorb an internally duplicated slash
if ($stack[$i] == '' && $i && isset($stack[$i + 1])) {
continue;
}
if ($stack[$i] == '..') {
if (!empty($result)) {
$segment = array_pop($result);
if ($segment === '' && empty($result)) {
// error case: attempted to back out too far:
// restore the leading slash
$result[] = '';
} elseif ($segment === '..') {
$result[] = '..'; // cannot remove .. with ..
}
} else {
// relative path, preserve the double-dots
$result[] = '..';
}
$is_folder = true;
continue;
}
if ($stack[$i] == '.') {
// silently absorb
$is_folder = true;
continue;
}
$result[] = $stack[$i];
}
if ($is_folder) {
$result[] = '';
}
return $result;
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php 0000644 00000001110 15266554155 0022462 0 ustar 00 get('EmbeddedURI', true)) {
return true;
}
return parent::filter($uri, $config, $context);
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php 0000644 00000006130 15266554155 0016603 0 ustar 00 target = $config->get('URI.' . $this->name);
$this->parser = new HTMLPurifier_URIParser();
$this->doEmbed = $config->get('URI.MungeResources');
$this->secretKey = $config->get('URI.MungeSecretKey');
if ($this->secretKey && !function_exists('hash_hmac')) {
throw new Exception("Cannot use %URI.MungeSecretKey without hash_hmac support.");
}
return true;
}
/**
* @param HTMLPurifier_URI $uri
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool
*/
public function filter(&$uri, $config, $context)
{
if ($context->get('EmbeddedURI', true) && !$this->doEmbed) {
return true;
}
$scheme_obj = $uri->getSchemeObj($config, $context);
if (!$scheme_obj) {
return true;
} // ignore unknown schemes, maybe another postfilter did it
if (!$scheme_obj->browsable) {
return true;
} // ignore non-browsable schemes, since we can't munge those in a reasonable way
if ($uri->isBenign($config, $context)) {
return true;
} // don't redirect if a benign URL
$this->makeReplace($uri, $config, $context);
$this->replace = array_map('rawurlencode', $this->replace);
$new_uri = strtr($this->target, $this->replace);
$new_uri = $this->parser->parse($new_uri);
// don't redirect if the target host is the same as the
// starting host
if ($uri->host === $new_uri->host) {
return true;
}
$uri = $new_uri; // overwrite
return true;
}
/**
* @param HTMLPurifier_URI $uri
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
*/
protected function makeReplace($uri, $config, $context)
{
$string = $uri->toString();
// always available
$this->replace['%s'] = $string;
$this->replace['%r'] = $context->get('EmbeddedURI', true) ?: '';
$token = $context->get('CurrentToken', true) ?: '';
$this->replace['%n'] = $token ? $token->name : '';
$this->replace['%m'] = $context->get('CurrentAttr', true) ?: '';
$this->replace['%p'] = $context->get('CurrentCSSProperty', true) ?: '';
// not always available
if ($this->secretKey) {
$this->replace['%t'] = hash_hmac("sha256", $string, $this->secretKey);
}
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php 0000644 00000002227 15266554155 0020301 0 ustar 00 blacklist = $config->get('URI.HostBlacklist');
return true;
}
/**
* @param HTMLPurifier_URI $uri
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool
*/
public function filter(&$uri, $config, $context)
{
foreach ($this->blacklist as $blacklisted_host_fragment) {
if ($uri->host !== null && strpos($uri->host, $blacklisted_host_fragment) !== false) {
return false;
}
}
return true;
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php 0000644 00000000716 15266554155 0020772 0 ustar 00 get('EmbeddedURI', true);
}
}
// vim: et sw=4 sts=4
htmlpurifier/library/HTMLPurifier/Lexer/DirectLex.php 0000644 00000046467 15266554155 0016706 0 ustar 00 get('HTML.Trusted')) {
$html = preg_replace_callback(
'#()#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 + substr_count($html, $nl, 0, $cursor);
}
}
$position_next_lt = strpos($html, '<', $cursor);
$position_next_gt = strpos($html, '>', $cursor);
// triggers on "asdf" but not "asdf "
// 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->parseText(
substr(
$html,
$cursor,
$position_next_lt - $cursor
), $config
)
);
if ($maintain_line_numbers) {
$token->rawPosition($current_line, $current_col);
$current_line += substr_count($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->parseText(
substr(
$html,
$cursor
), $config
)
);
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 += substr_count($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 += substr_count($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 += substr_count($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 to
foreach ($definition->info[$token->name]->attr_transform_pre as $transform) { $attr = $transform->transform($o = $attr, $config, $context); if ($e) { if ($attr != $o) { $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); } } } // create alias to this element's attribute definition array, see // also $d_defs (global attribute definition array) // DEFINITION CALL $defs = $definition->info[$token->name]->attr; $attr_key = false; $context->register('CurrentAttr', $attr_key); // iterate through all the attribute keypairs // Watch out for name collisions: $key has previously been used foreach ($attr as $attr_key => $value) { // call the definition if (isset($defs[$attr_key])) { // there is a local definition defined if ($defs[$attr_key] === false) { // We've explicitly been told not to allow this element. // This is usually when there's a global definition // that must be overridden. // Theoretically speaking, we could have a // AttrDef_DenyAll, but this is faster! $result = false; } else { // validate according to the element's definition $result = $defs[$attr_key]->validate( $value, $config, $context ); } } elseif (isset($d_defs[$attr_key])) { // there is a global definition defined, validate according // to the global definition $result = $d_defs[$attr_key]->validate( $value, $config, $context ); } else { // system never heard of the attribute? DELETE! $result = false; } // put the results into effect if ($result === false || $result === null) { // this is a generic error message that should replaced // with more specific ones when possible if ($e) { $e->send(E_ERROR, 'AttrValidator: Attribute removed'); } // remove the attribute unset($attr[$attr_key]); } elseif (is_string($result)) { // generally, if a substitution is happening, there // was some sort of implicit correction going on. We'll // delegate it to the attribute classes to say exactly what. // simple substitution $attr[$attr_key] = $result; } else { // nothing happens } // we'd also want slightly more complicated substitution // involving an array as the return value, // although we're not sure how colliding attributes would // resolve (certain ones would be completely overridden, // others would prepend themselves). } $context->destroy('CurrentAttr'); // post transforms // global (error reporting untested) foreach ($definition->info_attr_transform_post as $transform) { $attr = $transform->transform($o = $attr, $config, $context); if ($e) { if ($attr != $o) { $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); } } } // local (error reporting untested) foreach ($definition->info[$token->name]->attr_transform_post as $transform) { $attr = $transform->transform($o = $attr, $config, $context); if ($e) { if ($attr != $o) { $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); } } } $token->attr = $attr; // destroy CurrentToken if we made it ourselves if (!$current_token) { $context->destroy('CurrentToken'); } } } // vim: et sw=4 sts=4 htmlpurifier/library/HTMLPurifier/Node/Comment.php 0000644 00000001325 15266554155 0016213 0 ustar 00 data = $data; $this->line = $line; $this->col = $col; } public function toTokenPair() { return array(new HTMLPurifier_Token_Comment($this->data, $this->line, $this->col), null); } } htmlpurifier/library/HTMLPurifier/Node/Text.php 0000644 00000002544 15266554155 0015541 0 ustar 00 data = $data; $this->is_whitespace = $is_whitespace; $this->line = $line; $this->col = $col; } public function toTokenPair() { return array(new HTMLPurifier_Token_Text($this->data, $this->line, $this->col), null); } } // vim: et sw=4 sts=4 htmlpurifier/library/HTMLPurifier/Node/Element.php 0000644 00000003275 15266554155 0016210 0 ustar 00 form or the form, i.e. * is it a pair of start/end tokens or an empty token. * @bool */ public $empty = false; public $endCol = null, $endLine = null, $endArmor = array(); public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) { $this->name = $name; $this->attr = $attr; $this->line = $line; $this->col = $col; $this->armor = $armor; } public function toTokenPair() { // XXX inefficiency here, normalization is not necessary if ($this->empty) { return array(new HTMLPurifier_Token_Empty($this->name, $this->attr, $this->line, $this->col, $this->armor), null); } else { $start = new HTMLPurifier_Token_Start($this->name, $this->attr, $this->line, $this->col, $this->armor); $end = new HTMLPurifier_Token_End($this->name, array(), $this->endLine, $this->endCol, $this->endArmor); //$end->start = $start; return array($start, $end); } } } htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php 0000644 00000003423 15266554155 0020615 0 ustar 00 array('href')); /** * @param HTMLPurifier_Config $config * @param HTMLPurifier_Context $context * @return string */ public function prepare($config, $context) { $this->docURL = $config->get('AutoFormat.PurifierLinkify.DocURL'); return parent::prepare($config, $context); } /** * @param HTMLPurifier_Token $token */ public function handleText(&$token) { if (!$this->allowsElement('a')) { return; } if (strpos($token->data, '%') === false) { return; } $bits = preg_split('#%([a-z0-9]+\.[a-z0-9]+)#Si', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE); $token = array(); // $i = index // $c = count // $l = is link for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) { if (!$l) { if ($bits[$i] === '') { continue; } $token[] = new HTMLPurifier_Token_Text($bits[$i]); } else { $token[] = new HTMLPurifier_Token_Start( 'a', array('href' => str_replace('%s', $bits[$i], $this->docURL)) ); $token[] = new HTMLPurifier_Token_Text('%' . $bits[$i]); $token[] = new HTMLPurifier_Token_End('a'); } } } } // vim: et sw=4 sts=4 htmlpurifier/library/HTMLPurifier/Injector/Linkify.php 0000644 00000004050 15266554155 0017104 0 ustar 00 array('href')); /** * @param HTMLPurifier_Token $token */ public function handleText(&$token) { if (!$this->allowsElement('a')) { return; } if (strpos($token->data, '://') === false) { // our really quick heuristic failed, abort // this may not work so well if we want to match things like // "google.com", but then again, most people don't return; } // there is/are URL(s). Let's split the string. // We use this regex: // https://gist.github.com/gruber/249502 // but with @cscott's backtracking fix and also // the Unicode characters un-Unicodified. $bits = preg_split( '/\\b((?:[a-z][\\w\\-]+:(?:\\/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}\\/)(?:[^\\s()<>]|\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\))+(?:\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'".,<>?\x{00ab}\x{00bb}\x{201c}\x{201d}\x{2018}\x{2019}]))/iu', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE); if ($bits === false) { return; } $token = array(); // $i = index // $c = count // $l = is link for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) { if (!$l) { if ($bits[$i] === '') { continue; } $token[] = new HTMLPurifier_Token_Text($bits[$i]); } else { $token[] = new HTMLPurifier_Token_Start('a', array('href' => $bits[$i])); $token[] = new HTMLPurifier_Token_Text($bits[$i]); $token[] = new HTMLPurifier_Token_End('a'); } } } } // vim: et sw=4 sts=4 htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php 0000644 00000001533 15266554155 0020305 0 ustar 00 start->attr['href'])) { $url = $token->start->attr['href']; unset($token->start->attr['href']); $token = array($token, new HTMLPurifier_Token_Text(" ($url)")); } else { // nothing to display } } } // vim: et sw=4 sts=4 htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php 0000644 00000032744 15266554155 0020250 0 ustar 00 armor['MakeWellFormed_TagClosedError'] = true; return $par; } /** * @param HTMLPurifier_Token_Text $token */ public function handleText(&$token) { $text = $token->data; // Does the current parent allow
tags? if ($this->allowsElement('p')) { if (empty($this->currentNesting) || strpos($text, "\n\n") !== false) { // Note that we have differing behavior when dealing with text // in the anonymous root node, or a node inside the document. // If the text as a double-newline, the treatment is the same; // if it doesn't, see the next if-block if you're in the document. $i = $nesting = null; if (!$this->forwardUntilEndToken($i, $current, $nesting) && $token->is_whitespace) { // State 1.1: ... ^ (whitespace, then document end) // ---- // This is a degenerate case } else { if (!$token->is_whitespace || $this->_isInline($current)) { // State 1.2: PAR1 // ---- // State 1.3: PAR1\n\nPAR2 // ------------ // State 1.4:
tag? } elseif (!empty($this->currentNesting) && $this->currentNesting[count($this->currentNesting) - 1]->name == 'p') { // State 3.1: ...
PAR1 // ---- // State 3.2: ...
PAR1\n\nPAR2 // ------------ $token = array(); $this->_splitText($text, $token); // Abort! } else { // State 4.1: ...PAR1 // ---- // State 4.2: ...PAR1\n\nPAR2 // ------------ } } /** * @param HTMLPurifier_Token $token */ public function handleElement(&$token) { // We don't have to check if we're already in a
tag for block // tokens, because the tag would have been autoclosed by MakeWellFormed. if ($this->allowsElement('p')) { if (!empty($this->currentNesting)) { if ($this->_isInline($token)) { // State 1:
PAR1
\n\n // --- // Quite frankly, this should be handled by splitText $token = array($this->_pStart(), $token); } else { // State 1.1.1:PAR1
// --- // State 1.1.2:is needed. if ($this->_pLookAhead()) { // State 1.3.1:
tags. } } } } else { // State 2.2:
// --- } } /** * Splits up a text in paragraph tokens and appends them * to the result stream that will replace the original * @param string $data String text data that will be processed * into paragraphs * @param HTMLPurifier_Token[] $result Reference to array of tokens that the * tags will be appended onto */ private function _splitText($data, &$result) { $raw_paragraphs = explode("\n\n", $data); $paragraphs = array(); // without empty paragraphs $needs_start = false; $needs_end = false; $c = count($raw_paragraphs); if ($c == 1) { // There were no double-newlines, abort quickly. In theory this // should never happen. $result[] = new HTMLPurifier_Token_Text($data); return; } for ($i = 0; $i < $c; $i++) { $par = $raw_paragraphs[$i]; if (trim($par) !== '') { $paragraphs[] = $par; } else { if ($i == 0) { // Double newline at the front if (empty($result)) { // The empty result indicates that the AutoParagraph // injector did not add any start paragraph tokens. // This means that we have been in a paragraph for // a while, and the newline means we should start a new one. $result[] = new HTMLPurifier_Token_End('p'); $result[] = new HTMLPurifier_Token_Text("\n\n"); // However, the start token should only be added if // there is more processing to be done (i.e. there are // real paragraphs in here). If there are none, the // next start paragraph tag will be handled by the // next call to the injector $needs_start = true; } else { // We just started a new paragraph! // Reinstate a double-newline for presentation's sake, since // it was in the source code. array_unshift($result, new HTMLPurifier_Token_Text("\n\n")); } } elseif ($i + 1 == $c) { // Double newline at the end // There should be a trailing
when we're finally done. $needs_end = true; } } } // Check if this was just a giant blob of whitespace. Move this earlier, // perhaps? if (empty($paragraphs)) { return; } // Add the start tag indicated by \n\n at the beginning of $data if ($needs_start) { $result[] = $this->_pStart(); } // Append the paragraphs onto the result foreach ($paragraphs as $par) { $result[] = new HTMLPurifier_Token_Text($par); $result[] = new HTMLPurifier_Token_End('p'); $result[] = new HTMLPurifier_Token_Text("\n\n"); $result[] = $this->_pStart(); } // Remove trailing start token; Injector will handle this later if // it was indeed needed. This prevents from needing to do a lookahead, // at the cost of a lookbehind later. array_pop($result); // If there is no need for an end tag, remove all of it and let // MakeWellFormed close it later. if (!$needs_end) { array_pop($result); // removes \n\n array_pop($result); // removes } } /** * Returns true if passed token is inline (and, ergo, allowed in * paragraph tags) * @param HTMLPurifier_Token $token * @return bool */ private function _isInline($token) { return isset($this->htmlDefinition->info['p']->child->elements[$token->name]); } /** * Looks ahead in the token list and determines whether or not we need * to insert atag. * @return bool */ private function _pLookAhead() { if ($this->currentToken instanceof HTMLPurifier_Token_Start) { $nesting = 1; } else { $nesting = 0; } $ok = false; $i = null; while ($this->forwardUntilEndToken($i, $current, $nesting)) { $result = $this->_checkNeedsP($current); if ($result !== null) { $ok = $result; break; } } return $ok; } /** * Determines if a particular token requires an earlier inline token * to get a paragraph. This should be used with _forwardUntilEndToken * @param HTMLPurifier_Token $current * @return bool */ private function _checkNeedsP($current) { if ($current instanceof HTMLPurifier_Token_Start) { if (!$this->_isInline($current)) { //