Cheetah
HTMLPurifier.standalone.php
Go to the documentation of this file.
1 <?php
2 
41 /*
42  HTML Purifier 4.12.0 - Standards Compliant HTML Filtering
43  Copyright (C) 2006-2008 Edward Z. Yang
44 
45  This library is free software; you can redistribute it and/or
46  modify it under the terms of the GNU Lesser General Public
47  License as published by the Free Software Foundation; either
48  version 2.1 of the License, or (at your option) any later version.
49 
50  This library is distributed in the hope that it will be useful,
51  but WITHOUT ANY WARRANTY; without even the implied warranty of
52  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
53  Lesser General Public License for more details.
54 
55  You should have received a copy of the GNU Lesser General Public
56  License along with this library; if not, write to the Free Software
57  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
58  */
59 
75 {
76 
81  public $version = '4.12.0';
82 
86  const VERSION = '4.12.0';
87 
92  public $config;
93 
99  private $filters = array();
100 
105  private static $instance;
106 
110  protected $strategy;
111 
115  protected $generator;
116 
122  public $context;
123 
134  public function __construct($config = null)
135  {
136  $this->config = HTMLPurifier_Config::create($config);
137  $this->strategy = new HTMLPurifier_Strategy_Core();
138  }
139 
145  public function addFilter($filter)
146  {
147  trigger_error(
148  'HTMLPurifier->addFilter() is deprecated, use configuration directives' .
149  ' in the Filter namespace or Filter.Custom',
150  E_USER_WARNING
151  );
152  $this->filters[] = $filter;
153  }
154 
166  public function purify($html, $config = null)
167  {
168  // :TODO: make the config merge in, instead of replace
170 
171  // implementation is partially environment dependant, partially
172  // configuration dependant
174 
176 
177  // setup HTML generator
178  $this->generator = new HTMLPurifier_Generator($config, $context);
179  $context->register('Generator', $this->generator);
180 
181  // set up global context variables
182  if ($config->get('Core.CollectErrors')) {
183  // may get moved out if other facilities use it
184  $language_factory = HTMLPurifier_LanguageFactory::instance();
185  $language = $language_factory->create($config, $context);
186  $context->register('Locale', $language);
187 
188  $error_collector = new HTMLPurifier_ErrorCollector($context);
189  $context->register('ErrorCollector', $error_collector);
190  }
191 
192  // setup id_accumulator context, necessary due to the fact that
193  // AttrValidator can be called from many places
195  $context->register('IDAccumulator', $id_accumulator);
196 
198 
199  // setup filters
200  $filter_flags = $config->getBatch('Filter');
201  $custom_filters = $filter_flags['Custom'];
202  unset($filter_flags['Custom']);
203  $filters = array();
204  foreach ($filter_flags as $filter => $flag) {
205  if (!$flag) {
206  continue;
207  }
208  if (strpos($filter, '.') !== false) {
209  continue;
210  }
211  $class = "HTMLPurifier_Filter_$filter";
212  $filters[] = new $class;
213  }
214  foreach ($custom_filters as $filter) {
215  // maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat
216  $filters[] = $filter;
217  }
218  $filters = array_merge($filters, $this->filters);
219  // maybe prepare(), but later
220 
221  for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) {
222  $html = $filters[$i]->preFilter($html, $config, $context);
223  }
224 
225  // purified HTML
226  $html =
227  $this->generator->generateFromTokens(
228  // list of tokens
229  $this->strategy->execute(
230  // list of un-purified tokens
231  $lexer->tokenizeHTML(
232  // un-purified HTML
233  $html,
234  $config,
235  $context
236  ),
237  $config,
238  $context
239  )
240  );
241 
242  for ($i = $filter_size - 1; $i >= 0; $i--) {
243  $html = $filters[$i]->postFilter($html, $config, $context);
244  }
245 
247  $this->context =& $context;
248  return $html;
249  }
250 
260  public function purifyArray($array_of_html, $config = null)
261  {
262  $context_array = array();
263  foreach($array_of_html as $key=>$value){
264  if (is_array($value)) {
265  $array[$key] = $this->purifyArray($value, $config);
266  } else {
267  $array[$key] = $this->purify($value, $config);
268  }
269  $context_array[$key] = $this->context;
270  }
271  $this->context = $context_array;
272  return $array;
273  }
274 
285  public static function instance($prototype = null)
286  {
287  if (!self::$instance || $prototype) {
288  if ($prototype instanceof HTMLPurifier) {
289  self::$instance = $prototype;
290  } elseif ($prototype) {
291  self::$instance = new HTMLPurifier($prototype);
292  } else {
293  self::$instance = new HTMLPurifier();
294  }
295  }
296  return self::$instance;
297  }
298 
310  public static function getInstance($prototype = null)
311  {
312  return HTMLPurifier::instance($prototype);
313  }
314 }
315 
316 
317 
318 
319 
328 {
329  public static function arborize($tokens, $config, $context) {
330  $definition = $config->getHTMLDefinition();
331  $parent = new HTMLPurifier_Token_Start($definition->info_parent);
332  $stack = array($parent->toNode());
333  foreach ($tokens as $token) {
334  $token->skip = null; // [MUT]
335  $token->carryover = null; // [MUT]
336  if ($token instanceof HTMLPurifier_Token_End) {
337  $token->start = null; // [MUT]
338  $r = array_pop($stack);
339  //assert($r->name === $token->name);
340  //assert(empty($token->attr));
341  $r->endCol = $token->col;
342  $r->endLine = $token->line;
343  $r->endArmor = $token->armor;
344  continue;
345  }
346  $node = $token->toNode();
347  $stack[count($stack)-1]->children[] = $node;
348  if ($token instanceof HTMLPurifier_Token_Start) {
349  $stack[] = $node;
350  }
351  }
352  //assert(count($stack) == 1);
353  return $stack[0];
354  }
355 
356  public static function flatten($node, $config, $context) {
357  $level = 0;
358  $nodes = array($level => new HTMLPurifier_Queue(array($node)));
359  $closingTokens = array();
360  $tokens = array();
361  do {
362  while (!$nodes[$level]->isEmpty()) {
363  $node = $nodes[$level]->shift(); // FIFO
364  list($start, $end) = $node->toTokenPair();
365  if ($level > 0) {
366  $tokens[] = $start;
367  }
368  if ($end !== NULL) {
369  $closingTokens[$level][] = $end;
370  }
371  if ($node instanceof HTMLPurifier_Node_Element) {
372  $level++;
373  $nodes[$level] = new HTMLPurifier_Queue();
374  foreach ($node->children as $childNode) {
375  $nodes[$level]->push($childNode);
376  }
377  }
378  }
379  $level--;
380  if ($level && isset($closingTokens[$level])) {
381  while ($token = array_pop($closingTokens[$level])) {
382  $tokens[] = $token;
383  }
384  }
385  } while ($level > 0);
386  return $tokens;
387  }
388 }
389 
390 
391 
397 {
398 
403  public $info = array();
404 
412  public function __construct($attr_types, $modules)
413  {
414  $this->doConstruct($attr_types, $modules);
415  }
416 
417  public function doConstruct($attr_types, $modules)
418  {
419  // load extensions from the modules
420  foreach ($modules as $module) {
421  foreach ($module->attr_collections as $coll_i => $coll) {
422  if (!isset($this->info[$coll_i])) {
423  $this->info[$coll_i] = array();
424  }
425  foreach ($coll as $attr_i => $attr) {
426  if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
427  // merge in includes
428  $this->info[$coll_i][$attr_i] = array_merge(
429  $this->info[$coll_i][$attr_i],
430  $attr
431  );
432  continue;
433  }
434  $this->info[$coll_i][$attr_i] = $attr;
435  }
436  }
437  }
438  // perform internal expansions and inclusions
439  foreach ($this->info as $name => $attr) {
440  // merge attribute collections that include others
441  $this->performInclusions($this->info[$name]);
442  // replace string identifiers with actual attribute objects
443  $this->expandIdentifiers($this->info[$name], $attr_types);
444  }
445  }
446 
452  public function performInclusions(&$attr)
453  {
454  if (!isset($attr[0])) {
455  return;
456  }
457  $merge = $attr[0];
458  $seen = array(); // recursion guard
459  // loop through all the inclusions
460  for ($i = 0; isset($merge[$i]); $i++) {
461  if (isset($seen[$merge[$i]])) {
462  continue;
463  }
464  $seen[$merge[$i]] = true;
465  // foreach attribute of the inclusion, copy it over
466  if (!isset($this->info[$merge[$i]])) {
467  continue;
468  }
469  foreach ($this->info[$merge[$i]] as $key => $value) {
470  if (isset($attr[$key])) {
471  continue;
472  } // also catches more inclusions
473  $attr[$key] = $value;
474  }
475  if (isset($this->info[$merge[$i]][0])) {
476  // recursion
477  $merge = array_merge($merge, $this->info[$merge[$i]][0]);
478  }
479  }
480  unset($attr[0]);
481  }
482 
489  public function expandIdentifiers(&$attr, $attr_types)
490  {
491  // because foreach will process new elements we add, make sure we
492  // skip duplicates
493  $processed = array();
494 
495  foreach ($attr as $def_i => $def) {
496  // skip inclusions
497  if ($def_i === 0) {
498  continue;
499  }
500 
501  if (isset($processed[$def_i])) {
502  continue;
503  }
504 
505  // determine whether or not attribute is required
506  if ($required = (strpos($def_i, '*') !== false)) {
507  // rename the definition
508  unset($attr[$def_i]);
509  $def_i = trim($def_i, '*');
510  $attr[$def_i] = $def;
511  }
512 
513  $processed[$def_i] = true;
514 
515  // if we've already got a literal object, move on
516  if (is_object($def)) {
517  // preserve previous required
518  $attr[$def_i]->required = ($required || $attr[$def_i]->required);
519  continue;
520  }
521 
522  if ($def === false) {
523  unset($attr[$def_i]);
524  continue;
525  }
526 
527  if ($t = $attr_types->get($def)) {
528  $attr[$def_i] = $t;
529  $attr[$def_i]->required = $required;
530  } else {
531  unset($attr[$def_i]);
532  }
533  }
534  }
535 }
536 
537 
538 
539 
540 
551 abstract class HTMLPurifier_AttrDef
552 {
553 
559  public $minimized = false;
560 
566  public $required = false;
567 
575  abstract public function validate($string, $config, $context);
576 
598  public function parseCDATA($string)
599  {
600  $string = trim($string);
601  $string = str_replace(array("\n", "\t", "\r"), ' ', $string);
602  return $string;
603  }
604 
610  public function make($string)
611  {
612  // default implementation, return a flyweight of this object.
613  // If $string has an effect on the returned object (i.e. you
614  // need to overload this method), it is best
615  // to clone or instantiate new copies. (Instantiation is safer.)
616  return $this;
617  }
618 
625  protected function mungeRgb($string)
626  {
627  $p = '\s*(\d+(\.\d+)?([%]?))\s*';
628 
629  if (preg_match('/(rgba|hsla)\‍(/', $string)) {
630  return preg_replace('/(rgba|hsla)\‍('.$p.','.$p.','.$p.','.$p.'\‍)/', '\1(\2,\5,\8,\11)', $string);
631  }
632 
633  return preg_replace('/(rgb|hsl)\‍('.$p.','.$p.','.$p.'\‍)/', '\1(\2,\5,\8)', $string);
634  }
635 
640  protected function expandCSSEscape($string)
641  {
642  // flexibly parse it
643  $ret = '';
644  for ($i = 0, $c = strlen($string); $i < $c; $i++) {
645  if ($string[$i] === '\\') {
646  $i++;
647  if ($i >= $c) {
648  $ret .= '\\';
649  break;
650  }
651  if (ctype_xdigit($string[$i])) {
652  $code = $string[$i];
653  for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
654  if (!ctype_xdigit($string[$i])) {
655  break;
656  }
657  $code .= $string[$i];
658  }
659  // We have to be extremely careful when adding
660  // new characters, to make sure we're not breaking
661  // the encoding.
662  $char = HTMLPurifier_Encoder::unichr(hexdec($code));
663  if (HTMLPurifier_Encoder::cleanUTF8($char) === '') {
664  continue;
665  }
666  $ret .= $char;
667  if ($i < $c && trim($string[$i]) !== '') {
668  $i--;
669  }
670  continue;
671  }
672  if ($string[$i] === "\n") {
673  continue;
674  }
675  }
676  $ret .= $string[$i];
677  }
678  return $ret;
679  }
680 }
681 
682 
683 
684 
685 
701 {
702 
712  abstract public function transform($attr, $config, $context);
713 
720  public function prependCSS(&$attr, $css)
721  {
722  $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
723  $attr['style'] = $css . $attr['style'];
724  }
725 
732  public function confiscateAttr(&$attr, $key)
733  {
734  if (!isset($attr[$key])) {
735  return null;
736  }
737  $value = $attr[$key];
738  unset($attr[$key]);
739  return $value;
740  }
741 }
742 
743 
744 
745 
746 
751 {
756  protected $info = array();
757 
762  public function __construct()
763  {
764  // XXX This is kind of poor, since we don't actually /clone/
765  // instances; instead, we use the supplied make() attribute. So,
766  // the underlying class must know how to deal with arguments.
767  // With the old implementation of Enum, that ignored its
768  // arguments when handling a make dispatch, the IAlign
769  // definition wouldn't work.
770 
771  // pseudo-types, must be instantiated via shorthand
772  $this->info['Enum'] = new HTMLPurifier_AttrDef_Enum();
773  $this->info['Bool'] = new HTMLPurifier_AttrDef_HTML_Bool();
774 
775  $this->info['CDATA'] = new HTMLPurifier_AttrDef_Text();
776  $this->info['ID'] = new HTMLPurifier_AttrDef_HTML_ID();
777  $this->info['Length'] = new HTMLPurifier_AttrDef_HTML_Length();
778  $this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength();
779  $this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens();
780  $this->info['Pixels'] = new HTMLPurifier_AttrDef_HTML_Pixels();
781  $this->info['Text'] = new HTMLPurifier_AttrDef_Text();
782  $this->info['URI'] = new HTMLPurifier_AttrDef_URI();
783  $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang();
784  $this->info['Color'] = new HTMLPurifier_AttrDef_HTML_Color();
785  $this->info['IAlign'] = self::makeEnum('top,middle,bottom,left,right');
786  $this->info['LAlign'] = self::makeEnum('top,bottom,left,right');
787  $this->info['FrameTarget'] = new HTMLPurifier_AttrDef_HTML_FrameTarget();
788 
789  // unimplemented aliases
790  $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text();
791  $this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text();
792  $this->info['Charsets'] = new HTMLPurifier_AttrDef_Text();
793  $this->info['Character'] = new HTMLPurifier_AttrDef_Text();
794 
795  // "proprietary" types
796  $this->info['Class'] = new HTMLPurifier_AttrDef_HTML_Class();
797 
798  // number is really a positive integer (one or more digits)
799  // FIXME: ^^ not always, see start and value of list items
800  $this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true);
801  }
802 
803  private static function makeEnum($in)
804  {
805  return new HTMLPurifier_AttrDef_Clone(new HTMLPurifier_AttrDef_Enum(explode(',', $in)));
806  }
807 
813  public function get($type)
814  {
815  // determine if there is any extra info tacked on
816  if (strpos($type, '#') !== false) {
817  list($type, $string) = explode('#', $type, 2);
818  } else {
819  $string = '';
820  }
821 
822  if (!isset($this->info[$type])) {
823  trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR);
824  return;
825  }
826  return $this->info[$type]->make($string);
827  }
828 
834  public function set($type, $impl)
835  {
836  $this->info[$type] = $impl;
837  }
838 }
839 
840 
841 
842 
843 
850 {
851 
859  public function validateToken($token, $config, $context)
860  {
861  $definition = $config->getHTMLDefinition();
862  $e =& $context->get('ErrorCollector', true);
863 
864  // initialize IDAccumulator if necessary
865  $ok =& $context->get('IDAccumulator', true);
866  if (!$ok) {
867  $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
868  $context->register('IDAccumulator', $id_accumulator);
869  }
870 
871  // initialize CurrentToken if necessary
872  $current_token =& $context->get('CurrentToken', true);
873  if (!$current_token) {
874  $context->register('CurrentToken', $token);
875  }
876 
877  if (!$token instanceof HTMLPurifier_Token_Start &&
878  !$token instanceof HTMLPurifier_Token_Empty
879  ) {
880  return;
881  }
882 
883  // create alias to global definition array, see also $defs
884  // DEFINITION CALL
885  $d_defs = $definition->info_global_attr;
886 
887  // don't update token until the very end, to ensure an atomic update
888  $attr = $token->attr;
889 
890  // do global transformations (pre)
891  // nothing currently utilizes this
892  foreach ($definition->info_attr_transform_pre as $transform) {
893  $attr = $transform->transform($o = $attr, $config, $context);
894  if ($e) {
895  if ($attr != $o) {
896  $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
897  }
898  }
899  }
900 
901  // do local transformations only applicable to this element (pre)
902  // ex. <p align="right"> to <p style="text-align:right;">
903  foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
904  $attr = $transform->transform($o = $attr, $config, $context);
905  if ($e) {
906  if ($attr != $o) {
907  $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
908  }
909  }
910  }
911 
912  // create alias to this element's attribute definition array, see
913  // also $d_defs (global attribute definition array)
914  // DEFINITION CALL
915  $defs = $definition->info[$token->name]->attr;
916 
917  $attr_key = false;
918  $context->register('CurrentAttr', $attr_key);
919 
920  // iterate through all the attribute keypairs
921  // Watch out for name collisions: $key has previously been used
922  foreach ($attr as $attr_key => $value) {
923 
924  // call the definition
925  if (isset($defs[$attr_key])) {
926  // there is a local definition defined
927  if ($defs[$attr_key] === false) {
928  // We've explicitly been told not to allow this element.
929  // This is usually when there's a global definition
930  // that must be overridden.
931  // Theoretically speaking, we could have a
932  // AttrDef_DenyAll, but this is faster!
933  $result = false;
934  } else {
935  // validate according to the element's definition
936  $result = $defs[$attr_key]->validate(
937  $value,
938  $config,
939  $context
940  );
941  }
942  } elseif (isset($d_defs[$attr_key])) {
943  // there is a global definition defined, validate according
944  // to the global definition
945  $result = $d_defs[$attr_key]->validate(
946  $value,
947  $config,
948  $context
949  );
950  } else {
951  // system never heard of the attribute? DELETE!
952  $result = false;
953  }
954 
955  // put the results into effect
956  if ($result === false || $result === null) {
957  // this is a generic error message that should replaced
958  // with more specific ones when possible
959  if ($e) {
960  $e->send(E_ERROR, 'AttrValidator: Attribute removed');
961  }
962 
963  // remove the attribute
964  unset($attr[$attr_key]);
965  } elseif (is_string($result)) {
966  // generally, if a substitution is happening, there
967  // was some sort of implicit correction going on. We'll
968  // delegate it to the attribute classes to say exactly what.
969 
970  // simple substitution
971  $attr[$attr_key] = $result;
972  } else {
973  // nothing happens
974  }
975 
976  // we'd also want slightly more complicated substitution
977  // involving an array as the return value,
978  // although we're not sure how colliding attributes would
979  // resolve (certain ones would be completely overriden,
980  // others would prepend themselves).
981  }
982 
983  $context->destroy('CurrentAttr');
984 
985  // post transforms
986 
987  // global (error reporting untested)
988  foreach ($definition->info_attr_transform_post as $transform) {
989  $attr = $transform->transform($o = $attr, $config, $context);
990  if ($e) {
991  if ($attr != $o) {
992  $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
993  }
994  }
995  }
996 
997  // local (error reporting untested)
998  foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
999  $attr = $transform->transform($o = $attr, $config, $context);
1000  if ($e) {
1001  if ($attr != $o) {
1002  $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
1003  }
1004  }
1005  }
1006 
1007  $token->attr = $attr;
1008 
1009  // destroy CurrentToken if we made it ourselves
1010  if (!$current_token) {
1011  $context->destroy('CurrentToken');
1012  }
1013 
1014  }
1015 
1016 
1017 }
1018 
1019 
1020 
1021 
1022 
1023 // constants are slow, so we use as few as possible
1024 if (!defined('HTMLPURIFIER_PREFIX')) {
1025  define('HTMLPURIFIER_PREFIX', dirname(__FILE__) . '/standalone');
1026  set_include_path(HTMLPURIFIER_PREFIX . PATH_SEPARATOR . get_include_path());
1027 }
1028 
1029 // accomodations for versions earlier than 5.0.2
1030 // borrowed from PHP_Compat, LGPL licensed, by Aidan Lister <aidan@php.net>
1031 if (!defined('PHP_EOL')) {
1032  switch (strtoupper(substr(PHP_OS, 0, 3))) {
1033  case 'WIN':
1034  define('PHP_EOL', "\r\n");
1035  break;
1036  case 'DAR':
1037  define('PHP_EOL', "\r");
1038  break;
1039  default:
1040  define('PHP_EOL', "\n");
1041  }
1042 }
1043 
1052 {
1053 
1059  public static function autoload($class)
1060  {
1061  $file = HTMLPurifier_Bootstrap::getPath($class);
1062  if (!$file) {
1063  return false;
1064  }
1065  // Technically speaking, it should be ok and more efficient to
1066  // just do 'require', but Antonio Parraga reports that with
1067  // Zend extensions such as Zend debugger and APC, this invariant
1068  // may be broken. Since we have efficient alternatives, pay
1069  // the cost here and avoid the bug.
1070  require_once HTMLPURIFIER_PREFIX . '/' . $file;
1071  return true;
1072  }
1073 
1079  public static function getPath($class)
1080  {
1081  if (strncmp('HTMLPurifier', $class, 12) !== 0) {
1082  return false;
1083  }
1084  // Custom implementations
1085  if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
1086  $code = str_replace('_', '-', substr($class, 22));
1087  $file = 'HTMLPurifier/Language/classes/' . $code . '.php';
1088  } else {
1089  $file = str_replace('_', '/', $class) . '.php';
1090  }
1091  if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) {
1092  return false;
1093  }
1094  return $file;
1095  }
1096 
1100  public static function registerAutoload()
1101  {
1102  $autoload = array('HTMLPurifier_Bootstrap', 'autoload');
1103  if (($funcs = spl_autoload_functions()) === false) {
1104  spl_autoload_register($autoload);
1105  } elseif (function_exists('spl_autoload_unregister')) {
1106  if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
1107  // prepend flag exists, no need for shenanigans
1108  spl_autoload_register($autoload, true, true);
1109  } else {
1110  $buggy = version_compare(PHP_VERSION, '5.2.11', '<');
1111  $compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&
1112  version_compare(PHP_VERSION, '5.1.0', '>=');
1113  foreach ($funcs as $func) {
1114  if ($buggy && is_array($func)) {
1115  // :TRICKY: There are some compatibility issues and some
1116  // places where we need to error out
1117  $reflector = new ReflectionMethod($func[0], $func[1]);
1118  if (!$reflector->isStatic()) {
1119  throw new Exception(
1120  'HTML Purifier autoloader registrar is not compatible
1121  with non-static object methods due to PHP Bug #44144;
1122  Please do not use HTMLPurifier.autoload.php (or any
1123  file that includes this file); instead, place the code:
1124  spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\'))
1125  after your own autoloaders.'
1126  );
1127  }
1128  // Suprisingly, spl_autoload_register supports the
1129  // Class::staticMethod callback format, although call_user_func doesn't
1130  if ($compat) {
1131  $func = implode('::', $func);
1132  }
1133  }
1134  spl_autoload_unregister($func);
1135  }
1136  spl_autoload_register($autoload);
1137  foreach ($funcs as $func) {
1138  spl_autoload_register($func);
1139  }
1140  }
1141  }
1142  }
1143 }
1144 
1145 
1146 
1147 
1148 
1154 {
1155 
1160  public $setup = false;
1161 
1172  public $optimized = null;
1173 
1178  public $type;
1179 
1185  abstract protected function doSetup($config);
1186 
1191  public function setup($config)
1192  {
1193  if ($this->setup) {
1194  return;
1195  }
1196  $this->setup = true;
1197  $this->doSetup($config);
1198  }
1199 }
1200 
1201 
1202 
1203 
1204 
1210 {
1211 
1212  public $type = 'CSS';
1213 
1218  public $info = array();
1219 
1224  protected function doSetup($config)
1225  {
1226  $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
1227  array('left', 'right', 'center', 'justify'),
1228  false
1229  );
1230 
1231  $border_style =
1232  $this->info['border-bottom-style'] =
1233  $this->info['border-right-style'] =
1234  $this->info['border-left-style'] =
1235  $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum(
1236  array(
1237  'none',
1238  'hidden',
1239  'dotted',
1240  'dashed',
1241  'solid',
1242  'double',
1243  'groove',
1244  'ridge',
1245  'inset',
1246  'outset'
1247  ),
1248  false
1249  );
1250 
1251  $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
1252 
1253  $this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
1254  array('none', 'left', 'right', 'both'),
1255  false
1256  );
1257  $this->info['float'] = new HTMLPurifier_AttrDef_Enum(
1258  array('none', 'left', 'right'),
1259  false
1260  );
1261  $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
1262  array('normal', 'italic', 'oblique'),
1263  false
1264  );
1265  $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
1266  array('normal', 'small-caps'),
1267  false
1268  );
1269 
1270  $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
1271  array(
1272  new HTMLPurifier_AttrDef_Enum(array('none')),
1274  )
1275  );
1276 
1277  $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
1278  array('inside', 'outside'),
1279  false
1280  );
1281  $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
1282  array(
1283  'disc',
1284  'circle',
1285  'square',
1286  'decimal',
1287  'lower-roman',
1288  'upper-roman',
1289  'lower-alpha',
1290  'upper-alpha',
1291  'none'
1292  ),
1293  false
1294  );
1295  $this->info['list-style-image'] = $uri_or_none;
1296 
1297  $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
1298 
1299  $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
1300  array('capitalize', 'uppercase', 'lowercase', 'none'),
1301  false
1302  );
1303  $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
1304 
1305  $this->info['background-image'] = $uri_or_none;
1306  $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
1307  array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
1308  );
1309  $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
1310  array('scroll', 'fixed')
1311  );
1312  $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
1313 
1314  $border_color =
1315  $this->info['border-top-color'] =
1316  $this->info['border-bottom-color'] =
1317  $this->info['border-left-color'] =
1318  $this->info['border-right-color'] =
1319  $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(
1320  array(
1321  new HTMLPurifier_AttrDef_Enum(array('transparent')),
1323  )
1324  );
1325 
1326  $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
1327 
1328  $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
1329 
1330  $border_width =
1331  $this->info['border-top-width'] =
1332  $this->info['border-bottom-width'] =
1333  $this->info['border-left-width'] =
1334  $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(
1335  array(
1336  new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
1337  new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
1338  )
1339  );
1340 
1341  $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
1342 
1343  $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
1344  array(
1345  new HTMLPurifier_AttrDef_Enum(array('normal')),
1347  )
1348  );
1349 
1350  $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
1351  array(
1352  new HTMLPurifier_AttrDef_Enum(array('normal')),
1354  )
1355  );
1356 
1357  $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(
1358  array(
1360  array(
1361  'xx-small',
1362  'x-small',
1363  'small',
1364  'medium',
1365  'large',
1366  'x-large',
1367  'xx-large',
1368  'larger',
1369  'smaller'
1370  )
1371  ),
1374  )
1375  );
1376 
1377  $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(
1378  array(
1379  new HTMLPurifier_AttrDef_Enum(array('normal')),
1380  new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
1383  )
1384  );
1385 
1386  $margin =
1387  $this->info['margin-top'] =
1388  $this->info['margin-bottom'] =
1389  $this->info['margin-left'] =
1390  $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
1391  array(
1394  new HTMLPurifier_AttrDef_Enum(array('auto'))
1395  )
1396  );
1397 
1398  $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
1399 
1400  // non-negative
1401  $padding =
1402  $this->info['padding-top'] =
1403  $this->info['padding-bottom'] =
1404  $this->info['padding-left'] =
1405  $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
1406  array(
1409  )
1410  );
1411 
1412  $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
1413 
1414  $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(
1415  array(
1418  )
1419  );
1420 
1421  $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(
1422  array(
1425  new HTMLPurifier_AttrDef_Enum(array('auto', 'initial', 'inherit'))
1426  )
1427  );
1428  $trusted_min_wh = new HTMLPurifier_AttrDef_CSS_Composite(
1429  array(
1432  new HTMLPurifier_AttrDef_Enum(array('initial', 'inherit'))
1433  )
1434  );
1435  $trusted_max_wh = new HTMLPurifier_AttrDef_CSS_Composite(
1436  array(
1439  new HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit'))
1440  )
1441  );
1442  $max = $config->get('CSS.MaxImgLength');
1443 
1444  $this->info['width'] =
1445  $this->info['height'] =
1446  $max === null ?
1447  $trusted_wh :
1449  'img',
1450  // For img tags:
1452  array(
1453  new HTMLPurifier_AttrDef_CSS_Length('0', $max),
1454  new HTMLPurifier_AttrDef_Enum(array('auto'))
1455  )
1456  ),
1457  // For everyone else:
1458  $trusted_wh
1459  );
1460  $this->info['min-width'] =
1461  $this->info['min-height'] =
1462  $max === null ?
1463  $trusted_min_wh :
1465  'img',
1466  // For img tags:
1468  array(
1469  new HTMLPurifier_AttrDef_CSS_Length('0', $max),
1470  new HTMLPurifier_AttrDef_Enum(array('initial', 'inherit'))
1471  )
1472  ),
1473  // For everyone else:
1474  $trusted_min_wh
1475  );
1476  $this->info['max-width'] =
1477  $this->info['max-height'] =
1478  $max === null ?
1479  $trusted_max_wh :
1481  'img',
1482  // For img tags:
1484  array(
1485  new HTMLPurifier_AttrDef_CSS_Length('0', $max),
1486  new HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit'))
1487  )
1488  ),
1489  // For everyone else:
1490  $trusted_max_wh
1491  );
1492 
1493  $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
1494 
1495  $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
1496 
1497  // this could use specialized code
1498  $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
1499  array(
1500  'normal',
1501  'bold',
1502  'bolder',
1503  'lighter',
1504  '100',
1505  '200',
1506  '300',
1507  '400',
1508  '500',
1509  '600',
1510  '700',
1511  '800',
1512  '900'
1513  ),
1514  false
1515  );
1516 
1517  // MUST be called after other font properties, as it references
1518  // a CSSDefinition object
1519  $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
1520 
1521  // same here
1522  $this->info['border'] =
1523  $this->info['border-bottom'] =
1524  $this->info['border-top'] =
1525  $this->info['border-left'] =
1526  $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
1527 
1528  $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(
1529  array('collapse', 'separate')
1530  );
1531 
1532  $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(
1533  array('top', 'bottom')
1534  );
1535 
1536  $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(
1537  array('auto', 'fixed')
1538  );
1539 
1540  $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(
1541  array(
1543  array(
1544  'baseline',
1545  'sub',
1546  'super',
1547  'top',
1548  'text-top',
1549  'middle',
1550  'bottom',
1551  'text-bottom'
1552  )
1553  ),
1556  )
1557  );
1558 
1559  $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
1560 
1561  // These CSS properties don't work on many browsers, but we live
1562  // in THE FUTURE!
1563  $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(
1564  array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line')
1565  );
1566 
1567  if ($config->get('CSS.Proprietary')) {
1568  $this->doSetupProprietary($config);
1569  }
1570 
1571  if ($config->get('CSS.AllowTricky')) {
1572  $this->doSetupTricky($config);
1573  }
1574 
1575  if ($config->get('CSS.Trusted')) {
1576  $this->doSetupTrusted($config);
1577  }
1578 
1579  $allow_important = $config->get('CSS.AllowImportant');
1580  // wrap all attr-defs with decorator that handles !important
1581  foreach ($this->info as $k => $v) {
1582  $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
1583  }
1584 
1585  $this->setupConfigStuff($config);
1586  }
1587 
1591  protected function doSetupProprietary($config)
1592  {
1593  // Internet Explorer only scrollbar colors
1594  $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
1595  $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
1596  $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
1597  $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
1598  $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
1599  $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
1600 
1601  // vendor specific prefixes of opacity
1602  $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
1603  $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
1604 
1605  // only opacity, for now
1606  $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
1607 
1608  // more CSS3
1609  $this->info['page-break-after'] =
1610  $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum(
1611  array(
1612  'auto',
1613  'always',
1614  'avoid',
1615  'left',
1616  'right'
1617  )
1618  );
1619  $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
1620 
1621  $border_radius = new HTMLPurifier_AttrDef_CSS_Composite(
1622  array(
1623  new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative
1624  new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
1625  ));
1626 
1627  $this->info['border-top-left-radius'] =
1628  $this->info['border-top-right-radius'] =
1629  $this->info['border-bottom-right-radius'] =
1630  $this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2);
1631  // TODO: support SLASH syntax
1632  $this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
1633 
1634  }
1635 
1639  protected function doSetupTricky($config)
1640  {
1641  $this->info['display'] = new HTMLPurifier_AttrDef_Enum(
1642  array(
1643  'inline',
1644  'block',
1645  'list-item',
1646  'run-in',
1647  'compact',
1648  'marker',
1649  'table',
1650  'inline-block',
1651  'inline-table',
1652  'table-row-group',
1653  'table-header-group',
1654  'table-footer-group',
1655  'table-row',
1656  'table-column-group',
1657  'table-column',
1658  'table-cell',
1659  'table-caption',
1660  'none'
1661  )
1662  );
1663  $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(
1664  array('visible', 'hidden', 'collapse')
1665  );
1666  $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
1667  $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
1668  }
1669 
1673  protected function doSetupTrusted($config)
1674  {
1675  $this->info['position'] = new HTMLPurifier_AttrDef_Enum(
1676  array('static', 'relative', 'absolute', 'fixed')
1677  );
1678  $this->info['top'] =
1679  $this->info['left'] =
1680  $this->info['right'] =
1681  $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(
1682  array(
1685  new HTMLPurifier_AttrDef_Enum(array('auto')),
1686  )
1687  );
1688  $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(
1689  array(
1691  new HTMLPurifier_AttrDef_Enum(array('auto')),
1692  )
1693  );
1694  }
1695 
1703  protected function setupConfigStuff($config)
1704  {
1705  // setup allowed elements
1706  $support = "(for information on implementing this, see the " .
1707  "support forums) ";
1708  $allowed_properties = $config->get('CSS.AllowedProperties');
1709  if ($allowed_properties !== null) {
1710  foreach ($this->info as $name => $d) {
1711  if (!isset($allowed_properties[$name])) {
1712  unset($this->info[$name]);
1713  }
1714  unset($allowed_properties[$name]);
1715  }
1716  // emit errors
1717  foreach ($allowed_properties as $name => $d) {
1718  // :TODO: Is this htmlspecialchars() call really necessary?
1719  $name = htmlspecialchars($name);
1720  trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
1721  }
1722  }
1723 
1724  $forbidden_properties = $config->get('CSS.ForbiddenProperties');
1725  if ($forbidden_properties !== null) {
1726  foreach ($this->info as $name => $d) {
1727  if (isset($forbidden_properties[$name])) {
1728  unset($this->info[$name]);
1729  }
1730  }
1731  }
1732  }
1733 }
1734 
1735 
1736 
1737 
1738 
1743 {
1749  public $type;
1750 
1759 
1764  public $elements = array();
1765 
1772  public function getAllowedElements($config)
1773  {
1774  return $this->elements;
1775  }
1776 
1785  abstract public function validateChildren($children, $config, $context);
1786 }
1787 
1788 
1789 
1790 
1791 
1807 {
1808 
1813  public $version = '4.12.0';
1814 
1820  public $autoFinalize = true;
1821 
1822  // protected member variables
1823 
1829  protected $serials = array();
1830 
1835  protected $serial;
1836 
1841  protected $parser = null;
1842 
1849  public $def;
1850 
1855  protected $definitions;
1856 
1861  protected $finalized = false;
1862 
1867  protected $plist;
1868 
1873  private $aliasMode;
1874 
1881  public $chatty = true;
1882 
1887  private $lock;
1888 
1895  public function __construct($definition, $parent = null)
1896  {
1897  $parent = $parent ? $parent : $definition->defaultPlist;
1898  $this->plist = new HTMLPurifier_PropertyList($parent);
1899  $this->def = $definition; // keep a copy around for checking
1900  $this->parser = new HTMLPurifier_VarParser_Flexible();
1901  }
1902 
1912  public static function create($config, $schema = null)
1913  {
1914  if ($config instanceof HTMLPurifier_Config) {
1915  // pass-through
1916  return $config;
1917  }
1918  if (!$schema) {
1920  } else {
1921  $ret = new HTMLPurifier_Config($schema);
1922  }
1923  if (is_string($config)) {
1924  $ret->loadIni($config);
1925  } elseif (is_array($config)) $ret->loadArray($config);
1926  return $ret;
1927  }
1928 
1934  public static function inherit(HTMLPurifier_Config $config)
1935  {
1936  return new HTMLPurifier_Config($config->def, $config->plist);
1937  }
1938 
1943  public static function createDefault()
1944  {
1945  $definition = HTMLPurifier_ConfigSchema::instance();
1946  $config = new HTMLPurifier_Config($definition);
1947  return $config;
1948  }
1949 
1958  public function get($key, $a = null)
1959  {
1960  if ($a !== null) {
1961  $this->triggerError(
1962  "Using deprecated API: use \$config->get('$key.$a') instead",
1963  E_USER_WARNING
1964  );
1965  $key = "$key.$a";
1966  }
1967  if (!$this->finalized) {
1968  $this->autoFinalize();
1969  }
1970  if (!isset($this->def->info[$key])) {
1971  // can't add % due to SimpleTest bug
1972  $this->triggerError(
1973  'Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
1974  E_USER_WARNING
1975  );
1976  return;
1977  }
1978  if (isset($this->def->info[$key]->isAlias)) {
1979  $d = $this->def->info[$key];
1980  $this->triggerError(
1981  'Cannot get value from aliased directive, use real name ' . $d->key,
1982  E_USER_ERROR
1983  );
1984  return;
1985  }
1986  if ($this->lock) {
1987  list($ns) = explode('.', $key);
1988  if ($ns !== $this->lock) {
1989  $this->triggerError(
1990  'Cannot get value of namespace ' . $ns . ' when lock for ' .
1991  $this->lock .
1992  ' is active, this probably indicates a Definition setup method ' .
1993  'is accessing directives that are not within its namespace',
1994  E_USER_ERROR
1995  );
1996  return;
1997  }
1998  }
1999  return $this->plist->get($key);
2000  }
2001 
2009  public function getBatch($namespace)
2010  {
2011  if (!$this->finalized) {
2012  $this->autoFinalize();
2013  }
2014  $full = $this->getAll();
2015  if (!isset($full[$namespace])) {
2016  $this->triggerError(
2017  'Cannot retrieve undefined namespace ' .
2018  htmlspecialchars($namespace),
2019  E_USER_WARNING
2020  );
2021  return;
2022  }
2023  return $full[$namespace];
2024  }
2025 
2036  public function getBatchSerial($namespace)
2037  {
2038  if (empty($this->serials[$namespace])) {
2039  $batch = $this->getBatch($namespace);
2040  unset($batch['DefinitionRev']);
2041  $this->serials[$namespace] = sha1(serialize($batch));
2042  }
2043  return $this->serials[$namespace];
2044  }
2045 
2052  public function getSerial()
2053  {
2054  if (empty($this->serial)) {
2055  $this->serial = sha1(serialize($this->getAll()));
2056  }
2057  return $this->serial;
2058  }
2059 
2065  public function getAll()
2066  {
2067  if (!$this->finalized) {
2068  $this->autoFinalize();
2069  }
2070  $ret = array();
2071  foreach ($this->plist->squash() as $name => $value) {
2072  list($ns, $key) = explode('.', $name, 2);
2073  $ret[$ns][$key] = $value;
2074  }
2075  return $ret;
2076  }
2077 
2085  public function set($key, $value, $a = null)
2086  {
2087  if (strpos($key, '.') === false) {
2088  $namespace = $key;
2089  $directive = $value;
2090  $value = $a;
2091  $key = "$key.$directive";
2092  $this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE);
2093  } else {
2094  list($namespace) = explode('.', $key);
2095  }
2096  if ($this->isFinalized('Cannot set directive after finalization')) {
2097  return;
2098  }
2099  if (!isset($this->def->info[$key])) {
2100  $this->triggerError(
2101  'Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
2102  E_USER_WARNING
2103  );
2104  return;
2105  }
2106  $def = $this->def->info[$key];
2107 
2108  if (isset($def->isAlias)) {
2109  if ($this->aliasMode) {
2110  $this->triggerError(
2111  'Double-aliases not allowed, please fix '.
2112  'ConfigSchema bug with' . $key,
2113  E_USER_ERROR
2114  );
2115  return;
2116  }
2117  $this->aliasMode = true;
2118  $this->set($def->key, $value);
2119  $this->aliasMode = false;
2120  $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE);
2121  return;
2122  }
2123 
2124  // Raw type might be negative when using the fully optimized form
2125  // of stdClass, which indicates allow_null == true
2126  $rtype = is_int($def) ? $def : $def->type;
2127  if ($rtype < 0) {
2128  $type = -$rtype;
2129  $allow_null = true;
2130  } else {
2131  $type = $rtype;
2132  $allow_null = isset($def->allow_null);
2133  }
2134 
2135  try {
2136  $value = $this->parser->parse($value, $type, $allow_null);
2137  } catch (HTMLPurifier_VarParserException $e) {
2138  $this->triggerError(
2139  'Value for ' . $key . ' is of invalid type, should be ' .
2141  E_USER_WARNING
2142  );
2143  return;
2144  }
2145  if (is_string($value) && is_object($def)) {
2146  // resolve value alias if defined
2147  if (isset($def->aliases[$value])) {
2148  $value = $def->aliases[$value];
2149  }
2150  // check to see if the value is allowed
2151  if (isset($def->allowed) && !isset($def->allowed[$value])) {
2152  $this->triggerError(
2153  'Value not supported, valid values are: ' .
2154  $this->_listify($def->allowed),
2155  E_USER_WARNING
2156  );
2157  return;
2158  }
2159  }
2160  $this->plist->set($key, $value);
2161 
2162  // reset definitions if the directives they depend on changed
2163  // this is a very costly process, so it's discouraged
2164  // with finalization
2165  if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') {
2166  $this->definitions[$namespace] = null;
2167  }
2168 
2169  $this->serials[$namespace] = false;
2170  }
2171 
2179  private function _listify($lookup)
2180  {
2181  $list = array();
2182  foreach ($lookup as $name => $b) {
2183  $list[] = $name;
2184  }
2185  return implode(', ', $list);
2186  }
2187 
2202  public function getHTMLDefinition($raw = false, $optimized = false)
2203  {
2204  return $this->getDefinition('HTML', $raw, $optimized);
2205  }
2206 
2221  public function getCSSDefinition($raw = false, $optimized = false)
2222  {
2223  return $this->getDefinition('CSS', $raw, $optimized);
2224  }
2225 
2240  public function getURIDefinition($raw = false, $optimized = false)
2241  {
2242  return $this->getDefinition('URI', $raw, $optimized);
2243  }
2244 
2262  public function getDefinition($type, $raw = false, $optimized = false)
2263  {
2264  if ($optimized && !$raw) {
2265  throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false");
2266  }
2267  if (!$this->finalized) {
2268  $this->autoFinalize();
2269  }
2270  // temporarily suspend locks, so we can handle recursive definition calls
2271  $lock = $this->lock;
2272  $this->lock = null;
2274  $cache = $factory->create($type, $this);
2275  $this->lock = $lock;
2276  if (!$raw) {
2277  // full definition
2278  // ---------------
2279  // check if definition is in memory
2280  if (!empty($this->definitions[$type])) {
2281  $def = $this->definitions[$type];
2282  // check if the definition is setup
2283  if ($def->setup) {
2284  return $def;
2285  } else {
2286  $def->setup($this);
2287  if ($def->optimized) {
2288  $cache->add($def, $this);
2289  }
2290  return $def;
2291  }
2292  }
2293  // check if definition is in cache
2294  $def = $cache->get($this);
2295  if ($def) {
2296  // definition in cache, save to memory and return it
2297  $this->definitions[$type] = $def;
2298  return $def;
2299  }
2300  // initialize it
2301  $def = $this->initDefinition($type);
2302  // set it up
2303  $this->lock = $type;
2304  $def->setup($this);
2305  $this->lock = null;
2306  // save in cache
2307  $cache->add($def, $this);
2308  // return it
2309  return $def;
2310  } else {
2311  // raw definition
2312  // --------------
2313  // check preconditions
2314  $def = null;
2315  if ($optimized) {
2316  if (is_null($this->get($type . '.DefinitionID'))) {
2317  // fatally error out if definition ID not set
2318  throw new HTMLPurifier_Exception(
2319  "Cannot retrieve raw version without specifying %$type.DefinitionID"
2320  );
2321  }
2322  }
2323  if (!empty($this->definitions[$type])) {
2324  $def = $this->definitions[$type];
2325  if ($def->setup && !$optimized) {
2326  $extra = $this->chatty ?
2327  " (try moving this code block earlier in your initialization)" :
2328  "";
2329  throw new HTMLPurifier_Exception(
2330  "Cannot retrieve raw definition after it has already been setup" .
2331  $extra
2332  );
2333  }
2334  if ($def->optimized === null) {
2335  $extra = $this->chatty ? " (try flushing your cache)" : "";
2336  throw new HTMLPurifier_Exception(
2337  "Optimization status of definition is unknown" . $extra
2338  );
2339  }
2340  if ($def->optimized !== $optimized) {
2341  $msg = $optimized ? "optimized" : "unoptimized";
2342  $extra = $this->chatty ?
2343  " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)"
2344  : "";
2345  throw new HTMLPurifier_Exception(
2346  "Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra
2347  );
2348  }
2349  }
2350  // check if definition was in memory
2351  if ($def) {
2352  if ($def->setup) {
2353  // invariant: $optimized === true (checked above)
2354  return null;
2355  } else {
2356  return $def;
2357  }
2358  }
2359  // if optimized, check if definition was in cache
2360  // (because we do the memory check first, this formulation
2361  // is prone to cache slamming, but I think
2362  // guaranteeing that either /all/ of the raw
2363  // setup code or /none/ of it is run is more important.)
2364  if ($optimized) {
2365  // This code path only gets run once; once we put
2366  // something in $definitions (which is guaranteed by the
2367  // trailing code), we always short-circuit above.
2368  $def = $cache->get($this);
2369  if ($def) {
2370  // save the full definition for later, but don't
2371  // return it yet
2372  $this->definitions[$type] = $def;
2373  return null;
2374  }
2375  }
2376  // check invariants for creation
2377  if (!$optimized) {
2378  if (!is_null($this->get($type . '.DefinitionID'))) {
2379  if ($this->chatty) {
2380  $this->triggerError(
2381  'Due to a documentation error in previous version of HTML Purifier, your ' .
2382  'definitions are not being cached. If this is OK, you can remove the ' .
2383  '%$type.DefinitionRev and %$type.DefinitionID declaration. Otherwise, ' .
2384  'modify your code to use maybeGetRawDefinition, and test if the returned ' .
2385  'value is null before making any edits (if it is null, that means that a ' .
2386  'cached version is available, and no raw operations are necessary). See ' .
2387  '<a href="http://htmlpurifier.org/docs/enduser-customize.html#optimized">' .
2388  'Customize</a> for more details',
2389  E_USER_WARNING
2390  );
2391  } else {
2392  $this->triggerError(
2393  "Useless DefinitionID declaration",
2394  E_USER_WARNING
2395  );
2396  }
2397  }
2398  }
2399  // initialize it
2400  $def = $this->initDefinition($type);
2401  $def->optimized = $optimized;
2402  return $def;
2403  }
2404  throw new HTMLPurifier_Exception("The impossible happened!");
2405  }
2406 
2415  private function initDefinition($type)
2416  {
2417  // quick checks failed, let's create the object
2418  if ($type == 'HTML') {
2420  } elseif ($type == 'CSS') {
2422  } elseif ($type == 'URI') {
2424  } else {
2425  throw new HTMLPurifier_Exception(
2426  "Definition of $type type not supported"
2427  );
2428  }
2429  $this->definitions[$type] = $def;
2430  return $def;
2431  }
2432 
2433  public function maybeGetRawDefinition($name)
2434  {
2435  return $this->getDefinition($name, true, true);
2436  }
2437 
2441  public function maybeGetRawHTMLDefinition()
2442  {
2443  return $this->getDefinition('HTML', true, true);
2444  }
2445 
2449  public function maybeGetRawCSSDefinition()
2450  {
2451  return $this->getDefinition('CSS', true, true);
2452  }
2453 
2457  public function maybeGetRawURIDefinition()
2458  {
2459  return $this->getDefinition('URI', true, true);
2460  }
2461 
2468  public function loadArray($config_array)
2469  {
2470  if ($this->isFinalized('Cannot load directives after finalization')) {
2471  return;
2472  }
2473  foreach ($config_array as $key => $value) {
2474  $key = str_replace('_', '.', $key);
2475  if (strpos($key, '.') !== false) {
2476  $this->set($key, $value);
2477  } else {
2478  $namespace = $key;
2479  $namespace_values = $value;
2480  foreach ($namespace_values as $directive => $value2) {
2481  $this->set($namespace .'.'. $directive, $value2);
2482  }
2483  }
2484  }
2485  }
2486 
2497  public static function getAllowedDirectivesForForm($allowed, $schema = null)
2498  {
2499  if (!$schema) {
2501  }
2502  if ($allowed !== true) {
2503  if (is_string($allowed)) {
2504  $allowed = array($allowed);
2505  }
2506  $allowed_ns = array();
2507  $allowed_directives = array();
2508  $blacklisted_directives = array();
2509  foreach ($allowed as $ns_or_directive) {
2510  if (strpos($ns_or_directive, '.') !== false) {
2511  // directive
2512  if ($ns_or_directive[0] == '-') {
2513  $blacklisted_directives[substr($ns_or_directive, 1)] = true;
2514  } else {
2515  $allowed_directives[$ns_or_directive] = true;
2516  }
2517  } else {
2518  // namespace
2519  $allowed_ns[$ns_or_directive] = true;
2520  }
2521  }
2522  }
2523  $ret = array();
2524  foreach ($schema->info as $key => $def) {
2525  list($ns, $directive) = explode('.', $key, 2);
2526  if ($allowed !== true) {
2527  if (isset($blacklisted_directives["$ns.$directive"])) {
2528  continue;
2529  }
2530  if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) {
2531  continue;
2532  }
2533  }
2534  if (isset($def->isAlias)) {
2535  continue;
2536  }
2537  if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') {
2538  continue;
2539  }
2540  $ret[] = array($ns, $directive);
2541  }
2542  return $ret;
2543  }
2544 
2557  public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
2558  {
2559  $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);
2561  return $config;
2562  }
2563 
2572  public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true)
2573  {
2574  $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);
2575  $this->loadArray($ret);
2576  }
2577 
2590  public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
2591  {
2592  if ($index !== false) {
2593  $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
2594  }
2595  $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();
2596 
2597  $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);
2598  $ret = array();
2599  foreach ($allowed as $key) {
2600  list($ns, $directive) = $key;
2601  $skey = "$ns.$directive";
2602  if (!empty($array["Null_$skey"])) {
2603  $ret[$ns][$directive] = null;
2604  continue;
2605  }
2606  if (!isset($array[$skey])) {
2607  continue;
2608  }
2609  $value = $mq ? stripslashes($array[$skey]) : $array[$skey];
2610  $ret[$ns][$directive] = $value;
2611  }
2612  return $ret;
2613  }
2614 
2620  public function loadIni($filename)
2621  {
2622  if ($this->isFinalized('Cannot load directives after finalization')) {
2623  return;
2624  }
2625  $array = parse_ini_file($filename, true);
2626  $this->loadArray($array);
2627  }
2628 
2636  public function isFinalized($error = false)
2637  {
2638  if ($this->finalized && $error) {
2639  $this->triggerError($error, E_USER_ERROR);
2640  }
2641  return $this->finalized;
2642  }
2643 
2648  public function autoFinalize()
2649  {
2650  if ($this->autoFinalize) {
2651  $this->finalize();
2652  } else {
2653  $this->plist->squash(true);
2654  }
2655  }
2656 
2660  public function finalize()
2661  {
2662  $this->finalized = true;
2663  $this->parser = null;
2664  }
2665 
2673  protected function triggerError($msg, $no)
2674  {
2675  // determine previous stack frame
2676  $extra = '';
2677  if ($this->chatty) {
2678  $trace = debug_backtrace();
2679  // zip(tail(trace), trace) -- but PHP is not Haskell har har
2680  for ($i = 0, $c = count($trace); $i < $c - 1; $i++) {
2681  // XXX this is not correct on some versions of HTML Purifier
2682  if (isset($trace[$i + 1]['class']) && $trace[$i + 1]['class'] === 'HTMLPurifier_Config') {
2683  continue;
2684  }
2685  $frame = $trace[$i];
2686  $extra = " invoked on line {$frame['line']} in file {$frame['file']}";
2687  break;
2688  }
2689  }
2690  trigger_error($msg . $extra, $no);
2691  }
2692 
2699  public function serialize()
2700  {
2701  $this->getDefinition('HTML');
2702  $this->getDefinition('CSS');
2703  $this->getDefinition('URI');
2704  return serialize($this);
2705  }
2706 
2707 }
2708 
2709 
2710 
2711 
2712 
2717 {
2723  public $defaults = array();
2724 
2730 
2762  public $info = array();
2763 
2768  protected static $singleton;
2769 
2770  public function __construct()
2771  {
2772  $this->defaultPlist = new HTMLPurifier_PropertyList();
2773  }
2774 
2779  public static function makeFromSerial()
2780  {
2781  $contents = file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser');
2782  $r = unserialize($contents);
2783  if (!$r) {
2784  $hash = sha1($contents);
2785  trigger_error("Unserialization of configuration schema failed, sha1 of file was $hash", E_USER_ERROR);
2786  }
2787  return $r;
2788  }
2789 
2795  public static function instance($prototype = null)
2796  {
2797  if ($prototype !== null) {
2799  } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) {
2801  }
2803  }
2804 
2816  public function add($key, $default, $type, $allow_null)
2817  {
2818  $obj = new stdClass();
2819  $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
2820  if ($allow_null) {
2821  $obj->allow_null = true;
2822  }
2823  $this->info[$key] = $obj;
2824  $this->defaults[$key] = $default;
2825  $this->defaultPlist->set($key, $default);
2826  }
2827 
2836  public function addValueAliases($key, $aliases)
2837  {
2838  if (!isset($this->info[$key]->aliases)) {
2839  $this->info[$key]->aliases = array();
2840  }
2841  foreach ($aliases as $alias => $real) {
2842  $this->info[$key]->aliases[$alias] = $real;
2843  }
2844  }
2845 
2853  public function addAllowedValues($key, $allowed)
2854  {
2855  $this->info[$key]->allowed = $allowed;
2856  }
2857 
2863  public function addAlias($key, $new_key)
2864  {
2865  $obj = new stdClass;
2866  $obj->key = $new_key;
2867  $obj->isAlias = true;
2868  $this->info[$key] = $obj;
2869  }
2870 
2874  public function postProcess()
2875  {
2876  foreach ($this->info as $key => $v) {
2877  if (count((array) $v) == 1) {
2878  $this->info[$key] = $v->type;
2879  } elseif (count((array) $v) == 2 && isset($v->allow_null)) {
2880  $this->info[$key] = -$v->type;
2881  }
2882  }
2883  }
2884 }
2885 
2886 
2887 
2888 
2889 
2894 {
2895 
2900  public $info = array();
2901 
2907  public $lookup = array();
2908 
2913  protected $keys = array();
2918  protected $values = array();
2919 
2925  public function __construct($modules)
2926  {
2927  if (!is_array($modules)) {
2928  $modules = array($modules);
2929  }
2930  // populate content_sets based on module hints
2931  // sorry, no way of overloading
2932  foreach ($modules as $module) {
2933  foreach ($module->content_sets as $key => $value) {
2934  $temp = $this->convertToLookup($value);
2935  if (isset($this->lookup[$key])) {
2936  // add it into the existing content set
2937  $this->lookup[$key] = array_merge($this->lookup[$key], $temp);
2938  } else {
2939  $this->lookup[$key] = $temp;
2940  }
2941  }
2942  }
2943  $old_lookup = false;
2944  while ($old_lookup !== $this->lookup) {
2945  $old_lookup = $this->lookup;
2946  foreach ($this->lookup as $i => $set) {
2947  $add = array();
2948  foreach ($set as $element => $x) {
2949  if (isset($this->lookup[$element])) {
2950  $add += $this->lookup[$element];
2951  unset($this->lookup[$i][$element]);
2952  }
2953  }
2954  $this->lookup[$i] += $add;
2955  }
2956  }
2957 
2958  foreach ($this->lookup as $key => $lookup) {
2959  $this->info[$key] = implode(' | ', array_keys($lookup));
2960  }
2961  $this->keys = array_keys($this->info);
2962  $this->values = array_values($this->info);
2963  }
2964 
2970  public function generateChildDef(&$def, $module)
2971  {
2972  if (!empty($def->child)) { // already done!
2973  return;
2974  }
2975  $content_model = $def->content_model;
2976  if (is_string($content_model)) {
2977  // Assume that $this->keys is alphanumeric
2978  $def->content_model = preg_replace_callback(
2979  '/\b(' . implode('|', $this->keys) . ')\b/',
2980  array($this, 'generateChildDefCallback'),
2981  $content_model
2982  );
2983  //$def->content_model = str_replace(
2984  // $this->keys, $this->values, $content_model);
2985  }
2986  $def->child = $this->getChildDef($def, $module);
2987  }
2988 
2989  public function generateChildDefCallback($matches)
2990  {
2991  return $this->info[$matches[0]];
2992  }
2993 
3003  public function getChildDef($def, $module)
3004  {
3005  $value = $def->content_model;
3006  if (is_object($value)) {
3007  trigger_error(
3008  'Literal object child definitions should be stored in '.
3009  'ElementDef->child not ElementDef->content_model',
3010  E_USER_NOTICE
3011  );
3012  return $value;
3013  }
3014  switch ($def->content_model_type) {
3015  case 'required':
3016  return new HTMLPurifier_ChildDef_Required($value);
3017  case 'optional':
3018  return new HTMLPurifier_ChildDef_Optional($value);
3019  case 'empty':
3020  return new HTMLPurifier_ChildDef_Empty();
3021  case 'custom':
3022  return new HTMLPurifier_ChildDef_Custom($value);
3023  }
3024  // defer to its module
3025  $return = false;
3026  if ($module->defines_child_def) { // save a func call
3027  $return = $module->getChildDef($def);
3028  }
3029  if ($return !== false) {
3030  return $return;
3031  }
3032  // error-out
3033  trigger_error(
3034  'Could not determine which ChildDef class to instantiate',
3035  E_USER_ERROR
3036  );
3037  return false;
3038  }
3039 
3046  protected function convertToLookup($string)
3047  {
3048  $array = explode('|', str_replace(' ', '', $string));
3049  $ret = array();
3050  foreach ($array as $k) {
3051  $ret[$k] = true;
3052  }
3053  return $ret;
3054  }
3055 }
3056 
3057 
3058 
3059 
3060 
3069 {
3070 
3075  private $_storage = array();
3076 
3082  public function register($name, &$ref)
3083  {
3084  if (array_key_exists($name, $this->_storage)) {
3085  trigger_error(
3086  "Name $name produces collision, cannot re-register",
3087  E_USER_ERROR
3088  );
3089  return;
3090  }
3091  $this->_storage[$name] =& $ref;
3092  }
3093 
3100  public function &get($name, $ignore_error = false)
3101  {
3102  if (!array_key_exists($name, $this->_storage)) {
3103  if (!$ignore_error) {
3104  trigger_error(
3105  "Attempted to retrieve non-existent variable $name",
3106  E_USER_ERROR
3107  );
3108  }
3109  $var = null; // so we can return by reference
3110  return $var;
3111  }
3112  return $this->_storage[$name];
3113  }
3114 
3119  public function destroy($name)
3120  {
3121  if (!array_key_exists($name, $this->_storage)) {
3122  trigger_error(
3123  "Attempted to destroy non-existent variable $name",
3124  E_USER_ERROR
3125  );
3126  return;
3127  }
3128  unset($this->_storage[$name]);
3129  }
3130 
3136  public function exists($name)
3137  {
3138  return array_key_exists($name, $this->_storage);
3139  }
3140 
3145  public function loadArray($context_array)
3146  {
3147  foreach ($context_array as $key => $discard) {
3148  $this->register($key, $context_array[$key]);
3149  }
3150  }
3151 }
3152 
3153 
3154 
3155 
3156 
3166 {
3170  public $type;
3171 
3176  public function __construct($type)
3177  {
3178  $this->type = $type;
3179  }
3180 
3186  public function generateKey($config)
3187  {
3188  return $config->version . ',' . // possibly replace with function calls
3189  $config->getBatchSerial($this->type) . ',' .
3190  $config->get($this->type . '.DefinitionRev');
3191  }
3192 
3200  public function isOld($key, $config)
3201  {
3202  if (substr_count($key, ',') < 2) {
3203  return true;
3204  }
3205  list($version, $hash, $revision) = explode(',', $key, 3);
3206  $compare = version_compare($version, $config->version);
3207  // version mismatch, is always old
3208  if ($compare != 0) {
3209  return true;
3210  }
3211  // versions match, ids match, check revision number
3212  if ($hash == $config->getBatchSerial($this->type) &&
3213  $revision < $config->get($this->type . '.DefinitionRev')) {
3214  return true;
3215  }
3216  return false;
3217  }
3218 
3225  public function checkDefType($def)
3226  {
3227  if ($def->type !== $this->type) {
3228  trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}");
3229  return false;
3230  }
3231  return true;
3232  }
3233 
3239  abstract public function add($def, $config);
3240 
3246  abstract public function set($def, $config);
3247 
3253  abstract public function replace($def, $config);
3254 
3259  abstract public function get($config);
3260 
3265  abstract public function remove($config);
3266 
3271  abstract public function flush($config);
3272 
3280  abstract public function cleanup($config);
3281 }
3282 
3283 
3284 
3285 
3286 
3291 {
3295  protected $caches = array('Serializer' => array());
3296 
3300  protected $implementations = array();
3301 
3305  protected $decorators = array();
3306 
3310  public function setup()
3311  {
3312  $this->addDecorator('Cleanup');
3313  }
3314 
3320  public static function instance($prototype = null)
3321  {
3322  static $instance;
3323  if ($prototype !== null) {
3324  $instance = $prototype;
3325  } elseif ($instance === null || $prototype === true) {
3326  $instance = new HTMLPurifier_DefinitionCacheFactory();
3327  $instance->setup();
3328  }
3329  return $instance;
3330  }
3331 
3337  public function register($short, $long)
3338  {
3339  $this->implementations[$short] = $long;
3340  }
3341 
3348  public function create($type, $config)
3349  {
3350  $method = $config->get('Cache.DefinitionImpl');
3351  if ($method === null) {
3352  return new HTMLPurifier_DefinitionCache_Null($type);
3353  }
3354  if (!empty($this->caches[$method][$type])) {
3355  return $this->caches[$method][$type];
3356  }
3357  if (isset($this->implementations[$method]) &&
3358  class_exists($class = $this->implementations[$method], false)) {
3359  $cache = new $class($type);
3360  } else {
3361  if ($method != 'Serializer') {
3362  trigger_error("Unrecognized DefinitionCache $method, using Serializer instead", E_USER_WARNING);
3363  }
3364  $cache = new HTMLPurifier_DefinitionCache_Serializer($type);
3365  }
3366  foreach ($this->decorators as $decorator) {
3367  $new_cache = $decorator->decorate($cache);
3368  // prevent infinite recursion in PHP 4
3369  unset($cache);
3370  $cache = $new_cache;
3371  }
3372  $this->caches[$method][$type] = $cache;
3373  return $this->caches[$method][$type];
3374  }
3375 
3380  public function addDecorator($decorator)
3381  {
3382  if (is_string($decorator)) {
3383  $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator";
3384  $decorator = new $class;
3385  }
3386  $this->decorators[$decorator->name] = $decorator;
3387  }
3388 }
3389 
3390 
3391 
3392 
3393 
3401 {
3406  public $name;
3407 
3413  public $modules = array();
3414 
3419  public $tidyModules = array();
3420 
3425  public $xml = true;
3426 
3431  public $aliases = array();
3432 
3437  public $dtdPublic;
3438 
3443  public $dtdSystem;
3444 
3445  public function __construct(
3446  $name = null,
3447  $xml = true,
3448  $modules = array(),
3449  $tidyModules = array(),
3450  $aliases = array(),
3451  $dtd_public = null,
3452  $dtd_system = null
3453  ) {
3454  $this->name = $name;
3455  $this->xml = $xml;
3456  $this->modules = $modules;
3457  $this->tidyModules = $tidyModules;
3458  $this->aliases = $aliases;
3459  $this->dtdPublic = $dtd_public;
3460  $this->dtdSystem = $dtd_system;
3461  }
3462 }
3463 
3464 
3465 
3466 
3467 
3469 {
3470 
3475  protected $doctypes;
3476 
3481  protected $aliases;
3482 
3496  public function register(
3497  $doctype,
3498  $xml = true,
3499  $modules = array(),
3500  $tidy_modules = array(),
3501  $aliases = array(),
3502  $dtd_public = null,
3503  $dtd_system = null
3504  ) {
3505  if (!is_array($modules)) {
3506  $modules = array($modules);
3507  }
3508  if (!is_array($tidy_modules)) {
3509  $tidy_modules = array($tidy_modules);
3510  }
3511  if (!is_array($aliases)) {
3512  $aliases = array($aliases);
3513  }
3514  if (!is_object($doctype)) {
3515  $doctype = new HTMLPurifier_Doctype(
3516  $doctype,
3517  $xml,
3518  $modules,
3519  $tidy_modules,
3520  $aliases,
3521  $dtd_public,
3522  $dtd_system
3523  );
3524  }
3525  $this->doctypes[$doctype->name] = $doctype;
3526  $name = $doctype->name;
3527  // hookup aliases
3528  foreach ($doctype->aliases as $alias) {
3529  if (isset($this->doctypes[$alias])) {
3530  continue;
3531  }
3532  $this->aliases[$alias] = $name;
3533  }
3534  // remove old aliases
3535  if (isset($this->aliases[$name])) {
3536  unset($this->aliases[$name]);
3537  }
3538  return $doctype;
3539  }
3540 
3548  public function get($doctype)
3549  {
3550  if (isset($this->aliases[$doctype])) {
3551  $doctype = $this->aliases[$doctype];
3552  }
3553  if (!isset($this->doctypes[$doctype])) {
3554  trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR);
3555  $anon = new HTMLPurifier_Doctype($doctype);
3556  return $anon;
3557  }
3558  return $this->doctypes[$doctype];
3559  }
3560 
3571  public function make($config)
3572  {
3573  return clone $this->get($this->getDoctypeFromConfig($config));
3574  }
3575 
3582  {
3583  // recommended test
3584  $doctype = $config->get('HTML.Doctype');
3585  if (!empty($doctype)) {
3586  return $doctype;
3587  }
3588  $doctype = $config->get('HTML.CustomDoctype');
3589  if (!empty($doctype)) {
3590  return $doctype;
3591  }
3592  // backwards-compatibility
3593  if ($config->get('HTML.XHTML')) {
3594  $doctype = 'XHTML 1.0';
3595  } else {
3596  $doctype = 'HTML 4.01';
3597  }
3598  if ($config->get('HTML.Strict')) {
3599  $doctype .= ' Strict';
3600  } else {
3601  $doctype .= ' Transitional';
3602  }
3603  return $doctype;
3604  }
3605 }
3606 
3607 
3608 
3609 
3610 
3620 {
3626  public $standalone = true;
3627 
3640  public $attr = array();
3641 
3642  // XXX: Design note: currently, it's not possible to override
3643  // previously defined AttrTransforms without messing around with
3644  // the final generated config. This is by design; a previous version
3645  // used an associated list of attr_transform, but it was extremely
3646  // easy to accidentally override other attribute transforms by
3647  // forgetting to specify an index (and just using 0.) While we
3648  // could check this by checking the index number and complaining,
3649  // there is a second problem which is that it is not at all easy to
3650  // tell when something is getting overridden. Combine this with a
3651  // codebase where this isn't really being used, and it's perfect for
3652  // nuking.
3653 
3658  public $attr_transform_pre = array();
3659 
3664  public $attr_transform_post = array();
3665 
3670  public $child;
3671 
3681 
3691 
3699  public $descendants_are_inline = false;
3700 
3706  public $required_attr = array();
3707 
3720  public $excludes = array();
3721 
3726  public $autoclose = array();
3727 
3734  public $wrap;
3735 
3741  public $formatting;
3742 
3747  {
3748  $def = new HTMLPurifier_ElementDef();
3749  $def->content_model = $content_model;
3750  $def->content_model_type = $content_model_type;
3751  $def->attr = $attr;
3752  return $def;
3753  }
3754 
3761  public function mergeIn($def)
3762  {
3763  // later keys takes precedence
3764  foreach ($def->attr as $k => $v) {
3765  if ($k === 0) {
3766  // merge in the includes
3767  // sorry, no way to override an include
3768  foreach ($v as $v2) {
3769  $this->attr[0][] = $v2;
3770  }
3771  continue;
3772  }
3773  if ($v === false) {
3774  if (isset($this->attr[$k])) {
3775  unset($this->attr[$k]);
3776  }
3777  continue;
3778  }
3779  $this->attr[$k] = $v;
3780  }
3781  $this->_mergeAssocArray($this->excludes, $def->excludes);
3782  $this->attr_transform_pre = array_merge($this->attr_transform_pre, $def->attr_transform_pre);
3783  $this->attr_transform_post = array_merge($this->attr_transform_post, $def->attr_transform_post);
3784 
3785  if (!empty($def->content_model)) {
3786  $this->content_model =
3787  str_replace("#SUPER", $this->content_model, $def->content_model);
3788  $this->child = false;
3789  }
3790  if (!empty($def->content_model_type)) {
3791  $this->content_model_type = $def->content_model_type;
3792  $this->child = false;
3793  }
3794  if (!is_null($def->child)) {
3795  $this->child = $def->child;
3796  }
3797  if (!is_null($def->formatting)) {
3798  $this->formatting = $def->formatting;
3799  }
3800  if ($def->descendants_are_inline) {
3801  $this->descendants_are_inline = $def->descendants_are_inline;
3802  }
3803  }
3804 
3810  private function _mergeAssocArray(&$a1, $a2)
3811  {
3812  foreach ($a2 as $k => $v) {
3813  if ($v === false) {
3814  if (isset($a1[$k])) {
3815  unset($a1[$k]);
3816  }
3817  continue;
3818  }
3819  $a1[$k] = $v;
3820  }
3821  }
3822 }
3823 
3824 
3825 
3826 
3827 
3833 {
3834 
3838  private function __construct()
3839  {
3840  trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR);
3841  }
3842 
3846  public static function muteErrorHandler()
3847  {
3848  }
3849 
3857  public static function unsafeIconv($in, $out, $text)
3858  {
3859  set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
3860  $r = iconv($in, $out, $text);
3861  restore_error_handler();
3862  return $r;
3863  }
3864 
3873  public static function iconv($in, $out, $text, $max_chunk_size = 8000)
3874  {
3875  $code = self::testIconvTruncateBug();
3876  if ($code == self::ICONV_OK) {
3877  return self::unsafeIconv($in, $out, $text);
3878  } elseif ($code == self::ICONV_TRUNCATES) {
3879  // we can only work around this if the input character set
3880  // is utf-8
3881  if ($in == 'utf-8') {
3882  if ($max_chunk_size < 4) {
3883  trigger_error('max_chunk_size is too small', E_USER_WARNING);
3884  return false;
3885  }
3886  // split into 8000 byte chunks, but be careful to handle
3887  // multibyte boundaries properly
3888  if (($c = strlen($text)) <= $max_chunk_size) {
3889  return self::unsafeIconv($in, $out, $text);
3890  }
3891  $r = '';
3892  $i = 0;
3893  while (true) {
3894  if ($i + $max_chunk_size >= $c) {
3895  $r .= self::unsafeIconv($in, $out, substr($text, $i));
3896  break;
3897  }
3898  // wibble the boundary
3899  if (0x80 != (0xC0 & ord($text[$i + $max_chunk_size]))) {
3900  $chunk_size = $max_chunk_size;
3901  } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 1]))) {
3902  $chunk_size = $max_chunk_size - 1;
3903  } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 2]))) {
3904  $chunk_size = $max_chunk_size - 2;
3905  } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 3]))) {
3906  $chunk_size = $max_chunk_size - 3;
3907  } else {
3908  return false; // rather confusing UTF-8...
3909  }
3910  $chunk = substr($text, $i, $chunk_size); // substr doesn't mind overlong lengths
3911  $r .= self::unsafeIconv($in, $out, $chunk);
3912  $i += $chunk_size;
3913  }
3914  return $r;
3915  } else {
3916  return false;
3917  }
3918  } else {
3919  return false;
3920  }
3921  }
3922 
3959  public static function cleanUTF8($str, $force_php = false)
3960  {
3961  // UTF-8 validity is checked since PHP 4.3.5
3962  // This is an optimization: if the string is already valid UTF-8, no
3963  // need to do PHP stuff. 99% of the time, this will be the case.
3964  if (preg_match(
3965  '/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du',
3966  $str
3967  )) {
3968  return $str;
3969  }
3970 
3971  $mState = 0; // cached expected number of octets after the current octet
3972  // until the beginning of the next UTF8 character sequence
3973  $mUcs4 = 0; // cached Unicode character
3974  $mBytes = 1; // cached expected number of octets in the current sequence
3975 
3976  // original code involved an $out that was an array of Unicode
3977  // codepoints. Instead of having to convert back into UTF-8, we've
3978  // decided to directly append valid UTF-8 characters onto a string
3979  // $out once they're done. $char accumulates raw bytes, while $mUcs4
3980  // turns into the Unicode code point, so there's some redundancy.
3981 
3982  $out = '';
3983  $char = '';
3984 
3985  $len = strlen($str);
3986  for ($i = 0; $i < $len; $i++) {
3987  $in = ord($str[$i]);
3988  $char .= $str[$i]; // append byte to char
3989  if (0 == $mState) {
3990  // When mState is zero we expect either a US-ASCII character
3991  // or a multi-octet sequence.
3992  if (0 == (0x80 & ($in))) {
3993  // US-ASCII, pass straight through.
3994  if (($in <= 31 || $in == 127) &&
3995  !($in == 9 || $in == 13 || $in == 10) // save \r\t\n
3996  ) {
3997  // control characters, remove
3998  } else {
3999  $out .= $char;
4000  }
4001  // reset
4002  $char = '';
4003  $mBytes = 1;
4004  } elseif (0xC0 == (0xE0 & ($in))) {
4005  // First octet of 2 octet sequence
4006  $mUcs4 = ($in);
4007  $mUcs4 = ($mUcs4 & 0x1F) << 6;
4008  $mState = 1;
4009  $mBytes = 2;
4010  } elseif (0xE0 == (0xF0 & ($in))) {
4011  // First octet of 3 octet sequence
4012  $mUcs4 = ($in);
4013  $mUcs4 = ($mUcs4 & 0x0F) << 12;
4014  $mState = 2;
4015  $mBytes = 3;
4016  } elseif (0xF0 == (0xF8 & ($in))) {
4017  // First octet of 4 octet sequence
4018  $mUcs4 = ($in);
4019  $mUcs4 = ($mUcs4 & 0x07) << 18;
4020  $mState = 3;
4021  $mBytes = 4;
4022  } elseif (0xF8 == (0xFC & ($in))) {
4023  // First octet of 5 octet sequence.
4024  //
4025  // This is illegal because the encoded codepoint must be
4026  // either:
4027  // (a) not the shortest form or
4028  // (b) outside the Unicode range of 0-0x10FFFF.
4029  // Rather than trying to resynchronize, we will carry on
4030  // until the end of the sequence and let the later error
4031  // handling code catch it.
4032  $mUcs4 = ($in);
4033  $mUcs4 = ($mUcs4 & 0x03) << 24;
4034  $mState = 4;
4035  $mBytes = 5;
4036  } elseif (0xFC == (0xFE & ($in))) {
4037  // First octet of 6 octet sequence, see comments for 5
4038  // octet sequence.
4039  $mUcs4 = ($in);
4040  $mUcs4 = ($mUcs4 & 1) << 30;
4041  $mState = 5;
4042  $mBytes = 6;
4043  } else {
4044  // Current octet is neither in the US-ASCII range nor a
4045  // legal first octet of a multi-octet sequence.
4046  $mState = 0;
4047  $mUcs4 = 0;
4048  $mBytes = 1;
4049  $char = '';
4050  }
4051  } else {
4052  // When mState is non-zero, we expect a continuation of the
4053  // multi-octet sequence
4054  if (0x80 == (0xC0 & ($in))) {
4055  // Legal continuation.
4056  $shift = ($mState - 1) * 6;
4057  $tmp = $in;
4058  $tmp = ($tmp & 0x0000003F) << $shift;
4059  $mUcs4 |= $tmp;
4060 
4061  if (0 == --$mState) {
4062  // End of the multi-octet sequence. mUcs4 now contains
4063  // the final Unicode codepoint to be output
4064 
4065  // Check for illegal sequences and codepoints.
4066 
4067  // From Unicode 3.1, non-shortest form is illegal
4068  if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
4069  ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
4070  ((4 == $mBytes) && ($mUcs4 < 0x10000)) ||
4071  (4 < $mBytes) ||
4072  // From Unicode 3.2, surrogate characters = illegal
4073  (($mUcs4 & 0xFFFFF800) == 0xD800) ||
4074  // Codepoints outside the Unicode range are illegal
4075  ($mUcs4 > 0x10FFFF)
4076  ) {
4077 
4078  } elseif (0xFEFF != $mUcs4 && // omit BOM
4079  // check for valid Char unicode codepoints
4080  (
4081  0x9 == $mUcs4 ||
4082  0xA == $mUcs4 ||
4083  0xD == $mUcs4 ||
4084  (0x20 <= $mUcs4 && 0x7E >= $mUcs4) ||
4085  // 7F-9F is not strictly prohibited by XML,
4086  // but it is non-SGML, and thus we don't allow it
4087  (0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) ||
4088  (0xE000 <= $mUcs4 && 0xFFFD >= $mUcs4) ||
4089  (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
4090  )
4091  ) {
4092  $out .= $char;
4093  }
4094  // initialize UTF8 cache (reset)
4095  $mState = 0;
4096  $mUcs4 = 0;
4097  $mBytes = 1;
4098  $char = '';
4099  }
4100  } else {
4101  // ((0xC0 & (*in) != 0x80) && (mState != 0))
4102  // Incomplete multi-octet sequence.
4103  // used to result in complete fail, but we'll reset
4104  $mState = 0;
4105  $mUcs4 = 0;
4106  $mBytes = 1;
4107  $char ='';
4108  }
4109  }
4110  }
4111  return $out;
4112  }
4113 
4127  // +----------+----------+----------+----------+
4128  // | 33222222 | 22221111 | 111111 | |
4129  // | 10987654 | 32109876 | 54321098 | 76543210 | bit
4130  // +----------+----------+----------+----------+
4131  // | | | | 0xxxxxxx | 1 byte 0x00000000..0x0000007F
4132  // | | | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF
4133  // | | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF
4134  // | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF
4135  // +----------+----------+----------+----------+
4136  // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF)
4137  // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes
4138  // +----------+----------+----------+----------+
4139 
4140  public static function unichr($code)
4141  {
4142  if ($code > 1114111 or $code < 0 or
4143  ($code >= 55296 and $code <= 57343) ) {
4144  // bits are set outside the "valid" range as defined
4145  // by UNICODE 4.1.0
4146  return '';
4147  }
4148 
4149  $x = $y = $z = $w = 0;
4150  if ($code < 128) {
4151  // regular ASCII character
4152  $x = $code;
4153  } else {
4154  // set up bits for UTF-8
4155  $x = ($code & 63) | 128;
4156  if ($code < 2048) {
4157  $y = (($code & 2047) >> 6) | 192;
4158  } else {
4159  $y = (($code & 4032) >> 6) | 128;
4160  if ($code < 65536) {
4161  $z = (($code >> 12) & 15) | 224;
4162  } else {
4163  $z = (($code >> 12) & 63) | 128;
4164  $w = (($code >> 18) & 7) | 240;
4165  }
4166  }
4167  }
4168  // set up the actual character
4169  $ret = '';
4170  if ($w) {
4171  $ret .= chr($w);
4172  }
4173  if ($z) {
4174  $ret .= chr($z);
4175  }
4176  if ($y) {
4177  $ret .= chr($y);
4178  }
4179  $ret .= chr($x);
4180 
4181  return $ret;
4182  }
4183 
4187  public static function iconvAvailable()
4188  {
4189  static $iconv = null;
4190  if ($iconv === null) {
4191  $iconv = function_exists('iconv') && self::testIconvTruncateBug() != self::ICONV_UNUSABLE;
4192  }
4193  return $iconv;
4194  }
4195 
4203  public static function convertToUTF8($str, $config, $context)
4204  {
4205  $encoding = $config->get('Core.Encoding');
4206  if ($encoding === 'utf-8') {
4207  return $str;
4208  }
4209  static $iconv = null;
4210  if ($iconv === null) {
4211  $iconv = self::iconvAvailable();
4212  }
4213  if ($iconv && !$config->get('Test.ForceNoIconv')) {
4214  // unaffected by bugs, since UTF-8 support all characters
4215  $str = self::unsafeIconv($encoding, 'utf-8//IGNORE', $str);
4216  if ($str === false) {
4217  // $encoding is not a valid encoding
4218  trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR);
4219  return '';
4220  }
4221  // If the string is bjorked by Shift_JIS or a similar encoding
4222  // that doesn't support all of ASCII, convert the naughty
4223  // characters to their true byte-wise ASCII/UTF-8 equivalents.
4224  $str = strtr($str, self::testEncodingSupportsASCII($encoding));
4225  return $str;
4226  } elseif ($encoding === 'iso-8859-1') {
4227  $str = utf8_encode($str);
4228  return $str;
4229  }
4231  if ($bug == self::ICONV_OK) {
4232  trigger_error('Encoding not supported, please install iconv', E_USER_ERROR);
4233  } else {
4234  trigger_error(
4235  'You have a buggy version of iconv, see https://bugs.php.net/bug.php?id=48147 ' .
4236  'and http://sourceware.org/bugzilla/show_bug.cgi?id=13541',
4237  E_USER_ERROR
4238  );
4239  }
4240  }
4241 
4251  public static function convertFromUTF8($str, $config, $context)
4252  {
4253  $encoding = $config->get('Core.Encoding');
4254  if ($escape = $config->get('Core.EscapeNonASCIICharacters')) {
4255  $str = self::convertToASCIIDumbLossless($str);
4256  }
4257  if ($encoding === 'utf-8') {
4258  return $str;
4259  }
4260  static $iconv = null;
4261  if ($iconv === null) {
4262  $iconv = self::iconvAvailable();
4263  }
4264  if ($iconv && !$config->get('Test.ForceNoIconv')) {
4265  // Undo our previous fix in convertToUTF8, otherwise iconv will barf
4266  $ascii_fix = self::testEncodingSupportsASCII($encoding);
4267  if (!$escape && !empty($ascii_fix)) {
4268  $clear_fix = array();
4269  foreach ($ascii_fix as $utf8 => $native) {
4270  $clear_fix[$utf8] = '';
4271  }
4272  $str = strtr($str, $clear_fix);
4273  }
4274  $str = strtr($str, array_flip($ascii_fix));
4275  // Normal stuff
4276  $str = self::iconv('utf-8', $encoding . '//IGNORE', $str);
4277  return $str;
4278  } elseif ($encoding === 'iso-8859-1') {
4279  $str = utf8_decode($str);
4280  return $str;
4281  }
4282  trigger_error('Encoding not supported', E_USER_ERROR);
4283  // You might be tempted to assume that the ASCII representation
4284  // might be OK, however, this is *not* universally true over all
4285  // encodings. So we take the conservative route here, rather
4286  // than forcibly turn on %Core.EscapeNonASCIICharacters
4287  }
4288 
4305  public static function convertToASCIIDumbLossless($str)
4306  {
4307  $bytesleft = 0;
4308  $result = '';
4309  $working = 0;
4310  $len = strlen($str);
4311  for ($i = 0; $i < $len; $i++) {
4312  $bytevalue = ord($str[$i]);
4313  if ($bytevalue <= 0x7F) { //0xxx xxxx
4314  $result .= chr($bytevalue);
4315  $bytesleft = 0;
4316  } elseif ($bytevalue <= 0xBF) { //10xx xxxx
4317  $working = $working << 6;
4318  $working += ($bytevalue & 0x3F);
4319  $bytesleft--;
4320  if ($bytesleft <= 0) {
4321  $result .= "&#" . $working . ";";
4322  }
4323  } elseif ($bytevalue <= 0xDF) { //110x xxxx
4324  $working = $bytevalue & 0x1F;
4325  $bytesleft = 1;
4326  } elseif ($bytevalue <= 0xEF) { //1110 xxxx
4327  $working = $bytevalue & 0x0F;
4328  $bytesleft = 2;
4329  } else { //1111 0xxx
4330  $working = $bytevalue & 0x07;
4331  $bytesleft = 3;
4332  }
4333  }
4334  return $result;
4335  }
4336 
4338  const ICONV_OK = 0;
4339 
4342  const ICONV_TRUNCATES = 1;
4343 
4346  const ICONV_UNUSABLE = 2;
4347 
4362  public static function testIconvTruncateBug()
4363  {
4364  static $code = null;
4365  if ($code === null) {
4366  // better not use iconv, otherwise infinite loop!
4367  $r = self::unsafeIconv('utf-8', 'ascii//IGNORE', "\xCE\xB1" . str_repeat('a', 9000));
4368  if ($r === false) {
4369  $code = self::ICONV_UNUSABLE;
4370  } elseif (($c = strlen($r)) < 9000) {
4371  $code = self::ICONV_TRUNCATES;
4372  } elseif ($c > 9000) {
4373  trigger_error(
4374  'Your copy of iconv is extremely buggy. Please notify HTML Purifier maintainers: ' .
4375  'include your iconv version as per phpversion()',
4376  E_USER_ERROR
4377  );
4378  } else {
4379  $code = self::ICONV_OK;
4380  }
4381  }
4382  return $code;
4383  }
4384 
4396  public static function testEncodingSupportsASCII($encoding, $bypass = false)
4397  {
4398  // All calls to iconv here are unsafe, proof by case analysis:
4399  // If ICONV_OK, no difference.
4400  // If ICONV_TRUNCATE, all calls involve one character inputs,
4401  // so bug is not triggered.
4402  // If ICONV_UNUSABLE, this call is irrelevant
4403  static $encodings = array();
4404  if (!$bypass) {
4405  if (isset($encodings[$encoding])) {
4406  return $encodings[$encoding];
4407  }
4408  $lenc = strtolower($encoding);
4409  switch ($lenc) {
4410  case 'shift_jis':
4411  return array("\xC2\xA5" => '\\', "\xE2\x80\xBE" => '~');
4412  case 'johab':
4413  return array("\xE2\x82\xA9" => '\\');
4414  }
4415  if (strpos($lenc, 'iso-8859-') === 0) {
4416  return array();
4417  }
4418  }
4419  $ret = array();
4420  if (self::unsafeIconv('UTF-8', $encoding, 'a') === false) {
4421  return false;
4422  }
4423  for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars
4424  $c = chr($i); // UTF-8 char
4425  $r = self::unsafeIconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion
4426  if ($r === '' ||
4427  // This line is needed for iconv implementations that do not
4428  // omit characters that do not exist in the target character set
4429  ($r === $c && self::unsafeIconv($encoding, 'UTF-8//IGNORE', $r) !== $c)
4430  ) {
4431  // Reverse engineer: what's the UTF-8 equiv of this byte
4432  // sequence? This assumes that there's no variable width
4433  // encoding that doesn't support ASCII.
4434  $ret[self::unsafeIconv($encoding, 'UTF-8//IGNORE', $c)] = $c;
4435  }
4436  }
4437  $encodings[$encoding] = $ret;
4438  return $ret;
4439  }
4440 }
4441 
4442 
4443 
4444 
4445 
4450 {
4455  public $table;
4456 
4464  public function setup($file = false)
4465  {
4466  if (!$file) {
4467  $file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/EntityLookup/entities.ser';
4468  }
4469  $this->table = unserialize(file_get_contents($file));
4470  }
4471 
4477  public static function instance($prototype = false)
4478  {
4479  // no references, since PHP doesn't copy unless modified
4480  static $instance = null;
4481  if ($prototype) {
4482  $instance = $prototype;
4483  } elseif (!$instance) {
4484  $instance = new HTMLPurifier_EntityLookup();
4485  $instance->setup();
4486  }
4487  return $instance;
4488  }
4489 }
4490 
4491 
4492 
4493 
4494 
4495 // if want to implement error collecting here, we'll need to use some sort
4496 // of global data (probably trigger_error) because it's impossible to pass
4497 // $config or $context to the callback functions.
4498 
4503 {
4504 
4509  protected $_entity_lookup;
4510 
4516 
4522 
4527 
4528  public function __construct() {
4529  // From
4530  // http://stackoverflow.com/questions/15532252/why-is-reg-being-rendered-as-without-the-bounding-semicolon
4531  $semi_optional = "quot|QUOT|lt|LT|gt|GT|amp|AMP|AElig|Aacute|Acirc|Agrave|Aring|Atilde|Auml|COPY|Ccedil|ETH|Eacute|Ecirc|Egrave|Euml|Iacute|Icirc|Igrave|Iuml|Ntilde|Oacute|Ocirc|Ograve|Oslash|Otilde|Ouml|REG|THORN|Uacute|Ucirc|Ugrave|Uuml|Yacute|aacute|acirc|acute|aelig|agrave|aring|atilde|auml|brvbar|ccedil|cedil|cent|copy|curren|deg|divide|eacute|ecirc|egrave|eth|euml|frac12|frac14|frac34|iacute|icirc|iexcl|igrave|iquest|iuml|laquo|macr|micro|middot|nbsp|not|ntilde|oacute|ocirc|ograve|ordf|ordm|oslash|otilde|ouml|para|plusmn|pound|raquo|reg|sect|shy|sup1|sup2|sup3|szlig|thorn|times|uacute|ucirc|ugrave|uml|uuml|yacute|yen|yuml";
4532 
4533  // NB: three empty captures to put the fourth match in the right
4534  // place
4535  $this->_semiOptionalPrefixRegex = "/&()()()($semi_optional)/";
4536 
4537  $this->_textEntitiesRegex =
4538  '/&(?:'.
4539  // hex
4540  '[#]x([a-fA-F0-9]+);?|'.
4541  // dec
4542  '[#]0*(\d+);?|'.
4543  // string (mandatory semicolon)
4544  // NB: order matters: match semicolon preferentially
4545  '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
4546  // string (optional semicolon)
4547  "($semi_optional)".
4548  ')/';
4549 
4550  $this->_attrEntitiesRegex =
4551  '/&(?:'.
4552  // hex
4553  '[#]x([a-fA-F0-9]+);?|'.
4554  // dec
4555  '[#]0*(\d+);?|'.
4556  // string (mandatory semicolon)
4557  // NB: order matters: match semicolon preferentially
4558  '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
4559  // string (optional semicolon)
4560  // don't match if trailing is equals or alphanumeric (URL
4561  // like)
4562  "($semi_optional)(?![=;A-Za-z0-9])".
4563  ')/';
4564 
4565  }
4566 
4574  public function substituteTextEntities($string)
4575  {
4576  return preg_replace_callback(
4577  $this->_textEntitiesRegex,
4578  array($this, 'entityCallback'),
4579  $string
4580  );
4581  }
4582 
4590  public function substituteAttrEntities($string)
4591  {
4592  return preg_replace_callback(
4593  $this->_attrEntitiesRegex,
4594  array($this, 'entityCallback'),
4595  $string
4596  );
4597  }
4598 
4608  protected function entityCallback($matches)
4609  {
4610  $entity = $matches[0];
4611  $hex_part = @$matches[1];
4612  $dec_part = @$matches[2];
4613  $named_part = empty($matches[3]) ? (empty($matches[4]) ? "" : $matches[4]) : $matches[3];
4614  if ($hex_part !== NULL && $hex_part !== "") {
4615  return HTMLPurifier_Encoder::unichr(hexdec($hex_part));
4616  } elseif ($dec_part !== NULL && $dec_part !== "") {
4617  return HTMLPurifier_Encoder::unichr((int) $dec_part);
4618  } else {
4619  if (!$this->_entity_lookup) {
4620  $this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
4621  }
4622  if (isset($this->_entity_lookup->table[$named_part])) {
4623  return $this->_entity_lookup->table[$named_part];
4624  } else {
4625  // exact match didn't match anything, so test if
4626  // any of the semicolon optional match the prefix.
4627  // Test that this is an EXACT match is important to
4628  // prevent infinite loop
4629  if (!empty($matches[3])) {
4630  return preg_replace_callback(
4631  $this->_semiOptionalPrefixRegex,
4632  array($this, 'entityCallback'),
4633  $entity
4634  );
4635  }
4636  return $entity;
4637  }
4638  }
4639  }
4640 
4641  // LEGACY CODE BELOW
4642 
4648  '/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/';
4649  // 1. hex 2. dec 3. string (XML style)
4650 
4655  protected $_special_dec2str =
4656  array(
4657  34 => '"',
4658  38 => '&',
4659  39 => "'",
4660  60 => '<',
4661  62 => '>'
4662  );
4663 
4668  protected $_special_ent2dec =
4669  array(
4670  'quot' => 34,
4671  'amp' => 38,
4672  'lt' => 60,
4673  'gt' => 62
4674  );
4675 
4684  public function substituteNonSpecialEntities($string)
4685  {
4686  // it will try to detect missing semicolons, but don't rely on it
4687  return preg_replace_callback(
4688  $this->_substituteEntitiesRegex,
4689  array($this, 'nonSpecialEntityCallback'),
4690  $string
4691  );
4692  }
4693 
4703  protected function nonSpecialEntityCallback($matches)
4704  {
4705  // replaces all but big five
4706  $entity = $matches[0];
4707  $is_num = (@$matches[0][1] === '#');
4708  if ($is_num) {
4709  $is_hex = (@$entity[2] === 'x');
4710  $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
4711  // abort for special characters
4712  if (isset($this->_special_dec2str[$code])) {
4713  return $entity;
4714  }
4715  return HTMLPurifier_Encoder::unichr($code);
4716  } else {
4717  if (isset($this->_special_ent2dec[$matches[3]])) {
4718  return $entity;
4719  }
4720  if (!$this->_entity_lookup) {
4721  $this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
4722  }
4723  if (isset($this->_entity_lookup->table[$matches[3]])) {
4724  return $this->_entity_lookup->table[$matches[3]];
4725  } else {
4726  return $entity;
4727  }
4728  }
4729  }
4730 
4740  public function substituteSpecialEntities($string)
4741  {
4742  return preg_replace_callback(
4743  $this->_substituteEntitiesRegex,
4744  array($this, 'specialEntityCallback'),
4745  $string
4746  );
4747  }
4748 
4759  protected function specialEntityCallback($matches)
4760  {
4761  $entity = $matches[0];
4762  $is_num = (@$matches[0][1] === '#');
4763  if ($is_num) {
4764  $is_hex = (@$entity[2] === 'x');
4765  $int = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
4766  return isset($this->_special_dec2str[$int]) ?
4767  $this->_special_dec2str[$int] :
4768  $entity;
4769  } else {
4770  return isset($this->_special_ent2dec[$matches[3]]) ?
4771  $this->_special_dec2str[$this->_special_ent2dec[$matches[3]]] :
4772  $entity;
4773  }
4774  }
4775 }
4776 
4777 
4778 
4779 
4780 
4786 {
4787 
4792  const LINENO = 0;
4793  const SEVERITY = 1;
4794  const MESSAGE = 2;
4795  const CHILDREN = 3;
4796 
4800  protected $errors;
4801 
4805  protected $_current;
4806 
4810  protected $_stacks = array(array());
4811 
4815  protected $locale;
4816 
4820  protected $generator;
4821 
4825  protected $context;
4826 
4830  protected $lines = array();
4831 
4835  public function __construct($context)
4836  {
4837  $this->locale =& $context->get('Locale');
4838  $this->context = $context;
4839  $this->_current =& $this->_stacks[0];
4840  $this->errors =& $this->_stacks[0];
4841  }
4842 
4848  public function send($severity, $msg)
4849  {
4850  $args = array();
4851  if (func_num_args() > 2) {
4852  $args = func_get_args();
4853  array_shift($args);
4854  unset($args[0]);
4855  }
4856 
4857  $token = $this->context->get('CurrentToken', true);
4858  $line = $token ? $token->line : $this->context->get('CurrentLine', true);
4859  $col = $token ? $token->col : $this->context->get('CurrentCol', true);
4860  $attr = $this->context->get('CurrentAttr', true);
4861 
4862  // perform special substitutions, also add custom parameters
4863  $subst = array();
4864  if (!is_null($token)) {
4865  $args['CurrentToken'] = $token;
4866  }
4867  if (!is_null($attr)) {
4868  $subst['$CurrentAttr.Name'] = $attr;
4869  if (isset($token->attr[$attr])) {
4870  $subst['$CurrentAttr.Value'] = $token->attr[$attr];
4871  }
4872  }
4873 
4874  if (empty($args)) {
4875  $msg = $this->locale->getMessage($msg);
4876  } else {
4877  $msg = $this->locale->formatMessage($msg, $args);
4878  }
4879 
4880  if (!empty($subst)) {
4881  $msg = strtr($msg, $subst);
4882  }
4883 
4884  // (numerically indexed)
4885  $error = array(
4886  self::LINENO => $line,
4887  self::SEVERITY => $severity,
4888  self::MESSAGE => $msg,
4889  self::CHILDREN => array()
4890  );
4891  $this->_current[] = $error;
4892 
4893  // NEW CODE BELOW ...
4894  // Top-level errors are either:
4895  // TOKEN type, if $value is set appropriately, or
4896  // "syntax" type, if $value is null
4897  $new_struct = new HTMLPurifier_ErrorStruct();
4898  $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN;
4899  if ($token) {
4900  $new_struct->value = clone $token;
4901  }
4902  if (is_int($line) && is_int($col)) {
4903  if (isset($this->lines[$line][$col])) {
4904  $struct = $this->lines[$line][$col];
4905  } else {
4906  $struct = $this->lines[$line][$col] = $new_struct;
4907  }
4908  // These ksorts may present a performance problem
4909  ksort($this->lines[$line], SORT_NUMERIC);
4910  } else {
4911  if (isset($this->lines[-1])) {
4912  $struct = $this->lines[-1];
4913  } else {
4914  $struct = $this->lines[-1] = $new_struct;
4915  }
4916  }
4917  ksort($this->lines, SORT_NUMERIC);
4918 
4919  // Now, check if we need to operate on a lower structure
4920  if (!empty($attr)) {
4921  $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr);
4922  if (!$struct->value) {
4923  $struct->value = array($attr, 'PUT VALUE HERE');
4924  }
4925  }
4926  if (!empty($cssprop)) {
4927  $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop);
4928  if (!$struct->value) {
4929  // if we tokenize CSS this might be a little more difficult to do
4930  $struct->value = array($cssprop, 'PUT VALUE HERE');
4931  }
4932  }
4933 
4934  // Ok, structs are all setup, now time to register the error
4935  $struct->addError($severity, $msg);
4936  }
4937 
4941  public function getRaw()
4942  {
4943  return $this->errors;
4944  }
4945 
4952  public function getHTMLFormatted($config, $errors = null)
4953  {
4954  $ret = array();
4955 
4956  $this->generator = new HTMLPurifier_Generator($config, $this->context);
4957  if ($errors === null) {
4959  }
4960 
4961  // 'At line' message needs to be removed
4962 
4963  // generation code for new structure goes here. It needs to be recursive.
4964  foreach ($this->lines as $line => $col_array) {
4965  if ($line == -1) {
4966  continue;
4967  }
4968  foreach ($col_array as $col => $struct) {
4969  $this->_renderStruct($ret, $struct, $line, $col);
4970  }
4971  }
4972  if (isset($this->lines[-1])) {
4973  $this->_renderStruct($ret, $this->lines[-1]);
4974  }
4975 
4976  if (empty($errors)) {
4977  return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>';
4978  } else {
4979  return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>';
4980  }
4981 
4982  }
4983 
4984  private function _renderStruct(&$ret, $struct, $line = null, $col = null)
4985  {
4986  $stack = array($struct);
4987  $context_stack = array(array());
4988  while ($current = array_pop($stack)) {
4989  $context = array_pop($context_stack);
4990  foreach ($current->errors as $error) {
4991  list($severity, $msg) = $error;
4992  $string = '';
4993  $string .= '<div>';
4994  // W3C uses an icon to indicate the severity of the error.
4995  $error = $this->locale->getErrorName($severity);
4996  $string .= "<span class=\"error e$severity\"><strong>$error</strong></span> ";
4997  if (!is_null($line) && !is_null($col)) {
4998  $string .= "<em class=\"location\">Line $line, Column $col: </em> ";
4999  } else {
5000  $string .= '<em class="location">End of Document: </em> ';
5001  }
5002  $string .= '<strong class="description">' . $this->generator->escape($msg) . '</strong> ';
5003  $string .= '</div>';
5004  // Here, have a marker for the character on the column appropriate.
5005  // Be sure to clip extremely long lines.
5006  //$string .= '<pre>';
5007  //$string .= '';
5008  //$string .= '</pre>';
5009  $ret[] = $string;
5010  }
5011  foreach ($current->children as $array) {
5012  $context[] = $current;
5013  $stack = array_merge($stack, array_reverse($array, true));
5014  for ($i = count($array); $i > 0; $i--) {
5015  $context_stack[] = $context;
5016  }
5017  }
5018  }
5019  }
5020 }
5021 
5022 
5023 
5024 
5025 
5033 {
5034 
5039  const TOKEN = 0;
5040  const ATTR = 1;
5041  const CSSPROP = 2;
5042 
5047  public $type;
5048 
5057  public $value;
5058 
5063  public $errors = array();
5064 
5071  public $children = array();
5072 
5078  public function getChild($type, $id)
5079  {
5080  if (!isset($this->children[$type][$id])) {
5081  $this->children[$type][$id] = new HTMLPurifier_ErrorStruct();
5082  $this->children[$type][$id]->type = $type;
5083  }
5084  return $this->children[$type][$id];
5085  }
5086 
5091  public function addError($severity, $message)
5092  {
5093  $this->errors[] = array($severity, $message);
5094  }
5095 }
5096 
5097 
5098 
5099 
5100 
5105 class HTMLPurifier_Exception extends Exception
5106 {
5107 
5108 }
5109 
5110 
5111 
5112 
5113 
5134 {
5135 
5140  public $name;
5141 
5149  public function preFilter($html, $config, $context)
5150  {
5151  return $html;
5152  }
5153 
5161  public function postFilter($html, $config, $context)
5162  {
5163  return $html;
5164  }
5165 }
5166 
5167 
5168 
5169 
5170 
5179 {
5180 
5185  private $_xhtml = true;
5186 
5191  private $_scriptFix = false;
5192 
5198  private $_def;
5199 
5204  private $_sortAttr;
5205 
5210  private $_flashCompat;
5211 
5216  private $_innerHTMLFix;
5217 
5223  private $_flashStack = array();
5224 
5229  protected $config;
5230 
5235  public function __construct($config, $context)
5236  {
5237  $this->config = $config;
5238  $this->_scriptFix = $config->get('Output.CommentScriptContents');
5239  $this->_innerHTMLFix = $config->get('Output.FixInnerHTML');
5240  $this->_sortAttr = $config->get('Output.SortAttr');
5241  $this->_flashCompat = $config->get('Output.FlashCompat');
5242  $this->_def = $config->getHTMLDefinition();
5243  $this->_xhtml = $this->_def->doctype->xml;
5244  }
5245 
5251  public function generateFromTokens($tokens)
5252  {
5253  if (!$tokens) {
5254  return '';
5255  }
5256 
5257  // Basic algorithm
5258  $html = '';
5259  for ($i = 0, $size = count($tokens); $i < $size; $i++) {
5260  if ($this->_scriptFix && $tokens[$i]->name === 'script'
5261  && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) {
5262  // script special case
5263  // the contents of the script block must be ONE token
5264  // for this to work.
5265  $html .= $this->generateFromToken($tokens[$i++]);
5266  $html .= $this->generateScriptFromToken($tokens[$i++]);
5267  }
5268  $html .= $this->generateFromToken($tokens[$i]);
5269  }
5270 
5271  // Tidy cleanup
5272  if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) {
5273  $tidy = new Tidy;
5274  $tidy->parseString(
5275  $html,
5276  array(
5277  'indent'=> true,
5278  'output-xhtml' => $this->_xhtml,
5279  'show-body-only' => true,
5280  'indent-spaces' => 2,
5281  'wrap' => 68,
5282  ),
5283  'utf8'
5284  );
5285  $tidy->cleanRepair();
5286  $html = (string) $tidy; // explicit cast necessary
5287  }
5288 
5289  // Normalize newlines to system defined value
5290  if ($this->config->get('Core.NormalizeNewlines')) {
5291  $nl = $this->config->get('Output.Newline');
5292  if ($nl === null) {
5293  $nl = PHP_EOL;
5294  }
5295  if ($nl !== "\n") {
5296  $html = str_replace("\n", $nl, $html);
5297  }
5298  }
5299  return $html;
5300  }
5301 
5307  public function generateFromToken($token)
5308  {
5309  if (!$token instanceof HTMLPurifier_Token) {
5310  trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING);
5311  return '';
5312 
5313  } elseif ($token instanceof HTMLPurifier_Token_Start) {
5314  $attr = $this->generateAttributes($token->attr, $token->name);
5315  if ($this->_flashCompat) {
5316  if ($token->name == "object") {
5317  $flash = new stdClass();
5318  $flash->attr = $token->attr;
5319  $flash->param = array();
5320  $this->_flashStack[] = $flash;
5321  }
5322  }
5323  return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
5324 
5325  } elseif ($token instanceof HTMLPurifier_Token_End) {
5326  $_extra = '';
5327  if ($this->_flashCompat) {
5328  if ($token->name == "object" && !empty($this->_flashStack)) {
5329  // doesn't do anything for now
5330  }
5331  }
5332  return $_extra . '</' . $token->name . '>';
5333 
5334  } elseif ($token instanceof HTMLPurifier_Token_Empty) {
5335  if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) {
5336  $this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value'];
5337  }
5338  $attr = $this->generateAttributes($token->attr, $token->name);
5339  return '<' . $token->name . ($attr ? ' ' : '') . $attr .
5340  ( $this->_xhtml ? ' /': '' ) // <br /> v. <br>
5341  . '>';
5342 
5343  } elseif ($token instanceof HTMLPurifier_Token_Text) {
5344  return $this->escape($token->data, ENT_NOQUOTES);
5345 
5346  } elseif ($token instanceof HTMLPurifier_Token_Comment) {
5347  return '<!--' . $token->data . '-->';
5348  } else {
5349  return '';
5350 
5351  }
5352  }
5353 
5361  public function generateScriptFromToken($token)
5362  {
5363  if (!$token instanceof HTMLPurifier_Token_Text) {
5364  return $this->generateFromToken($token);
5365  }
5366  // Thanks <http://lachy.id.au/log/2005/05/script-comments>
5367  $data = preg_replace('#//\s*$#', '', $token->data);
5368  return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>';
5369  }
5370 
5379  public function generateAttributes($assoc_array_of_attributes, $element = '')
5380  {
5381  $html = '';
5382  if ($this->_sortAttr) {
5383  ksort($assoc_array_of_attributes);
5384  }
5385  foreach ($assoc_array_of_attributes as $key => $value) {
5386  if (!$this->_xhtml) {
5387  // Remove namespaced attributes
5388  if (strpos($key, ':') !== false) {
5389  continue;
5390  }
5391  // Check if we should minimize the attribute: val="val" -> val
5392  if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) {
5393  $html .= $key . ' ';
5394  continue;
5395  }
5396  }
5397  // Workaround for Internet Explorer innerHTML bug.
5398  // Essentially, Internet Explorer, when calculating
5399  // innerHTML, omits quotes if there are no instances of
5400  // angled brackets, quotes or spaces. However, when parsing
5401  // HTML (for example, when you assign to innerHTML), it
5402  // treats backticks as quotes. Thus,
5403  // <img alt="``" />
5404  // becomes
5405  // <img alt=`` />
5406  // becomes
5407  // <img alt='' />
5408  // Fortunately, all we need to do is trigger an appropriate
5409  // quoting style, which we do by adding an extra space.
5410  // This also is consistent with the W3C spec, which states
5411  // that user agents may ignore leading or trailing
5412  // whitespace (in fact, most don't, at least for attributes
5413  // like alt, but an extra space at the end is barely
5414  // noticeable). Still, we have a configuration knob for
5415  // this, since this transformation is not necesary if you
5416  // don't process user input with innerHTML or you don't plan
5417  // on supporting Internet Explorer.
5418  if ($this->_innerHTMLFix) {
5419  if (strpos($value, '`') !== false) {
5420  // check if correct quoting style would not already be
5421  // triggered
5422  if (strcspn($value, '"\' <>') === strlen($value)) {
5423  // protect!
5424  $value .= ' ';
5425  }
5426  }
5427  }
5428  $html .= $key.'="'.$this->escape($value).'" ';
5429  }
5430  return rtrim($html);
5431  }
5432 
5443  public function escape($string, $quote = null)
5444  {
5445  // Workaround for APC bug on Mac Leopard reported by sidepodcast
5446  // http://htmlpurifier.org/phorum/read.php?3,4823,4846
5447  if ($quote === null) {
5448  $quote = ENT_COMPAT;
5449  }
5450  return htmlspecialchars($string, $quote, 'UTF-8');
5451  }
5452 }
5453 
5454 
5455 
5456 
5457 
5482 {
5483 
5484  // FULLY-PUBLIC VARIABLES ---------------------------------------------
5485 
5490  public $info = array();
5491 
5496  public $info_global_attr = array();
5497 
5502  public $info_parent = 'div';
5503 
5510 
5516  public $info_block_wrapper = 'p';
5517 
5522  public $info_tag_transform = array();
5523 
5528  public $info_attr_transform_pre = array();
5529 
5534  public $info_attr_transform_post = array();
5535 
5541  public $info_content_sets = array();
5542 
5547  public $info_injector = array();
5548 
5553  public $doctype;
5554 
5555 
5556 
5557  // RAW CUSTOMIZATION STUFF --------------------------------------------
5558 
5568  public function addAttribute($element_name, $attr_name, $def)
5569  {
5570  $module = $this->getAnonymousModule();
5571  if (!isset($module->info[$element_name])) {
5572  $element = $module->addBlankElement($element_name);
5573  } else {
5574  $element = $module->info[$element_name];
5575  }
5576  $element->attr[$attr_name] = $def;
5577  }
5578 
5584  public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array())
5585  {
5586  $module = $this->getAnonymousModule();
5587  // assume that if the user is calling this, the element
5588  // is safe. This may not be a good idea
5589  $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes);
5590  return $element;
5591  }
5592 
5601  public function addBlankElement($element_name)
5602  {
5603  $module = $this->getAnonymousModule();
5604  $element = $module->addBlankElement($element_name);
5605  return $element;
5606  }
5607 
5614  public function getAnonymousModule()
5615  {
5616  if (!$this->_anonModule) {
5617  $this->_anonModule = new HTMLPurifier_HTMLModule();
5618  $this->_anonModule->name = 'Anonymous';
5619  }
5620  return $this->_anonModule;
5621  }
5622 
5623  private $_anonModule = null;
5624 
5625  // PUBLIC BUT INTERNAL VARIABLES --------------------------------------
5626 
5630  public $type = 'HTML';
5631 
5635  public $manager;
5636 
5640  public function __construct()
5641  {
5642  $this->manager = new HTMLPurifier_HTMLModuleManager();
5643  }
5644 
5648  protected function doSetup($config)
5649  {
5650  $this->processModules($config);
5651  $this->setupConfigStuff($config);
5652  unset($this->manager);
5653 
5654  // cleanup some of the element definitions
5655  foreach ($this->info as $k => $v) {
5656  unset($this->info[$k]->content_model);
5657  unset($this->info[$k]->content_model_type);
5658  }
5659  }
5660 
5665  protected function processModules($config)
5666  {
5667  if ($this->_anonModule) {
5668  // for user specific changes
5669  // this is late-loaded so we don't have to deal with PHP4
5670  // reference wonky-ness
5671  $this->manager->addModule($this->_anonModule);
5672  unset($this->_anonModule);
5673  }
5674 
5675  $this->manager->setup($config);
5676  $this->doctype = $this->manager->doctype;
5677 
5678  foreach ($this->manager->modules as $module) {
5679  foreach ($module->info_tag_transform as $k => $v) {
5680  if ($v === false) {
5681  unset($this->info_tag_transform[$k]);
5682  } else {
5683  $this->info_tag_transform[$k] = $v;
5684  }
5685  }
5686  foreach ($module->info_attr_transform_pre as $k => $v) {
5687  if ($v === false) {
5688  unset($this->info_attr_transform_pre[$k]);
5689  } else {
5690  $this->info_attr_transform_pre[$k] = $v;
5691  }
5692  }
5693  foreach ($module->info_attr_transform_post as $k => $v) {
5694  if ($v === false) {
5695  unset($this->info_attr_transform_post[$k]);
5696  } else {
5697  $this->info_attr_transform_post[$k] = $v;
5698  }
5699  }
5700  foreach ($module->info_injector as $k => $v) {
5701  if ($v === false) {
5702  unset($this->info_injector[$k]);
5703  } else {
5704  $this->info_injector[$k] = $v;
5705  }
5706  }
5707  }
5708  $this->info = $this->manager->getElements();
5709  $this->info_content_sets = $this->manager->contentSets->lookup;
5710  }
5711 
5716  protected function setupConfigStuff($config)
5717  {
5718  $block_wrapper = $config->get('HTML.BlockWrapper');
5719  if (isset($this->info_content_sets['Block'][$block_wrapper])) {
5720  $this->info_block_wrapper = $block_wrapper;
5721  } else {
5722  trigger_error(
5723  'Cannot use non-block element as block wrapper',
5724  E_USER_ERROR
5725  );
5726  }
5727 
5728  $parent = $config->get('HTML.Parent');
5729  $def = $this->manager->getElement($parent, true);
5730  if ($def) {
5731  $this->info_parent = $parent;
5732  $this->info_parent_def = $def;
5733  } else {
5734  trigger_error(
5735  'Cannot use unrecognized element as parent',
5736  E_USER_ERROR
5737  );
5738  $this->info_parent_def = $this->manager->getElement($this->info_parent, true);
5739  }
5740 
5741  // support template text
5742  $support = "(for information on implementing this, see the support forums) ";
5743 
5744  // setup allowed elements -----------------------------------------
5745 
5746  $allowed_elements = $config->get('HTML.AllowedElements');
5747  $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early
5748 
5749  if (!is_array($allowed_elements) && !is_array($allowed_attributes)) {
5750  $allowed = $config->get('HTML.Allowed');
5751  if (is_string($allowed)) {
5752  list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed);
5753  }
5754  }
5755 
5756  if (is_array($allowed_elements)) {
5757  foreach ($this->info as $name => $d) {
5758  if (!isset($allowed_elements[$name])) {
5759  unset($this->info[$name]);
5760  }
5761  unset($allowed_elements[$name]);
5762  }
5763  // emit errors
5764  foreach ($allowed_elements as $element => $d) {
5765  $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful!
5766  trigger_error("Element '$element' is not supported $support", E_USER_WARNING);
5767  }
5768  }
5769 
5770  // setup allowed attributes ---------------------------------------
5771 
5772  $allowed_attributes_mutable = $allowed_attributes; // by copy!
5773  if (is_array($allowed_attributes)) {
5774  // This actually doesn't do anything, since we went away from
5775  // global attributes. It's possible that userland code uses
5776  // it, but HTMLModuleManager doesn't!
5777  foreach ($this->info_global_attr as $attr => $x) {
5778  $keys = array($attr, "*@$attr", "*.$attr");
5779  $delete = true;
5780  foreach ($keys as $key) {
5781  if ($delete && isset($allowed_attributes[$key])) {
5782  $delete = false;
5783  }
5784  if (isset($allowed_attributes_mutable[$key])) {
5785  unset($allowed_attributes_mutable[$key]);
5786  }
5787  }
5788  if ($delete) {
5789  unset($this->info_global_attr[$attr]);
5790  }
5791  }
5792 
5793  foreach ($this->info as $tag => $info) {
5794  foreach ($info->attr as $attr => $x) {
5795  $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr");
5796  $delete = true;
5797  foreach ($keys as $key) {
5798  if ($delete && isset($allowed_attributes[$key])) {
5799  $delete = false;
5800  }
5801  if (isset($allowed_attributes_mutable[$key])) {
5802  unset($allowed_attributes_mutable[$key]);
5803  }
5804  }
5805  if ($delete) {
5806  if ($this->info[$tag]->attr[$attr]->required) {
5807  trigger_error(
5808  "Required attribute '$attr' in element '$tag' " .
5809  "was not allowed, which means '$tag' will not be allowed either",
5810  E_USER_WARNING
5811  );
5812  }
5813  unset($this->info[$tag]->attr[$attr]);
5814  }
5815  }
5816  }
5817  // emit errors
5818  foreach ($allowed_attributes_mutable as $elattr => $d) {
5819  $bits = preg_split('/[.@]/', $elattr, 2);
5820  $c = count($bits);
5821  switch ($c) {
5822  case 2:
5823  if ($bits[0] !== '*') {
5824  $element = htmlspecialchars($bits[0]);
5825  $attribute = htmlspecialchars($bits[1]);
5826  if (!isset($this->info[$element])) {
5827  trigger_error(
5828  "Cannot allow attribute '$attribute' if element " .
5829  "'$element' is not allowed/supported $support"
5830  );
5831  } else {
5832  trigger_error(
5833  "Attribute '$attribute' in element '$element' not supported $support",
5834  E_USER_WARNING
5835  );
5836  }
5837  break;
5838  }
5839  // otherwise fall through
5840  case 1:
5841  $attribute = htmlspecialchars($bits[0]);
5842  trigger_error(
5843  "Global attribute '$attribute' is not ".
5844  "supported in any elements $support",
5845  E_USER_WARNING
5846  );
5847  break;
5848  }
5849  }
5850  }
5851 
5852  // setup forbidden elements ---------------------------------------
5853 
5854  $forbidden_elements = $config->get('HTML.ForbiddenElements');
5855  $forbidden_attributes = $config->get('HTML.ForbiddenAttributes');
5856 
5857  foreach ($this->info as $tag => $info) {
5858  if (isset($forbidden_elements[$tag])) {
5859  unset($this->info[$tag]);
5860  continue;
5861  }
5862  foreach ($info->attr as $attr => $x) {
5863  if (isset($forbidden_attributes["$tag@$attr"]) ||
5864  isset($forbidden_attributes["*@$attr"]) ||
5865  isset($forbidden_attributes[$attr])
5866  ) {
5867  unset($this->info[$tag]->attr[$attr]);
5868  continue;
5869  } elseif (isset($forbidden_attributes["$tag.$attr"])) { // this segment might get removed eventually
5870  // $tag.$attr are not user supplied, so no worries!
5871  trigger_error(
5872  "Error with $tag.$attr: tag.attr syntax not supported for " .
5873  "HTML.ForbiddenAttributes; use tag@attr instead",
5874  E_USER_WARNING
5875  );
5876  }
5877  }
5878  }
5879  foreach ($forbidden_attributes as $key => $v) {
5880  if (strlen($key) < 2) {
5881  continue;
5882  }
5883  if ($key[0] != '*') {
5884  continue;
5885  }
5886  if ($key[1] == '.') {
5887  trigger_error(
5888  "Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead",
5889  E_USER_WARNING
5890  );
5891  }
5892  }
5893 
5894  // setup injectors -----------------------------------------------------
5895  foreach ($this->info_injector as $i => $injector) {
5896  if ($injector->checkNeeded($config) !== false) {
5897  // remove injector that does not have it's required
5898  // elements/attributes present, and is thus not needed.
5899  unset($this->info_injector[$i]);
5900  }
5901  }
5902  }
5903 
5913  public function parseTinyMCEAllowedList($list)
5914  {
5915  $list = str_replace(array(' ', "\t"), '', $list);
5916 
5917  $elements = array();
5918  $attributes = array();
5919 
5920  $chunks = preg_split('/(,|[\n\r]+)/', $list);
5921  foreach ($chunks as $chunk) {
5922  if (empty($chunk)) {
5923  continue;
5924  }
5925  // remove TinyMCE element control characters
5926  if (!strpos($chunk, '[')) {
5927  $element = $chunk;
5928  $attr = false;
5929  } else {
5930  list($element, $attr) = explode('[', $chunk);
5931  }
5932  if ($element !== '*') {
5933  $elements[$element] = true;
5934  }
5935  if (!$attr) {
5936  continue;
5937  }
5938  $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ]
5939  $attr = explode('|', $attr);
5940  foreach ($attr as $key) {
5941  $attributes["$element.$key"] = true;
5942  }
5943  }
5944  return array($elements, $attributes);
5945  }
5946 }
5947 
5948 
5949 
5950 
5951 
5968 {
5969 
5970  // -- Overloadable ----------------------------------------------------
5971 
5976  public $name;
5977 
5983  public $elements = array();
5984 
5991  public $info = array();
5992 
6000  public $content_sets = array();
6001 
6011  public $attr_collections = array();
6012 
6017  public $info_tag_transform = array();
6018 
6023  public $info_attr_transform_pre = array();
6024 
6029  public $info_attr_transform_post = array();
6030 
6038  public $info_injector = array();
6039 
6047  public $defines_child_def = false;
6048 
6062  public $safe = true;
6063 
6072  public function getChildDef($def)
6073  {
6074  return false;
6075  }
6076 
6077  // -- Convenience -----------------------------------------------------
6078 
6093  public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array())
6094  {
6095  $this->elements[] = $element;
6096  // parse content_model
6097  list($content_model_type, $content_model) = $this->parseContents($contents);
6098  // merge in attribute inclusions
6099  $this->mergeInAttrIncludes($attr, $attr_includes);
6100  // add element to content sets
6101  if ($type) {
6102  $this->addElementToContentSet($element, $type);
6103  }
6104  // create element
6105  $this->info[$element] = HTMLPurifier_ElementDef::create(
6106  $content_model,
6107  $content_model_type,
6108  $attr
6109  );
6110  // literal object $contents means direct child manipulation
6111  if (!is_string($contents)) {
6112  $this->info[$element]->child = $contents;
6113  }
6114  return $this->info[$element];
6115  }
6116 
6123  public function addBlankElement($element)
6124  {
6125  if (!isset($this->info[$element])) {
6126  $this->elements[] = $element;
6127  $this->info[$element] = new HTMLPurifier_ElementDef();
6128  $this->info[$element]->standalone = false;
6129  } else {
6130  trigger_error("Definition for $element already exists in module, cannot redefine");
6131  }
6132  return $this->info[$element];
6133  }
6134 
6141  public function addElementToContentSet($element, $type)
6142  {
6143  if (!isset($this->content_sets[$type])) {
6144  $this->content_sets[$type] = '';
6145  } else {
6146  $this->content_sets[$type] .= ' | ';
6147  }
6148  $this->content_sets[$type] .= $element;
6149  }
6150 
6161  public function parseContents($contents)
6162  {
6163  if (!is_string($contents)) {
6164  return array(null, null);
6165  } // defer
6166  switch ($contents) {
6167  // check for shorthand content model forms
6168  case 'Empty':
6169  return array('empty', '');
6170  case 'Inline':
6171  return array('optional', 'Inline | #PCDATA');
6172  case 'Flow':
6173  return array('optional', 'Flow | #PCDATA');
6174  }
6175  list($content_model_type, $content_model) = explode(':', $contents);
6176  $content_model_type = strtolower(trim($content_model_type));
6177  $content_model = trim($content_model);
6178  return array($content_model_type, $content_model);
6179  }
6180 
6187  public function mergeInAttrIncludes(&$attr, $attr_includes)
6188  {
6189  if (!is_array($attr_includes)) {
6190  if (empty($attr_includes)) {
6191  $attr_includes = array();
6192  } else {
6193  $attr_includes = array($attr_includes);
6194  }
6195  }
6196  $attr[0] = $attr_includes;
6197  }
6198 
6207  public function makeLookup($list)
6208  {
6209  if (is_string($list)) {
6210  $list = func_get_args();
6211  }
6212  $ret = array();
6213  foreach ($list as $value) {
6214  if (is_null($value)) {
6215  continue;
6216  }
6217  $ret[$value] = true;
6218  }
6219  return $ret;
6220  }
6221 
6228  public function setup($config)
6229  {
6230  }
6231 }
6232 
6233 
6234 
6235 
6236 
6238 {
6239 
6243  public $doctypes;
6244 
6249  public $doctype;
6250 
6254  public $attrTypes;
6255 
6261  public $modules = array();
6262 
6269  public $registeredModules = array();
6270 
6277  public $userModules = array();
6278 
6284  public $elementLookup = array();
6285 
6290  public $prefixes = array('HTMLPurifier_HTMLModule_');
6291 
6296 
6301 
6306  public $trusted = false;
6307 
6308  public function __construct()
6309  {
6310  // editable internal objects
6311  $this->attrTypes = new HTMLPurifier_AttrTypes();
6312  $this->doctypes = new HTMLPurifier_DoctypeRegistry();
6313 
6314  // setup basic modules
6315  $common = array(
6316  'CommonAttributes', 'Text', 'Hypertext', 'List',
6317  'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
6318  'StyleAttribute',
6319  // Unsafe:
6320  'Scripting', 'Object', 'Forms',
6321  // Sorta legacy, but present in strict:
6322  'Name',
6323  );
6324  $transitional = array('Legacy', 'Target', 'Iframe');
6325  $xml = array('XMLCommonAttributes');
6326  $non_xml = array('NonXMLCommonAttributes');
6327 
6328  // setup basic doctypes
6329  $this->doctypes->register(
6330  'HTML 4.01 Transitional',
6331  false,
6332  array_merge($common, $transitional, $non_xml),
6333  array('Tidy_Transitional', 'Tidy_Proprietary'),
6334  array(),
6335  '-//W3C//DTD HTML 4.01 Transitional//EN',
6336  'http://www.w3.org/TR/html4/loose.dtd'
6337  );
6338 
6339  $this->doctypes->register(
6340  'HTML 4.01 Strict',
6341  false,
6342  array_merge($common, $non_xml),
6343  array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
6344  array(),
6345  '-//W3C//DTD HTML 4.01//EN',
6346  'http://www.w3.org/TR/html4/strict.dtd'
6347  );
6348 
6349  $this->doctypes->register(
6350  'XHTML 1.0 Transitional',
6351  true,
6352  array_merge($common, $transitional, $xml, $non_xml),
6353  array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'),
6354  array(),
6355  '-//W3C//DTD XHTML 1.0 Transitional//EN',
6356  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
6357  );
6358 
6359  $this->doctypes->register(
6360  'XHTML 1.0 Strict',
6361  true,
6362  array_merge($common, $xml, $non_xml),
6363  array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
6364  array(),
6365  '-//W3C//DTD XHTML 1.0 Strict//EN',
6366  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
6367  );
6368 
6369  $this->doctypes->register(
6370  'XHTML 1.1',
6371  true,
6372  // Iframe is a real XHTML 1.1 module, despite being
6373  // "transitional"!
6374  array_merge($common, $xml, array('Ruby', 'Iframe')),
6375  array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'), // Tidy_XHTML1_1
6376  array(),
6377  '-//W3C//DTD XHTML 1.1//EN',
6378  'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
6379  );
6380 
6381  }
6382 
6404  public function registerModule($module, $overload = false)
6405  {
6406  if (is_string($module)) {
6407  // attempt to load the module
6408  $original_module = $module;
6409  $ok = false;
6410  foreach ($this->prefixes as $prefix) {
6411  $module = $prefix . $original_module;
6412  if (class_exists($module)) {
6413  $ok = true;
6414  break;
6415  }
6416  }
6417  if (!$ok) {
6418  $module = $original_module;
6419  if (!class_exists($module)) {
6420  trigger_error(
6421  $original_module . ' module does not exist',
6422  E_USER_ERROR
6423  );
6424  return;
6425  }
6426  }
6427  $module = new $module();
6428  }
6429  if (empty($module->name)) {
6430  trigger_error('Module instance of ' . get_class($module) . ' must have name');
6431  return;
6432  }
6433  if (!$overload && isset($this->registeredModules[$module->name])) {
6434  trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING);
6435  }
6436  $this->registeredModules[$module->name] = $module;
6437  }
6438 
6443  public function addModule($module)
6444  {
6445  $this->registerModule($module);
6446  if (is_object($module)) {
6447  $module = $module->name;
6448  }
6449  $this->userModules[] = $module;
6450  }
6451 
6456  public function addPrefix($prefix)
6457  {
6458  $this->prefixes[] = $prefix;
6459  }
6460 
6466  public function setup($config)
6467  {
6468  $this->trusted = $config->get('HTML.Trusted');
6469 
6470  // generate
6471  $this->doctype = $this->doctypes->make($config);
6472  $modules = $this->doctype->modules;
6473 
6474  // take out the default modules that aren't allowed
6475  $lookup = $config->get('HTML.AllowedModules');
6476  $special_cases = $config->get('HTML.CoreModules');
6477 
6478  if (is_array($lookup)) {
6479  foreach ($modules as $k => $m) {
6480  if (isset($special_cases[$m])) {
6481  continue;
6482  }
6483  if (!isset($lookup[$m])) {
6484  unset($modules[$k]);
6485  }
6486  }
6487  }
6488 
6489  // custom modules
6490  if ($config->get('HTML.Proprietary')) {
6491  $modules[] = 'Proprietary';
6492  }
6493  if ($config->get('HTML.SafeObject')) {
6494  $modules[] = 'SafeObject';
6495  }
6496  if ($config->get('HTML.SafeEmbed')) {
6497  $modules[] = 'SafeEmbed';
6498  }
6499  if ($config->get('HTML.SafeScripting') !== array()) {
6500  $modules[] = 'SafeScripting';
6501  }
6502  if ($config->get('HTML.Nofollow')) {
6503  $modules[] = 'Nofollow';
6504  }
6505  if ($config->get('HTML.TargetBlank')) {
6506  $modules[] = 'TargetBlank';
6507  }
6508  // NB: HTML.TargetNoreferrer and HTML.TargetNoopener must be AFTER HTML.TargetBlank
6509  // so that its post-attr-transform gets run afterwards.
6510  if ($config->get('HTML.TargetNoreferrer')) {
6511  $modules[] = 'TargetNoreferrer';
6512  }
6513  if ($config->get('HTML.TargetNoopener')) {
6514  $modules[] = 'TargetNoopener';
6515  }
6516 
6517  // merge in custom modules
6518  $modules = array_merge($modules, $this->userModules);
6519 
6520  foreach ($modules as $module) {
6521  $this->processModule($module);
6522  $this->modules[$module]->setup($config);
6523  }
6524 
6525  foreach ($this->doctype->tidyModules as $module) {
6526  $this->processModule($module);
6527  $this->modules[$module]->setup($config);
6528  }
6529 
6530  // prepare any injectors
6531  foreach ($this->modules as $module) {
6532  $n = array();
6533  foreach ($module->info_injector as $injector) {
6534  if (!is_object($injector)) {
6535  $class = "HTMLPurifier_Injector_$injector";
6536  $injector = new $class;
6537  }
6538  $n[$injector->name] = $injector;
6539  }
6540  $module->info_injector = $n;
6541  }
6542 
6543  // setup lookup table based on all valid modules
6544  foreach ($this->modules as $module) {
6545  foreach ($module->info as $name => $def) {
6546  if (!isset($this->elementLookup[$name])) {
6547  $this->elementLookup[$name] = array();
6548  }
6549  $this->elementLookup[$name][] = $module->name;
6550  }
6551  }
6552 
6553  // note the different choice
6554  $this->contentSets = new HTMLPurifier_ContentSets(
6555  // content set assembly deals with all possible modules,
6556  // not just ones deemed to be "safe"
6557  $this->modules
6558  );
6559  $this->attrCollections = new HTMLPurifier_AttrCollections(
6560  $this->attrTypes,
6561  // there is no way to directly disable a global attribute,
6562  // but using AllowedAttributes or simply not including
6563  // the module in your custom doctype should be sufficient
6564  $this->modules
6565  );
6566  }
6567 
6572  public function processModule($module)
6573  {
6574  if (!isset($this->registeredModules[$module]) || is_object($module)) {
6575  $this->registerModule($module);
6576  }
6577  $this->modules[$module] = $this->registeredModules[$module];
6578  }
6579 
6584  public function getElements()
6585  {
6586  $elements = array();
6587  foreach ($this->modules as $module) {
6588  if (!$this->trusted && !$module->safe) {
6589  continue;
6590  }
6591  foreach ($module->info as $name => $v) {
6592  if (isset($elements[$name])) {
6593  continue;
6594  }
6595  $elements[$name] = $this->getElement($name);
6596  }
6597  }
6598 
6599  // remove dud elements, this happens when an element that
6600  // appeared to be safe actually wasn't
6601  foreach ($elements as $n => $v) {
6602  if ($v === false) {
6603  unset($elements[$n]);
6604  }
6605  }
6606 
6607  return $elements;
6608 
6609  }
6610 
6621  public function getElement($name, $trusted = null)
6622  {
6623  if (!isset($this->elementLookup[$name])) {
6624  return false;
6625  }
6626 
6627  // setup global state variables
6628  $def = false;
6629  if ($trusted === null) {
6631  }
6632 
6633  // iterate through each module that has registered itself to this
6634  // element
6635  foreach ($this->elementLookup[$name] as $module_name) {
6636  $module = $this->modules[$module_name];
6637 
6638  // refuse to create/merge from a module that is deemed unsafe--
6639  // pretend the module doesn't exist--when trusted mode is not on.
6640  if (!$trusted && !$module->safe) {
6641  continue;
6642  }
6643 
6644  // clone is used because, ideally speaking, the original
6645  // definition should not be modified. Usually, this will
6646  // make no difference, but for consistency's sake
6647  $new_def = clone $module->info[$name];
6648 
6649  if (!$def && $new_def->standalone) {
6650  $def = $new_def;
6651  } elseif ($def) {
6652  // This will occur even if $new_def is standalone. In practice,
6653  // this will usually result in a full replacement.
6654  $def->mergeIn($new_def);
6655  } else {
6656  // :TODO:
6657  // non-standalone definitions that don't have a standalone
6658  // to merge into could be deferred to the end
6659  // HOWEVER, it is perfectly valid for a non-standalone
6660  // definition to lack a standalone definition, even
6661  // after all processing: this allows us to safely
6662  // specify extra attributes for elements that may not be
6663  // enabled all in one place. In particular, this might
6664  // be the case for trusted elements. WARNING: care must
6665  // be taken that the /extra/ definitions are all safe.
6666  continue;
6667  }
6668 
6669  // attribute value expansions
6670  $this->attrCollections->performInclusions($def->attr);
6671  $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes);
6672 
6673  // descendants_are_inline, for ChildDef_Chameleon
6674  if (is_string($def->content_model) &&
6675  strpos($def->content_model, 'Inline') !== false) {
6676  if ($name != 'del' && $name != 'ins') {
6677  // this is for you, ins/del
6678  $def->descendants_are_inline = true;
6679  }
6680  }
6681 
6682  $this->contentSets->generateChildDef($def, $module);
6683  }
6684 
6685  // This can occur if there is a blank definition, but no base to
6686  // mix it in with
6687  if (!$def) {
6688  return false;
6689  }
6690 
6691  // add information on required attributes
6692  foreach ($def->attr as $attr_name => $attr_def) {
6693  if ($attr_def->required) {
6694  $def->required_attr[] = $attr_name;
6695  }
6696  }
6697  return $def;
6698  }
6699 }
6700 
6701 
6702 
6703 
6704 
6712 {
6713 
6718  public $ids = array();
6719 
6726  public static function build($config, $context)
6727  {
6728  $id_accumulator = new HTMLPurifier_IDAccumulator();
6729  $id_accumulator->load($config->get('Attr.IDBlacklist'));
6730  return $id_accumulator;
6731  }
6732 
6738  public function add($id)
6739  {
6740  if (isset($this->ids[$id])) {
6741  return false;
6742  }
6743  return $this->ids[$id] = true;
6744  }
6745 
6751  public function load($array_of_ids)
6752  {
6753  foreach ($array_of_ids as $id) {
6754  $this->ids[$id] = true;
6755  }
6756  }
6757 }
6758 
6759 
6760 
6761 
6762 
6777 {
6778 
6783  public $name;
6784 
6788  protected $htmlDefinition;
6789 
6795  protected $currentNesting;
6796 
6801  protected $currentToken;
6802 
6807  protected $inputZipper;
6808 
6815  public $needed = array();
6816 
6821  protected $rewindOffset = false;
6822 
6832  public function rewindOffset($offset)
6833  {
6834  $this->rewindOffset = $offset;
6835  }
6836 
6841  public function getRewindOffset()
6842  {
6843  $r = $this->rewindOffset;
6844  $this->rewindOffset = false;
6845  return $r;
6846  }
6847 
6857  public function prepare($config, $context)
6858  {
6859  $this->htmlDefinition = $config->getHTMLDefinition();
6860  // Even though this might fail, some unit tests ignore this and
6861  // still test checkNeeded, so be careful. Maybe get rid of that
6862  // dependency.
6863  $result = $this->checkNeeded($config);
6864  if ($result !== false) {
6865  return $result;
6866  }
6867  $this->currentNesting =& $context->get('CurrentNesting');
6868  $this->currentToken =& $context->get('CurrentToken');
6869  $this->inputZipper =& $context->get('InputZipper');
6870  return false;
6871  }
6872 
6880  public function checkNeeded($config)
6881  {
6882  $def = $config->getHTMLDefinition();
6883  foreach ($this->needed as $element => $attributes) {
6884  if (is_int($element)) {
6885  $element = $attributes;
6886  }
6887  if (!isset($def->info[$element])) {
6888  return $element;
6889  }
6890  if (!is_array($attributes)) {
6891  continue;
6892  }
6893  foreach ($attributes as $name) {
6894  if (!isset($def->info[$element]->attr[$name])) {
6895  return "$element.$name";
6896  }
6897  }
6898  }
6899  return false;
6900  }
6901 
6907  public function allowsElement($name)
6908  {
6909  if (!empty($this->currentNesting)) {
6910  $parent_token = array_pop($this->currentNesting);
6911  $this->currentNesting[] = $parent_token;
6912  $parent = $this->htmlDefinition->info[$parent_token->name];
6913  } else {
6914  $parent = $this->htmlDefinition->info_parent_def;
6915  }
6916  if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) {
6917  return false;
6918  }
6919  // check for exclusion
6920  if (!empty($this->currentNesting)) {
6921  for ($i = count($this->currentNesting) - 2; $i >= 0; $i--) {
6922  $node = $this->currentNesting[$i];
6923  $def = $this->htmlDefinition->info[$node->name];
6924  if (isset($def->excludes[$name])) {
6925  return false;
6926  }
6927  }
6928  }
6929  return true;
6930  }
6931 
6942  protected function forward(&$i, &$current)
6943  {
6944  if ($i === null) {
6945  $i = count($this->inputZipper->back) - 1;
6946  } else {
6947  $i--;
6948  }
6949  if ($i < 0) {
6950  return false;
6951  }
6952  $current = $this->inputZipper->back[$i];
6953  return true;
6954  }
6955 
6966  protected function forwardUntilEndToken(&$i, &$current, &$nesting)
6967  {
6968  $result = $this->forward($i, $current);
6969  if (!$result) {
6970  return false;
6971  }
6972  if ($nesting === null) {
6973  $nesting = 0;
6974  }
6975  if ($current instanceof HTMLPurifier_Token_Start) {
6976  $nesting++;
6977  } elseif ($current instanceof HTMLPurifier_Token_End) {
6978  if ($nesting <= 0) {
6979  return false;
6980  }
6981  $nesting--;
6982  }
6983  return true;
6984  }
6985 
6996  protected function backward(&$i, &$current)
6997  {
6998  if ($i === null) {
6999  $i = count($this->inputZipper->front) - 1;
7000  } else {
7001  $i--;
7002  }
7003  if ($i < 0) {
7004  return false;
7005  }
7006  $current = $this->inputZipper->front[$i];
7007  return true;
7008  }
7009 
7013  public function handleText(&$token)
7014  {
7015  }
7016 
7020  public function handleElement(&$token)
7021  {
7022  }
7023 
7027  public function handleEnd(&$token)
7028  {
7029  $this->notifyEnd($token);
7030  }
7031 
7038  public function notifyEnd($token)
7039  {
7040  }
7041 }
7042 
7043 
7044 
7045 
7046 
7052 {
7053 
7058  public $code = 'en';
7059 
7064  public $fallback = false;
7065 
7070  public $messages = array();
7071 
7076  public $errorNames = array();
7077 
7084  public $error = false;
7085 
7091  public $_loaded = false;
7092 
7096  protected $config;
7097 
7101  protected $context;
7102 
7107  public function __construct($config, $context)
7108  {
7109  $this->config = $config;
7110  $this->context = $context;
7111  }
7112 
7117  public function load()
7118  {
7119  if ($this->_loaded) {
7120  return;
7121  }
7123  $factory->loadLanguage($this->code);
7124  foreach ($factory->keys as $key) {
7125  $this->$key = $factory->cache[$this->code][$key];
7126  }
7127  $this->_loaded = true;
7128  }
7129 
7135  public function getMessage($key)
7136  {
7137  if (!$this->_loaded) {
7138  $this->load();
7139  }
7140  if (!isset($this->messages[$key])) {
7141  return "[$key]";
7142  }
7143  return $this->messages[$key];
7144  }
7145 
7151  public function getErrorName($int)
7152  {
7153  if (!$this->_loaded) {
7154  $this->load();
7155  }
7156  if (!isset($this->errorNames[$int])) {
7157  return "[Error: $int]";
7158  }
7159  return $this->errorNames[$int];
7160  }
7161 
7167  public function listify($array)
7168  {
7169  $sep = $this->getMessage('Item separator');
7170  $sep_last = $this->getMessage('Item separator last');
7171  $ret = '';
7172  for ($i = 0, $c = count($array); $i < $c; $i++) {
7173  if ($i == 0) {
7174  } elseif ($i + 1 < $c) {
7175  $ret .= $sep;
7176  } else {
7177  $ret .= $sep_last;
7178  }
7179  $ret .= $array[$i];
7180  }
7181  return $ret;
7182  }
7183 
7192  public function formatMessage($key, $args = array())
7193  {
7194  if (!$this->_loaded) {
7195  $this->load();
7196  }
7197  if (!isset($this->messages[$key])) {
7198  return "[$key]";
7199  }
7200  $raw = $this->messages[$key];
7201  $subst = array();
7202  $generator = false;
7203  foreach ($args as $i => $value) {
7204  if (is_object($value)) {
7205  if ($value instanceof HTMLPurifier_Token) {
7206  // factor this out some time
7207  if (!$generator) {
7208  $generator = $this->context->get('Generator');
7209  }
7210  if (isset($value->name)) {
7211  $subst['$'.$i.'.Name'] = $value->name;
7212  }
7213  if (isset($value->data)) {
7214  $subst['$'.$i.'.Data'] = $value->data;
7215  }
7216  $subst['$'.$i.'.Compact'] =
7217  $subst['$'.$i.'.Serialized'] = $generator->generateFromToken($value);
7218  // a more complex algorithm for compact representation
7219  // could be introduced for all types of tokens. This
7220  // may need to be factored out into a dedicated class
7221  if (!empty($value->attr)) {
7222  $stripped_token = clone $value;
7223  $stripped_token->attr = array();
7224  $subst['$'.$i.'.Compact'] = $generator->generateFromToken($stripped_token);
7225  }
7226  $subst['$'.$i.'.Line'] = $value->line ? $value->line : 'unknown';
7227  }
7228  continue;
7229  } elseif (is_array($value)) {
7230  $keys = array_keys($value);
7231  if (array_keys($keys) === $keys) {
7232  // list
7233  $subst['$'.$i] = $this->listify($value);
7234  } else {
7235  // associative array
7236  // no $i implementation yet, sorry
7237  $subst['$'.$i.'.Keys'] = $this->listify($keys);
7238  $subst['$'.$i.'.Values'] = $this->listify(array_values($value));
7239  }
7240  continue;
7241  }
7242  $subst['$' . $i] = $value;
7243  }
7244  return strtr($raw, $subst);
7245  }
7246 }
7247 
7248 
7249 
7250 
7251 
7260 {
7261 
7267  public $cache;
7268 
7274  public $keys = array('fallback', 'messages', 'errorNames');
7275 
7281  protected $validator;
7282 
7288  protected $dir;
7289 
7294  protected $mergeable_keys_map = array('messages' => true, 'errorNames' => true);
7295 
7300  protected $mergeable_keys_list = array();
7301 
7308  public static function instance($prototype = null)
7309  {
7310  static $instance = null;
7311  if ($prototype !== null) {
7312  $instance = $prototype;
7313  } elseif ($instance === null || $prototype == true) {
7314  $instance = new HTMLPurifier_LanguageFactory();
7315  $instance->setup();
7316  }
7317  return $instance;
7318  }
7319 
7324  public function setup()
7325  {
7326  $this->validator = new HTMLPurifier_AttrDef_Lang();
7327  $this->dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier';
7328  }
7329 
7337  public function create($config, $context, $code = false)
7338  {
7339  // validate language code
7340  if ($code === false) {
7341  $code = $this->validator->validate(
7342  $config->get('Core.Language'),
7343  $config,
7344  $context
7345  );
7346  } else {
7347  $code = $this->validator->validate($code, $config, $context);
7348  }
7349  if ($code === false) {
7350  $code = 'en'; // malformed code becomes English
7351  }
7352 
7353  $pcode = str_replace('-', '_', $code); // make valid PHP classname
7354  static $depth = 0; // recursion protection
7355 
7356  if ($code == 'en') {
7357  $lang = new HTMLPurifier_Language($config, $context);
7358  } else {
7359  $class = 'HTMLPurifier_Language_' . $pcode;
7360  $file = $this->dir . '/Language/classes/' . $code . '.php';
7361  if (file_exists($file) || class_exists($class, false)) {
7362  $lang = new $class($config, $context);
7363  } else {
7364  // Go fallback
7365  $raw_fallback = $this->getFallbackFor($code);
7366  $fallback = $raw_fallback ? $raw_fallback : 'en';
7367  $depth++;
7368  $lang = $this->create($config, $context, $fallback);
7369  if (!$raw_fallback) {
7370  $lang->error = true;
7371  }
7372  $depth--;
7373  }
7374  }
7375  $lang->code = $code;
7376  return $lang;
7377  }
7378 
7385  public function getFallbackFor($code)
7386  {
7387  $this->loadLanguage($code);
7388  return $this->cache[$code]['fallback'];
7389  }
7390 
7395  public function loadLanguage($code)
7396  {
7397  static $languages_seen = array(); // recursion guard
7398 
7399  // abort if we've already loaded it
7400  if (isset($this->cache[$code])) {
7401  return;
7402  }
7403 
7404  // generate filename
7405  $filename = $this->dir . '/Language/messages/' . $code . '.php';
7406 
7407  // default fallback : may be overwritten by the ensuing include
7408  $fallback = ($code != 'en') ? 'en' : false;
7409 
7410  // load primary localisation
7411  if (!file_exists($filename)) {
7412  // skip the include: will rely solely on fallback
7413  $filename = $this->dir . '/Language/messages/en.php';
7414  $cache = array();
7415  } else {
7416  include $filename;
7417  $cache = compact($this->keys);
7418  }
7419 
7420  // load fallback localisation
7421  if (!empty($fallback)) {
7422 
7423  // infinite recursion guard
7424  if (isset($languages_seen[$code])) {
7425  trigger_error(
7426  'Circular fallback reference in language ' .
7427  $code,
7428  E_USER_ERROR
7429  );
7430  $fallback = 'en';
7431  }
7432  $language_seen[$code] = true;
7433 
7434  // load the fallback recursively
7435  $this->loadLanguage($fallback);
7436  $fallback_cache = $this->cache[$fallback];
7437 
7438  // merge fallback with current language
7439  foreach ($this->keys as $key) {
7440  if (isset($cache[$key]) && isset($fallback_cache[$key])) {
7441  if (isset($this->mergeable_keys_map[$key])) {
7442  $cache[$key] = $cache[$key] + $fallback_cache[$key];
7443  } elseif (isset($this->mergeable_keys_list[$key])) {
7444  $cache[$key] = array_merge($fallback_cache[$key], $cache[$key]);
7445  }
7446  } else {
7447  $cache[$key] = $fallback_cache[$key];
7448  }
7449  }
7450  }
7451 
7452  // save to cache for later retrieval
7453  $this->cache[$code] = $cache;
7454  return;
7455  }
7456 }
7457 
7458 
7459 
7460 
7461 
7467 {
7468 
7473  protected $n;
7474 
7479  protected $unit;
7480 
7485  protected $isValid;
7486 
7491  protected static $allowedUnits = array(
7492  'em' => true, 'ex' => true, 'px' => true, 'in' => true,
7493  'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true,
7494  'ch' => true, 'rem' => true, 'vw' => true, 'vh' => true,
7495  'vmin' => true, 'vmax' => true
7496  );
7497 
7502  public function __construct($n = '0', $u = false)
7503  {
7504  $this->n = (string) $n;
7505  $this->unit = $u !== false ? (string) $u : false;
7506  }
7507 
7513  public static function make($s)
7514  {
7515  if ($s instanceof HTMLPurifier_Length) {
7516  return $s;
7517  }
7518  $n_length = strspn($s, '1234567890.+-');
7519  $n = substr($s, 0, $n_length);
7520  $unit = substr($s, $n_length);
7521  if ($unit === '') {
7522  $unit = false;
7523  }
7524  return new HTMLPurifier_Length($n, $unit);
7525  }
7526 
7531  protected function validate()
7532  {
7533  // Special case:
7534  if ($this->n === '+0' || $this->n === '-0') {
7535  $this->n = '0';
7536  }
7537  if ($this->n === '0' && $this->unit === false) {
7538  return true;
7539  }
7540  if (!ctype_lower($this->unit)) {
7541  $this->unit = strtolower($this->unit);
7542  }
7543  if (!isset(HTMLPurifier_Length::$allowedUnits[$this->unit])) {
7544  return false;
7545  }
7546  // Hack:
7548  $result = $def->validate($this->n, false, false);
7549  if ($result === false) {
7550  return false;
7551  }
7552  $this->n = $result;
7553  return true;
7554  }
7555 
7560  public function toString()
7561  {
7562  if (!$this->isValid()) {
7563  return false;
7564  }
7565  return $this->n . $this->unit;
7566  }
7567 
7572  public function getN()
7573  {
7574  return $this->n;
7575  }
7576 
7581  public function getUnit()
7582  {
7583  return $this->unit;
7584  }
7585 
7590  public function isValid()
7591  {
7592  if ($this->isValid === null) {
7593  $this->isValid = $this->validate();
7594  }
7595  return $this->isValid;
7596  }
7597 
7605  public function compareTo($l)
7606  {
7607  if ($l === false) {
7608  return false;
7609  }
7610  if ($l->unit !== $this->unit) {
7611  $converter = new HTMLPurifier_UnitConverter();
7612  $l = $converter->convert($l, $this->unit);
7613  if ($l === false) {
7614  return false;
7615  }
7616  }
7617  return $this->n - $l->n;
7618  }
7619 }
7620 
7621 
7622 
7623 
7624 
7665 {
7666 
7671  public $tracksLineNumbers = false;
7672 
7673  // -- STATIC ----------------------------------------------------------
7674 
7691  public static function create($config)
7692  {
7693  if (!($config instanceof HTMLPurifier_Config)) {
7694  $lexer = $config;
7695  trigger_error(
7696  "Passing a prototype to
7697  HTMLPurifier_Lexer::create() is deprecated, please instead
7698  use %Core.LexerImpl",
7699  E_USER_WARNING
7700  );
7701  } else {
7702  $lexer = $config->get('Core.LexerImpl');
7703  }
7704 
7705  $needs_tracking =
7706  $config->get('Core.MaintainLineNumbers') ||
7707  $config->get('Core.CollectErrors');
7708 
7709  $inst = null;
7710  if (is_object($lexer)) {
7711  $inst = $lexer;
7712  } else {
7713  if (is_null($lexer)) {
7714  do {
7715  // auto-detection algorithm
7716  if ($needs_tracking) {
7717  $lexer = 'DirectLex';
7718  break;
7719  }
7720 
7721  if (class_exists('DOMDocument', false) &&
7722  method_exists('DOMDocument', 'loadHTML') &&
7723  !extension_loaded('domxml')
7724  ) {
7725  // check for DOM support, because while it's part of the
7726  // core, it can be disabled compile time. Also, the PECL
7727  // domxml extension overrides the default DOM, and is evil
7728  // and nasty and we shan't bother to support it
7729  $lexer = 'DOMLex';
7730  } else {
7731  $lexer = 'DirectLex';
7732  }
7733  } while (0);
7734  } // do..while so we can break
7735 
7736  // instantiate recognized string names
7737  switch ($lexer) {
7738  case 'DOMLex':
7739  $inst = new HTMLPurifier_Lexer_DOMLex();
7740  break;
7741  case 'DirectLex':
7742  $inst = new HTMLPurifier_Lexer_DirectLex();
7743  break;
7744  case 'PH5P':
7745  $inst = new HTMLPurifier_Lexer_PH5P();
7746  break;
7747  default:
7748  throw new HTMLPurifier_Exception(
7749  "Cannot instantiate unrecognized Lexer type " .
7750  htmlspecialchars($lexer)
7751  );
7752  }
7753  }
7754 
7755  if (!$inst) {
7756  throw new HTMLPurifier_Exception('No lexer was instantiated');
7757  }
7758 
7759  // once PHP DOM implements native line numbers, or we
7760  // hack out something using XSLT, remove this stipulation
7761  if ($needs_tracking && !$inst->tracksLineNumbers) {
7762  throw new HTMLPurifier_Exception(
7763  'Cannot use lexer that does not support line numbers with ' .
7764  'Core.MaintainLineNumbers or Core.CollectErrors (use DirectLex instead)'
7765  );
7766  }
7767 
7768  return $inst;
7769 
7770  }
7771 
7772  // -- CONVENIENCE MEMBERS ---------------------------------------------
7773 
7774  public function __construct()
7775  {
7776  $this->_entity_parser = new HTMLPurifier_EntityParser();
7777  }
7778 
7784  array(
7785  '&quot;' => '"',
7786  '&amp;' => '&',
7787  '&lt;' => '<',
7788  '&gt;' => '>',
7789  '&#39;' => "'",
7790  '&#039;' => "'",
7791  '&#x27;' => "'"
7792  );
7793 
7794  public function parseText($string, $config) {
7795  return $this->parseData($string, false, $config);
7796  }
7797 
7798  public function parseAttr($string, $config) {
7799  return $this->parseData($string, true, $config);
7800  }
7801 
7811  public function parseData($string, $is_attr, $config)
7812  {
7813  // following functions require at least one character
7814  if ($string === '') {
7815  return '';
7816  }
7817 
7818  // subtracts amps that cannot possibly be escaped
7819  $num_amp = substr_count($string, '&') - substr_count($string, '& ') -
7820  ($string[strlen($string) - 1] === '&' ? 1 : 0);
7821 
7822  if (!$num_amp) {
7823  return $string;
7824  } // abort if no entities
7825  $num_esc_amp = substr_count($string, '&amp;');
7826  $string = strtr($string, $this->_special_entity2str);
7827 
7828  // code duplication for sake of optimization, see above
7829  $num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') -
7830  ($string[strlen($string) - 1] === '&' ? 1 : 0);
7831 
7832  if ($num_amp_2 <= $num_esc_amp) {
7833  return $string;
7834  }
7835 
7836  // hmm... now we have some uncommon entities. Use the callback.
7837  if ($config->get('Core.LegacyEntityDecoder')) {
7838  $string = $this->_entity_parser->substituteSpecialEntities($string);
7839  } else {
7840  if ($is_attr) {
7841  $string = $this->_entity_parser->substituteAttrEntities($string);
7842  } else {
7843  $string = $this->_entity_parser->substituteTextEntities($string);
7844  }
7845  }
7846  return $string;
7847  }
7848 
7856  public function tokenizeHTML($string, $config, $context)
7857  {
7858  trigger_error('Call to abstract class', E_USER_ERROR);
7859  }
7860 
7866  protected static function escapeCDATA($string)
7867  {
7868  return preg_replace_callback(
7869  '/<!\[CDATA\[(.+?)\]\]>/s',
7870  array('HTMLPurifier_Lexer', 'CDATACallback'),
7871  $string
7872  );
7873  }
7874 
7880  protected static function escapeCommentedCDATA($string)
7881  {
7882  return preg_replace_callback(
7883  '#<!--//--><!\[CDATA\[//><!--(.+?)//--><!\]\]>#s',
7884  array('HTMLPurifier_Lexer', 'CDATACallback'),
7885  $string
7886  );
7887  }
7888 
7894  protected static function removeIEConditional($string)
7895  {
7896  return preg_replace(
7897  '#<!--\[if [^>]+\]>.*?<!\[endif\]-->#si', // probably should generalize for all strings
7898  '',
7899  $string
7900  );
7901  }
7902 
7912  protected static function CDATACallback($matches)
7913  {
7914  // not exactly sure why the character set is needed, but whatever
7915  return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8');
7916  }
7917 
7927  public function normalize($html, $config, $context)
7928  {
7929  // normalize newlines to \n
7930  if ($config->get('Core.NormalizeNewlines')) {
7931  $html = str_replace("\r\n", "\n", $html);
7932  $html = str_replace("\r", "\n", $html);
7933  }
7934 
7935  if ($config->get('HTML.Trusted')) {
7936  // escape convoluted CDATA
7937  $html = $this->escapeCommentedCDATA($html);
7938  }
7939 
7940  // escape CDATA
7941  $html = $this->escapeCDATA($html);
7942 
7943  $html = $this->removeIEConditional($html);
7944 
7945  // extract body from document if applicable
7946  if ($config->get('Core.ConvertDocumentToFragment')) {
7947  $e = false;
7948  if ($config->get('Core.CollectErrors')) {
7949  $e =& $context->get('ErrorCollector');
7950  }
7951  $new_html = $this->extractBody($html);
7952  if ($e && $new_html != $html) {
7953  $e->send(E_WARNING, 'Lexer: Extracted body');
7954  }
7955  $html = $new_html;
7956  }
7957 
7958  // expand entities that aren't the big five
7959  if ($config->get('Core.LegacyEntityDecoder')) {
7960  $html = $this->_entity_parser->substituteNonSpecialEntities($html);
7961  }
7962 
7963  // clean into wellformed UTF-8 string for an SGML context: this has
7964  // to be done after entity expansion because the entities sometimes
7965  // represent non-SGML characters (horror, horror!)
7967 
7968  // if processing instructions are to removed, remove them now
7969  if ($config->get('Core.RemoveProcessingInstructions')) {
7970  $html = preg_replace('#<\?.+?\?>#s', '', $html);
7971  }
7972 
7973  $hidden_elements = $config->get('Core.HiddenElements');
7974  if ($config->get('Core.AggressivelyRemoveScript') &&
7975  !($config->get('HTML.Trusted') || !$config->get('Core.RemoveScriptContents')
7976  || empty($hidden_elements["script"]))) {
7977  $html = preg_replace('#<script[^>]*>.*?</script>#i', '', $html);
7978  }
7979 
7980  return $html;
7981  }
7982 
7987  public function extractBody($html)
7988  {
7989  $matches = array();
7990  $result = preg_match('|(.*?)<body[^>]*>(.*)</body>|is', $html, $matches);
7991  if ($result) {
7992  // Make sure it's not in a comment
7993  $comment_start = strrpos($matches[1], '<!--');
7994  $comment_end = strrpos($matches[1], '-->');
7995  if ($comment_start === false ||
7996  ($comment_end !== false && $comment_end > $comment_start)) {
7997  return $matches[2];
7998  }
7999  }
8000  return $html;
8001  }
8002 }
8003 
8004 
8005 
8006 
8007 
8016 abstract class HTMLPurifier_Node
8017 {
8022  public $line;
8023 
8028  public $col;
8029 
8035  public $armor = array();
8036 
8044  public $dead = false;
8045 
8051  abstract public function toTokenPair();
8052 }
8053 
8054 
8055 
8056 
8057 
8067 {
8068 
8073  protected $preserve = array();
8074 
8079  public function __construct($preserve = false)
8080  {
8081  // unreserved letters, ought to const-ify
8082  for ($i = 48; $i <= 57; $i++) { // digits
8083  $this->preserve[$i] = true;
8084  }
8085  for ($i = 65; $i <= 90; $i++) { // upper-case
8086  $this->preserve[$i] = true;
8087  }
8088  for ($i = 97; $i <= 122; $i++) { // lower-case
8089  $this->preserve[$i] = true;
8090  }
8091  $this->preserve[45] = true; // Dash -
8092  $this->preserve[46] = true; // Period .
8093  $this->preserve[95] = true; // Underscore _
8094  $this->preserve[126]= true; // Tilde ~
8095 
8096  // extra letters not to escape
8097  if ($preserve !== false) {
8098  for ($i = 0, $c = strlen($preserve); $i < $c; $i++) {
8099  $this->preserve[ord($preserve[$i])] = true;
8100  }
8101  }
8102  }
8103 
8114  public function encode($string)
8115  {
8116  $ret = '';
8117  for ($i = 0, $c = strlen($string); $i < $c; $i++) {
8118  if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])])) {
8119  $ret .= '%' . sprintf('%02X', $int);
8120  } else {
8121  $ret .= $string[$i];
8122  }
8123  }
8124  return $ret;
8125  }
8126 
8135  public function normalize($string)
8136  {
8137  if ($string == '') {
8138  return '';
8139  }
8140  $parts = explode('%', $string);
8141  $ret = array_shift($parts);
8142  foreach ($parts as $part) {
8143  $length = strlen($part);
8144  if ($length < 2) {
8145  $ret .= '%25' . $part;
8146  continue;
8147  }
8148  $encoding = substr($part, 0, 2);
8149  $text = substr($part, 2);
8150  if (!ctype_xdigit($encoding)) {
8151  $ret .= '%25' . $part;
8152  continue;
8153  }
8154  $int = hexdec($encoding);
8155  if (isset($this->preserve[$int])) {
8156  $ret .= chr($int) . $text;
8157  continue;
8158  }
8159  $encoding = strtoupper($encoding);
8160  $ret .= '%' . $encoding . $text;
8161  }
8162  return $ret;
8163  }
8164 }
8165 
8166 
8167 
8168 
8169 
8174 {
8179  protected $data = array();
8180 
8185  protected $parent;
8186 
8191  protected $cache;
8192 
8196  public function __construct($parent = null)
8197  {
8198  $this->parent = $parent;
8199  }
8200 
8206  public function get($name)
8207  {
8208  if ($this->has($name)) {
8209  return $this->data[$name];
8210  }
8211  // possible performance bottleneck, convert to iterative if necessary
8212  if ($this->parent) {
8213  return $this->parent->get($name);
8214  }
8215  throw new HTMLPurifier_Exception("Key '$name' not found");
8216  }
8217 
8223  public function set($name, $value)
8224  {
8225  $this->data[$name] = $value;
8226  }
8227 
8233  public function has($name)
8234  {
8235  return array_key_exists($name, $this->data);
8236  }
8237 
8243  public function reset($name = null)
8244  {
8245  if ($name == null) {
8246  $this->data = array();
8247  } else {
8248  unset($this->data[$name]);
8249  }
8250  }
8251 
8258  public function squash($force = false)
8259  {
8260  if ($this->cache !== null && !$force) {
8261  return $this->cache;
8262  }
8263  if ($this->parent) {
8264  return $this->cache = array_merge($this->parent->squash($force), $this->data);
8265  } else {
8266  return $this->cache = $this->data;
8267  }
8268  }
8269 
8274  public function getParent()
8275  {
8276  return $this->parent;
8277  }
8278 
8283  public function setParent($plist)
8284  {
8285  $this->parent = $plist;
8286  }
8287 }
8288 
8289 
8290 
8291 
8292 
8296 class HTMLPurifier_PropertyListIterator extends FilterIterator
8297 {
8298 
8302  protected $l;
8306  protected $filter;
8307 
8312  public function __construct(Iterator $iterator, $filter = null)
8313  {
8314  parent::__construct($iterator);
8315  $this->l = strlen($filter);
8316  $this->filter = $filter;
8317  }
8318 
8322  public function accept()
8323  {
8324  $key = $this->getInnerIterator()->key();
8325  if (strncmp($key, $this->filter, $this->l) !== 0) {
8326  return false;
8327  }
8328  return true;
8329  }
8330 }
8331 
8332 
8333 
8334 
8335 
8354  private $input;
8355  private $output;
8356 
8357  public function __construct($input = array()) {
8358  $this->input = $input;
8359  $this->output = array();
8360  }
8361 
8365  public function shift() {
8366  if (empty($this->output)) {
8367  $this->output = array_reverse($this->input);
8368  $this->input = array();
8369  }
8370  if (empty($this->output)) {
8371  return NULL;
8372  }
8373  return array_pop($this->output);
8374  }
8375 
8379  public function push($x) {
8380  array_push($this->input, $x);
8381  }
8382 
8386  public function isEmpty() {
8387  return empty($this->input) && empty($this->output);
8388  }
8389 }
8390 
8391 
8392 
8403 {
8404 
8413  abstract public function execute($tokens, $config, $context);
8414 }
8415 
8416 
8417 
8418 
8419 
8428 class HTMLPurifier_StringHash extends ArrayObject
8429 {
8433  protected $accessed = array();
8434 
8440  public function offsetGet($index)
8441  {
8442  $this->accessed[$index] = true;
8443  return parent::offsetGet($index);
8444  }
8445 
8450  public function getAccessed()
8451  {
8452  return $this->accessed;
8453  }
8454 
8458  public function resetAccessed()
8459  {
8460  $this->accessed = array();
8461  }
8462 }
8463 
8464 
8465 
8466 
8467 
8494 {
8495 
8499  public $default = 'ID';
8500 
8506  public function parseFile($file)
8507  {
8508  if (!file_exists($file)) {
8509  return false;
8510  }
8511  $fh = fopen($file, 'r');
8512  if (!$fh) {
8513  return false;
8514  }
8515  $ret = $this->parseHandle($fh);
8516  fclose($fh);
8517  return $ret;
8518  }
8519 
8525  public function parseMultiFile($file)
8526  {
8527  if (!file_exists($file)) {
8528  return false;
8529  }
8530  $ret = array();
8531  $fh = fopen($file, 'r');
8532  if (!$fh) {
8533  return false;
8534  }
8535  while (!feof($fh)) {
8536  $ret[] = $this->parseHandle($fh);
8537  }
8538  fclose($fh);
8539  return $ret;
8540  }
8541 
8551  protected function parseHandle($fh)
8552  {
8553  $state = false;
8554  $single = false;
8555  $ret = array();
8556  do {
8557  $line = fgets($fh);
8558  if ($line === false) {
8559  break;
8560  }
8561  $line = rtrim($line, "\n\r");
8562  if (!$state && $line === '') {
8563  continue;
8564  }
8565  if ($line === '----') {
8566  break;
8567  }
8568  if (strncmp('--#', $line, 3) === 0) {
8569  // Comment
8570  continue;
8571  } elseif (strncmp('--', $line, 2) === 0) {
8572  // Multiline declaration
8573  $state = trim($line, '- ');
8574  if (!isset($ret[$state])) {
8575  $ret[$state] = '';
8576  }
8577  continue;
8578  } elseif (!$state) {
8579  $single = true;
8580  if (strpos($line, ':') !== false) {
8581  // Single-line declaration
8582  list($state, $line) = explode(':', $line, 2);
8583  $line = trim($line);
8584  } else {
8585  // Use default declaration
8586  $state = $this->default;
8587  }
8588  }
8589  if ($single) {
8590  $ret[$state] = $line;
8591  $single = false;
8592  $state = false;
8593  } else {
8594  $ret[$state] .= "$line\n";
8595  }
8596  } while (!feof($fh));
8597  return $ret;
8598  }
8599 }
8600 
8601 
8602 
8603 
8604 
8609 {
8610 
8616 
8623  abstract public function transform($tag, $config, $context);
8624 
8632  protected function prependCSS(&$attr, $css)
8633  {
8634  $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
8635  $attr['style'] = $css . $attr['style'];
8636  }
8637 }
8638 
8639 
8640 
8641 
8642 
8646 abstract class HTMLPurifier_Token
8647 {
8652  public $line;
8653 
8658  public $col;
8659 
8666  public $armor = array();
8667 
8672  public $skip;
8673 
8677  public $rewind;
8678 
8682  public $carryover;
8683 
8688  public function __get($n)
8689  {
8690  if ($n === 'type') {
8691  trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);
8692  switch (get_class($this)) {
8693  case 'HTMLPurifier_Token_Start':
8694  return 'start';
8695  case 'HTMLPurifier_Token_Empty':
8696  return 'empty';
8697  case 'HTMLPurifier_Token_End':
8698  return 'end';
8699  case 'HTMLPurifier_Token_Text':
8700  return 'text';
8701  case 'HTMLPurifier_Token_Comment':
8702  return 'comment';
8703  default:
8704  return null;
8705  }
8706  }
8707  }
8708 
8714  public function position($l = null, $c = null)
8715  {
8716  $this->line = $l;
8717  $this->col = $c;
8718  }
8719 
8725  public function rawPosition($l, $c)
8726  {
8727  if ($c === -1) {
8728  $l++;
8729  }
8730  $this->line = $l;
8731  $this->col = $c;
8732  }
8733 
8737  abstract public function toNode();
8738 }
8739 
8740 
8741 
8742 
8743 
8756 {
8757  // p stands for prototype
8758 
8762  private $p_start;
8763 
8767  private $p_end;
8768 
8772  private $p_empty;
8773 
8777  private $p_text;
8778 
8782  private $p_comment;
8783 
8787  public function __construct()
8788  {
8789  $this->p_start = new HTMLPurifier_Token_Start('', array());
8790  $this->p_end = new HTMLPurifier_Token_End('');
8791  $this->p_empty = new HTMLPurifier_Token_Empty('', array());
8792  $this->p_text = new HTMLPurifier_Token_Text('');
8793  $this->p_comment = new HTMLPurifier_Token_Comment('');
8794  }
8795 
8802  public function createStart($name, $attr = array())
8803  {
8804  $p = clone $this->p_start;
8805  $p->__construct($name, $attr);
8806  return $p;
8807  }
8808 
8814  public function createEnd($name)
8815  {
8816  $p = clone $this->p_end;
8817  $p->__construct($name);
8818  return $p;
8819  }
8820 
8827  public function createEmpty($name, $attr = array())
8828  {
8829  $p = clone $this->p_empty;
8830  $p->__construct($name, $attr);
8831  return $p;
8832  }
8833 
8839  public function createText($data)
8840  {
8841  $p = clone $this->p_text;
8842  $p->__construct($data);
8843  return $p;
8844  }
8845 
8851  public function createComment($data)
8852  {
8853  $p = clone $this->p_comment;
8854  $p->__construct($data);
8855  return $p;
8856  }
8857 }
8858 
8859 
8860 
8861 
8862 
8872 {
8876  public $scheme;
8877 
8881  public $userinfo;
8882 
8886  public $host;
8887 
8891  public $port;
8892 
8896  public $path;
8897 
8901  public $query;
8902 
8906  public $fragment;
8907 
8919  {
8920  $this->scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme);
8921  $this->userinfo = $userinfo;
8922  $this->host = $host;
8923  $this->port = is_null($port) ? $port : (int)$port;
8924  $this->path = $path;
8925  $this->query = $query;
8926  $this->fragment = $fragment;
8927  }
8928 
8935  public function getSchemeObj($config, $context)
8936  {
8938  if ($this->scheme !== null) {
8939  $scheme_obj = $registry->getScheme($this->scheme, $config, $context);
8940  if (!$scheme_obj) {
8941  return false;
8942  } // invalid scheme, clean it out
8943  } else {
8944  // no scheme: retrieve the default one
8945  $def = $config->getDefinition('URI');
8946  $scheme_obj = $def->getDefaultScheme($config, $context);
8947  if (!$scheme_obj) {
8948  if ($def->defaultScheme !== null) {
8949  // something funky happened to the default scheme object
8950  trigger_error(
8951  'Default scheme object "' . $def->defaultScheme . '" was not readable',
8952  E_USER_WARNING
8953  );
8954  } // suppress error if it's null
8955  return false;
8956  }
8957  }
8958  return $scheme_obj;
8959  }
8960 
8968  public function validate($config, $context)
8969  {
8970  // ABNF definitions from RFC 3986
8971  $chars_sub_delims = '!$&\'()*+,;=';
8972  $chars_gen_delims = ':/?#[]@';
8973  $chars_pchar = $chars_sub_delims . ':@';
8974 
8975  // validate host
8976  if (!is_null($this->host)) {
8977  $host_def = new HTMLPurifier_AttrDef_URI_Host();
8978  $this->host = $host_def->validate($this->host, $config, $context);
8979  if ($this->host === false) {
8980  $this->host = null;
8981  }
8982  }
8983 
8984  // validate scheme
8985  // NOTE: It's not appropriate to check whether or not this
8986  // scheme is in our registry, since a URIFilter may convert a
8987  // URI that we don't allow into one we do. So instead, we just
8988  // check if the scheme can be dropped because there is no host
8989  // and it is our default scheme.
8990  if (!is_null($this->scheme) && is_null($this->host) || $this->host === '') {
8991  // support for relative paths is pretty abysmal when the
8992  // scheme is present, so axe it when possible
8993  $def = $config->getDefinition('URI');
8994  if ($def->defaultScheme === $this->scheme) {
8995  $this->scheme = null;
8996  }
8997  }
8998 
8999  // validate username
9000  if (!is_null($this->userinfo)) {
9001  $encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':');
9002  $this->userinfo = $encoder->encode($this->userinfo);
9003  }
9004 
9005  // validate port
9006  if (!is_null($this->port)) {
9007  if ($this->port < 1 || $this->port > 65535) {
9008  $this->port = null;
9009  }
9010  }
9011 
9012  // validate path
9013  $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/');
9014  if (!is_null($this->host)) { // this catches $this->host === ''
9015  // path-abempty (hier and relative)
9016  // http://www.example.com/my/path
9017  // //www.example.com/my/path (looks odd, but works, and
9018  // recognized by most browsers)
9019  // (this set is valid or invalid on a scheme by scheme
9020  // basis, so we'll deal with it later)
9021  // file:///my/path
9022  // ///my/path
9023  $this->path = $segments_encoder->encode($this->path);
9024  } elseif ($this->path !== '') {
9025  if ($this->path[0] === '/') {
9026  // path-absolute (hier and relative)
9027  // http:/my/path
9028  // /my/path
9029  if (strlen($this->path) >= 2 && $this->path[1] === '/') {
9030  // This could happen if both the host gets stripped
9031  // out
9032  // http://my/path
9033  // //my/path
9034  $this->path = '';
9035  } else {
9036  $this->path = $segments_encoder->encode($this->path);
9037  }
9038  } elseif (!is_null($this->scheme)) {
9039  // path-rootless (hier)
9040  // http:my/path
9041  // Short circuit evaluation means we don't need to check nz
9042  $this->path = $segments_encoder->encode($this->path);
9043  } else {
9044  // path-noscheme (relative)
9045  // my/path
9046  // (once again, not checking nz)
9047  $segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@');
9048  $c = strpos($this->path, '/');
9049  if ($c !== false) {
9050  $this->path =
9051  $segment_nc_encoder->encode(substr($this->path, 0, $c)) .
9052  $segments_encoder->encode(substr($this->path, $c));
9053  } else {
9054  $this->path = $segment_nc_encoder->encode($this->path);
9055  }
9056  }
9057  } else {
9058  // path-empty (hier and relative)
9059  $this->path = ''; // just to be safe
9060  }
9061 
9062  // qf = query and fragment
9063  $qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?');
9064 
9065  if (!is_null($this->query)) {
9066  $this->query = $qf_encoder->encode($this->query);
9067  }
9068 
9069  if (!is_null($this->fragment)) {
9070  $this->fragment = $qf_encoder->encode($this->fragment);
9071  }
9072  return true;
9073  }
9074 
9079  public function toString()
9080  {
9081  // reconstruct authority
9082  $authority = null;
9083  // there is a rendering difference between a null authority
9084  // (http:foo-bar) and an empty string authority
9085  // (http:///foo-bar).
9086  if (!is_null($this->host)) {
9087  $authority = '';
9088  if (!is_null($this->userinfo)) {
9089  $authority .= $this->userinfo . '@';
9090  }
9091  $authority .= $this->host;
9092  if (!is_null($this->port)) {
9093  $authority .= ':' . $this->port;
9094  }
9095  }
9096 
9097  // Reconstruct the result
9098  // One might wonder about parsing quirks from browsers after
9099  // this reconstruction. Unfortunately, parsing behavior depends
9100  // on what *scheme* was employed (file:///foo is handled *very*
9101  // differently than http:///foo), so unfortunately we have to
9102  // defer to the schemes to do the right thing.
9103  $result = '';
9104  if (!is_null($this->scheme)) {
9105  $result .= $this->scheme . ':';
9106  }
9107  if (!is_null($authority)) {
9108  $result .= '//' . $authority;
9109  }
9110  $result .= $this->path;
9111  if (!is_null($this->query)) {
9112  $result .= '?' . $this->query;
9113  }
9114  if (!is_null($this->fragment)) {
9115  $result .= '#' . $this->fragment;
9116  }
9117 
9118  return $result;
9119  }
9120 
9133  public function isLocal($config, $context)
9134  {
9135  if ($this->host === null) {
9136  return true;
9137  }
9138  $uri_def = $config->getDefinition('URI');
9139  if ($uri_def->host === $this->host) {
9140  return true;
9141  }
9142  return false;
9143  }
9144 
9155  public function isBenign($config, $context)
9156  {
9157  if (!$this->isLocal($config, $context)) {
9158  return false;
9159  }
9160 
9161  $scheme_obj = $this->getSchemeObj($config, $context);
9162  if (!$scheme_obj) {
9163  return false;
9164  } // conservative approach
9165 
9166  $current_scheme_obj = $config->getDefinition('URI')->getDefaultScheme($config, $context);
9167  if ($current_scheme_obj->secure) {
9168  if (!$scheme_obj->secure) {
9169  return false;
9170  }
9171  }
9172  return true;
9173  }
9174 }
9175 
9176 
9177 
9178 
9179 
9181 {
9182 
9183  public $type = 'URI';
9184  protected $filters = array();
9185  protected $postFilters = array();
9186  protected $registeredFilters = array();
9187 
9191  public $base;
9192 
9196  public $host;
9197 
9202 
9203  public function __construct()
9204  {
9212  }
9213 
9214  public function registerFilter($filter)
9215  {
9216  $this->registeredFilters[$filter->name] = $filter;
9217  }
9218 
9219  public function addFilter($filter, $config)
9220  {
9221  $r = $filter->prepare($config);
9222  if ($r === false) return; // null is ok, for backwards compat
9223  if ($filter->post) {
9224  $this->postFilters[$filter->name] = $filter;
9225  } else {
9226  $this->filters[$filter->name] = $filter;
9227  }
9228  }
9229 
9230  protected function doSetup($config)
9231  {
9232  $this->setupMemberVariables($config);
9233  $this->setupFilters($config);
9234  }
9235 
9236  protected function setupFilters($config)
9237  {
9238  foreach ($this->registeredFilters as $name => $filter) {
9239  if ($filter->always_load) {
9240  $this->addFilter($filter, $config);
9241  } else {
9242  $conf = $config->get('URI.' . $name);
9243  if ($conf !== false && $conf !== null) {
9244  $this->addFilter($filter, $config);
9245  }
9246  }
9247  }
9248  unset($this->registeredFilters);
9249  }
9250 
9251  protected function setupMemberVariables($config)
9252  {
9253  $this->host = $config->get('URI.Host');
9254  $base_uri = $config->get('URI.Base');
9255  if (!is_null($base_uri)) {
9256  $parser = new HTMLPurifier_URIParser();
9257  $this->base = $parser->parse($base_uri);
9258  $this->defaultScheme = $this->base->scheme;
9259  if (is_null($this->host)) $this->host = $this->base->host;
9260  }
9261  if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme');
9262  }
9263 
9264  public function getDefaultScheme($config, $context)
9265  {
9266  return HTMLPurifier_URISchemeRegistry::instance()->getScheme($this->defaultScheme, $config, $context);
9267  }
9268 
9269  public function filter(&$uri, $config, $context)
9270  {
9271  foreach ($this->filters as $name => $f) {
9272  $result = $f->filter($uri, $config, $context);
9273  if (!$result) return false;
9274  }
9275  return true;
9276  }
9277 
9278  public function postFilter(&$uri, $config, $context)
9279  {
9280  foreach ($this->postFilters as $name => $f) {
9281  $result = $f->filter($uri, $config, $context);
9282  if (!$result) return false;
9283  }
9284  return true;
9285  }
9286 
9287 }
9288 
9289 
9290 
9291 
9292 
9319 {
9320 
9325  public $name;
9326 
9331  public $post = false;
9332 
9339  public $always_load = false;
9340 
9347  public function prepare($config)
9348  {
9349  return true;
9350  }
9351 
9361  abstract public function filter(&$uri, $config, $context);
9362 }
9363 
9364 
9365 
9366 
9367 
9373 {
9374 
9378  protected $percentEncoder;
9379 
9380  public function __construct()
9381  {
9382  $this->percentEncoder = new HTMLPurifier_PercentEncoder();
9383  }
9384 
9391  public function parse($uri)
9392  {
9393  $uri = $this->percentEncoder->normalize($uri);
9394 
9395  // Regexp is as per Appendix B.
9396  // Note that ["<>] are an addition to the RFC's recommended
9397  // characters, because they represent external delimeters.
9398  $r_URI = '!'.
9399  '(([a-zA-Z0-9\.\+\-]+):)?'. // 2. Scheme
9400  '(//([^/?#"<>]*))?'. // 4. Authority
9401  '([^?#"<>]*)'. // 5. Path
9402  '(\?([^#"<>]*))?'. // 7. Query
9403  '(#([^"<>]*))?'. // 8. Fragment
9404  '!';
9405 
9406  $matches = array();
9407  $result = preg_match($r_URI, $uri, $matches);
9408 
9409  if (!$result) return false; // *really* invalid URI
9410 
9411  // seperate out parts
9412  $scheme = !empty($matches[1]) ? $matches[2] : null;
9413  $authority = !empty($matches[3]) ? $matches[4] : null;
9414  $path = $matches[5]; // always present, can be empty
9415  $query = !empty($matches[6]) ? $matches[7] : null;
9416  $fragment = !empty($matches[8]) ? $matches[9] : null;
9417 
9418  // further parse authority
9419  if ($authority !== null) {
9420  $r_authority = "/^((.+?)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/";
9421  $matches = array();
9422  preg_match($r_authority, $authority, $matches);
9423  $userinfo = !empty($matches[1]) ? $matches[2] : null;
9424  $host = !empty($matches[3]) ? $matches[3] : '';
9425  $port = !empty($matches[4]) ? (int) $matches[5] : null;
9426  } else {
9427  $port = $host = $userinfo = null;
9428  }
9429 
9430  return new HTMLPurifier_URI(
9431  $scheme, $userinfo, $host, $port, $path, $query, $fragment);
9432  }
9433 
9434 }
9435 
9436 
9437 
9438 
9439 
9444 {
9445 
9452  public $default_port = null;
9453 
9459  public $browsable = false;
9460 
9466  public $secure = false;
9467 
9473  public $hierarchical = false;
9474 
9481  public $may_omit_host = false;
9482 
9490  abstract public function doValidate(&$uri, $config, $context);
9491 
9500  public function validate(&$uri, $config, $context)
9501  {
9502  if ($this->default_port == $uri->port) {
9503  $uri->port = null;
9504  }
9505  // kludge: browsers do funny things when the scheme but not the
9506  // authority is set
9507  if (!$this->may_omit_host &&
9508  // if the scheme is present, a missing host is always in error
9509  (!is_null($uri->scheme) && ($uri->host === '' || is_null($uri->host))) ||
9510  // if the scheme is not present, a *blank* host is in error,
9511  // since this translates into '///path' which most browsers
9512  // interpret as being 'http://path'.
9513  (is_null($uri->scheme) && $uri->host === '')
9514  ) {
9515  do {
9516  if (is_null($uri->scheme)) {
9517  if (substr($uri->path, 0, 2) != '//') {
9518  $uri->host = null;
9519  break;
9520  }
9521  // URI is '////path', so we cannot nullify the
9522  // host to preserve semantics. Try expanding the
9523  // hostname instead (fall through)
9524  }
9525  // first see if we can manually insert a hostname
9526  $host = $config->get('URI.Host');
9527  if (!is_null($host)) {
9528  $uri->host = $host;
9529  } else {
9530  // we can't do anything sensible, reject the URL.
9531  return false;
9532  }
9533  } while (false);
9534  }
9535  return $this->doValidate($uri, $config, $context);
9536  }
9537 }
9538 
9539 
9540 
9541 
9542 
9547 {
9548 
9557  public static function instance($prototype = null)
9558  {
9559  static $instance = null;
9560  if ($prototype !== null) {
9561  $instance = $prototype;
9562  } elseif ($instance === null || $prototype == true) {
9563  $instance = new HTMLPurifier_URISchemeRegistry();
9564  }
9565  return $instance;
9566  }
9567 
9572  protected $schemes = array();
9573 
9581  public function getScheme($scheme, $config, $context)
9582  {
9583  if (!$config) {
9585  }
9586 
9587  // important, otherwise attacker could include arbitrary file
9588  $allowed_schemes = $config->get('URI.AllowedSchemes');
9589  if (!$config->get('URI.OverrideAllowedSchemes') &&
9590  !isset($allowed_schemes[$scheme])
9591  ) {
9592  return;
9593  }
9594 
9595  if (isset($this->schemes[$scheme])) {
9596  return $this->schemes[$scheme];
9597  }
9598  if (!isset($allowed_schemes[$scheme])) {
9599  return;
9600  }
9601 
9602  $class = 'HTMLPurifier_URIScheme_' . $scheme;
9603  if (!class_exists($class)) {
9604  return;
9605  }
9606  $this->schemes[$scheme] = new $class();
9607  return $this->schemes[$scheme];
9608  }
9609 
9615  public function register($scheme, $scheme_obj)
9616  {
9617  $this->schemes[$scheme] = $scheme_obj;
9618  }
9619 }
9620 
9621 
9622 
9623 
9624 
9630 {
9631 
9632  const ENGLISH = 1;
9633  const METRIC = 2;
9634  const DIGITAL = 3;
9635 
9645  protected static $units = array(
9646  self::ENGLISH => array(
9647  'px' => 3, // This is as per CSS 2.1 and Firefox. Your mileage may vary
9648  'pt' => 4,
9649  'pc' => 48,
9650  'in' => 288,
9651  self::METRIC => array('pt', '0.352777778', 'mm'),
9652  ),
9653  self::METRIC => array(
9654  'mm' => 1,
9655  'cm' => 10,
9656  self::ENGLISH => array('mm', '2.83464567', 'pt'),
9657  ),
9658  );
9659 
9664  protected $outputPrecision;
9665 
9671 
9676  private $bcmath;
9677 
9678  public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false)
9679  {
9680  $this->outputPrecision = $output_precision;
9681  $this->internalPrecision = $internal_precision;
9682  $this->bcmath = !$force_no_bcmath && function_exists('bcmul');
9683  }
9684 
9704  public function convert($length, $to_unit)
9705  {
9706  if (!$length->isValid()) {
9707  return false;
9708  }
9709 
9710  $n = $length->getN();
9711  $unit = $length->getUnit();
9712 
9713  if ($n === '0' || $unit === false) {
9714  return new HTMLPurifier_Length('0', false);
9715  }
9716 
9717  $state = $dest_state = false;
9718  foreach (self::$units as $k => $x) {
9719  if (isset($x[$unit])) {
9720  $state = $k;
9721  }
9722  if (isset($x[$to_unit])) {
9723  $dest_state = $k;
9724  }
9725  }
9726  if (!$state || !$dest_state) {
9727  return false;
9728  }
9729 
9730  // Some calculations about the initial precision of the number;
9731  // this will be useful when we need to do final rounding.
9732  $sigfigs = $this->getSigFigs($n);
9733  if ($sigfigs < $this->outputPrecision) {
9734  $sigfigs = $this->outputPrecision;
9735  }
9736 
9737  // BCMath's internal precision deals only with decimals. Use
9738  // our default if the initial number has no decimals, or increase
9739  // it by how ever many decimals, thus, the number of guard digits
9740  // will always be greater than or equal to internalPrecision.
9741  $log = (int)floor(log(abs($n), 10));
9742  $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision
9743 
9744  for ($i = 0; $i < 2; $i++) {
9745 
9746  // Determine what unit IN THIS SYSTEM we need to convert to
9747  if ($dest_state === $state) {
9748  // Simple conversion
9749  $dest_unit = $to_unit;
9750  } else {
9751  // Convert to the smallest unit, pending a system shift
9752  $dest_unit = self::$units[$state][$dest_state][0];
9753  }
9754 
9755  // Do the conversion if necessary
9756  if ($dest_unit !== $unit) {
9757  $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp);
9758  $n = $this->mul($n, $factor, $cp);
9759  $unit = $dest_unit;
9760  }
9761 
9762  // Output was zero, so bail out early. Shouldn't ever happen.
9763  if ($n === '') {
9764  $n = '0';
9765  $unit = $to_unit;
9766  break;
9767  }
9768 
9769  // It was a simple conversion, so bail out
9770  if ($dest_state === $state) {
9771  break;
9772  }
9773 
9774  if ($i !== 0) {
9775  // Conversion failed! Apparently, the system we forwarded
9776  // to didn't have this unit. This should never happen!
9777  return false;
9778  }
9779 
9780  // Pre-condition: $i == 0
9781 
9782  // Perform conversion to next system of units
9783  $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp);
9784  $unit = self::$units[$state][$dest_state][2];
9785  $state = $dest_state;
9786 
9787  // One more loop around to convert the unit in the new system.
9788 
9789  }
9790 
9791  // Post-condition: $unit == $to_unit
9792  if ($unit !== $to_unit) {
9793  return false;
9794  }
9795 
9796  // Useful for debugging:
9797  //echo "<pre>n";
9798  //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n</pre>\n";
9799 
9800  $n = $this->round($n, $sigfigs);
9801  if (strpos($n, '.') !== false) {
9802  $n = rtrim($n, '0');
9803  }
9804  $n = rtrim($n, '.');
9805 
9806  return new HTMLPurifier_Length($n, $unit);
9807  }
9808 
9814  public function getSigFigs($n)
9815  {
9816  $n = ltrim($n, '0+-');
9817  $dp = strpos($n, '.'); // decimal position
9818  if ($dp === false) {
9819  $sigfigs = strlen(rtrim($n, '0'));
9820  } else {
9821  $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character
9822  if ($dp !== 0) {
9823  $sigfigs--;
9824  }
9825  }
9826  return $sigfigs;
9827  }
9828 
9836  private function add($s1, $s2, $scale)
9837  {
9838  if ($this->bcmath) {
9839  return bcadd($s1, $s2, $scale);
9840  } else {
9841  return $this->scale((float)$s1 + (float)$s2, $scale);
9842  }
9843  }
9844 
9852  private function mul($s1, $s2, $scale)
9853  {
9854  if ($this->bcmath) {
9855  return bcmul($s1, $s2, $scale);
9856  } else {
9857  return $this->scale((float)$s1 * (float)$s2, $scale);
9858  }
9859  }
9860 
9868  private function div($s1, $s2, $scale)
9869  {
9870  if ($this->bcmath) {
9871  return bcdiv($s1, $s2, $scale);
9872  } else {
9873  return $this->scale((float)$s1 / (float)$s2, $scale);
9874  }
9875  }
9876 
9884  private function round($n, $sigfigs)
9885  {
9886  $new_log = (int)floor(log(abs($n), 10)); // Number of digits left of decimal - 1
9887  $rp = $sigfigs - $new_log - 1; // Number of decimal places needed
9888  $neg = $n < 0 ? '-' : ''; // Negative sign
9889  if ($this->bcmath) {
9890  if ($rp >= 0) {
9891  $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1);
9892  $n = bcdiv($n, '1', $rp);
9893  } else {
9894  // This algorithm partially depends on the standardized
9895  // form of numbers that comes out of bcmath.
9896  $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0);
9897  $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1);
9898  }
9899  return $n;
9900  } else {
9901  return $this->scale(round($n, $sigfigs - $new_log - 1), $rp + 1);
9902  }
9903  }
9904 
9911  private function scale($r, $scale)
9912  {
9913  if ($scale < 0) {
9914  // The f sprintf type doesn't support negative numbers, so we
9915  // need to cludge things manually. First get the string.
9916  $r = sprintf('%.0f', (float)$r);
9917  // Due to floating point precision loss, $r will more than likely
9918  // look something like 4652999999999.9234. We grab one more digit
9919  // than we need to precise from $r and then use that to round
9920  // appropriately.
9921  $precise = (string)round(substr($r, 0, strlen($r) + $scale), -1);
9922  // Now we return it, truncating the zero that was rounded off.
9923  return substr($precise, 0, -1) . str_repeat('0', -$scale + 1);
9924  }
9925  return sprintf('%.' . $scale . 'f', (float)$r);
9926  }
9927 }
9928 
9929 
9930 
9931 
9932 
9938 {
9939 
9940  const C_STRING = 1;
9941  const ISTRING = 2;
9942  const TEXT = 3;
9943  const ITEXT = 4;
9944  const C_INT = 5;
9945  const C_FLOAT = 6;
9946  const C_BOOL = 7;
9947  const LOOKUP = 8;
9948  const ALIST = 9;
9949  const HASH = 10;
9950  const C_MIXED = 11;
9951 
9956  public static $types = array(
9957  'string' => self::C_STRING,
9958  'istring' => self::ISTRING,
9959  'text' => self::TEXT,
9960  'itext' => self::ITEXT,
9961  'int' => self::C_INT,
9962  'float' => self::C_FLOAT,
9963  'bool' => self::C_BOOL,
9964  'lookup' => self::LOOKUP,
9965  'list' => self::ALIST,
9966  'hash' => self::HASH,
9967  'mixed' => self::C_MIXED
9968  );
9969 
9974  public static $stringTypes = array(
9975  self::C_STRING => true,
9976  self::ISTRING => true,
9977  self::TEXT => true,
9978  self::ITEXT => true,
9979  );
9980 
9991  final public function parse($var, $type, $allow_null = false)
9992  {
9993  if (is_string($type)) {
9994  if (!isset(HTMLPurifier_VarParser::$types[$type])) {
9995  throw new HTMLPurifier_VarParserException("Invalid type '$type'");
9996  } else {
9997  $type = HTMLPurifier_VarParser::$types[$type];
9998  }
9999  }
10000  $var = $this->parseImplementation($var, $type, $allow_null);
10001  if ($allow_null && $var === null) {
10002  return null;
10003  }
10004  // These are basic checks, to make sure nothing horribly wrong
10005  // happened in our implementations.
10006  switch ($type) {
10007  case (self::C_STRING):
10008  case (self::ISTRING):
10009  case (self::TEXT):
10010  case (self::ITEXT):
10011  if (!is_string($var)) {
10012  break;
10013  }
10014  if ($type == self::ISTRING || $type == self::ITEXT) {
10015  $var = strtolower($var);
10016  }
10017  return $var;
10018  case (self::C_INT):
10019  if (!is_int($var)) {
10020  break;
10021  }
10022  return $var;
10023  case (self::C_FLOAT):
10024  if (!is_float($var)) {
10025  break;
10026  }
10027  return $var;
10028  case (self::C_BOOL):
10029  if (!is_bool($var)) {
10030  break;
10031  }
10032  return $var;
10033  case (self::LOOKUP):
10034  case (self::ALIST):
10035  case (self::HASH):
10036  if (!is_array($var)) {
10037  break;
10038  }
10039  if ($type === self::LOOKUP) {
10040  foreach ($var as $k) {
10041  if ($k !== true) {
10042  $this->error('Lookup table contains value other than true');
10043  }
10044  }
10045  } elseif ($type === self::ALIST) {
10046  $keys = array_keys($var);
10047  if (array_keys($keys) !== $keys) {
10048  $this->error('Indices for list are not uniform');
10049  }
10050  }
10051  return $var;
10052  case (self::C_MIXED):
10053  return $var;
10054  default:
10055  $this->errorInconsistent(get_class($this), $type);
10056  }
10057  $this->errorGeneric($var, $type);
10058  }
10059 
10068  protected function parseImplementation($var, $type, $allow_null)
10069  {
10070  return $var;
10071  }
10072 
10077  protected function error($msg)
10078  {
10079  throw new HTMLPurifier_VarParserException($msg);
10080  }
10081 
10091  protected function errorInconsistent($class, $type)
10092  {
10093  throw new HTMLPurifier_Exception(
10094  "Inconsistency in $class: " . HTMLPurifier_VarParser::getTypeName($type) .
10095  " not implemented"
10096  );
10097  }
10098 
10104  protected function errorGeneric($var, $type)
10105  {
10106  $vtype = gettype($var);
10107  $this->error("Expected type " . HTMLPurifier_VarParser::getTypeName($type) . ", got $vtype");
10108  }
10109 
10114  public static function getTypeName($type)
10115  {
10116  static $lookup;
10117  if (!$lookup) {
10118  // Lazy load the alternative lookup table
10119  $lookup = array_flip(HTMLPurifier_VarParser::$types);
10120  }
10121  if (!isset($lookup[$type])) {
10122  return 'unknown';
10123  }
10124  return $lookup[$type];
10125  }
10126 }
10127 
10128 
10129 
10130 
10131 
10136 {
10137 
10138 }
10139 
10140 
10141 
10142 
10143 
10163 {
10164  public $front, $back;
10165 
10166  public function __construct($front, $back) {
10167  $this->front = $front;
10168  $this->back = $back;
10169  }
10170 
10177  static public function fromArray($array) {
10178  $z = new self(array(), array_reverse($array));
10179  $t = $z->delete(); // delete the "dummy hole"
10180  return array($z, $t);
10181  }
10182 
10188  public function toArray($t = NULL) {
10189  $a = $this->front;
10190  if ($t !== NULL) $a[] = $t;
10191  for ($i = count($this->back)-1; $i >= 0; $i--) {
10192  $a[] = $this->back[$i];
10193  }
10194  return $a;
10195  }
10196 
10202  public function next($t) {
10203  if ($t !== NULL) array_push($this->front, $t);
10204  return empty($this->back) ? NULL : array_pop($this->back);
10205  }
10206 
10213  public function advance($t, $n) {
10214  for ($i = 0; $i < $n; $i++) {
10215  $t = $this->next($t);
10216  }
10217  return $t;
10218  }
10219 
10225  public function prev($t) {
10226  if ($t !== NULL) array_push($this->back, $t);
10227  return empty($this->front) ? NULL : array_pop($this->front);
10228  }
10229 
10235  public function delete() {
10236  return empty($this->back) ? NULL : array_pop($this->back);
10237  }
10238 
10243  public function done() {
10244  return empty($this->back);
10245  }
10246 
10251  public function insertBefore($t) {
10252  if ($t !== NULL) array_push($this->front, $t);
10253  }
10254 
10259  public function insertAfter($t) {
10260  if ($t !== NULL) array_push($this->back, $t);
10261  }
10262 
10283  public function splice($t, $delete, $replacement) {
10284  // delete
10285  $old = array();
10286  $r = $t;
10287  for ($i = $delete; $i > 0; $i--) {
10288  $old[] = $r;
10289  $r = $this->delete();
10290  }
10291  // insert
10292  for ($i = count($replacement)-1; $i >= 0; $i--) {
10293  $this->insertAfter($r);
10294  $r = $replacement[$i];
10295  }
10296  return array($old, $r);
10297  }
10298 }
10299 
10300 
10301 
10314 {
10315 
10322  public function validate($css, $config, $context)
10323  {
10324  $css = $this->parseCDATA($css);
10325 
10326  $definition = $config->getCSSDefinition();
10327  $allow_duplicates = $config->get("CSS.AllowDuplicates");
10328 
10329 
10330  // According to the CSS2.1 spec, the places where a
10331  // non-delimiting semicolon can appear are in strings
10332  // escape sequences. So here is some dumb hack to
10333  // handle quotes.
10334  $len = strlen($css);
10335  $accum = "";
10336  $declarations = array();
10337  $quoted = false;
10338  for ($i = 0; $i < $len; $i++) {
10339  $c = strcspn($css, ";'\"", $i);
10340  $accum .= substr($css, $i, $c);
10341  $i += $c;
10342  if ($i == $len) break;
10343  $d = $css[$i];
10344  if ($quoted) {
10345  $accum .= $d;
10346  if ($d == $quoted) {
10347  $quoted = false;
10348  }
10349  } else {
10350  if ($d == ";") {
10351  $declarations[] = $accum;
10352  $accum = "";
10353  } else {
10354  $accum .= $d;
10355  $quoted = $d;
10356  }
10357  }
10358  }
10359  if ($accum != "") $declarations[] = $accum;
10360 
10361  $propvalues = array();
10362  $new_declarations = '';
10363 
10367  $property = false;
10368  $context->register('CurrentCSSProperty', $property);
10369 
10370  foreach ($declarations as $declaration) {
10371  if (!$declaration) {
10372  continue;
10373  }
10374  if (!strpos($declaration, ':')) {
10375  continue;
10376  }
10377  list($property, $value) = explode(':', $declaration, 2);
10378  $property = trim($property);
10379  $value = trim($value);
10380  $ok = false;
10381  do {
10382  if (isset($definition->info[$property])) {
10383  $ok = true;
10384  break;
10385  }
10386  if (ctype_lower($property)) {
10387  break;
10388  }
10389  $property = strtolower($property);
10390  if (isset($definition->info[$property])) {
10391  $ok = true;
10392  break;
10393  }
10394  } while (0);
10395  if (!$ok) {
10396  continue;
10397  }
10398  // inefficient call, since the validator will do this again
10399  if (strtolower(trim($value)) !== 'inherit') {
10400  // inherit works for everything (but only on the base property)
10401  $result = $definition->info[$property]->validate(
10402  $value,
10403  $config,
10404  $context
10405  );
10406  } else {
10407  $result = 'inherit';
10408  }
10409  if ($result === false) {
10410  continue;
10411  }
10412  if ($allow_duplicates) {
10413  $new_declarations .= "$property:$result;";
10414  } else {
10415  $propvalues[$property] = $result;
10416  }
10417  }
10418 
10419  $context->destroy('CurrentCSSProperty');
10420 
10421  // procedure does not write the new CSS simultaneously, so it's
10422  // slightly inefficient, but it's the only way of getting rid of
10423  // duplicates. Perhaps config to optimize it, but not now.
10424 
10425  foreach ($propvalues as $prop => $value) {
10426  $new_declarations .= "$prop:$value;";
10427  }
10428 
10429  return $new_declarations ? $new_declarations : false;
10430 
10431  }
10432 
10433 }
10434 
10435 
10436 
10437 
10438 
10444 {
10449  protected $clone;
10450 
10454  public function __construct($clone)
10455  {
10456  $this->clone = $clone;
10457  }
10458 
10465  public function validate($v, $config, $context)
10466  {
10467  return $this->clone->validate($v, $config, $context);
10468  }
10469 
10474  public function make($string)
10475  {
10476  return clone $this->clone;
10477  }
10478 }
10479 
10480 
10481 
10482 
10483 
10484 // Enum = Enumerated
10492 {
10493 
10499  public $valid_values = array();
10500 
10505  protected $case_sensitive = false; // values according to W3C spec
10506 
10511  public function __construct($valid_values = array(), $case_sensitive = false)
10512  {
10513  $this->valid_values = array_flip($valid_values);
10514  $this->case_sensitive = $case_sensitive;
10515  }
10516 
10523  public function validate($string, $config, $context)
10524  {
10525  $string = trim($string);
10526  if (!$this->case_sensitive) {
10527  // we may want to do full case-insensitive libraries
10528  $string = ctype_lower($string) ? $string : strtolower($string);
10529  }
10530  $result = isset($this->valid_values[$string]);
10531 
10532  return $result ? $string : false;
10533  }
10534 
10541  public function make($string)
10542  {
10543  if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') {
10544  $string = substr($string, 2);
10545  $sensitive = true;
10546  } else {
10547  $sensitive = false;
10548  }
10549  $values = explode(',', $string);
10550  return new HTMLPurifier_AttrDef_Enum($values, $sensitive);
10551  }
10552 }
10553 
10554 
10555 
10556 
10557 
10566 {
10567 
10572  protected $negative = true;
10573 
10578  protected $zero = true;
10579 
10584  protected $positive = true;
10585 
10591  public function __construct($negative = true, $zero = true, $positive = true)
10592  {
10593  $this->negative = $negative;
10594  $this->zero = $zero;
10595  $this->positive = $positive;
10596  }
10597 
10604  public function validate($integer, $config, $context)
10605  {
10606  $integer = $this->parseCDATA($integer);
10607  if ($integer === '') {
10608  return false;
10609  }
10610 
10611  // we could possibly simply typecast it to integer, but there are
10612  // certain fringe cases that must not return an integer.
10613 
10614  // clip leading sign
10615  if ($this->negative && $integer[0] === '-') {
10616  $digits = substr($integer, 1);
10617  if ($digits === '0') {
10618  $integer = '0';
10619  } // rm minus sign for zero
10620  } elseif ($this->positive && $integer[0] === '+') {
10621  $digits = $integer = substr($integer, 1); // rm unnecessary plus
10622  } else {
10623  $digits = $integer;
10624  }
10625 
10626  // test if it's numeric
10627  if (!ctype_digit($digits)) {
10628  return false;
10629  }
10630 
10631  // perform scope tests
10632  if (!$this->zero && $integer == 0) {
10633  return false;
10634  }
10635  if (!$this->positive && $integer > 0) {
10636  return false;
10637  }
10638  if (!$this->negative && $integer < 0) {
10639  return false;
10640  }
10641 
10642  return $integer;
10643  }
10644 }
10645 
10646 
10647 
10648 
10649 
10655 {
10656 
10663  public function validate($string, $config, $context)
10664  {
10665  $string = trim($string);
10666  if (!$string) {
10667  return false;
10668  }
10669 
10670  $subtags = explode('-', $string);
10671  $num_subtags = count($subtags);
10672 
10673  if ($num_subtags == 0) { // sanity check
10674  return false;
10675  }
10676 
10677  // process primary subtag : $subtags[0]
10678  $length = strlen($subtags[0]);
10679  switch ($length) {
10680  case 0:
10681  return false;
10682  case 1:
10683  if (!($subtags[0] == 'x' || $subtags[0] == 'i')) {
10684  return false;
10685  }
10686  break;
10687  case 2:
10688  case 3:
10689  if (!ctype_alpha($subtags[0])) {
10690  return false;
10691  } elseif (!ctype_lower($subtags[0])) {
10692  $subtags[0] = strtolower($subtags[0]);
10693  }
10694  break;
10695  default:
10696  return false;
10697  }
10698 
10699  $new_string = $subtags[0];
10700  if ($num_subtags == 1) {
10701  return $new_string;
10702  }
10703 
10704  // process second subtag : $subtags[1]
10705  $length = strlen($subtags[1]);
10706  if ($length == 0 || ($length == 1 && $subtags[1] != 'x') || $length > 8 || !ctype_alnum($subtags[1])) {
10707  return $new_string;
10708  }
10709  if (!ctype_lower($subtags[1])) {
10710  $subtags[1] = strtolower($subtags[1]);
10711  }
10712 
10713  $new_string .= '-' . $subtags[1];
10714  if ($num_subtags == 2) {
10715  return $new_string;
10716  }
10717 
10718  // process all other subtags, index 2 and up
10719  for ($i = 2; $i < $num_subtags; $i++) {
10720  $length = strlen($subtags[$i]);
10721  if ($length == 0 || $length > 8 || !ctype_alnum($subtags[$i])) {
10722  return $new_string;
10723  }
10724  if (!ctype_lower($subtags[$i])) {
10725  $subtags[$i] = strtolower($subtags[$i]);
10726  }
10727  $new_string .= '-' . $subtags[$i];
10728  }
10729  return $new_string;
10730  }
10731 }
10732 
10733 
10734 
10735 
10736 
10741 {
10742 
10746  protected $tag;
10747 
10751  protected $withTag;
10752 
10756  protected $withoutTag;
10757 
10763  public function __construct($tag, $with_tag, $without_tag)
10764  {
10765  $this->tag = $tag;
10766  $this->withTag = $with_tag;
10767  $this->withoutTag = $without_tag;
10768  }
10769 
10776  public function validate($string, $config, $context)
10777  {
10778  $token = $context->get('CurrentToken', true);
10779  if (!$token || $token->name !== $this->tag) {
10780  return $this->withoutTag->validate($string, $config, $context);
10781  } else {
10782  return $this->withTag->validate($string, $config, $context);
10783  }
10784  }
10785 }
10786 
10787 
10788 
10789 
10790 
10795 {
10796 
10803  public function validate($string, $config, $context)
10804  {
10805  return $this->parseCDATA($string);
10806  }
10807 }
10808 
10809 
10810 
10811 
10812 
10818 {
10819 
10823  protected $parser;
10824 
10828  protected $embedsResource;
10829 
10833  public function __construct($embeds_resource = false)
10834  {
10835  $this->parser = new HTMLPurifier_URIParser();
10836  $this->embedsResource = (bool)$embeds_resource;
10837  }
10838 
10843  public function make($string)
10844  {
10845  $embeds = ($string === 'embedded');
10846  return new HTMLPurifier_AttrDef_URI($embeds);
10847  }
10848 
10855  public function validate($uri, $config, $context)
10856  {
10857  if ($config->get('URI.Disable')) {
10858  return false;
10859  }
10860 
10861  $uri = $this->parseCDATA($uri);
10862 
10863  // parse the URI
10864  $uri = $this->parser->parse($uri);
10865  if ($uri === false) {
10866  return false;
10867  }
10868 
10869  // add embedded flag to context for validators
10870  $context->register('EmbeddedURI', $this->embedsResource);
10871 
10872  $ok = false;
10873  do {
10874 
10875  // generic validation
10876  $result = $uri->validate($config, $context);
10877  if (!$result) {
10878  break;
10879  }
10880 
10881  // chained filtering
10882  $uri_def = $config->getDefinition('URI');
10883  $result = $uri_def->filter($uri, $config, $context);
10884  if (!$result) {
10885  break;
10886  }
10887 
10888  // scheme-specific validation
10889  $scheme_obj = $uri->getSchemeObj($config, $context);
10890  if (!$scheme_obj) {
10891  break;
10892  }
10893  if ($this->embedsResource && !$scheme_obj->browsable) {
10894  break;
10895  }
10896  $result = $scheme_obj->validate($uri, $config, $context);
10897  if (!$result) {
10898  break;
10899  }
10900 
10901  // Post chained filtering
10902  $result = $uri_def->postFilter($uri, $config, $context);
10903  if (!$result) {
10904  break;
10905  }
10906 
10907  // survived gauntlet
10908  $ok = true;
10909 
10910  } while (false);
10911 
10912  $context->destroy('EmbeddedURI');
10913  if (!$ok) {
10914  return false;
10915  }
10916  // back to string
10917  return $uri->toString();
10918  }
10919 }
10920 
10921 
10922 
10923 
10924 
10929 {
10930 
10935  protected $non_negative = false;
10936 
10940  public function __construct($non_negative = false)
10941  {
10942  $this->non_negative = $non_negative;
10943  }
10944 
10953  public function validate($number, $config, $context)
10954  {
10955  $number = $this->parseCDATA($number);
10956 
10957  if ($number === '') {
10958  return false;
10959  }
10960  if ($number === '0') {
10961  return '0';
10962  }
10963 
10964  $sign = '';
10965  switch ($number[0]) {
10966  case '-':
10967  if ($this->non_negative) {
10968  return false;
10969  }
10970  $sign = '-';
10971  case '+':
10972  $number = substr($number, 1);
10973  }
10974 
10975  if (ctype_digit($number)) {
10976  $number = ltrim($number, '0');
10977  return $number ? $sign . $number : '0';
10978  }
10979 
10980  // Period is the only non-numeric character allowed
10981  if (strpos($number, '.') === false) {
10982  return false;
10983  }
10984 
10985  list($left, $right) = explode('.', $number, 2);
10986 
10987  if ($left === '' && $right === '') {
10988  return false;
10989  }
10990  if ($left !== '' && !ctype_digit($left)) {
10991  return false;
10992  }
10993 
10994  $left = ltrim($left, '0');
10995  $right = rtrim($right, '0');
10996 
10997  if ($right === '') {
10998  return $left ? $sign . $left : '0';
10999  } elseif (!ctype_digit($right)) {
11000  return false;
11001  }
11002  return $sign . $left . '.' . $right;
11003  }
11004 }
11005 
11006 
11007 
11008 
11009 
11011 {
11012 
11013  public function __construct()
11014  {
11015  parent::__construct(false); // opacity is non-negative, but we will clamp it
11016  }
11017 
11024  public function validate($number, $config, $context)
11025  {
11026  $result = parent::validate($number, $config, $context);
11027  if ($result === false) {
11028  return $result;
11029  }
11030  $float = (float)$result;
11031  if ($float < 0.0) {
11032  $result = '0';
11033  }
11034  if ($float > 1.0) {
11035  $result = '1';
11036  }
11037  return $result;
11038  }
11039 }
11040 
11041 
11042 
11043 
11044 
11050 {
11051 
11057  protected $info;
11058 
11062  public function __construct($config)
11063  {
11064  $def = $config->getCSSDefinition();
11065  $this->info['background-color'] = $def->info['background-color'];
11066  $this->info['background-image'] = $def->info['background-image'];
11067  $this->info['background-repeat'] = $def->info['background-repeat'];
11068  $this->info['background-attachment'] = $def->info['background-attachment'];
11069  $this->info['background-position'] = $def->info['background-position'];
11070  }
11071 
11078  public function validate($string, $config, $context)
11079  {
11080  // regular pre-processing
11081  $string = $this->parseCDATA($string);
11082  if ($string === '') {
11083  return false;
11084  }
11085 
11086  // munge rgb() decl if necessary
11087  $string = $this->mungeRgb($string);
11088 
11089  // assumes URI doesn't have spaces in it
11090  $bits = explode(' ', $string); // bits to process
11091 
11092  $caught = array();
11093  $caught['color'] = false;
11094  $caught['image'] = false;
11095  $caught['repeat'] = false;
11096  $caught['attachment'] = false;
11097  $caught['position'] = false;
11098 
11099  $i = 0; // number of catches
11100 
11101  foreach ($bits as $bit) {
11102  if ($bit === '') {
11103  continue;
11104  }
11105  foreach ($caught as $key => $status) {
11106  if ($key != 'position') {
11107  if ($status !== false) {
11108  continue;
11109  }
11110  $r = $this->info['background-' . $key]->validate($bit, $config, $context);
11111  } else {
11112  $r = $bit;
11113  }
11114  if ($r === false) {
11115  continue;
11116  }
11117  if ($key == 'position') {
11118  if ($caught[$key] === false) {
11119  $caught[$key] = '';
11120  }
11121  $caught[$key] .= $r . ' ';
11122  } else {
11123  $caught[$key] = $r;
11124  }
11125  $i++;
11126  break;
11127  }
11128  }
11129 
11130  if (!$i) {
11131  return false;
11132  }
11133  if ($caught['position'] !== false) {
11134  $caught['position'] = $this->info['background-position']->
11135  validate($caught['position'], $config, $context);
11136  }
11137 
11138  $ret = array();
11139  foreach ($caught as $value) {
11140  if ($value === false) {
11141  continue;
11142  }
11143  $ret[] = $value;
11144  }
11145 
11146  if (empty($ret)) {
11147  return false;
11148  }
11149  return implode(' ', $ret);
11150  }
11151 }
11152 
11153 
11154 
11155 
11156 
11157 /* W3C says:
11158  [ // adjective and number must be in correct order, even if
11159  // you could switch them without introducing ambiguity.
11160  // some browsers support that syntax
11161  [
11162  <percentage> | <length> | left | center | right
11163  ]
11164  [
11165  <percentage> | <length> | top | center | bottom
11166  ]?
11167  ] |
11168  [ // this signifies that the vertical and horizontal adjectives
11169  // can be arbitrarily ordered, however, there can only be two,
11170  // one of each, or none at all
11171  [
11172  left | center | right
11173  ] ||
11174  [
11175  top | center | bottom
11176  ]
11177  ]
11178  top, left = 0%
11179  center, (none) = 50%
11180  bottom, right = 100%
11181 */
11182 
11183 /* QuirksMode says:
11184  keyword + length/percentage must be ordered correctly, as per W3C
11185 
11186  Internet Explorer and Opera, however, support arbitrary ordering. We
11187  should fix it up.
11188 
11189  Minor issue though, not strictly necessary.
11190 */
11191 
11192 // control freaks may appreciate the ability to convert these to
11193 // percentages or something, but it's not necessary
11194 
11199 {
11200 
11204  protected $length;
11205 
11209  protected $percentage;
11210 
11211  public function __construct()
11212  {
11213  $this->length = new HTMLPurifier_AttrDef_CSS_Length();
11214  $this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage();
11215  }
11216 
11223  public function validate($string, $config, $context)
11224  {
11225  $string = $this->parseCDATA($string);
11226  $bits = explode(' ', $string);
11227 
11228  $keywords = array();
11229  $keywords['h'] = false; // left, right
11230  $keywords['v'] = false; // top, bottom
11231  $keywords['ch'] = false; // center (first word)
11232  $keywords['cv'] = false; // center (second word)
11233  $measures = array();
11234 
11235  $i = 0;
11236 
11237  $lookup = array(
11238  'top' => 'v',
11239  'bottom' => 'v',
11240  'left' => 'h',
11241  'right' => 'h',
11242  'center' => 'c'
11243  );
11244 
11245  foreach ($bits as $bit) {
11246  if ($bit === '') {
11247  continue;
11248  }
11249 
11250  // test for keyword
11251  $lbit = ctype_lower($bit) ? $bit : strtolower($bit);
11252  if (isset($lookup[$lbit])) {
11253  $status = $lookup[$lbit];
11254  if ($status == 'c') {
11255  if ($i == 0) {
11256  $status = 'ch';
11257  } else {
11258  $status = 'cv';
11259  }
11260  }
11261  $keywords[$status] = $lbit;
11262  $i++;
11263  }
11264 
11265  // test for length
11266  $r = $this->length->validate($bit, $config, $context);
11267  if ($r !== false) {
11268  $measures[] = $r;
11269  $i++;
11270  }
11271 
11272  // test for percentage
11273  $r = $this->percentage->validate($bit, $config, $context);
11274  if ($r !== false) {
11275  $measures[] = $r;
11276  $i++;
11277  }
11278  }
11279 
11280  if (!$i) {
11281  return false;
11282  } // no valid values were caught
11283 
11284  $ret = array();
11285 
11286  // first keyword
11287  if ($keywords['h']) {
11288  $ret[] = $keywords['h'];
11289  } elseif ($keywords['ch']) {
11290  $ret[] = $keywords['ch'];
11291  $keywords['cv'] = false; // prevent re-use: center = center center
11292  } elseif (count($measures)) {
11293  $ret[] = array_shift($measures);
11294  }
11295 
11296  if ($keywords['v']) {
11297  $ret[] = $keywords['v'];
11298  } elseif ($keywords['cv']) {
11299  $ret[] = $keywords['cv'];
11300  } elseif (count($measures)) {
11301  $ret[] = array_shift($measures);
11302  }
11303 
11304  if (empty($ret)) {
11305  return false;
11306  }
11307  return implode(' ', $ret);
11308  }
11309 }
11310 
11311 
11312 
11313 
11314 
11319 {
11320 
11325  protected $info = array();
11326 
11330  public function __construct($config)
11331  {
11332  $def = $config->getCSSDefinition();
11333  $this->info['border-width'] = $def->info['border-width'];
11334  $this->info['border-style'] = $def->info['border-style'];
11335  $this->info['border-top-color'] = $def->info['border-top-color'];
11336  }
11337 
11344  public function validate($string, $config, $context)
11345  {
11346  $string = $this->parseCDATA($string);
11347  $string = $this->mungeRgb($string);
11348  $bits = explode(' ', $string);
11349  $done = array(); // segments we've finished
11350  $ret = ''; // return value
11351  foreach ($bits as $bit) {
11352  foreach ($this->info as $propname => $validator) {
11353  if (isset($done[$propname])) {
11354  continue;
11355  }
11356  $r = $validator->validate($bit, $config, $context);
11357  if ($r !== false) {
11358  $ret .= $r . ' ';
11359  $done[$propname] = true;
11360  break;
11361  }
11362  }
11363  }
11364  return rtrim($ret);
11365  }
11366 }
11367 
11368 
11369 
11370 
11371 
11376 {
11377 
11381  protected $alpha;
11382 
11383  public function __construct()
11384  {
11385  $this->alpha = new HTMLPurifier_AttrDef_CSS_AlphaValue();
11386  }
11387 
11394  public function validate($color, $config, $context)
11395  {
11396  static $colors = null;
11397  if ($colors === null) {
11398  $colors = $config->get('Core.ColorKeywords');
11399  }
11400 
11401  $color = trim($color);
11402  if ($color === '') {
11403  return false;
11404  }
11405 
11406  $lower = strtolower($color);
11407  if (isset($colors[$lower])) {
11408  return $colors[$lower];
11409  }
11410 
11411  if (preg_match('#(rgb|rgba|hsl|hsla)\‍(#', $color, $matches) === 1) {
11412  $length = strlen($color);
11413  if (strpos($color, ')') !== $length - 1) {
11414  return false;
11415  }
11416 
11417  // get used function : rgb, rgba, hsl or hsla
11418  $function = $matches[1];
11419 
11420  $parameters_size = 3;
11421  $alpha_channel = false;
11422  if (substr($function, -1) === 'a') {
11423  $parameters_size = 4;
11424  $alpha_channel = true;
11425  }
11426 
11427  /*
11428  * Allowed types for values :
11429  * parameter_position => [type => max_value]
11430  */
11431  $allowed_types = array(
11432  1 => array('percentage' => 100, 'integer' => 255),
11433  2 => array('percentage' => 100, 'integer' => 255),
11434  3 => array('percentage' => 100, 'integer' => 255),
11435  );
11436  $allow_different_types = false;
11437 
11438  if (strpos($function, 'hsl') !== false) {
11439  $allowed_types = array(
11440  1 => array('integer' => 360),
11441  2 => array('percentage' => 100),
11442  3 => array('percentage' => 100),
11443  );
11444  $allow_different_types = true;
11445  }
11446 
11447  $values = trim(str_replace($function, '', $color), ' ()');
11448 
11449  $parts = explode(',', $values);
11450  if (count($parts) !== $parameters_size) {
11451  return false;
11452  }
11453 
11454  $type = false;
11455  $new_parts = array();
11456  $i = 0;
11457 
11458  foreach ($parts as $part) {
11459  $i++;
11460  $part = trim($part);
11461 
11462  if ($part === '') {
11463  return false;
11464  }
11465 
11466  // different check for alpha channel
11467  if ($alpha_channel === true && $i === count($parts)) {
11468  $result = $this->alpha->validate($part, $config, $context);
11469 
11470  if ($result === false) {
11471  return false;
11472  }
11473 
11474  $new_parts[] = (string)$result;
11475  continue;
11476  }
11477 
11478  if (substr($part, -1) === '%') {
11479  $current_type = 'percentage';
11480  } else {
11481  $current_type = 'integer';
11482  }
11483 
11484  if (!array_key_exists($current_type, $allowed_types[$i])) {
11485  return false;
11486  }
11487 
11488  if (!$type) {
11489  $type = $current_type;
11490  }
11491 
11492  if ($allow_different_types === false && $type != $current_type) {
11493  return false;
11494  }
11495 
11496  $max_value = $allowed_types[$i][$current_type];
11497 
11498  if ($current_type == 'integer') {
11499  // Return value between range 0 -> $max_value
11500  $new_parts[] = (int)max(min($part, $max_value), 0);
11501  } elseif ($current_type == 'percentage') {
11502  $new_parts[] = (float)max(min(rtrim($part, '%'), $max_value), 0) . '%';
11503  }
11504  }
11505 
11506  $new_values = implode(',', $new_parts);
11507 
11508  $color = $function . '(' . $new_values . ')';
11509  } else {
11510  // hexadecimal handling
11511  if ($color[0] === '#') {
11512  $hex = substr($color, 1);
11513  } else {
11514  $hex = $color;
11515  $color = '#' . $color;
11516  }
11517  $length = strlen($hex);
11518  if ($length !== 3 && $length !== 6) {
11519  return false;
11520  }
11521  if (!ctype_xdigit($hex)) {
11522  return false;
11523  }
11524  }
11525  return $color;
11526  }
11527 
11528 }
11529 
11530 
11531 
11532 
11533 
11544 {
11545 
11551  public $defs;
11552 
11556  public function __construct($defs)
11557  {
11558  $this->defs = $defs;
11559  }
11560 
11567  public function validate($string, $config, $context)
11568  {
11569  foreach ($this->defs as $i => $def) {
11570  $result = $this->defs[$i]->validate($string, $config, $context);
11571  if ($result !== false) {
11572  return $result;
11573  }
11574  }
11575  return false;
11576  }
11577 }
11578 
11579 
11580 
11581 
11582 
11587 {
11591  public $def;
11595  public $element;
11596 
11601  public function __construct($def, $element)
11602  {
11603  $this->def = $def;
11604  $this->element = $element;
11605  }
11606 
11614  public function validate($string, $config, $context)
11615  {
11616  $token = $context->get('CurrentToken', true);
11617  if ($token && $token->name == $this->element) {
11618  return false;
11619  }
11620  return $this->def->validate($string, $config, $context);
11621  }
11622 }
11623 
11624 
11625 
11626 
11627 
11634 {
11638  protected $intValidator;
11639 
11640  public function __construct()
11641  {
11642  $this->intValidator = new HTMLPurifier_AttrDef_Integer();
11643  }
11644 
11651  public function validate($value, $config, $context)
11652  {
11653  $value = $this->parseCDATA($value);
11654  if ($value === 'none') {
11655  return $value;
11656  }
11657  // if we looped this we could support multiple filters
11658  $function_length = strcspn($value, '(');
11659  $function = trim(substr($value, 0, $function_length));
11660  if ($function !== 'alpha' &&
11661  $function !== 'Alpha' &&
11662  $function !== 'progid:DXImageTransform.Microsoft.Alpha'
11663  ) {
11664  return false;
11665  }
11666  $cursor = $function_length + 1;
11667  $parameters_length = strcspn($value, ')', $cursor);
11668  $parameters = substr($value, $cursor, $parameters_length);
11669  $params = explode(',', $parameters);
11670  $ret_params = array();
11671  $lookup = array();
11672  foreach ($params as $param) {
11673  list($key, $value) = explode('=', $param);
11674  $key = trim($key);
11675  $value = trim($value);
11676  if (isset($lookup[$key])) {
11677  continue;
11678  }
11679  if ($key !== 'opacity') {
11680  continue;
11681  }
11682  $value = $this->intValidator->validate($value, $config, $context);
11683  if ($value === false) {
11684  continue;
11685  }
11686  $int = (int)$value;
11687  if ($int > 100) {
11688  $value = '100';
11689  }
11690  if ($int < 0) {
11691  $value = '0';
11692  }
11693  $ret_params[] = "$key=$value";
11694  $lookup[$key] = true;
11695  }
11696  $ret_parameters = implode(',', $ret_params);
11697  $ret_function = "$function($ret_parameters)";
11698  return $ret_function;
11699  }
11700 }
11701 
11702 
11703 
11704 
11705 
11710 {
11711 
11720  protected $info = array();
11721 
11725  public function __construct($config)
11726  {
11727  $def = $config->getCSSDefinition();
11728  $this->info['font-style'] = $def->info['font-style'];
11729  $this->info['font-variant'] = $def->info['font-variant'];
11730  $this->info['font-weight'] = $def->info['font-weight'];
11731  $this->info['font-size'] = $def->info['font-size'];
11732  $this->info['line-height'] = $def->info['line-height'];
11733  $this->info['font-family'] = $def->info['font-family'];
11734  }
11735 
11742  public function validate($string, $config, $context)
11743  {
11744  static $system_fonts = array(
11745  'caption' => true,
11746  'icon' => true,
11747  'menu' => true,
11748  'message-box' => true,
11749  'small-caption' => true,
11750  'status-bar' => true
11751  );
11752 
11753  // regular pre-processing
11754  $string = $this->parseCDATA($string);
11755  if ($string === '') {
11756  return false;
11757  }
11758 
11759  // check if it's one of the keywords
11760  $lowercase_string = strtolower($string);
11761  if (isset($system_fonts[$lowercase_string])) {
11762  return $lowercase_string;
11763  }
11764 
11765  $bits = explode(' ', $string); // bits to process
11766  $stage = 0; // this indicates what we're looking for
11767  $caught = array(); // which stage 0 properties have we caught?
11768  $stage_1 = array('font-style', 'font-variant', 'font-weight');
11769  $final = ''; // output
11770 
11771  for ($i = 0, $size = count($bits); $i < $size; $i++) {
11772  if ($bits[$i] === '') {
11773  continue;
11774  }
11775  switch ($stage) {
11776  case 0: // attempting to catch font-style, font-variant or font-weight
11777  foreach ($stage_1 as $validator_name) {
11778  if (isset($caught[$validator_name])) {
11779  continue;
11780  }
11781  $r = $this->info[$validator_name]->validate(
11782  $bits[$i],
11783  $config,
11784  $context
11785  );
11786  if ($r !== false) {
11787  $final .= $r . ' ';
11788  $caught[$validator_name] = true;
11789  break;
11790  }
11791  }
11792  // all three caught, continue on
11793  if (count($caught) >= 3) {
11794  $stage = 1;
11795  }
11796  if ($r !== false) {
11797  break;
11798  }
11799  case 1: // attempting to catch font-size and perhaps line-height
11800  $found_slash = false;
11801  if (strpos($bits[$i], '/') !== false) {
11802  list($font_size, $line_height) =
11803  explode('/', $bits[$i]);
11804  if ($line_height === '') {
11805  // ooh, there's a space after the slash!
11806  $line_height = false;
11807  $found_slash = true;
11808  }
11809  } else {
11810  $font_size = $bits[$i];
11811  $line_height = false;
11812  }
11813  $r = $this->info['font-size']->validate(
11814  $font_size,
11815  $config,
11816  $context
11817  );
11818  if ($r !== false) {
11819  $final .= $r;
11820  // attempt to catch line-height
11821  if ($line_height === false) {
11822  // we need to scroll forward
11823  for ($j = $i + 1; $j < $size; $j++) {
11824  if ($bits[$j] === '') {
11825  continue;
11826  }
11827  if ($bits[$j] === '/') {
11828  if ($found_slash) {
11829  return false;
11830  } else {
11831  $found_slash = true;
11832  continue;
11833  }
11834  }
11835  $line_height = $bits[$j];
11836  break;
11837  }
11838  } else {
11839  // slash already found
11840  $found_slash = true;
11841  $j = $i;
11842  }
11843  if ($found_slash) {
11844  $i = $j;
11845  $r = $this->info['line-height']->validate(
11846  $line_height,
11847  $config,
11848  $context
11849  );
11850  if ($r !== false) {
11851  $final .= '/' . $r;
11852  }
11853  }
11854  $final .= ' ';
11855  $stage = 2;
11856  break;
11857  }
11858  return false;
11859  case 2: // attempting to catch font-family
11860  $font_family =
11861  implode(' ', array_slice($bits, $i, $size - $i));
11862  $r = $this->info['font-family']->validate(
11863  $font_family,
11864  $config,
11865  $context
11866  );
11867  if ($r !== false) {
11868  $final .= $r . ' ';
11869  // processing completed successfully
11870  return rtrim($final);
11871  }
11872  return false;
11873  }
11874  }
11875  return false;
11876  }
11877 }
11878 
11879 
11880 
11881 
11882 
11887 {
11888 
11889  protected $mask = null;
11890 
11891  public function __construct()
11892  {
11893  $this->mask = '_- ';
11894  for ($c = 'a'; $c <= 'z'; $c++) {
11895  $this->mask .= $c;
11896  }
11897  for ($c = 'A'; $c <= 'Z'; $c++) {
11898  $this->mask .= $c;
11899  }
11900  for ($c = '0'; $c <= '9'; $c++) {
11901  $this->mask .= $c;
11902  } // cast-y, but should be fine
11903  // special bytes used by UTF-8
11904  for ($i = 0x80; $i <= 0xFF; $i++) {
11905  // We don't bother excluding invalid bytes in this range,
11906  // because the our restriction of well-formed UTF-8 will
11907  // prevent these from ever occurring.
11908  $this->mask .= chr($i);
11909  }
11910 
11911  /*
11912  PHP's internal strcspn implementation is
11913  O(length of string * length of mask), making it inefficient
11914  for large masks. However, it's still faster than
11915  preg_match 8)
11916  for (p = s1;;) {
11917  spanp = s2;
11918  do {
11919  if (*spanp == c || p == s1_end) {
11920  return p - s1;
11921  }
11922  } while (spanp++ < (s2_end - 1));
11923  c = *++p;
11924  }
11925  */
11926  // possible optimization: invert the mask.
11927  }
11928 
11935  public function validate($string, $config, $context)
11936  {
11937  static $generic_names = array(
11938  'serif' => true,
11939  'sans-serif' => true,
11940  'monospace' => true,
11941  'fantasy' => true,
11942  'cursive' => true
11943  );
11944  $allowed_fonts = $config->get('CSS.AllowedFonts');
11945 
11946  // assume that no font names contain commas in them
11947  $fonts = explode(',', $string);
11948  $final = '';
11949  foreach ($fonts as $font) {
11950  $font = trim($font);
11951  if ($font === '') {
11952  continue;
11953  }
11954  // match a generic name
11955  if (isset($generic_names[$font])) {
11956  if ($allowed_fonts === null || isset($allowed_fonts[$font])) {
11957  $final .= $font . ', ';
11958  }
11959  continue;
11960  }
11961  // match a quoted name
11962  if ($font[0] === '"' || $font[0] === "'") {
11963  $length = strlen($font);
11964  if ($length <= 2) {
11965  continue;
11966  }
11967  $quote = $font[0];
11968  if ($font[$length - 1] !== $quote) {
11969  continue;
11970  }
11971  $font = substr($font, 1, $length - 2);
11972  }
11973 
11974  $font = $this->expandCSSEscape($font);
11975 
11976  // $font is a pure representation of the font name
11977 
11978  if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) {
11979  continue;
11980  }
11981 
11982  if (ctype_alnum($font) && $font !== '') {
11983  // very simple font, allow it in unharmed
11984  $final .= $font . ', ';
11985  continue;
11986  }
11987 
11988  // bugger out on whitespace. form feed (0C) really
11989  // shouldn't show up regardless
11990  $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font);
11991 
11992  // Here, there are various classes of characters which need
11993  // to be treated differently:
11994  // - Alphanumeric characters are essentially safe. We
11995  // handled these above.
11996  // - Spaces require quoting, though most parsers will do
11997  // the right thing if there aren't any characters that
11998  // can be misinterpreted
11999  // - Dashes rarely occur, but they fairly unproblematic
12000  // for parsing/rendering purposes.
12001  // The above characters cover the majority of Western font
12002  // names.
12003  // - Arbitrary Unicode characters not in ASCII. Because
12004  // most parsers give little thought to Unicode, treatment
12005  // of these codepoints is basically uniform, even for
12006  // punctuation-like codepoints. These characters can
12007  // show up in non-Western pages and are supported by most
12008  // major browsers, for example: "MS 明朝" is a
12009  // legitimate font-name
12010  // <http://ja.wikipedia.org/wiki/MS_明朝>. See
12011  // the CSS3 spec for more examples:
12012  // <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png>
12013  // You can see live samples of these on the Internet:
12014  // <http://www.google.co.jp/search?q=font-family+MS+明朝|ゴシック>
12015  // However, most of these fonts have ASCII equivalents:
12016  // for example, 'MS Mincho', and it's considered
12017  // professional to use ASCII font names instead of
12018  // Unicode font names. Thanks Takeshi Terada for
12019  // providing this information.
12020  // The following characters, to my knowledge, have not been
12021  // used to name font names.
12022  // - Single quote. While theoretically you might find a
12023  // font name that has a single quote in its name (serving
12024  // as an apostrophe, e.g. Dave's Scribble), I haven't
12025  // been able to find any actual examples of this.
12026  // Internet Explorer's cssText translation (which I
12027  // believe is invoked by innerHTML) normalizes any
12028  // quoting to single quotes, and fails to escape single
12029  // quotes. (Note that this is not IE's behavior for all
12030  // CSS properties, just some sort of special casing for
12031  // font-family). So a single quote *cannot* be used
12032  // safely in the font-family context if there will be an
12033  // innerHTML/cssText translation. Note that Firefox 3.x
12034  // does this too.
12035  // - Double quote. In IE, these get normalized to
12036  // single-quotes, no matter what the encoding. (Fun
12037  // fact, in IE8, the 'content' CSS property gained
12038  // support, where they special cased to preserve encoded
12039  // double quotes, but still translate unadorned double
12040  // quotes into single quotes.) So, because their
12041  // fixpoint behavior is identical to single quotes, they
12042  // cannot be allowed either. Firefox 3.x displays
12043  // single-quote style behavior.
12044  // - Backslashes are reduced by one (so \\ -> \‍) every
12045  // iteration, so they cannot be used safely. This shows
12046  // up in IE7, IE8 and FF3
12047  // - Semicolons, commas and backticks are handled properly.
12048  // - The rest of the ASCII punctuation is handled properly.
12049  // We haven't checked what browsers do to unadorned
12050  // versions, but this is not important as long as the
12051  // browser doesn't /remove/ surrounding quotes (as IE does
12052  // for HTML).
12053  //
12054  // With these results in hand, we conclude that there are
12055  // various levels of safety:
12056  // - Paranoid: alphanumeric, spaces and dashes(?)
12057  // - International: Paranoid + non-ASCII Unicode
12058  // - Edgy: Everything except quotes, backslashes
12059  // - NoJS: Standards compliance, e.g. sod IE. Note that
12060  // with some judicious character escaping (since certain
12061  // types of escaping doesn't work) this is theoretically
12062  // OK as long as innerHTML/cssText is not called.
12063  // We believe that international is a reasonable default
12064  // (that we will implement now), and once we do more
12065  // extensive research, we may feel comfortable with dropping
12066  // it down to edgy.
12067 
12068  // Edgy: alphanumeric, spaces, dashes, underscores and Unicode. Use of
12069  // str(c)spn assumes that the string was already well formed
12070  // Unicode (which of course it is).
12071  if (strspn($font, $this->mask) !== strlen($font)) {
12072  continue;
12073  }
12074 
12075  // Historical:
12076  // In the absence of innerHTML/cssText, these ugly
12077  // transforms don't pose a security risk (as \\ and \"
12078  // might--these escapes are not supported by most browsers).
12079  // We could try to be clever and use single-quote wrapping
12080  // when there is a double quote present, but I have choosen
12081  // not to implement that. (NOTE: you can reduce the amount
12082  // of escapes by one depending on what quoting style you use)
12083  // $font = str_replace('\\', '\\5C ', $font);
12084  // $font = str_replace('"', '\\22 ', $font);
12085  // $font = str_replace("'", '\\27 ', $font);
12086 
12087  // font possibly with spaces, requires quoting
12088  $final .= "'$font', ";
12089  }
12090  $final = rtrim($final, ', ');
12091  if ($final === '') {
12092  return false;
12093  }
12094  return $final;
12095  }
12096 
12097 }
12098 
12099 
12100 
12101 
12102 
12107 {
12108 
12115  public function validate($string, $config, $context)
12116  {
12117  $string = trim($string);
12118 
12119  // early abort: '' and '0' (strings that convert to false) are invalid
12120  if (!$string) {
12121  return false;
12122  }
12123 
12124  $pattern = '/^(-?[A-Za-z_][A-Za-z_\-0-9]*)$/';
12125  if (!preg_match($pattern, $string)) {
12126  return false;
12127  }
12128  return $string;
12129  }
12130 }
12131 
12132 
12133 
12134 
12135 
12140 {
12144  public $def;
12148  public $allow;
12149 
12154  public function __construct($def, $allow = false)
12155  {
12156  $this->def = $def;
12157  $this->allow = $allow;
12158  }
12159 
12167  public function validate($string, $config, $context)
12168  {
12169  // test for ! and important tokens
12170  $string = trim($string);
12171  $is_important = false;
12172  // :TODO: optimization: test directly for !important and ! important
12173  if (strlen($string) >= 9 && substr($string, -9) === 'important') {
12174  $temp = rtrim(substr($string, 0, -9));
12175  // use a temp, because we might want to restore important
12176  if (strlen($temp) >= 1 && substr($temp, -1) === '!') {
12177  $string = rtrim(substr($temp, 0, -1));
12178  $is_important = true;
12179  }
12180  }
12181  $string = $this->def->validate($string, $config, $context);
12182  if ($this->allow && $is_important) {
12183  $string .= ' !important';
12184  }
12185  return $string;
12186  }
12187 }
12188 
12189 
12190 
12191 
12192 
12197 {
12198 
12202  protected $min;
12203 
12207  protected $max;
12208 
12213  public function __construct($min = null, $max = null)
12214  {
12215  $this->min = $min !== null ? HTMLPurifier_Length::make($min) : null;
12216  $this->max = $max !== null ? HTMLPurifier_Length::make($max) : null;
12217  }
12218 
12225  public function validate($string, $config, $context)
12226  {
12227  $string = $this->parseCDATA($string);
12228 
12229  // Optimizations
12230  if ($string === '') {
12231  return false;
12232  }
12233  if ($string === '0') {
12234  return '0';
12235  }
12236  if (strlen($string) === 1) {
12237  return false;
12238  }
12239 
12240  $length = HTMLPurifier_Length::make($string);
12241  if (!$length->isValid()) {
12242  return false;
12243  }
12244 
12245  if ($this->min) {
12246  $c = $length->compareTo($this->min);
12247  if ($c === false) {
12248  return false;
12249  }
12250  if ($c < 0) {
12251  return false;
12252  }
12253  }
12254  if ($this->max) {
12255  $c = $length->compareTo($this->max);
12256  if ($c === false) {
12257  return false;
12258  }
12259  if ($c > 0) {
12260  return false;
12261  }
12262  }
12263  return $length->toString();
12264  }
12265 }
12266 
12267 
12268 
12269 
12270 
12276 {
12277 
12283  protected $info;
12284 
12288  public function __construct($config)
12289  {
12290  $def = $config->getCSSDefinition();
12291  $this->info['list-style-type'] = $def->info['list-style-type'];
12292  $this->info['list-style-position'] = $def->info['list-style-position'];
12293  $this->info['list-style-image'] = $def->info['list-style-image'];
12294  }
12295 
12302  public function validate($string, $config, $context)
12303  {
12304  // regular pre-processing
12305  $string = $this->parseCDATA($string);
12306  if ($string === '') {
12307  return false;
12308  }
12309 
12310  // assumes URI doesn't have spaces in it
12311  $bits = explode(' ', strtolower($string)); // bits to process
12312 
12313  $caught = array();
12314  $caught['type'] = false;
12315  $caught['position'] = false;
12316  $caught['image'] = false;
12317 
12318  $i = 0; // number of catches
12319  $none = false;
12320 
12321  foreach ($bits as $bit) {
12322  if ($i >= 3) {
12323  return;
12324  } // optimization bit
12325  if ($bit === '') {
12326  continue;
12327  }
12328  foreach ($caught as $key => $status) {
12329  if ($status !== false) {
12330  continue;
12331  }
12332  $r = $this->info['list-style-' . $key]->validate($bit, $config, $context);
12333  if ($r === false) {
12334  continue;
12335  }
12336  if ($r === 'none') {
12337  if ($none) {
12338  continue;
12339  } else {
12340  $none = true;
12341  }
12342  if ($key == 'image') {
12343  continue;
12344  }
12345  }
12346  $caught[$key] = $r;
12347  $i++;
12348  break;
12349  }
12350  }
12351 
12352  if (!$i) {
12353  return false;
12354  }
12355 
12356  $ret = array();
12357 
12358  // construct type
12359  if ($caught['type']) {
12360  $ret[] = $caught['type'];
12361  }
12362 
12363  // construct image
12364  if ($caught['image']) {
12365  $ret[] = $caught['image'];
12366  }
12367 
12368  // construct position
12369  if ($caught['position']) {
12370  $ret[] = $caught['position'];
12371  }
12372 
12373  if (empty($ret)) {
12374  return false;
12375  }
12376  return implode(' ', $ret);
12377  }
12378 }
12379 
12380 
12381 
12382 
12383 
12396 {
12402  public $single;
12403 
12408  public $max;
12409 
12414  public function __construct($single, $max = 4)
12415  {
12416  $this->single = $single;
12417  $this->max = $max;
12418  }
12419 
12426  public function validate($string, $config, $context)
12427  {
12428  $string = $this->mungeRgb($this->parseCDATA($string));
12429  if ($string === '') {
12430  return false;
12431  }
12432  $parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n
12433  $length = count($parts);
12434  $final = '';
12435  for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) {
12436  if (ctype_space($parts[$i])) {
12437  continue;
12438  }
12439  $result = $this->single->validate($parts[$i], $config, $context);
12440  if ($result !== false) {
12441  $final .= $result . ' ';
12442  $num++;
12443  }
12444  }
12445  if ($final === '') {
12446  return false;
12447  }
12448  return rtrim($final);
12449  }
12450 }
12451 
12452 
12453 
12454 
12455 
12460 {
12461 
12466  protected $number_def;
12467 
12471  public function __construct($non_negative = false)
12472  {
12473  $this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
12474  }
12475 
12482  public function validate($string, $config, $context)
12483  {
12484  $string = $this->parseCDATA($string);
12485 
12486  if ($string === '') {
12487  return false;
12488  }
12489  $length = strlen($string);
12490  if ($length === 1) {
12491  return false;
12492  }
12493  if ($string[$length - 1] !== '%') {
12494  return false;
12495  }
12496 
12497  $number = substr($string, 0, $length - 1);
12498  $number = $this->number_def->validate($number, $config, $context);
12499 
12500  if ($number === false) {
12501  return false;
12502  }
12503  return "$number%";
12504  }
12505 }
12506 
12507 
12508 
12509 
12510 
12517 {
12518 
12525  public function validate($string, $config, $context)
12526  {
12527  static $allowed_values = array(
12528  'line-through' => true,
12529  'overline' => true,
12530  'underline' => true,
12531  );
12532 
12533  $string = strtolower($this->parseCDATA($string));
12534 
12535  if ($string === 'none') {
12536  return $string;
12537  }
12538 
12539  $parts = explode(' ', $string);
12540  $final = '';
12541  foreach ($parts as $part) {
12542  if (isset($allowed_values[$part])) {
12543  $final .= $part . ' ';
12544  }
12545  }
12546  $final = rtrim($final);
12547  if ($final === '') {
12548  return false;
12549  }
12550  return $final;
12551  }
12552 }
12553 
12554 
12555 
12556 
12557 
12568 {
12569 
12570  public function __construct()
12571  {
12572  parent::__construct(true); // always embedded
12573  }
12574 
12581  public function validate($uri_string, $config, $context)
12582  {
12583  // parse the URI out of the string and then pass it onto
12584  // the parent object
12585 
12586  $uri_string = $this->parseCDATA($uri_string);
12587  if (strpos($uri_string, 'url(') !== 0) {
12588  return false;
12589  }
12590  $uri_string = substr($uri_string, 4);
12591  if (strlen($uri_string) == 0) {
12592  return false;
12593  }
12594  $new_length = strlen($uri_string) - 1;
12595  if ($uri_string[$new_length] != ')') {
12596  return false;
12597  }
12598  $uri = trim(substr($uri_string, 0, $new_length));
12599 
12600  if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) {
12601  $quote = $uri[0];
12602  $new_length = strlen($uri) - 1;
12603  if ($uri[$new_length] !== $quote) {
12604  return false;
12605  }
12606  $uri = substr($uri, 1, $new_length - 1);
12607  }
12608 
12609  $uri = $this->expandCSSEscape($uri);
12610 
12611  $result = parent::validate($uri, $config, $context);
12612 
12613  if ($result === false) {
12614  return false;
12615  }
12616 
12617  // extra sanity check; should have been done by URI
12618  $result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result);
12619 
12620  // suspicious characters are ()'; we're going to percent encode
12621  // them for safety.
12622  $result = str_replace(array('(', ')', "'"), array('%28', '%29', '%27'), $result);
12623 
12624  // there's an extra bug where ampersands lose their escaping on
12625  // an innerHTML cycle, so a very unlucky query parameter could
12626  // then change the meaning of the URL. Unfortunately, there's
12627  // not much we can do about that...
12628  return "url(\"$result\")";
12629  }
12630 }
12631 
12632 
12633 
12634 
12635 
12640 {
12641 
12645  protected $name;
12646 
12650  public $minimized = true;
12651 
12655  public function __construct($name = false)
12656  {
12657  $this->name = $name;
12658  }
12659 
12666  public function validate($string, $config, $context)
12667  {
12668  return $this->name;
12669  }
12670 
12675  public function make($string)
12676  {
12677  return new HTMLPurifier_AttrDef_HTML_Bool($string);
12678  }
12679 }
12680 
12681 
12682 
12683 
12684 
12689 {
12690 
12697  public function validate($string, $config, $context)
12698  {
12699  $string = trim($string);
12700 
12701  // early abort: '' and '0' (strings that convert to false) are invalid
12702  if (!$string) {
12703  return false;
12704  }
12705 
12706  $tokens = $this->split($string, $config, $context);
12707  $tokens = $this->filter($tokens, $config, $context);
12708  if (empty($tokens)) {
12709  return false;
12710  }
12711  return implode(' ', $tokens);
12712  }
12713 
12721  protected function split($string, $config, $context)
12722  {
12723  // OPTIMIZABLE!
12724  // do the preg_match, capture all subpatterns for reformulation
12725 
12726  // we don't support U+00A1 and up codepoints or
12727  // escaping because I don't know how to do that with regexps
12728  // and plus it would complicate optimization efforts (you never
12729  // see that anyway).
12730  $pattern = '/(?:(?<=\s)|\A)' . // look behind for space or string start
12731  '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)' .
12732  '(?:(?=\s)|\z)/'; // look ahead for space or string end
12733  preg_match_all($pattern, $string, $matches);
12734  return $matches[1];
12735  }
12736 
12746  protected function filter($tokens, $config, $context)
12747  {
12748  return $tokens;
12749  }
12750 }
12751 
12752 
12753 
12754 
12755 
12760 {
12767  protected function split($string, $config, $context)
12768  {
12769  // really, this twiddle should be lazy loaded
12770  $name = $config->getDefinition('HTML')->doctype->name;
12771  if ($name == "XHTML 1.1" || $name == "XHTML 2.0") {
12772  return parent::split($string, $config, $context);
12773  } else {
12774  return preg_split('/\s+/', $string);
12775  }
12776  }
12777 
12784  protected function filter($tokens, $config, $context)
12785  {
12786  $allowed = $config->get('Attr.AllowedClasses');
12787  $forbidden = $config->get('Attr.ForbiddenClasses');
12788  $ret = array();
12789  foreach ($tokens as $token) {
12790  if (($allowed === null || isset($allowed[$token])) &&
12791  !isset($forbidden[$token]) &&
12792  // We need this O(n) check because of PHP's array
12793  // implementation that casts -0 to 0.
12794  !in_array($token, $ret, true)
12795  ) {
12796  $ret[] = $token;
12797  }
12798  }
12799  return $ret;
12800  }
12801 }
12802 
12803 
12804 
12809 {
12810 
12817  public function validate($string, $config, $context)
12818  {
12819  static $colors = null;
12820  if ($colors === null) {
12821  $colors = $config->get('Core.ColorKeywords');
12822  }
12823 
12824  $string = trim($string);
12825 
12826  if (empty($string)) {
12827  return false;
12828  }
12829  $lower = strtolower($string);
12830  if (isset($colors[$lower])) {
12831  return $colors[$lower];
12832  }
12833  if ($string[0] === '#') {
12834  $hex = substr($string, 1);
12835  } else {
12836  $hex = $string;
12837  }
12838 
12839  $length = strlen($hex);
12840  if ($length !== 3 && $length !== 6) {
12841  return false;
12842  }
12843  if (!ctype_xdigit($hex)) {
12844  return false;
12845  }
12846  if ($length === 3) {
12847  $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
12848  }
12849  return "#$hex";
12850  }
12851 }
12852 
12853 
12854 
12855 
12856 
12861 {
12862 
12866  public $valid_values = false; // uninitialized value
12867 
12871  protected $case_sensitive = false;
12872 
12873  public function __construct()
12874  {
12875  }
12876 
12883  public function validate($string, $config, $context)
12884  {
12885  if ($this->valid_values === false) {
12886  $this->valid_values = $config->get('Attr.AllowedFrameTargets');
12887  }
12888  return parent::validate($string, $config, $context);
12889  }
12890 }
12891 
12892 
12893 
12894 
12895 
12906 {
12907 
12908  // selector is NOT a valid thing to use for IDREFs, because IDREFs
12909  // *must* target IDs that exist, whereas selector #ids do not.
12910 
12916  protected $selector;
12917 
12921  public function __construct($selector = false)
12922  {
12923  $this->selector = $selector;
12924  }
12925 
12932  public function validate($id, $config, $context)
12933  {
12934  if (!$this->selector && !$config->get('Attr.EnableID')) {
12935  return false;
12936  }
12937 
12938  $id = trim($id); // trim it first
12939 
12940  if ($id === '') {
12941  return false;
12942  }
12943 
12944  $prefix = $config->get('Attr.IDPrefix');
12945  if ($prefix !== '') {
12946  $prefix .= $config->get('Attr.IDPrefixLocal');
12947  // prevent re-appending the prefix
12948  if (strpos($id, $prefix) !== 0) {
12949  $id = $prefix . $id;
12950  }
12951  } elseif ($config->get('Attr.IDPrefixLocal') !== '') {
12952  trigger_error(
12953  '%Attr.IDPrefixLocal cannot be used unless ' .
12954  '%Attr.IDPrefix is set',
12955  E_USER_WARNING
12956  );
12957  }
12958 
12959  if (!$this->selector) {
12960  $id_accumulator =& $context->get('IDAccumulator');
12961  if (isset($id_accumulator->ids[$id])) {
12962  return false;
12963  }
12964  }
12965 
12966  // we purposely avoid using regex, hopefully this is faster
12967 
12968  if ($config->get('Attr.ID.HTML5') === true) {
12969  if (preg_match('/[\t\n\x0b\x0c ]/', $id)) {
12970  return false;
12971  }
12972  } else {
12973  if (ctype_alpha($id)) {
12974  // OK
12975  } else {
12976  if (!ctype_alpha(@$id[0])) {
12977  return false;
12978  }
12979  // primitive style of regexps, I suppose
12980  $trim = trim(
12981  $id,
12982  'A..Za..z0..9:-._'
12983  );
12984  if ($trim !== '') {
12985  return false;
12986  }
12987  }
12988  }
12989 
12990  $regexp = $config->get('Attr.IDBlacklistRegexp');
12991  if ($regexp && preg_match($regexp, $id)) {
12992  return false;
12993  }
12994 
12995  if (!$this->selector) {
12996  $id_accumulator->add($id);
12997  }
12998 
12999  // if no change was made to the ID, return the result
13000  // else, return the new id if stripping whitespace made it
13001  // valid, or return false.
13002  return $id;
13003  }
13004 }
13005 
13006 
13007 
13008 
13009 
13014 {
13015 
13019  protected $max;
13020 
13024  public function __construct($max = null)
13025  {
13026  $this->max = $max;
13027  }
13028 
13035  public function validate($string, $config, $context)
13036  {
13037  $string = trim($string);
13038  if ($string === '0') {
13039  return $string;
13040  }
13041  if ($string === '') {
13042  return false;
13043  }
13044  $length = strlen($string);
13045  if (substr($string, $length - 2) == 'px') {
13046  $string = substr($string, 0, $length - 2);
13047  }
13048  if (!is_numeric($string)) {
13049  return false;
13050  }
13051  $int = (int)$string;
13052 
13053  if ($int < 0) {
13054  return '0';
13055  }
13056 
13057  // upper-bound value, extremely high values can
13058  // crash operating systems, see <http://ha.ckers.org/imagecrash.html>
13059  // WARNING, above link WILL crash you if you're using Windows
13060 
13061  if ($this->max !== null && $int > $this->max) {
13062  return (string)$this->max;
13063  }
13064  return (string)$int;
13065  }
13066 
13071  public function make($string)
13072  {
13073  if ($string === '') {
13074  $max = null;
13075  } else {
13076  $max = (int)$string;
13077  }
13078  $class = get_class($this);
13079  return new $class($max);
13080  }
13081 }
13082 
13083 
13084 
13085 
13086 
13095 {
13096 
13103  public function validate($string, $config, $context)
13104  {
13105  $string = trim($string);
13106  if ($string === '') {
13107  return false;
13108  }
13109 
13110  $parent_result = parent::validate($string, $config, $context);
13111  if ($parent_result !== false) {
13112  return $parent_result;
13113  }
13114 
13115  $length = strlen($string);
13116  $last_char = $string[$length - 1];
13117 
13118  if ($last_char !== '%') {
13119  return false;
13120  }
13121 
13122  $points = substr($string, 0, $length - 1);
13123 
13124  if (!is_numeric($points)) {
13125  return false;
13126  }
13127 
13128  $points = (int)$points;
13129 
13130  if ($points < 0) {
13131  return '0%';
13132  }
13133  if ($points > 100) {
13134  return '100%';
13135  }
13136  return ((string)$points) . '%';
13137  }
13138 }
13139 
13140 
13141 
13142 
13143 
13151 {
13152 
13157  protected $name;
13158 
13162  public function __construct($name)
13163  {
13164  $configLookup = array(
13165  'rel' => 'AllowedRel',
13166  'rev' => 'AllowedRev'
13167  );
13168  if (!isset($configLookup[$name])) {
13169  trigger_error(
13170  'Unrecognized attribute name for link ' .
13171  'relationship.',
13172  E_USER_ERROR
13173  );
13174  return;
13175  }
13176  $this->name = $configLookup[$name];
13177  }
13178 
13185  public function validate($string, $config, $context)
13186  {
13187  $allowed = $config->get('Attr.' . $this->name);
13188  if (empty($allowed)) {
13189  return false;
13190  }
13191 
13192  $string = $this->parseCDATA($string);
13193  $parts = explode(' ', $string);
13194 
13195  // lookup to prevent duplicates
13196  $ret_lookup = array();
13197  foreach ($parts as $part) {
13198  $part = strtolower(trim($part));
13199  if (!isset($allowed[$part])) {
13200  continue;
13201  }
13202  $ret_lookup[$part] = true;
13203  }
13204 
13205  if (empty($ret_lookup)) {
13206  return false;
13207  }
13208  $string = implode(' ', array_keys($ret_lookup));
13209  return $string;
13210  }
13211 }
13212 
13213 
13214 
13215 
13216 
13224 {
13225 
13232  public function validate($string, $config, $context)
13233  {
13234  $string = trim($string);
13235  if ($string === '') {
13236  return false;
13237  }
13238 
13239  $parent_result = parent::validate($string, $config, $context);
13240  if ($parent_result !== false) {
13241  return $parent_result;
13242  }
13243 
13244  $length = strlen($string);
13245  $last_char = $string[$length - 1];
13246 
13247  if ($last_char !== '*') {
13248  return false;
13249  }
13250 
13251  $int = substr($string, 0, $length - 1);
13252 
13253  if ($int == '') {
13254  return '*';
13255  }
13256  if (!is_numeric($int)) {
13257  return false;
13258  }
13259 
13260  $int = (int)$int;
13261  if ($int < 0) {
13262  return false;
13263  }
13264  if ($int == 0) {
13265  return '0';
13266  }
13267  if ($int == 1) {
13268  return '*';
13269  }
13270  return ((string)$int) . '*';
13271  }
13272 }
13273 
13274 
13275 
13276 
13277 
13279 {
13280 
13286  public function unpack($string)
13287  {
13288  // needs to be implemented
13289  }
13290 
13291 }
13292 
13293 // sub-implementations
13294 
13295 
13296 
13297 
13298 
13303 {
13304 
13309  protected $ipv4;
13310 
13315  protected $ipv6;
13316 
13317  public function __construct()
13318  {
13319  $this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4();
13320  $this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6();
13321  }
13322 
13329  public function validate($string, $config, $context)
13330  {
13331  $length = strlen($string);
13332  // empty hostname is OK; it's usually semantically equivalent:
13333  // the default host as defined by a URI scheme is used:
13334  //
13335  // If the URI scheme defines a default for host, then that
13336  // default applies when the host subcomponent is undefined
13337  // or when the registered name is empty (zero length).
13338  if ($string === '') {
13339  return '';
13340  }
13341  if ($length > 1 && $string[0] === '[' && $string[$length - 1] === ']') {
13342  //IPv6
13343  $ip = substr($string, 1, $length - 2);
13344  $valid = $this->ipv6->validate($ip, $config, $context);
13345  if ($valid === false) {
13346  return false;
13347  }
13348  return '[' . $valid . ']';
13349  }
13350 
13351  // need to do checks on unusual encodings too
13352  $ipv4 = $this->ipv4->validate($string, $config, $context);
13353  if ($ipv4 !== false) {
13354  return $ipv4;
13355  }
13356 
13357  // A regular domain name.
13358 
13359  // This doesn't match I18N domain names, but we don't have proper IRI support,
13360  // so force users to insert Punycode.
13361 
13362  // There is not a good sense in which underscores should be
13363  // allowed, since it's technically not! (And if you go as
13364  // far to allow everything as specified by the DNS spec...
13365  // well, that's literally everything, modulo some space limits
13366  // for the components and the overall name (which, by the way,
13367  // we are NOT checking!). So we (arbitrarily) decide this:
13368  // let's allow underscores wherever we would have allowed
13369  // hyphens, if they are enabled. This is a pretty good match
13370  // for browser behavior, for example, a large number of browsers
13371  // cannot handle foo_.example.com, but foo_bar.example.com is
13372  // fairly well supported.
13373  $underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
13374 
13375  // Based off of RFC 1738, but amended so that
13376  // as per RFC 3696, the top label need only not be all numeric.
13377  // The productions describing this are:
13378  $a = '[a-z]'; // alpha
13379  $an = '[a-z0-9]'; // alphanum
13380  $and = "[a-z0-9-$underscore]"; // alphanum | "-"
13381  // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
13382  $domainlabel = "$an(?:$and*$an)?";
13383  // AMENDED as per RFC 3696
13384  // toplabel = alphanum | alphanum *( alphanum | "-" ) alphanum
13385  // side condition: not all numeric
13386  $toplabel = "$an(?:$and*$an)?";
13387  // hostname = *( domainlabel "." ) toplabel [ "." ]
13388  if (preg_match("/^(?:$domainlabel\.)*($toplabel)\.?$/i", $string, $matches)) {
13389  if (!ctype_digit($matches[1])) {
13390  return $string;
13391  }
13392  }
13393 
13394  // PHP 5.3 and later support this functionality natively
13395  if (function_exists('idn_to_ascii')) {
13396  if (defined('IDNA_NONTRANSITIONAL_TO_ASCII') && defined('INTL_IDNA_VARIANT_UTS46')) {
13397  $string = idn_to_ascii($string, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
13398  } else {
13399  $string = idn_to_ascii($string);
13400  }
13401 
13402  // If we have Net_IDNA2 support, we can support IRIs by
13403  // punycoding them. (This is the most portable thing to do,
13404  // since otherwise we have to assume browsers support
13405  } elseif ($config->get('Core.EnableIDNA')) {
13406  $idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true));
13407  // we need to encode each period separately
13408  $parts = explode('.', $string);
13409  try {
13410  $new_parts = array();
13411  foreach ($parts as $part) {
13412  $encodable = false;
13413  for ($i = 0, $c = strlen($part); $i < $c; $i++) {
13414  if (ord($part[$i]) > 0x7a) {
13415  $encodable = true;
13416  break;
13417  }
13418  }
13419  if (!$encodable) {
13420  $new_parts[] = $part;
13421  } else {
13422  $new_parts[] = $idna->encode($part);
13423  }
13424  }
13425  $string = implode('.', $new_parts);
13426  } catch (Exception $e) {
13427  // XXX error reporting
13428  }
13429  }
13430  // Try again
13431  if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
13432  return $string;
13433  }
13434  return false;
13435  }
13436 }
13437 
13438 
13439 
13440 
13441 
13447 {
13448 
13453  protected $ip4;
13454 
13461  public function validate($aIP, $config, $context)
13462  {
13463  if (!$this->ip4) {
13464  $this->_loadRegex();
13465  }
13466 
13467  if (preg_match('#^' . $this->ip4 . '$#s', $aIP)) {
13468  return $aIP;
13469  }
13470  return false;
13471  }
13472 
13477  protected function _loadRegex()
13478  {
13479  $oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255
13480  $this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})";
13481  }
13482 }
13483 
13484 
13485 
13486 
13487 
13495 {
13496 
13503  public function validate($aIP, $config, $context)
13504  {
13505  if (!$this->ip4) {
13506  $this->_loadRegex();
13507  }
13508 
13509  $original = $aIP;
13510 
13511  $hex = '[0-9a-fA-F]';
13512  $blk = '(?:' . $hex . '{1,4})';
13513  $pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128
13514 
13515  // prefix check
13516  if (strpos($aIP, '/') !== false) {
13517  if (preg_match('#' . $pre . '$#s', $aIP, $find)) {
13518  $aIP = substr($aIP, 0, 0 - strlen($find[0]));
13519  unset($find);
13520  } else {
13521  return false;
13522  }
13523  }
13524 
13525  // IPv4-compatiblity check
13526  if (preg_match('#(?<=:' . ')' . $this->ip4 . '$#s', $aIP, $find)) {
13527  $aIP = substr($aIP, 0, 0 - strlen($find[0]));
13528  $ip = explode('.', $find[0]);
13529  $ip = array_map('dechex', $ip);
13530  $aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3];
13531  unset($find, $ip);
13532  }
13533 
13534  // compression check
13535  $aIP = explode('::', $aIP);
13536  $c = count($aIP);
13537  if ($c > 2) {
13538  return false;
13539  } elseif ($c == 2) {
13540  list($first, $second) = $aIP;
13541  $first = explode(':', $first);
13542  $second = explode(':', $second);
13543 
13544  if (count($first) + count($second) > 8) {
13545  return false;
13546  }
13547 
13548  while (count($first) < 8) {
13549  array_push($first, '0');
13550  }
13551 
13552  array_splice($first, 8 - count($second), 8, $second);
13553  $aIP = $first;
13554  unset($first, $second);
13555  } else {
13556  $aIP = explode(':', $aIP[0]);
13557  }
13558  $c = count($aIP);
13559 
13560  if ($c != 8) {
13561  return false;
13562  }
13563 
13564  // All the pieces should be 16-bit hex strings. Are they?
13565  foreach ($aIP as $piece) {
13566  if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece))) {
13567  return false;
13568  }
13569  }
13570  return $original;
13571  }
13572 }
13573 
13574 
13575 
13576 
13577 
13583 {
13584 
13591  public function validate($string, $config, $context)
13592  {
13593  // no support for named mailboxes i.e. "Bob <bob@example.com>"
13594  // that needs more percent encoding to be done
13595  if ($string == '') {
13596  return false;
13597  }
13598  $string = trim($string);
13599  $result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string);
13600  return $result ? $string : false;
13601  }
13602 }
13603 
13604 
13605 
13606 
13607 
13612 {
13619  public function transform($attr, $config, $context)
13620  {
13621  if (!isset($attr['background'])) {
13622  return $attr;
13623  }
13624 
13625  $background = $this->confiscateAttr($attr, 'background');
13626  // some validation should happen here
13627 
13628  $this->prependCSS($attr, "background-image:url($background);");
13629  return $attr;
13630  }
13631 }
13632 
13633 
13634 
13635 
13636 
13637 // this MUST be placed in post, as it assumes that any value in dir is valid
13638 
13643 {
13644 
13651  public function transform($attr, $config, $context)
13652  {
13653  if (isset($attr['dir'])) {
13654  return $attr;
13655  }
13656  $attr['dir'] = $config->get('Attr.DefaultTextDir');
13657  return $attr;
13658  }
13659 }
13660 
13661 
13662 
13663 
13664 
13669 {
13676  public function transform($attr, $config, $context)
13677  {
13678  if (!isset($attr['bgcolor'])) {
13679  return $attr;
13680  }
13681 
13682  $bgcolor = $this->confiscateAttr($attr, 'bgcolor');
13683  // some validation should happen here
13684 
13685  $this->prependCSS($attr, "background-color:$bgcolor;");
13686  return $attr;
13687  }
13688 }
13689 
13690 
13691 
13692 
13693 
13698 {
13703  protected $attr;
13704 
13709  protected $css;
13710 
13715  public function __construct($attr, $css)
13716  {
13717  $this->attr = $attr;
13718  $this->css = $css;
13719  }
13720 
13727  public function transform($attr, $config, $context)
13728  {
13729  if (!isset($attr[$this->attr])) {
13730  return $attr;
13731  }
13732  unset($attr[$this->attr]);
13733  $this->prependCSS($attr, $this->css);
13734  return $attr;
13735  }
13736 }
13737 
13738 
13739 
13740 
13741 
13746 {
13753  public function transform($attr, $config, $context)
13754  {
13755  if (!isset($attr['border'])) {
13756  return $attr;
13757  }
13758  $border_width = $this->confiscateAttr($attr, 'border');
13759  // some validation should happen here
13760  $this->prependCSS($attr, "border:{$border_width}px solid;");
13761  return $attr;
13762  }
13763 }
13764 
13765 
13766 
13767 
13768 
13774 {
13779  protected $attr;
13780 
13785  protected $enumToCSS = array();
13786 
13793  protected $caseSensitive = false;
13794 
13800  public function __construct($attr, $enum_to_css, $case_sensitive = false)
13801  {
13802  $this->attr = $attr;
13803  $this->enumToCSS = $enum_to_css;
13804  $this->caseSensitive = (bool)$case_sensitive;
13805  }
13806 
13813  public function transform($attr, $config, $context)
13814  {
13815  if (!isset($attr[$this->attr])) {
13816  return $attr;
13817  }
13818 
13819  $value = trim($attr[$this->attr]);
13820  unset($attr[$this->attr]);
13821 
13822  if (!$this->caseSensitive) {
13823  $value = strtolower($value);
13824  }
13825 
13826  if (!isset($this->enumToCSS[$value])) {
13827  return $attr;
13828  }
13829  $this->prependCSS($attr, $this->enumToCSS[$value]);
13830  return $attr;
13831  }
13832 }
13833 
13834 
13835 
13836 
13837 
13838 // must be called POST validation
13839 
13847 {
13848 
13855  public function transform($attr, $config, $context)
13856  {
13857  $src = true;
13858  if (!isset($attr['src'])) {
13859  if ($config->get('Core.RemoveInvalidImg')) {
13860  return $attr;
13861  }
13862  $attr['src'] = $config->get('Attr.DefaultInvalidImage');
13863  $src = false;
13864  }
13865 
13866  if (!isset($attr['alt'])) {
13867  if ($src) {
13868  $alt = $config->get('Attr.DefaultImageAlt');
13869  if ($alt === null) {
13870  $attr['alt'] = basename($attr['src']);
13871  } else {
13872  $attr['alt'] = $alt;
13873  }
13874  } else {
13875  $attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt');
13876  }
13877  }
13878  return $attr;
13879  }
13880 }
13881 
13882 
13883 
13884 
13885 
13890 {
13894  protected $attr;
13895 
13899  protected $css = array(
13900  'hspace' => array('left', 'right'),
13901  'vspace' => array('top', 'bottom')
13902  );
13903 
13907  public function __construct($attr)
13908  {
13909  $this->attr = $attr;
13910  if (!isset($this->css[$attr])) {
13911  trigger_error(htmlspecialchars($attr) . ' is not valid space attribute');
13912  }
13913  }
13914 
13921  public function transform($attr, $config, $context)
13922  {
13923  if (!isset($attr[$this->attr])) {
13924  return $attr;
13925  }
13926 
13927  $width = $this->confiscateAttr($attr, $this->attr);
13928  // some validation could happen here
13929 
13930  if (!isset($this->css[$this->attr])) {
13931  return $attr;
13932  }
13933 
13934  $style = '';
13935  foreach ($this->css[$this->attr] as $suffix) {
13936  $property = "margin-$suffix";
13937  $style .= "$property:{$width}px;";
13938  }
13939  $this->prependCSS($attr, $style);
13940  return $attr;
13941  }
13942 }
13943 
13944 
13945 
13946 
13947 
13953 {
13957  protected $pixels;
13958 
13959  public function __construct()
13960  {
13961  $this->pixels = new HTMLPurifier_AttrDef_HTML_Pixels();
13962  }
13963 
13970  public function transform($attr, $config, $context)
13971  {
13972  if (!isset($attr['type'])) {
13973  $t = 'text';
13974  } else {
13975  $t = strtolower($attr['type']);
13976  }
13977  if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') {
13978  unset($attr['checked']);
13979  }
13980  if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') {
13981  unset($attr['maxlength']);
13982  }
13983  if (isset($attr['size']) && $t !== 'text' && $t !== 'password') {
13984  $result = $this->pixels->validate($attr['size'], $config, $context);
13985  if ($result === false) {
13986  unset($attr['size']);
13987  } else {
13988  $attr['size'] = $result;
13989  }
13990  }
13991  if (isset($attr['src']) && $t !== 'image') {
13992  unset($attr['src']);
13993  }
13994  if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) {
13995  $attr['value'] = '';
13996  }
13997  return $attr;
13998  }
13999 }
14000 
14001 
14002 
14003 
14004 
14011 {
14012 
14019  public function transform($attr, $config, $context)
14020  {
14021  $lang = isset($attr['lang']) ? $attr['lang'] : false;
14022  $xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;
14023 
14024  if ($lang !== false && $xml_lang === false) {
14025  $attr['xml:lang'] = $lang;
14026  } elseif ($xml_lang !== false) {
14027  $attr['lang'] = $xml_lang;
14028  }
14029  return $attr;
14030  }
14031 }
14032 
14033 
14034 
14035 
14036 
14041 {
14042 
14046  protected $name;
14047 
14051  protected $cssName;
14052 
14053  public function __construct($name, $css_name = null)
14054  {
14055  $this->name = $name;
14056  $this->cssName = $css_name ? $css_name : $name;
14057  }
14058 
14065  public function transform($attr, $config, $context)
14066  {
14067  if (!isset($attr[$this->name])) {
14068  return $attr;
14069  }
14070  $length = $this->confiscateAttr($attr, $this->name);
14071  if (ctype_digit($length)) {
14072  $length .= 'px';
14073  }
14074  $this->prependCSS($attr, $this->cssName . ":$length;");
14075  return $attr;
14076  }
14077 }
14078 
14079 
14080 
14081 
14082 
14087 {
14088 
14095  public function transform($attr, $config, $context)
14096  {
14097  // Abort early if we're using relaxed definition of name
14098  if ($config->get('HTML.Attr.Name.UseCDATA')) {
14099  return $attr;
14100  }
14101  if (!isset($attr['name'])) {
14102  return $attr;
14103  }
14104  $id = $this->confiscateAttr($attr, 'name');
14105  if (isset($attr['id'])) {
14106  return $attr;
14107  }
14108  $attr['id'] = $id;
14109  return $attr;
14110  }
14111 }
14112 
14113 
14114 
14115 
14116 
14123 {
14124 
14125  public function __construct()
14126  {
14127  $this->idDef = new HTMLPurifier_AttrDef_HTML_ID();
14128  }
14129 
14136  public function transform($attr, $config, $context)
14137  {
14138  if (!isset($attr['name'])) {
14139  return $attr;
14140  }
14141  $name = $attr['name'];
14142  if (isset($attr['id']) && $attr['id'] === $name) {
14143  return $attr;
14144  }
14145  $result = $this->idDef->validate($name, $config, $context);
14146  if ($result === false) {
14147  unset($attr['name']);
14148  } else {
14149  $attr['name'] = $result;
14150  }
14151  return $attr;
14152  }
14153 }
14154 
14155 
14156 
14157 
14158 
14159 // must be called POST validation
14160 
14166 {
14170  private $parser;
14171 
14172  public function __construct()
14173  {
14174  $this->parser = new HTMLPurifier_URIParser();
14175  }
14176 
14183  public function transform($attr, $config, $context)
14184  {
14185  if (!isset($attr['href'])) {
14186  return $attr;
14187  }
14188 
14189  // XXX Kind of inefficient
14190  $url = $this->parser->parse($attr['href']);
14191  $scheme = $url->getSchemeObj($config, $context);
14192 
14193  if ($scheme->browsable && !$url->isLocal($config, $context)) {
14194  if (isset($attr['rel'])) {
14195  $rels = explode(' ', $attr['rel']);
14196  if (!in_array('nofollow', $rels)) {
14197  $rels[] = 'nofollow';
14198  }
14199  $attr['rel'] = implode(' ', $rels);
14200  } else {
14201  $attr['rel'] = 'nofollow';
14202  }
14203  }
14204  return $attr;
14205  }
14206 }
14207 
14208 
14209 
14210 
14211 
14213 {
14217  public $name = "SafeEmbed";
14218 
14225  public function transform($attr, $config, $context)
14226  {
14227  $attr['allowscriptaccess'] = 'never';
14228  $attr['allownetworking'] = 'internal';
14229  $attr['type'] = 'application/x-shockwave-flash';
14230  return $attr;
14231  }
14232 }
14233 
14234 
14235 
14236 
14237 
14242 {
14246  public $name = "SafeObject";
14247 
14254  public function transform($attr, $config, $context)
14255  {
14256  if (!isset($attr['type'])) {
14257  $attr['type'] = 'application/x-shockwave-flash';
14258  }
14259  return $attr;
14260  }
14261 }
14262 
14263 
14264 
14265 
14266 
14280 {
14284  public $name = "SafeParam";
14285 
14289  private $uri;
14290 
14291  public function __construct()
14292  {
14293  $this->uri = new HTMLPurifier_AttrDef_URI(true); // embedded
14294  $this->wmode = new HTMLPurifier_AttrDef_Enum(array('window', 'opaque', 'transparent'));
14295  }
14296 
14303  public function transform($attr, $config, $context)
14304  {
14305  // If we add support for other objects, we'll need to alter the
14306  // transforms.
14307  switch ($attr['name']) {
14308  // application/x-shockwave-flash
14309  // Keep this synchronized with Injector/SafeObject.php
14310  case 'allowScriptAccess':
14311  $attr['value'] = 'never';
14312  break;
14313  case 'allowNetworking':
14314  $attr['value'] = 'internal';
14315  break;
14316  case 'allowFullScreen':
14317  if ($config->get('HTML.FlashAllowFullScreen')) {
14318  $attr['value'] = ($attr['value'] == 'true') ? 'true' : 'false';
14319  } else {
14320  $attr['value'] = 'false';
14321  }
14322  break;
14323  case 'wmode':
14324  $attr['value'] = $this->wmode->validate($attr['value'], $config, $context);
14325  break;
14326  case 'movie':
14327  case 'src':
14328  $attr['name'] = "movie";
14329  $attr['value'] = $this->uri->validate($attr['value'], $config, $context);
14330  break;
14331  case 'flashvars':
14332  // we're going to allow arbitrary inputs to the SWF, on
14333  // the reasoning that it could only hack the SWF, not us.
14334  break;
14335  // add other cases to support other param name/value pairs
14336  default:
14337  $attr['name'] = $attr['value'] = null;
14338  }
14339  return $attr;
14340  }
14341 }
14342 
14343 
14344 
14345 
14346 
14351 {
14358  public function transform($attr, $config, $context)
14359  {
14360  if (!isset($attr['type'])) {
14361  $attr['type'] = 'text/javascript';
14362  }
14363  return $attr;
14364  }
14365 }
14366 
14367 
14368 
14369 
14370 
14371 // must be called POST validation
14372 
14379 {
14383  private $parser;
14384 
14385  public function __construct()
14386  {
14387  $this->parser = new HTMLPurifier_URIParser();
14388  }
14389 
14396  public function transform($attr, $config, $context)
14397  {
14398  if (!isset($attr['href'])) {
14399  return $attr;
14400  }
14401 
14402  // XXX Kind of inefficient
14403  $url = $this->parser->parse($attr['href']);
14404  $scheme = $url->getSchemeObj($config, $context);
14405 
14406  if ($scheme->browsable && !$url->isBenign($config, $context)) {
14407  $attr['target'] = '_blank';
14408  }
14409  return $attr;
14410  }
14411 }
14412 
14413 
14414 
14415 
14416 
14417 // must be called POST validation
14418 
14427 {
14434  public function transform($attr, $config, $context)
14435  {
14436  if (isset($attr['rel'])) {
14437  $rels = explode(' ', $attr['rel']);
14438  } else {
14439  $rels = array();
14440  }
14441  if (isset($attr['target']) && !in_array('noopener', $rels)) {
14442  $rels[] = 'noopener';
14443  }
14444  if (!empty($rels) || isset($attr['rel'])) {
14445  $attr['rel'] = implode(' ', $rels);
14446  }
14447 
14448  return $attr;
14449  }
14450 }
14451 
14452 
14453 
14454 
14455 // must be called POST validation
14456 
14465 {
14472  public function transform($attr, $config, $context)
14473  {
14474  if (isset($attr['rel'])) {
14475  $rels = explode(' ', $attr['rel']);
14476  } else {
14477  $rels = array();
14478  }
14479  if (isset($attr['target']) && !in_array('noreferrer', $rels)) {
14480  $rels[] = 'noreferrer';
14481  }
14482  if (!empty($rels) || isset($attr['rel'])) {
14483  $attr['rel'] = implode(' ', $rels);
14484  }
14485 
14486  return $attr;
14487  }
14488 }
14489 
14490 
14491 
14492 
14497 {
14504  public function transform($attr, $config, $context)
14505  {
14506  // Calculated from Firefox
14507  if (!isset($attr['cols'])) {
14508  $attr['cols'] = '22';
14509  }
14510  if (!isset($attr['rows'])) {
14511  $attr['rows'] = '3';
14512  }
14513  return $attr;
14514  }
14515 }
14516 
14517 
14518 
14519 
14520 
14531 {
14532 
14537  public $inline;
14538 
14543  public $block;
14544 
14548  public $type = 'chameleon';
14549 
14554  public function __construct($inline, $block)
14555  {
14556  $this->inline = new HTMLPurifier_ChildDef_Optional($inline);
14557  $this->block = new HTMLPurifier_ChildDef_Optional($block);
14558  $this->elements = $this->block->elements;
14559  }
14560 
14567  public function validateChildren($children, $config, $context)
14568  {
14569  if ($context->get('IsInline') === false) {
14570  return $this->block->validateChildren(
14571  $children,
14572  $config,
14573  $context
14574  );
14575  } else {
14576  return $this->inline->validateChildren(
14577  $children,
14578  $config,
14579  $context
14580  );
14581  }
14582  }
14583 }
14584 
14585 
14586 
14587 
14588 
14596 {
14600  public $type = 'custom';
14601 
14605  public $allow_empty = false;
14606 
14611  public $dtd_regex;
14612 
14617  private $_pcre_regex;
14618 
14622  public function __construct($dtd_regex)
14623  {
14624  $this->dtd_regex = $dtd_regex;
14625  $this->_compileRegex();
14626  }
14627 
14631  protected function _compileRegex()
14632  {
14633  $raw = str_replace(' ', '', $this->dtd_regex);
14634  if ($raw[0] != '(') {
14635  $raw = "($raw)";
14636  }
14637  $el = '[#a-zA-Z0-9_.-]+';
14638  $reg = $raw;
14639 
14640  // COMPLICATED! AND MIGHT BE BUGGY! I HAVE NO CLUE WHAT I'M
14641  // DOING! Seriously: if there's problems, please report them.
14642 
14643  // collect all elements into the $elements array
14644  preg_match_all("/$el/", $reg, $matches);
14645  foreach ($matches[0] as $match) {
14646  $this->elements[$match] = true;
14647  }
14648 
14649  // setup all elements as parentheticals with leading commas
14650  $reg = preg_replace("/$el/", '(,\\0)', $reg);
14651 
14652  // remove commas when they were not solicited
14653  $reg = preg_replace("/([^,(|]\‍(+),/", '\\1', $reg);
14654 
14655  // remove all non-paranthetical commas: they are handled by first regex
14656  $reg = preg_replace("/,\‍(/", '(', $reg);
14657 
14658  $this->_pcre_regex = $reg;
14659  }
14660 
14667  public function validateChildren($children, $config, $context)
14668  {
14669  $list_of_children = '';
14670  $nesting = 0; // depth into the nest
14671  foreach ($children as $node) {
14672  if (!empty($node->is_whitespace)) {
14673  continue;
14674  }
14675  $list_of_children .= $node->name . ',';
14676  }
14677  // add leading comma to deal with stray comma declarations
14678  $list_of_children = ',' . rtrim($list_of_children, ',');
14679  $okay =
14680  preg_match(
14681  '/^,?' . $this->_pcre_regex . '$/',
14682  $list_of_children
14683  );
14684  return (bool)$okay;
14685  }
14686 }
14687 
14688 
14689 
14690 
14691 
14700 {
14704  public $allow_empty = true;
14705 
14709  public $type = 'empty';
14710 
14711  public function __construct()
14712  {
14713  }
14714 
14721  public function validateChildren($children, $config, $context)
14722  {
14723  return array();
14724  }
14725 }
14726 
14727 
14728 
14729 
14730 
14741 {
14745  public $type = 'list';
14749  // lying a little bit, so that we can handle ul and ol ourselves
14750  // XXX: This whole business with 'wrap' is all a bit unsatisfactory
14751  public $elements = array('li' => true, 'ul' => true, 'ol' => true);
14752 
14759  public function validateChildren($children, $config, $context)
14760  {
14761  // Flag for subclasses
14762  $this->whitespace = false;
14763 
14764  // if there are no tokens, delete parent node
14765  if (empty($children)) {
14766  return false;
14767  }
14768 
14769  // if li is not allowed, delete parent node
14770  if (!isset($config->getHTMLDefinition()->info['li'])) {
14771  trigger_error("Cannot allow ul/ol without allowing li", E_USER_WARNING);
14772  return false;
14773  }
14774 
14775  // the new set of children
14776  $result = array();
14777 
14778  // a little sanity check to make sure it's not ALL whitespace
14779  $all_whitespace = true;
14780 
14781  $current_li = null;
14782 
14783  foreach ($children as $node) {
14784  if (!empty($node->is_whitespace)) {
14785  $result[] = $node;
14786  continue;
14787  }
14788  $all_whitespace = false; // phew, we're not talking about whitespace
14789 
14790  if ($node->name === 'li') {
14791  // good
14792  $current_li = $node;
14793  $result[] = $node;
14794  } else {
14795  // we want to tuck this into the previous li
14796  // Invariant: we expect the node to be ol/ul
14797  // ToDo: Make this more robust in the case of not ol/ul
14798  // by distinguishing between existing li and li created
14799  // to handle non-list elements; non-list elements should
14800  // not be appended to an existing li; only li created
14801  // for non-list. This distinction is not currently made.
14802  if ($current_li === null) {
14803  $current_li = new HTMLPurifier_Node_Element('li');
14804  $result[] = $current_li;
14805  }
14806  $current_li->children[] = $node;
14807  $current_li->empty = false; // XXX fascinating! Check for this error elsewhere ToDo
14808  }
14809  }
14810  if (empty($result)) {
14811  return false;
14812  }
14813  if ($all_whitespace) {
14814  return false;
14815  }
14816  return $result;
14817  }
14818 }
14819 
14820 
14821 
14822 
14823 
14828 {
14833  public $elements = array();
14834 
14839  protected $whitespace = false;
14840 
14844  public function __construct($elements)
14845  {
14846  if (is_string($elements)) {
14847  $elements = str_replace(' ', '', $elements);
14848  $elements = explode('|', $elements);
14849  }
14850  $keys = array_keys($elements);
14851  if ($keys == array_keys($keys)) {
14852  $elements = array_flip($elements);
14853  foreach ($elements as $i => $x) {
14854  $elements[$i] = true;
14855  if (empty($i)) {
14856  unset($elements[$i]);
14857  } // remove blank
14858  }
14859  }
14860  $this->elements = $elements;
14861  }
14862 
14866  public $allow_empty = false;
14867 
14871  public $type = 'required';
14872 
14879  public function validateChildren($children, $config, $context)
14880  {
14881  // Flag for subclasses
14882  $this->whitespace = false;
14883 
14884  // if there are no tokens, delete parent node
14885  if (empty($children)) {
14886  return false;
14887  }
14888 
14889  // the new set of children
14890  $result = array();
14891 
14892  // whether or not parsed character data is allowed
14893  // this controls whether or not we silently drop a tag
14894  // or generate escaped HTML from it
14895  $pcdata_allowed = isset($this->elements['#PCDATA']);
14896 
14897  // a little sanity check to make sure it's not ALL whitespace
14898  $all_whitespace = true;
14899 
14900  $stack = array_reverse($children);
14901  while (!empty($stack)) {
14902  $node = array_pop($stack);
14903  if (!empty($node->is_whitespace)) {
14904  $result[] = $node;
14905  continue;
14906  }
14907  $all_whitespace = false; // phew, we're not talking about whitespace
14908 
14909  if (!isset($this->elements[$node->name])) {
14910  // special case text
14911  // XXX One of these ought to be redundant or something
14912  if ($pcdata_allowed && $node instanceof HTMLPurifier_Node_Text) {
14913  $result[] = $node;
14914  continue;
14915  }
14916  // spill the child contents in
14917  // ToDo: Make configurable
14918  if ($node instanceof HTMLPurifier_Node_Element) {
14919  for ($i = count($node->children) - 1; $i >= 0; $i--) {
14920  $stack[] = $node->children[$i];
14921  }
14922  continue;
14923  }
14924  continue;
14925  }
14926  $result[] = $node;
14927  }
14928  if (empty($result)) {
14929  return false;
14930  }
14931  if ($all_whitespace) {
14932  $this->whitespace = true;
14933  return false;
14934  }
14935  return $result;
14936  }
14937 }
14938 
14939 
14940 
14941 
14942 
14951 {
14955  public $allow_empty = true;
14956 
14960  public $type = 'optional';
14961 
14968  public function validateChildren($children, $config, $context)
14969  {
14970  $result = parent::validateChildren($children, $config, $context);
14971  // we assume that $children is not modified
14972  if ($result === false) {
14973  if (empty($children)) {
14974  return true;
14975  } elseif ($this->whitespace) {
14976  return $children;
14977  } else {
14978  return array();
14979  }
14980  }
14981  return $result;
14982  }
14983 }
14984 
14985 
14986 
14987 
14988 
14993 {
14997  protected $real_elements;
14998 
15002  protected $fake_elements;
15003 
15007  public $allow_empty = true;
15008 
15012  public $type = 'strictblockquote';
15013 
15017  protected $init = false;
15018 
15025  public function getAllowedElements($config)
15026  {
15027  $this->init($config);
15028  return $this->fake_elements;
15029  }
15030 
15037  public function validateChildren($children, $config, $context)
15038  {
15039  $this->init($config);
15040 
15041  // trick the parent class into thinking it allows more
15042  $this->elements = $this->fake_elements;
15043  $result = parent::validateChildren($children, $config, $context);
15044  $this->elements = $this->real_elements;
15045 
15046  if ($result === false) {
15047  return array();
15048  }
15049  if ($result === true) {
15050  $result = $children;
15051  }
15052 
15053  $def = $config->getHTMLDefinition();
15054  $block_wrap_name = $def->info_block_wrapper;
15055  $block_wrap = false;
15056  $ret = array();
15057 
15058  foreach ($result as $node) {
15059  if ($block_wrap === false) {
15060  if (($node instanceof HTMLPurifier_Node_Text && !$node->is_whitespace) ||
15061  ($node instanceof HTMLPurifier_Node_Element && !isset($this->elements[$node->name]))) {
15062  $block_wrap = new HTMLPurifier_Node_Element($def->info_block_wrapper);
15063  $ret[] = $block_wrap;
15064  }
15065  } else {
15066  if ($node instanceof HTMLPurifier_Node_Element && isset($this->elements[$node->name])) {
15067  $block_wrap = false;
15068 
15069  }
15070  }
15071  if ($block_wrap) {
15072  $block_wrap->children[] = $node;
15073  } else {
15074  $ret[] = $node;
15075  }
15076  }
15077  return $ret;
15078  }
15079 
15083  private function init($config)
15084  {
15085  if (!$this->init) {
15086  $def = $config->getHTMLDefinition();
15087  // allow all inline elements
15088  $this->real_elements = $this->elements;
15089  $this->fake_elements = $def->info_content_sets['Flow'];
15090  $this->fake_elements['#PCDATA'] = true;
15091  $this->init = true;
15092  }
15093  }
15094 }
15095 
15096 
15097 
15098 
15099 
15130 {
15134  public $allow_empty = false;
15135 
15139  public $type = 'table';
15140 
15144  public $elements = array(
15145  'tr' => true,
15146  'tbody' => true,
15147  'thead' => true,
15148  'tfoot' => true,
15149  'caption' => true,
15150  'colgroup' => true,
15151  'col' => true
15152  );
15153 
15154  public function __construct()
15155  {
15156  }
15157 
15164  public function validateChildren($children, $config, $context)
15165  {
15166  if (empty($children)) {
15167  return false;
15168  }
15169 
15170  // only one of these elements is allowed in a table
15171  $caption = false;
15172  $thead = false;
15173  $tfoot = false;
15174 
15175  // whitespace
15176  $initial_ws = array();
15177  $after_caption_ws = array();
15178  $after_thead_ws = array();
15179  $after_tfoot_ws = array();
15180 
15181  // as many of these as you want
15182  $cols = array();
15183  $content = array();
15184 
15185  $tbody_mode = false; // if true, then we need to wrap any stray
15186  // <tr>s with a <tbody>.
15187 
15188  $ws_accum =& $initial_ws;
15189 
15190  foreach ($children as $node) {
15191  if ($node instanceof HTMLPurifier_Node_Comment) {
15192  $ws_accum[] = $node;
15193  continue;
15194  }
15195  switch ($node->name) {
15196  case 'tbody':
15197  $tbody_mode = true;
15198  // fall through
15199  case 'tr':
15200  $content[] = $node;
15201  $ws_accum =& $content;
15202  break;
15203  case 'caption':
15204  // there can only be one caption!
15205  if ($caption !== false) break;
15206  $caption = $node;
15207  $ws_accum =& $after_caption_ws;
15208  break;
15209  case 'thead':
15210  $tbody_mode = true;
15211  // XXX This breaks rendering properties with
15212  // Firefox, which never floats a <thead> to
15213  // the top. Ever. (Our scheme will float the
15214  // first <thead> to the top.) So maybe
15215  // <thead>s that are not first should be
15216  // turned into <tbody>? Very tricky, indeed.
15217  if ($thead === false) {
15218  $thead = $node;
15219  $ws_accum =& $after_thead_ws;
15220  } else {
15221  // Oops, there's a second one! What
15222  // should we do? Current behavior is to
15223  // transmutate the first and last entries into
15224  // tbody tags, and then put into content.
15225  // Maybe a better idea is to *attach
15226  // it* to the existing thead or tfoot?
15227  // We don't do this, because Firefox
15228  // doesn't float an extra tfoot to the
15229  // bottom like it does for the first one.
15230  $node->name = 'tbody';
15231  $content[] = $node;
15232  $ws_accum =& $content;
15233  }
15234  break;
15235  case 'tfoot':
15236  // see above for some aveats
15237  $tbody_mode = true;
15238  if ($tfoot === false) {
15239  $tfoot = $node;
15240  $ws_accum =& $after_tfoot_ws;
15241  } else {
15242  $node->name = 'tbody';
15243  $content[] = $node;
15244  $ws_accum =& $content;
15245  }
15246  break;
15247  case 'colgroup':
15248  case 'col':
15249  $cols[] = $node;
15250  $ws_accum =& $cols;
15251  break;
15252  case '#PCDATA':
15253  // How is whitespace handled? We treat is as sticky to
15254  // the *end* of the previous element. So all of the
15255  // nonsense we have worked on is to keep things
15256  // together.
15257  if (!empty($node->is_whitespace)) {
15258  $ws_accum[] = $node;
15259  }
15260  break;
15261  }
15262  }
15263 
15264  if (empty($content)) {
15265  return false;
15266  }
15267 
15268  $ret = $initial_ws;
15269  if ($caption !== false) {
15270  $ret[] = $caption;
15271  $ret = array_merge($ret, $after_caption_ws);
15272  }
15273  if ($cols !== false) {
15274  $ret = array_merge($ret, $cols);
15275  }
15276  if ($thead !== false) {
15277  $ret[] = $thead;
15278  $ret = array_merge($ret, $after_thead_ws);
15279  }
15280  if ($tfoot !== false) {
15281  $ret[] = $tfoot;
15282  $ret = array_merge($ret, $after_tfoot_ws);
15283  }
15284 
15285  if ($tbody_mode) {
15286  // we have to shuffle tr into tbody
15287  $current_tr_tbody = null;
15288 
15289  foreach($content as $node) {
15290  switch ($node->name) {
15291  case 'tbody':
15292  $current_tr_tbody = null;
15293  $ret[] = $node;
15294  break;
15295  case 'tr':
15296  if ($current_tr_tbody === null) {
15297  $current_tr_tbody = new HTMLPurifier_Node_Element('tbody');
15298  $ret[] = $current_tr_tbody;
15299  }
15300  $current_tr_tbody->children[] = $node;
15301  break;
15302  case '#PCDATA':
15303  //assert($node->is_whitespace);
15304  if ($current_tr_tbody === null) {
15305  $ret[] = $node;
15306  } else {
15307  $current_tr_tbody->children[] = $node;
15308  }
15309  break;
15310  }
15311  }
15312  } else {
15313  $ret = array_merge($ret, $content);
15314  }
15315 
15316  return $ret;
15317 
15318  }
15319 }
15320 
15321 
15322 
15323 
15324 
15326 {
15327 
15332  public $cache;
15333 
15338  public $name;
15339 
15340  public function __construct()
15341  {
15342  }
15343 
15349  public function decorate(&$cache)
15350  {
15351  $decorator = $this->copy();
15352  // reference is necessary for mocks in PHP 4
15353  $decorator->cache =& $cache;
15354  $decorator->type = $cache->type;
15355  return $decorator;
15356  }
15357 
15362  public function copy()
15363  {
15365  }
15366 
15372  public function add($def, $config)
15373  {
15374  return $this->cache->add($def, $config);
15375  }
15376 
15382  public function set($def, $config)
15383  {
15384  return $this->cache->set($def, $config);
15385  }
15386 
15392  public function replace($def, $config)
15393  {
15394  return $this->cache->replace($def, $config);
15395  }
15396 
15401  public function get($config)
15402  {
15403  return $this->cache->get($config);
15404  }
15405 
15410  public function remove($config)
15411  {
15412  return $this->cache->remove($config);
15413  }
15414 
15419  public function flush($config)
15420  {
15421  return $this->cache->flush($config);
15422  }
15423 
15428  public function cleanup($config)
15429  {
15430  return $this->cache->cleanup($config);
15431  }
15432 }
15433 
15434 
15435 
15436 
15437 
15442 {
15443 
15449  public function add($def, $config)
15450  {
15451  return false;
15452  }
15453 
15459  public function set($def, $config)
15460  {
15461  return false;
15462  }
15463 
15469  public function replace($def, $config)
15470  {
15471  return false;
15472  }
15473 
15478  public function remove($config)
15479  {
15480  return false;
15481  }
15482 
15487  public function get($config)
15488  {
15489  return false;
15490  }
15491 
15496  public function flush($config)
15497  {
15498  return false;
15499  }
15500 
15505  public function cleanup($config)
15506  {
15507  return false;
15508  }
15509 }
15510 
15511 
15512 
15513 
15514 
15516 {
15517 
15523  public function add($def, $config)
15524  {
15525  if (!$this->checkDefType($def)) {
15526  return;
15527  }
15528  $file = $this->generateFilePath($config);
15529  if (file_exists($file)) {
15530  return false;
15531  }
15532  if (!$this->_prepareDir($config)) {
15533  return false;
15534  }
15535  return $this->_write($file, serialize($def), $config);
15536  }
15537 
15543  public function set($def, $config)
15544  {
15545  if (!$this->checkDefType($def)) {
15546  return;
15547  }
15548  $file = $this->generateFilePath($config);
15549  if (!$this->_prepareDir($config)) {
15550  return false;
15551  }
15552  return $this->_write($file, serialize($def), $config);
15553  }
15554 
15560  public function replace($def, $config)
15561  {
15562  if (!$this->checkDefType($def)) {
15563  return;
15564  }
15565  $file = $this->generateFilePath($config);
15566  if (!file_exists($file)) {
15567  return false;
15568  }
15569  if (!$this->_prepareDir($config)) {
15570  return false;
15571  }
15572  return $this->_write($file, serialize($def), $config);
15573  }
15574 
15579  public function get($config)
15580  {
15581  $file = $this->generateFilePath($config);
15582  if (!file_exists($file)) {
15583  return false;
15584  }
15585  return unserialize(file_get_contents($file));
15586  }
15587 
15592  public function remove($config)
15593  {
15594  $file = $this->generateFilePath($config);
15595  if (!file_exists($file)) {
15596  return false;
15597  }
15598  return unlink($file);
15599  }
15600 
15605  public function flush($config)
15606  {
15607  if (!$this->_prepareDir($config)) {
15608  return false;
15609  }
15611  $dh = opendir($dir);
15612  // Apparently, on some versions of PHP, readdir will return
15613  // an empty string if you pass an invalid argument to readdir.
15614  // So you need this test. See #49.
15615  if (false === $dh) {
15616  return false;
15617  }
15618  while (false !== ($filename = readdir($dh))) {
15619  if (empty($filename)) {
15620  continue;
15621  }
15622  if ($filename[0] === '.') {
15623  continue;
15624  }
15625  unlink($dir . '/' . $filename);
15626  }
15627  closedir($dh);
15628  return true;
15629  }
15630 
15635  public function cleanup($config)
15636  {
15637  if (!$this->_prepareDir($config)) {
15638  return false;
15639  }
15641  $dh = opendir($dir);
15642  // See #49 (and above).
15643  if (false === $dh) {
15644  return false;
15645  }
15646  while (false !== ($filename = readdir($dh))) {
15647  if (empty($filename)) {
15648  continue;
15649  }
15650  if ($filename[0] === '.') {
15651  continue;
15652  }
15653  $key = substr($filename, 0, strlen($filename) - 4);
15654  if ($this->isOld($key, $config)) {
15655  unlink($dir . '/' . $filename);
15656  }
15657  }
15658  closedir($dh);
15659  return true;
15660  }
15661 
15669  public function generateFilePath($config)
15670  {
15671  $key = $this->generateKey($config);
15672  return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
15673  }
15674 
15683  {
15684  $base = $this->generateBaseDirectoryPath($config);
15685  return $base . '/' . $this->type;
15686  }
15687 
15696  {
15697  $base = $config->get('Cache.SerializerPath');
15698  $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
15699  return $base;
15700  }
15701 
15709  private function _write($file, $data, $config)
15710  {
15711  $result = file_put_contents($file, $data);
15712  if ($result !== false) {
15713  // set permissions of the new file (no execute)
15714  $chmod = $config->get('Cache.SerializerPermissions');
15715  if ($chmod !== null) {
15716  chmod($file, $chmod & 0666);
15717  }
15718  }
15719  return $result;
15720  }
15721 
15727  private function _prepareDir($config)
15728  {
15729  $directory = $this->generateDirectoryPath($config);
15730  $chmod = $config->get('Cache.SerializerPermissions');
15731  if ($chmod === null) {
15732  if (!@mkdir($directory) && !is_dir($directory)) {
15733  trigger_error(
15734  'Could not create directory ' . $directory . '',
15735  E_USER_WARNING
15736  );
15737  return false;
15738  }
15739  return true;
15740  }
15741  if (!is_dir($directory)) {
15742  $base = $this->generateBaseDirectoryPath($config);
15743  if (!is_dir($base)) {
15744  trigger_error(
15745  'Base directory ' . $base . ' does not exist,
15746  please create or change using %Cache.SerializerPath',
15747  E_USER_WARNING
15748  );
15749  return false;
15750  } elseif (!$this->_testPermissions($base, $chmod)) {
15751  return false;
15752  }
15753  if (!@mkdir($directory, $chmod) && !is_dir($directory)) {
15754  trigger_error(
15755  'Could not create directory ' . $directory . '',
15756  E_USER_WARNING
15757  );
15758  return false;
15759  }
15760  if (!$this->_testPermissions($directory, $chmod)) {
15761  return false;
15762  }
15763  } elseif (!$this->_testPermissions($directory, $chmod)) {
15764  return false;
15765  }
15766  return true;
15767  }
15768 
15776  private function _testPermissions($dir, $chmod)
15777  {
15778  // early abort, if it is writable, everything is hunky-dory
15779  if (is_writable($dir)) {
15780  return true;
15781  }
15782  if (!is_dir($dir)) {
15783  // generally, you'll want to handle this beforehand
15784  // so a more specific error message can be given
15785  trigger_error(
15786  'Directory ' . $dir . ' does not exist',
15787  E_USER_WARNING
15788  );
15789  return false;
15790  }
15791  if (function_exists('posix_getuid') && $chmod !== null) {
15792  // POSIX system, we can give more specific advice
15793  if (fileowner($dir) === posix_getuid()) {
15794  // we can chmod it ourselves
15795  $chmod = $chmod | 0700;
15796  if (chmod($dir, $chmod)) {
15797  return true;
15798  }
15799  } elseif (filegroup($dir) === posix_getgid()) {
15800  $chmod = $chmod | 0070;
15801  } else {
15802  // PHP's probably running as nobody, so we'll
15803  // need to give global permissions
15804  $chmod = $chmod | 0777;
15805  }
15806  trigger_error(
15807  'Directory ' . $dir . ' not writable, ' .
15808  'please chmod to ' . decoct($chmod),
15809  E_USER_WARNING
15810  );
15811  } else {
15812  // generic error message
15813  trigger_error(
15814  'Directory ' . $dir . ' not writable, ' .
15815  'please alter file permissions',
15816  E_USER_WARNING
15817  );
15818  }
15819  return false;
15820  }
15821 }
15822 
15823 
15824 
15825 
15826 
15832 {
15836  public $name = 'Cleanup';
15837 
15841  public function copy()
15842  {
15844  }
15845 
15851  public function add($def, $config)
15852  {
15853  $status = parent::add($def, $config);
15854  if (!$status) {
15855  parent::cleanup($config);
15856  }
15857  return $status;
15858  }
15859 
15865  public function set($def, $config)
15866  {
15867  $status = parent::set($def, $config);
15868  if (!$status) {
15869  parent::cleanup($config);
15870  }
15871  return $status;
15872  }
15873 
15879  public function replace($def, $config)
15880  {
15881  $status = parent::replace($def, $config);
15882  if (!$status) {
15883  parent::cleanup($config);
15884  }
15885  return $status;
15886  }
15887 
15892  public function get($config)
15893  {
15894  $ret = parent::get($config);
15895  if (!$ret) {
15896  parent::cleanup($config);
15897  }
15898  return $ret;
15899  }
15900 }
15901 
15902 
15903 
15904 
15905 
15912 {
15916  protected $definitions;
15917 
15921  public $name = 'Memory';
15922 
15926  public function copy()
15927  {
15929  }
15930 
15936  public function add($def, $config)
15937  {
15938  $status = parent::add($def, $config);
15939  if ($status) {
15940  $this->definitions[$this->generateKey($config)] = $def;
15941  }
15942  return $status;
15943  }
15944 
15950  public function set($def, $config)
15951  {
15952  $status = parent::set($def, $config);
15953  if ($status) {
15954  $this->definitions[$this->generateKey($config)] = $def;
15955  }
15956  return $status;
15957  }
15958 
15964  public function replace($def, $config)
15965  {
15966  $status = parent::replace($def, $config);
15967  if ($status) {
15968  $this->definitions[$this->generateKey($config)] = $def;
15969  }
15970  return $status;
15971  }
15972 
15977  public function get($config)
15978  {
15979  $key = $this->generateKey($config);
15980  if (isset($this->definitions[$key])) {
15981  return $this->definitions[$key];
15982  }
15983  $this->definitions[$key] = parent::get($config);
15984  return $this->definitions[$key];
15985  }
15986 }
15987 
15988 
15989 
15990 
15991 
15997 {
15998 
16002  public $name = 'Bdo';
16003 
16007  public $attr_collections = array(
16008  'I18N' => array('dir' => false)
16009  );
16010 
16014  public function setup($config)
16015  {
16016  $bdo = $this->addElement(
16017  'bdo',
16018  'Inline',
16019  'Inline',
16020  array('Core', 'Lang'),
16021  array(
16022  'dir' => 'Enum#ltr,rtl', // required
16023  // The Abstract Module specification has the attribute
16024  // inclusions wrong for bdo: bdo allows Lang
16025  )
16026  );
16027  $bdo->attr_transform_post[] = new HTMLPurifier_AttrTransform_BdoDir();
16028 
16029  $this->attr_collections['I18N']['dir'] = 'Enum#ltr,rtl';
16030  }
16031 }
16032 
16033 
16034 
16035 
16036 
16038 {
16042  public $name = 'CommonAttributes';
16043 
16047  public $attr_collections = array(
16048  'Core' => array(
16049  0 => array('Style'),
16050  // 'xml:space' => false,
16051  'class' => 'Class',
16052  'id' => 'ID',
16053  'title' => 'CDATA',
16054  ),
16055  'Lang' => array(),
16056  'I18N' => array(
16057  0 => array('Lang'), // proprietary, for xml:lang/lang
16058  ),
16059  'Common' => array(
16060  0 => array('Core', 'I18N')
16061  )
16062  );
16063 }
16064 
16065 
16066 
16067 
16068 
16074 {
16075 
16079  public $name = 'Edit';
16080 
16084  public function setup($config)
16085  {
16086  $contents = 'Chameleon: #PCDATA | Inline ! #PCDATA | Flow';
16087  $attr = array(
16088  'cite' => 'URI',
16089  // 'datetime' => 'Datetime', // not implemented
16090  );
16091  $this->addElement('del', 'Inline', $contents, 'Common', $attr);
16092  $this->addElement('ins', 'Inline', $contents, 'Common', $attr);
16093  }
16094 
16095  // HTML 4.01 specifies that ins/del must not contain block
16096  // elements when used in an inline context, chameleon is
16097  // a complicated workaround to acheive this effect
16098 
16099  // Inline context ! Block context (exclamation mark is
16100  // separator, see getChildDef for parsing)
16101 
16105  public $defines_child_def = true;
16106 
16111  public function getChildDef($def)
16112  {
16113  if ($def->content_model_type != 'chameleon') {
16114  return false;
16115  }
16116  $value = explode('!', $def->content_model);
16117  return new HTMLPurifier_ChildDef_Chameleon($value[0], $value[1]);
16118  }
16119 }
16120 
16121 
16122 
16123 
16124 
16129 {
16133  public $name = 'Forms';
16134 
16138  public $safe = false;
16139 
16143  public $content_sets = array(
16144  'Block' => 'Form',
16145  'Inline' => 'Formctrl',
16146  );
16147 
16151  public function setup($config)
16152  {
16153  $form = $this->addElement(
16154  'form',
16155  'Form',
16156  'Required: Heading | List | Block | fieldset',
16157  'Common',
16158  array(
16159  'accept' => 'ContentTypes',
16160  'accept-charset' => 'Charsets',
16161  'action*' => 'URI',
16162  'method' => 'Enum#get,post',
16163  // really ContentType, but these two are the only ones used today
16164  'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data',
16165  )
16166  );
16167  $form->excludes = array('form' => true);
16168 
16169  $input = $this->addElement(
16170  'input',
16171  'Formctrl',
16172  'Empty',
16173  'Common',
16174  array(
16175  'accept' => 'ContentTypes',
16176  'accesskey' => 'Character',
16177  'alt' => 'Text',
16178  'checked' => 'Bool#checked',
16179  'disabled' => 'Bool#disabled',
16180  'maxlength' => 'Number',
16181  'name' => 'CDATA',
16182  'readonly' => 'Bool#readonly',
16183  'size' => 'Number',
16184  'src' => 'URI#embedded',
16185  'tabindex' => 'Number',
16186  'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image',
16187  'value' => 'CDATA',
16188  )
16189  );
16190  $input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input();
16191 
16192  $this->addElement(
16193  'select',
16194  'Formctrl',
16195  'Required: optgroup | option',
16196  'Common',
16197  array(
16198  'disabled' => 'Bool#disabled',
16199  'multiple' => 'Bool#multiple',
16200  'name' => 'CDATA',
16201  'size' => 'Number',
16202  'tabindex' => 'Number',
16203  )
16204  );
16205 
16206  $this->addElement(
16207  'option',
16208  false,
16209  'Optional: #PCDATA',
16210  'Common',
16211  array(
16212  'disabled' => 'Bool#disabled',
16213  'label' => 'Text',
16214  'selected' => 'Bool#selected',
16215  'value' => 'CDATA',
16216  )
16217  );
16218  // It's illegal for there to be more than one selected, but not
16219  // be multiple. Also, no selected means undefined behavior. This might
16220  // be difficult to implement; perhaps an injector, or a context variable.
16221 
16222  $textarea = $this->addElement(
16223  'textarea',
16224  'Formctrl',
16225  'Optional: #PCDATA',
16226  'Common',
16227  array(
16228  'accesskey' => 'Character',
16229  'cols*' => 'Number',
16230  'disabled' => 'Bool#disabled',
16231  'name' => 'CDATA',
16232  'readonly' => 'Bool#readonly',
16233  'rows*' => 'Number',
16234  'tabindex' => 'Number',
16235  )
16236  );
16237  $textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea();
16238 
16239  $button = $this->addElement(
16240  'button',
16241  'Formctrl',
16242  'Optional: #PCDATA | Heading | List | Block | Inline',
16243  'Common',
16244  array(
16245  'accesskey' => 'Character',
16246  'disabled' => 'Bool#disabled',
16247  'name' => 'CDATA',
16248  'tabindex' => 'Number',
16249  'type' => 'Enum#button,submit,reset',
16250  'value' => 'CDATA',
16251  )
16252  );
16253 
16254  // For exclusions, ideally we'd specify content sets, not literal elements
16255  $button->excludes = $this->makeLookup(
16256  'form',
16257  'fieldset', // Form
16258  'input',
16259  'select',
16260  'textarea',
16261  'label',
16262  'button', // Formctrl
16263  'a', // as per HTML 4.01 spec, this is omitted by modularization
16264  'isindex',
16265  'iframe' // legacy items
16266  );
16267 
16268  // Extra exclusion: img usemap="" is not permitted within this element.
16269  // We'll omit this for now, since we don't have any good way of
16270  // indicating it yet.
16271 
16272  // This is HIGHLY user-unfriendly; we need a custom child-def for this
16273  $this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common');
16274 
16275  $label = $this->addElement(
16276  'label',
16277  'Formctrl',
16278  'Optional: #PCDATA | Inline',
16279  'Common',
16280  array(
16281  'accesskey' => 'Character',
16282  // 'for' => 'IDREF', // IDREF not implemented, cannot allow
16283  )
16284  );
16285  $label->excludes = array('label' => true);
16286 
16287  $this->addElement(
16288  'legend',
16289  false,
16290  'Optional: #PCDATA | Inline',
16291  'Common',
16292  array(
16293  'accesskey' => 'Character',
16294  )
16295  );
16296 
16297  $this->addElement(
16298  'optgroup',
16299  false,
16300  'Required: option',
16301  'Common',
16302  array(
16303  'disabled' => 'Bool#disabled',
16304  'label*' => 'Text',
16305  )
16306  );
16307  // Don't forget an injector for <isindex>. This one's a little complex
16308  // because it maps to multiple elements.
16309  }
16310 }
16311 
16312 
16313 
16314 
16315 
16320 {
16321 
16325  public $name = 'Hypertext';
16326 
16330  public function setup($config)
16331  {
16332  $a = $this->addElement(
16333  'a',
16334  'Inline',
16335  'Inline',
16336  'Common',
16337  array(
16338  // 'accesskey' => 'Character',
16339  // 'charset' => 'Charset',
16340  'href' => 'URI',
16341  // 'hreflang' => 'LanguageCode',
16342  'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'),
16343  'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'),
16344  // 'tabindex' => 'Number',
16345  // 'type' => 'ContentType',
16346  )
16347  );
16348  $a->formatting = true;
16349  $a->excludes = array('a' => true);
16350  }
16351 }
16352 
16353 
16354 
16355 
16356 
16365 {
16366 
16370  public $name = 'Iframe';
16371 
16375  public $safe = false;
16376 
16380  public function setup($config)
16381  {
16382  if ($config->get('HTML.SafeIframe')) {
16383  $this->safe = true;
16384  }
16385  $this->addElement(
16386  'iframe',
16387  'Inline',
16388  'Flow',
16389  'Common',
16390  array(
16391  'src' => 'URI#embedded',
16392  'width' => 'Length',
16393  'height' => 'Length',
16394  'name' => 'ID',
16395  'scrolling' => 'Enum#yes,no,auto',
16396  'frameborder' => 'Enum#0,1',
16397  'longdesc' => 'URI',
16398  'marginheight' => 'Pixels',
16399  'marginwidth' => 'Pixels',
16400  )
16401  );
16402  }
16403 }
16404 
16405 
16406 
16407 
16408 
16415 {
16416 
16420  public $name = 'Image';
16421 
16425  public function setup($config)
16426  {
16427  $max = $config->get('HTML.MaxImgLength');
16428  $img = $this->addElement(
16429  'img',
16430  'Inline',
16431  'Empty',
16432  'Common',
16433  array(
16434  'alt*' => 'Text',
16435  // According to the spec, it's Length, but percents can
16436  // be abused, so we allow only Pixels.
16437  'height' => 'Pixels#' . $max,
16438  'width' => 'Pixels#' . $max,
16439  'longdesc' => 'URI',
16440  'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded
16441  )
16442  );
16443  if ($max === null || $config->get('HTML.Trusted')) {
16444  $img->attr['height'] =
16445  $img->attr['width'] = 'Length';
16446  }
16447 
16448  // kind of strange, but splitting things up would be inefficient
16449  $img->attr_transform_pre[] =
16450  $img->attr_transform_post[] =
16452  }
16453 }
16454 
16455 
16456 
16457 
16458 
16476 {
16480  public $name = 'Legacy';
16481 
16485  public function setup($config)
16486  {
16487  $this->addElement(
16488  'basefont',
16489  'Inline',
16490  'Empty',
16491  null,
16492  array(
16493  'color' => 'Color',
16494  'face' => 'Text', // extremely broad, we should
16495  'size' => 'Text', // tighten it
16496  'id' => 'ID'
16497  )
16498  );
16499  $this->addElement('center', 'Block', 'Flow', 'Common');
16500  $this->addElement(
16501  'dir',
16502  'Block',
16503  'Required: li',
16504  'Common',
16505  array(
16506  'compact' => 'Bool#compact'
16507  )
16508  );
16509  $this->addElement(
16510  'font',
16511  'Inline',
16512  'Inline',
16513  array('Core', 'I18N'),
16514  array(
16515  'color' => 'Color',
16516  'face' => 'Text', // extremely broad, we should
16517  'size' => 'Text', // tighten it
16518  )
16519  );
16520  $this->addElement(
16521  'menu',
16522  'Block',
16523  'Required: li',
16524  'Common',
16525  array(
16526  'compact' => 'Bool#compact'
16527  )
16528  );
16529 
16530  $s = $this->addElement('s', 'Inline', 'Inline', 'Common');
16531  $s->formatting = true;
16532 
16533  $strike = $this->addElement('strike', 'Inline', 'Inline', 'Common');
16534  $strike->formatting = true;
16535 
16536  $u = $this->addElement('u', 'Inline', 'Inline', 'Common');
16537  $u->formatting = true;
16538 
16539  // setup modifications to old elements
16540 
16541  $align = 'Enum#left,right,center,justify';
16542 
16543  $address = $this->addBlankElement('address');
16544  $address->content_model = 'Inline | #PCDATA | p';
16545  $address->content_model_type = 'optional';
16546  $address->child = false;
16547 
16548  $blockquote = $this->addBlankElement('blockquote');
16549  $blockquote->content_model = 'Flow | #PCDATA';
16550  $blockquote->content_model_type = 'optional';
16551  $blockquote->child = false;
16552 
16553  $br = $this->addBlankElement('br');
16554  $br->attr['clear'] = 'Enum#left,all,right,none';
16555 
16556  $caption = $this->addBlankElement('caption');
16557  $caption->attr['align'] = 'Enum#top,bottom,left,right';
16558 
16559  $div = $this->addBlankElement('div');
16560  $div->attr['align'] = $align;
16561 
16562  $dl = $this->addBlankElement('dl');
16563  $dl->attr['compact'] = 'Bool#compact';
16564 
16565  for ($i = 1; $i <= 6; $i++) {
16566  $h = $this->addBlankElement("h$i");
16567  $h->attr['align'] = $align;
16568  }
16569 
16570  $hr = $this->addBlankElement('hr');
16571  $hr->attr['align'] = $align;
16572  $hr->attr['noshade'] = 'Bool#noshade';
16573  $hr->attr['size'] = 'Pixels';
16574  $hr->attr['width'] = 'Length';
16575 
16576  $img = $this->addBlankElement('img');
16577  $img->attr['align'] = 'IAlign';
16578  $img->attr['border'] = 'Pixels';
16579  $img->attr['hspace'] = 'Pixels';
16580  $img->attr['vspace'] = 'Pixels';
16581 
16582  // figure out this integer business
16583 
16584  $li = $this->addBlankElement('li');
16585  $li->attr['value'] = new HTMLPurifier_AttrDef_Integer();
16586  $li->attr['type'] = 'Enum#s:1,i,I,a,A,disc,square,circle';
16587 
16588  $ol = $this->addBlankElement('ol');
16589  $ol->attr['compact'] = 'Bool#compact';
16590  $ol->attr['start'] = new HTMLPurifier_AttrDef_Integer();
16591  $ol->attr['type'] = 'Enum#s:1,i,I,a,A';
16592 
16593  $p = $this->addBlankElement('p');
16594  $p->attr['align'] = $align;
16595 
16596  $pre = $this->addBlankElement('pre');
16597  $pre->attr['width'] = 'Number';
16598 
16599  // script omitted
16600 
16601  $table = $this->addBlankElement('table');
16602  $table->attr['align'] = 'Enum#left,center,right';
16603  $table->attr['bgcolor'] = 'Color';
16604 
16605  $tr = $this->addBlankElement('tr');
16606  $tr->attr['bgcolor'] = 'Color';
16607 
16608  $th = $this->addBlankElement('th');
16609  $th->attr['bgcolor'] = 'Color';
16610  $th->attr['height'] = 'Length';
16611  $th->attr['nowrap'] = 'Bool#nowrap';
16612  $th->attr['width'] = 'Length';
16613 
16614  $td = $this->addBlankElement('td');
16615  $td->attr['bgcolor'] = 'Color';
16616  $td->attr['height'] = 'Length';
16617  $td->attr['nowrap'] = 'Bool#nowrap';
16618  $td->attr['width'] = 'Length';
16619 
16620  $ul = $this->addBlankElement('ul');
16621  $ul->attr['compact'] = 'Bool#compact';
16622  $ul->attr['type'] = 'Enum#square,disc,circle';
16623 
16624  // "safe" modifications to "unsafe" elements
16625  // WARNING: If you want to add support for an unsafe, legacy
16626  // attribute, make a new TrustedLegacy module with the trusted
16627  // bit set appropriately
16628 
16629  $form = $this->addBlankElement('form');
16630  $form->content_model = 'Flow | #PCDATA';
16631  $form->content_model_type = 'optional';
16632  $form->attr['target'] = 'FrameTarget';
16633 
16634  $input = $this->addBlankElement('input');
16635  $input->attr['align'] = 'IAlign';
16636 
16637  $legend = $this->addBlankElement('legend');
16638  $legend->attr['align'] = 'LAlign';
16639  }
16640 }
16641 
16642 
16643 
16644 
16645 
16650 {
16654  public $name = 'List';
16655 
16656  // According to the abstract schema, the List content set is a fully formed
16657  // one or more expr, but it invariably occurs in an optional declaration
16658  // so we're not going to do that subtlety. It might cause trouble
16659  // if a user defines "List" and expects that multiple lists are
16660  // allowed to be specified, but then again, that's not very intuitive.
16661  // Furthermore, the actual XML Schema may disagree. Regardless,
16662  // we don't have support for such nested expressions without using
16663  // the incredibly inefficient and draconic Custom ChildDef.
16664 
16668  public $content_sets = array('Flow' => 'List');
16669 
16673  public function setup($config)
16674  {
16675  $ol = $this->addElement('ol', 'List', new HTMLPurifier_ChildDef_List(), 'Common');
16676  $ul = $this->addElement('ul', 'List', new HTMLPurifier_ChildDef_List(), 'Common');
16677  // XXX The wrap attribute is handled by MakeWellFormed. This is all
16678  // quite unsatisfactory, because we generated this
16679  // *specifically* for lists, and now a big chunk of the handling
16680  // is done properly by the List ChildDef. So actually, we just
16681  // want enough information to make autoclosing work properly,
16682  // and then hand off the tricky stuff to the ChildDef.
16683  $ol->wrap = 'li';
16684  $ul->wrap = 'li';
16685  $this->addElement('dl', 'List', 'Required: dt | dd', 'Common');
16686 
16687  $this->addElement('li', false, 'Flow', 'Common');
16688 
16689  $this->addElement('dd', false, 'Flow', 'Common');
16690  $this->addElement('dt', false, 'Inline', 'Common');
16691  }
16692 }
16693 
16694 
16695 
16696 
16697 
16699 {
16703  public $name = 'Name';
16704 
16708  public function setup($config)
16709  {
16710  $elements = array('a', 'applet', 'form', 'frame', 'iframe', 'img', 'map');
16711  foreach ($elements as $name) {
16712  $element = $this->addBlankElement($name);
16713  $element->attr['name'] = 'CDATA';
16714  if (!$config->get('HTML.Attr.Name.UseCDATA')) {
16715  $element->attr_transform_post[] = new HTMLPurifier_AttrTransform_NameSync();
16716  }
16717  }
16718  }
16719 }
16720 
16721 
16722 
16723 
16724 
16730 {
16731 
16735  public $name = 'Nofollow';
16736 
16740  public function setup($config)
16741  {
16742  $a = $this->addBlankElement('a');
16743  $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_Nofollow();
16744  }
16745 }
16746 
16747 
16748 
16749 
16750 
16752 {
16756  public $name = 'NonXMLCommonAttributes';
16757 
16761  public $attr_collections = array(
16762  'Lang' => array(
16763  'lang' => 'LanguageCode',
16764  )
16765  );
16766 }
16767 
16768 
16769 
16770 
16771 
16778 {
16782  public $name = 'Object';
16783 
16787  public $safe = false;
16788 
16792  public function setup($config)
16793  {
16794  $this->addElement(
16795  'object',
16796  'Inline',
16797  'Optional: #PCDATA | Flow | param',
16798  'Common',
16799  array(
16800  'archive' => 'URI',
16801  'classid' => 'URI',
16802  'codebase' => 'URI',
16803  'codetype' => 'Text',
16804  'data' => 'URI',
16805  'declare' => 'Bool#declare',
16806  'height' => 'Length',
16807  'name' => 'CDATA',
16808  'standby' => 'Text',
16809  'tabindex' => 'Number',
16810  'type' => 'ContentType',
16811  'width' => 'Length'
16812  )
16813  );
16814 
16815  $this->addElement(
16816  'param',
16817  false,
16818  'Empty',
16819  null,
16820  array(
16821  'id' => 'ID',
16822  'name*' => 'Text',
16823  'type' => 'Text',
16824  'value' => 'Text',
16825  'valuetype' => 'Enum#data,ref,object'
16826  )
16827  );
16828  }
16829 }
16830 
16831 
16832 
16833 
16834 
16846 {
16847 
16851  public $name = 'Presentation';
16852 
16856  public function setup($config)
16857  {
16858  $this->addElement('hr', 'Block', 'Empty', 'Common');
16859  $this->addElement('sub', 'Inline', 'Inline', 'Common');
16860  $this->addElement('sup', 'Inline', 'Inline', 'Common');
16861  $b = $this->addElement('b', 'Inline', 'Inline', 'Common');
16862  $b->formatting = true;
16863  $big = $this->addElement('big', 'Inline', 'Inline', 'Common');
16864  $big->formatting = true;
16865  $i = $this->addElement('i', 'Inline', 'Inline', 'Common');
16866  $i->formatting = true;
16867  $small = $this->addElement('small', 'Inline', 'Inline', 'Common');
16868  $small->formatting = true;
16869  $tt = $this->addElement('tt', 'Inline', 'Inline', 'Common');
16870  $tt->formatting = true;
16871  }
16872 }
16873 
16874 
16875 
16876 
16877 
16883 {
16887  public $name = 'Proprietary';
16888 
16892  public function setup($config)
16893  {
16894  $this->addElement(
16895  'marquee',
16896  'Inline',
16897  'Flow',
16898  'Common',
16899  array(
16900  'direction' => 'Enum#left,right,up,down',
16901  'behavior' => 'Enum#alternate',
16902  'width' => 'Length',
16903  'height' => 'Length',
16904  'scrolldelay' => 'Number',
16905  'scrollamount' => 'Number',
16906  'loop' => 'Number',
16907  'bgcolor' => 'Color',
16908  'hspace' => 'Pixels',
16909  'vspace' => 'Pixels',
16910  )
16911  );
16912  }
16913 }
16914 
16915 
16916 
16917 
16918 
16924 {
16925 
16929  public $name = 'Ruby';
16930 
16934  public function setup($config)
16935  {
16936  $this->addElement(
16937  'ruby',
16938  'Inline',
16939  'Custom: ((rb, (rt | (rp, rt, rp))) | (rbc, rtc, rtc?))',
16940  'Common'
16941  );
16942  $this->addElement('rbc', false, 'Required: rb', 'Common');
16943  $this->addElement('rtc', false, 'Required: rt', 'Common');
16944  $rb = $this->addElement('rb', false, 'Inline', 'Common');
16945  $rb->excludes = array('ruby' => true);
16946  $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number'));
16947  $rt->excludes = array('ruby' => true);
16948  $this->addElement('rp', false, 'Optional: #PCDATA', 'Common');
16949  }
16950 }
16951 
16952 
16953 
16954 
16955 
16960 {
16964  public $name = 'SafeEmbed';
16965 
16969  public function setup($config)
16970  {
16971  $max = $config->get('HTML.MaxImgLength');
16972  $embed = $this->addElement(
16973  'embed',
16974  'Inline',
16975  'Empty',
16976  'Common',
16977  array(
16978  'src*' => 'URI#embedded',
16979  'type' => 'Enum#application/x-shockwave-flash',
16980  'width' => 'Pixels#' . $max,
16981  'height' => 'Pixels#' . $max,
16982  'allowscriptaccess' => 'Enum#never',
16983  'allownetworking' => 'Enum#internal',
16984  'flashvars' => 'Text',
16985  'wmode' => 'Enum#window,transparent,opaque',
16986  'name' => 'ID',
16987  )
16988  );
16989  $embed->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeEmbed();
16990  }
16991 }
16992 
16993 
16994 
16995 
16996 
17004 {
17008  public $name = 'SafeObject';
17009 
17013  public function setup($config)
17014  {
17015  // These definitions are not intrinsically safe: the attribute transforms
17016  // are a vital part of ensuring safety.
17017 
17018  $max = $config->get('HTML.MaxImgLength');
17019  $object = $this->addElement(
17020  'object',
17021  'Inline',
17022  'Optional: param | Flow | #PCDATA',
17023  'Common',
17024  array(
17025  // While technically not required by the spec, we're forcing
17026  // it to this value.
17027  'type' => 'Enum#application/x-shockwave-flash',
17028  'width' => 'Pixels#' . $max,
17029  'height' => 'Pixels#' . $max,
17030  'data' => 'URI#embedded',
17031  'codebase' => new HTMLPurifier_AttrDef_Enum(
17032  array(
17033  'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0'
17034  )
17035  ),
17036  )
17037  );
17038  $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject();
17039 
17040  $param = $this->addElement(
17041  'param',
17042  false,
17043  'Empty',
17044  false,
17045  array(
17046  'id' => 'ID',
17047  'name*' => 'Text',
17048  'value' => 'Text'
17049  )
17050  );
17051  $param->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeParam();
17052  $this->info_injector[] = 'SafeObject';
17053  }
17054 }
17055 
17056 
17057 
17058 
17059 
17065 {
17069  public $name = 'SafeScripting';
17070 
17074  public function setup($config)
17075  {
17076  // These definitions are not intrinsically safe: the attribute transforms
17077  // are a vital part of ensuring safety.
17078 
17079  $allowed = $config->get('HTML.SafeScripting');
17080  $script = $this->addElement(
17081  'script',
17082  'Inline',
17083  'Optional:', // Not `Empty` to not allow to autoclose the <script /> tag @see https://www.w3.org/TR/html4/interact/scripts.html
17084  null,
17085  array(
17086  // While technically not required by the spec, we're forcing
17087  // it to this value.
17088  'type' => 'Enum#text/javascript',
17089  'src*' => new HTMLPurifier_AttrDef_Enum(array_keys($allowed), /*case sensitive*/ true)
17090  )
17091  );
17092  $script->attr_transform_pre[] =
17093  $script->attr_transform_post[] = new HTMLPurifier_AttrTransform_ScriptRequired();
17094  }
17095 }
17096 
17097 
17098 
17099 
17100 
17101 /*
17102 
17103 WARNING: THIS MODULE IS EXTREMELY DANGEROUS AS IT ENABLES INLINE SCRIPTING
17104 INSIDE HTML PURIFIER DOCUMENTS. USE ONLY WITH TRUSTED USER INPUT!!!
17105 
17106 */
17107 
17115 {
17119  public $name = 'Scripting';
17120 
17124  public $elements = array('script', 'noscript');
17125 
17129  public $content_sets = array('Block' => 'script | noscript', 'Inline' => 'script | noscript');
17130 
17134  public $safe = false;
17135 
17139  public function setup($config)
17140  {
17141  // TODO: create custom child-definition for noscript that
17142  // auto-wraps stray #PCDATA in a similar manner to
17143  // blockquote's custom definition (we would use it but
17144  // blockquote's contents are optional while noscript's contents
17145  // are required)
17146 
17147  // TODO: convert this to new syntax, main problem is getting
17148  // both content sets working
17149 
17150  // In theory, this could be safe, but I don't see any reason to
17151  // allow it.
17152  $this->info['noscript'] = new HTMLPurifier_ElementDef();
17153  $this->info['noscript']->attr = array(0 => array('Common'));
17154  $this->info['noscript']->content_model = 'Heading | List | Block';
17155  $this->info['noscript']->content_model_type = 'required';
17156 
17157  $this->info['script'] = new HTMLPurifier_ElementDef();
17158  $this->info['script']->attr = array(
17159  'defer' => new HTMLPurifier_AttrDef_Enum(array('defer')),
17160  'src' => new HTMLPurifier_AttrDef_URI(true),
17161  'type' => new HTMLPurifier_AttrDef_Enum(array('text/javascript'))
17162  );
17163  $this->info['script']->content_model = '#PCDATA';
17164  $this->info['script']->content_model_type = 'optional';
17165  $this->info['script']->attr_transform_pre[] =
17166  $this->info['script']->attr_transform_post[] =
17168  }
17169 }
17170 
17171 
17172 
17173 
17174 
17180 {
17184  public $name = 'StyleAttribute';
17185 
17189  public $attr_collections = array(
17190  // The inclusion routine differs from the Abstract Modules but
17191  // is in line with the DTD and XML Schemas.
17192  'Style' => array('style' => false), // see constructor
17193  'Core' => array(0 => array('Style'))
17194  );
17195 
17199  public function setup($config)
17200  {
17201  $this->attr_collections['Style']['style'] = new HTMLPurifier_AttrDef_CSS();
17202  }
17203 }
17204 
17205 
17206 
17207 
17208 
17213 {
17217  public $name = 'Tables';
17218 
17222  public function setup($config)
17223  {
17224  $this->addElement('caption', false, 'Inline', 'Common');
17225 
17226  $this->addElement(
17227  'table',
17228  'Block',
17230  'Common',
17231  array(
17232  'border' => 'Pixels',
17233  'cellpadding' => 'Length',
17234  'cellspacing' => 'Length',
17235  'frame' => 'Enum#void,above,below,hsides,lhs,rhs,vsides,box,border',
17236  'rules' => 'Enum#none,groups,rows,cols,all',
17237  'summary' => 'Text',
17238  'width' => 'Length'
17239  )
17240  );
17241 
17242  // common attributes
17243  $cell_align = array(
17244  'align' => 'Enum#left,center,right,justify,char',
17245  'charoff' => 'Length',
17246  'valign' => 'Enum#top,middle,bottom,baseline',
17247  );
17248 
17249  $cell_t = array_merge(
17250  array(
17251  'abbr' => 'Text',
17252  'colspan' => 'Number',
17253  'rowspan' => 'Number',
17254  // Apparently, as of HTML5 this attribute only applies
17255  // to 'th' elements.
17256  'scope' => 'Enum#row,col,rowgroup,colgroup',
17257  ),
17258  $cell_align
17259  );
17260  $this->addElement('td', false, 'Flow', 'Common', $cell_t);
17261  $this->addElement('th', false, 'Flow', 'Common', $cell_t);
17262 
17263  $this->addElement('tr', false, 'Required: td | th', 'Common', $cell_align);
17264 
17265  $cell_col = array_merge(
17266  array(
17267  'span' => 'Number',
17268  'width' => 'MultiLength',
17269  ),
17270  $cell_align
17271  );
17272  $this->addElement('col', false, 'Empty', 'Common', $cell_col);
17273  $this->addElement('colgroup', false, 'Optional: col', 'Common', $cell_col);
17274 
17275  $this->addElement('tbody', false, 'Required: tr', 'Common', $cell_align);
17276  $this->addElement('thead', false, 'Required: tr', 'Common', $cell_align);
17277  $this->addElement('tfoot', false, 'Required: tr', 'Common', $cell_align);
17278  }
17279 }
17280 
17281 
17282 
17283 
17284 
17289 {
17293  public $name = 'Target';
17294 
17298  public function setup($config)
17299  {
17300  $elements = array('a');
17301  foreach ($elements as $name) {
17302  $e = $this->addBlankElement($name);
17303  $e->attr = array(
17304  'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget()
17305  );
17306  }
17307  }
17308 }
17309 
17310 
17311 
17312 
17313 
17319 {
17323  public $name = 'TargetBlank';
17324 
17328  public function setup($config)
17329  {
17330  $a = $this->addBlankElement('a');
17331  $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetBlank();
17332  }
17333 }
17334 
17335 
17336 
17337 
17338 
17344 {
17348  public $name = 'TargetNoopener';
17349 
17353  public function setup($config) {
17354  $a = $this->addBlankElement('a');
17355  $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoopener();
17356  }
17357 }
17358 
17359 
17360 
17366 {
17370  public $name = 'TargetNoreferrer';
17371 
17375  public function setup($config) {
17376  $a = $this->addBlankElement('a');
17377  $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoreferrer();
17378  }
17379 }
17380 
17381 
17382 
17396 {
17400  public $name = 'Text';
17401 
17405  public $content_sets = array(
17406  'Flow' => 'Heading | Block | Inline'
17407  );
17408 
17412  public function setup($config)
17413  {
17414  // Inline Phrasal -------------------------------------------------
17415  $this->addElement('abbr', 'Inline', 'Inline', 'Common');
17416  $this->addElement('acronym', 'Inline', 'Inline', 'Common');
17417  $this->addElement('cite', 'Inline', 'Inline', 'Common');
17418  $this->addElement('dfn', 'Inline', 'Inline', 'Common');
17419  $this->addElement('kbd', 'Inline', 'Inline', 'Common');
17420  $this->addElement('q', 'Inline', 'Inline', 'Common', array('cite' => 'URI'));
17421  $this->addElement('samp', 'Inline', 'Inline', 'Common');
17422  $this->addElement('var', 'Inline', 'Inline', 'Common');
17423 
17424  $em = $this->addElement('em', 'Inline', 'Inline', 'Common');
17425  $em->formatting = true;
17426 
17427  $strong = $this->addElement('strong', 'Inline', 'Inline', 'Common');
17428  $strong->formatting = true;
17429 
17430  $code = $this->addElement('code', 'Inline', 'Inline', 'Common');
17431  $code->formatting = true;
17432 
17433  // Inline Structural ----------------------------------------------
17434  $this->addElement('span', 'Inline', 'Inline', 'Common');
17435  $this->addElement('br', 'Inline', 'Empty', 'Core');
17436 
17437  // Block Phrasal --------------------------------------------------
17438  $this->addElement('address', 'Block', 'Inline', 'Common');
17439  $this->addElement('blockquote', 'Block', 'Optional: Heading | Block | List', 'Common', array('cite' => 'URI'));
17440  $pre = $this->addElement('pre', 'Block', 'Inline', 'Common');
17441  $pre->excludes = $this->makeLookup(
17442  'img',
17443  'big',
17444  'small',
17445  'object',
17446  'applet',
17447  'font',
17448  'basefont'
17449  );
17450  $this->addElement('h1', 'Heading', 'Inline', 'Common');
17451  $this->addElement('h2', 'Heading', 'Inline', 'Common');
17452  $this->addElement('h3', 'Heading', 'Inline', 'Common');
17453  $this->addElement('h4', 'Heading', 'Inline', 'Common');
17454  $this->addElement('h5', 'Heading', 'Inline', 'Common');
17455  $this->addElement('h6', 'Heading', 'Inline', 'Common');
17456 
17457  // Block Structural -----------------------------------------------
17458  $p = $this->addElement('p', 'Block', 'Inline', 'Common');
17459  $p->autoclose = array_flip(
17460  array("address", "blockquote", "center", "dir", "div", "dl", "fieldset", "ol", "p", "ul")
17461  );
17462 
17463  $this->addElement('div', 'Block', 'Flow', 'Common');
17464  }
17465 }
17466 
17467 
17468 
17469 
17470 
17477 {
17483  public $levels = array(0 => 'none', 'light', 'medium', 'heavy');
17484 
17490  public $defaultLevel = null;
17491 
17498  public $fixesForLevel = array(
17499  'light' => array(),
17500  'medium' => array(),
17501  'heavy' => array()
17502  );
17503 
17511  public function setup($config)
17512  {
17513  // create fixes, initialize fixesForLevel
17514  $fixes = $this->makeFixes();
17515  $this->makeFixesForLevel($fixes);
17516 
17517  // figure out which fixes to use
17518  $level = $config->get('HTML.TidyLevel');
17519  $fixes_lookup = $this->getFixesForLevel($level);
17520 
17521  // get custom fix declarations: these need namespace processing
17522  $add_fixes = $config->get('HTML.TidyAdd');
17523  $remove_fixes = $config->get('HTML.TidyRemove');
17524 
17525  foreach ($fixes as $name => $fix) {
17526  // needs to be refactored a little to implement globbing
17527  if (isset($remove_fixes[$name]) ||
17528  (!isset($add_fixes[$name]) && !isset($fixes_lookup[$name]))) {
17529  unset($fixes[$name]);
17530  }
17531  }
17532 
17533  // populate this module with necessary fixes
17534  $this->populate($fixes);
17535  }
17536 
17543  public function getFixesForLevel($level)
17544  {
17545  if ($level == $this->levels[0]) {
17546  return array();
17547  }
17548  $activated_levels = array();
17549  for ($i = 1, $c = count($this->levels); $i < $c; $i++) {
17550  $activated_levels[] = $this->levels[$i];
17551  if ($this->levels[$i] == $level) {
17552  break;
17553  }
17554  }
17555  if ($i == $c) {
17556  trigger_error(
17557  'Tidy level ' . htmlspecialchars($level) . ' not recognized',
17558  E_USER_WARNING
17559  );
17560  return array();
17561  }
17562  $ret = array();
17563  foreach ($activated_levels as $level) {
17564  foreach ($this->fixesForLevel[$level] as $fix) {
17565  $ret[$fix] = true;
17566  }
17567  }
17568  return $ret;
17569  }
17570 
17577  public function makeFixesForLevel($fixes)
17578  {
17579  if (!isset($this->defaultLevel)) {
17580  return;
17581  }
17582  if (!isset($this->fixesForLevel[$this->defaultLevel])) {
17583  trigger_error(
17584  'Default level ' . $this->defaultLevel . ' does not exist',
17585  E_USER_ERROR
17586  );
17587  return;
17588  }
17589  $this->fixesForLevel[$this->defaultLevel] = array_keys($fixes);
17590  }
17591 
17597  public function populate($fixes)
17598  {
17599  foreach ($fixes as $name => $fix) {
17600  // determine what the fix is for
17601  list($type, $params) = $this->getFixType($name);
17602  switch ($type) {
17603  case 'attr_transform_pre':
17604  case 'attr_transform_post':
17605  $attr = $params['attr'];
17606  if (isset($params['element'])) {
17607  $element = $params['element'];
17608  if (empty($this->info[$element])) {
17609  $e = $this->addBlankElement($element);
17610  } else {
17611  $e = $this->info[$element];
17612  }
17613  } else {
17614  $type = "info_$type";
17615  $e = $this;
17616  }
17617  // PHP does some weird parsing when I do
17618  // $e->$type[$attr], so I have to assign a ref.
17619  $f =& $e->$type;
17620  $f[$attr] = $fix;
17621  break;
17622  case 'tag_transform':
17623  $this->info_tag_transform[$params['element']] = $fix;
17624  break;
17625  case 'child':
17626  case 'content_model_type':
17627  $element = $params['element'];
17628  if (empty($this->info[$element])) {
17629  $e = $this->addBlankElement($element);
17630  } else {
17631  $e = $this->info[$element];
17632  }
17633  $e->$type = $fix;
17634  break;
17635  default:
17636  trigger_error("Fix type $type not supported", E_USER_ERROR);
17637  break;
17638  }
17639  }
17640  }
17641 
17650  public function getFixType($name)
17651  {
17652  // parse it
17653  $property = $attr = null;
17654  if (strpos($name, '#') !== false) {
17655  list($name, $property) = explode('#', $name);
17656  }
17657  if (strpos($name, '@') !== false) {
17658  list($name, $attr) = explode('@', $name);
17659  }
17660 
17661  // figure out the parameters
17662  $params = array();
17663  if ($name !== '') {
17664  $params['element'] = $name;
17665  }
17666  if (!is_null($attr)) {
17667  $params['attr'] = $attr;
17668  }
17669 
17670  // special case: attribute transform
17671  if (!is_null($attr)) {
17672  if (is_null($property)) {
17673  $property = 'pre';
17674  }
17675  $type = 'attr_transform_' . $property;
17676  return array($type, $params);
17677  }
17678 
17679  // special case: tag transform
17680  if (is_null($property)) {
17681  return array('tag_transform', $params);
17682  }
17683 
17684  return array($property, $params);
17685 
17686  }
17687 
17693  public function makeFixes()
17694  {
17695  }
17696 }
17697 
17698 
17699 
17700 
17701 
17703 {
17707  public $name = 'XMLCommonAttributes';
17708 
17712  public $attr_collections = array(
17713  'Lang' => array(
17714  'xml:lang' => 'LanguageCode',
17715  )
17716  );
17717 }
17718 
17719 
17720 
17721 
17722 
17727 {
17731  public $name = 'Tidy_Name';
17732 
17736  public $defaultLevel = 'heavy';
17737 
17741  public function makeFixes()
17742  {
17743  $r = array();
17744  // @name for img, a -----------------------------------------------
17745  // Technically, it's allowed even on strict, so we allow authors to use
17746  // it. However, it's deprecated in future versions of XHTML.
17747  $r['img@name'] =
17748  $r['a@name'] = new HTMLPurifier_AttrTransform_Name();
17749  return $r;
17750  }
17751 }
17752 
17753 
17754 
17755 
17756 
17758 {
17759 
17763  public $name = 'Tidy_Proprietary';
17764 
17768  public $defaultLevel = 'light';
17769 
17773  public function makeFixes()
17774  {
17775  $r = array();
17776  $r['table@background'] = new HTMLPurifier_AttrTransform_Background();
17777  $r['td@background'] = new HTMLPurifier_AttrTransform_Background();
17778  $r['th@background'] = new HTMLPurifier_AttrTransform_Background();
17779  $r['tr@background'] = new HTMLPurifier_AttrTransform_Background();
17780  $r['thead@background'] = new HTMLPurifier_AttrTransform_Background();
17781  $r['tfoot@background'] = new HTMLPurifier_AttrTransform_Background();
17782  $r['tbody@background'] = new HTMLPurifier_AttrTransform_Background();
17783  $r['table@height'] = new HTMLPurifier_AttrTransform_Length('height');
17784  return $r;
17785  }
17786 }
17787 
17788 
17789 
17790 
17791 
17793 {
17794 
17798  public function makeFixes()
17799  {
17800  $r = array();
17801 
17802  // == deprecated tag transforms ===================================
17803 
17804  $r['font'] = new HTMLPurifier_TagTransform_Font();
17805  $r['menu'] = new HTMLPurifier_TagTransform_Simple('ul');
17806  $r['dir'] = new HTMLPurifier_TagTransform_Simple('ul');
17807  $r['center'] = new HTMLPurifier_TagTransform_Simple('div', 'text-align:center;');
17808  $r['u'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:underline;');
17809  $r['s'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;');
17810  $r['strike'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;');
17811 
17812  // == deprecated attribute transforms =============================
17813 
17814  $r['caption@align'] =
17816  'align',
17817  array(
17818  // we're following IE's behavior, not Firefox's, due
17819  // to the fact that no one supports caption-side:right,
17820  // W3C included (with CSS 2.1). This is a slightly
17821  // unreasonable attribute!
17822  'left' => 'text-align:left;',
17823  'right' => 'text-align:right;',
17824  'top' => 'caption-side:top;',
17825  'bottom' => 'caption-side:bottom;' // not supported by IE
17826  )
17827  );
17828 
17829  // @align for img -------------------------------------------------
17830  $r['img@align'] =
17832  'align',
17833  array(
17834  'left' => 'float:left;',
17835  'right' => 'float:right;',
17836  'top' => 'vertical-align:top;',
17837  'middle' => 'vertical-align:middle;',
17838  'bottom' => 'vertical-align:baseline;',
17839  )
17840  );
17841 
17842  // @align for table -----------------------------------------------
17843  $r['table@align'] =
17845  'align',
17846  array(
17847  'left' => 'float:left;',
17848  'center' => 'margin-left:auto;margin-right:auto;',
17849  'right' => 'float:right;'
17850  )
17851  );
17852 
17853  // @align for hr -----------------------------------------------
17854  $r['hr@align'] =
17856  'align',
17857  array(
17858  // we use both text-align and margin because these work
17859  // for different browsers (IE and Firefox, respectively)
17860  // and the melange makes for a pretty cross-compatible
17861  // solution
17862  'left' => 'margin-left:0;margin-right:auto;text-align:left;',
17863  'center' => 'margin-left:auto;margin-right:auto;text-align:center;',
17864  'right' => 'margin-left:auto;margin-right:0;text-align:right;'
17865  )
17866  );
17867 
17868  // @align for h1, h2, h3, h4, h5, h6, p, div ----------------------
17869  // {{{
17870  $align_lookup = array();
17871  $align_values = array('left', 'right', 'center', 'justify');
17872  foreach ($align_values as $v) {
17873  $align_lookup[$v] = "text-align:$v;";
17874  }
17875  // }}}
17876  $r['h1@align'] =
17877  $r['h2@align'] =
17878  $r['h3@align'] =
17879  $r['h4@align'] =
17880  $r['h5@align'] =
17881  $r['h6@align'] =
17882  $r['p@align'] =
17883  $r['div@align'] =
17884  new HTMLPurifier_AttrTransform_EnumToCSS('align', $align_lookup);
17885 
17886  // @bgcolor for table, tr, td, th ---------------------------------
17887  $r['table@bgcolor'] =
17888  $r['td@bgcolor'] =
17889  $r['th@bgcolor'] =
17891 
17892  // @border for img ------------------------------------------------
17893  $r['img@border'] = new HTMLPurifier_AttrTransform_Border();
17894 
17895  // @clear for br --------------------------------------------------
17896  $r['br@clear'] =
17898  'clear',
17899  array(
17900  'left' => 'clear:left;',
17901  'right' => 'clear:right;',
17902  'all' => 'clear:both;',
17903  'none' => 'clear:none;',
17904  )
17905  );
17906 
17907  // @height for td, th ---------------------------------------------
17908  $r['td@height'] =
17909  $r['th@height'] =
17910  new HTMLPurifier_AttrTransform_Length('height');
17911 
17912  // @hspace for img ------------------------------------------------
17913  $r['img@hspace'] = new HTMLPurifier_AttrTransform_ImgSpace('hspace');
17914 
17915  // @noshade for hr ------------------------------------------------
17916  // this transformation is not precise but often good enough.
17917  // different browsers use different styles to designate noshade
17918  $r['hr@noshade'] =
17920  'noshade',
17921  'color:#808080;background-color:#808080;border:0;'
17922  );
17923 
17924  // @nowrap for td, th ---------------------------------------------
17925  $r['td@nowrap'] =
17926  $r['th@nowrap'] =
17928  'nowrap',
17929  'white-space:nowrap;'
17930  );
17931 
17932  // @size for hr --------------------------------------------------
17933  $r['hr@size'] = new HTMLPurifier_AttrTransform_Length('size', 'height');
17934 
17935  // @type for li, ol, ul -------------------------------------------
17936  // {{{
17937  $ul_types = array(
17938  'disc' => 'list-style-type:disc;',
17939  'square' => 'list-style-type:square;',
17940  'circle' => 'list-style-type:circle;'
17941  );
17942  $ol_types = array(
17943  '1' => 'list-style-type:decimal;',
17944  'i' => 'list-style-type:lower-roman;',
17945  'I' => 'list-style-type:upper-roman;',
17946  'a' => 'list-style-type:lower-alpha;',
17947  'A' => 'list-style-type:upper-alpha;'
17948  );
17949  $li_types = $ul_types + $ol_types;
17950  // }}}
17951 
17952  $r['ul@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ul_types);
17953  $r['ol@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ol_types, true);
17954  $r['li@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $li_types, true);
17955 
17956  // @vspace for img ------------------------------------------------
17957  $r['img@vspace'] = new HTMLPurifier_AttrTransform_ImgSpace('vspace');
17958 
17959  // @width for hr, td, th ------------------------------------------
17960  $r['td@width'] =
17961  $r['th@width'] =
17962  $r['hr@width'] = new HTMLPurifier_AttrTransform_Length('width');
17963 
17964  return $r;
17965  }
17966 }
17967 
17968 
17969 
17970 
17971 
17973 {
17977  public $name = 'Tidy_Strict';
17978 
17982  public $defaultLevel = 'light';
17983 
17987  public function makeFixes()
17988  {
17989  $r = parent::makeFixes();
17990  $r['blockquote#content_model_type'] = 'strictblockquote';
17991  return $r;
17992  }
17993 
17997  public $defines_child_def = true;
17998 
18003  public function getChildDef($def)
18004  {
18005  if ($def->content_model_type != 'strictblockquote') {
18006  return parent::getChildDef($def);
18007  }
18008  return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model);
18009  }
18010 }
18011 
18012 
18013 
18014 
18015 
18017 {
18021  public $name = 'Tidy_Transitional';
18022 
18026  public $defaultLevel = 'heavy';
18027 }
18028 
18029 
18030 
18031 
18032 
18034 {
18038  public $name = 'Tidy_XHTML';
18039 
18043  public $defaultLevel = 'medium';
18044 
18048  public function makeFixes()
18049  {
18050  $r = array();
18051  $r['@lang'] = new HTMLPurifier_AttrTransform_Lang();
18052  return $r;
18053  }
18054 }
18055 
18056 
18057 
18058 
18059 
18067 {
18071  public $name = 'AutoParagraph';
18072 
18076  public $needed = array('p');
18077 
18081  private function _pStart()
18082  {
18083  $par = new HTMLPurifier_Token_Start('p');
18084  $par->armor['MakeWellFormed_TagClosedError'] = true;
18085  return $par;
18086  }
18087 
18091  public function handleText(&$token)
18092  {
18093  $text = $token->data;
18094  // Does the current parent allow <p> tags?
18095  if ($this->allowsElement('p')) {
18096  if (empty($this->currentNesting) || strpos($text, "\n\n") !== false) {
18097  // Note that we have differing behavior when dealing with text
18098  // in the anonymous root node, or a node inside the document.
18099  // If the text as a double-newline, the treatment is the same;
18100  // if it doesn't, see the next if-block if you're in the document.
18101 
18102  $i = $nesting = null;
18103  if (!$this->forwardUntilEndToken($i, $current, $nesting) && $token->is_whitespace) {
18104  // State 1.1: ... ^ (whitespace, then document end)
18105  // ----
18106  // This is a degenerate case
18107  } else {
18108  if (!$token->is_whitespace || $this->_isInline($current)) {
18109  // State 1.2: PAR1
18110  // ----
18111 
18112  // State 1.3: PAR1\n\nPAR2
18113  // ------------
18114 
18115  // State 1.4: <div>PAR1\n\nPAR2 (see State 2)
18116  // ------------
18117  $token = array($this->_pStart());
18118  $this->_splitText($text, $token);
18119  } else {
18120  // State 1.5: \n<hr />
18121  // --
18122  }
18123  }
18124  } else {
18125  // State 2: <div>PAR1... (similar to 1.4)
18126  // ----
18127 
18128  // We're in an element that allows paragraph tags, but we're not
18129  // sure if we're going to need them.
18130  if ($this->_pLookAhead()) {
18131  // State 2.1: <div>PAR1<b>PAR1\n\nPAR2
18132  // ----
18133  // Note: This will always be the first child, since any
18134  // previous inline element would have triggered this very
18135  // same routine, and found the double newline. One possible
18136  // exception would be a comment.
18137  $token = array($this->_pStart(), $token);
18138  } else {
18139  // State 2.2.1: <div>PAR1<div>
18140  // ----
18141 
18142  // State 2.2.2: <div>PAR1<b>PAR1</b></div>
18143  // ----
18144  }
18145  }
18146  // Is the current parent a <p> tag?
18147  } elseif (!empty($this->currentNesting) &&
18148  $this->currentNesting[count($this->currentNesting) - 1]->name == 'p') {
18149  // State 3.1: ...<p>PAR1
18150  // ----
18151 
18152  // State 3.2: ...<p>PAR1\n\nPAR2
18153  // ------------
18154  $token = array();
18155  $this->_splitText($text, $token);
18156  // Abort!
18157  } else {
18158  // State 4.1: ...<b>PAR1
18159  // ----
18160 
18161  // State 4.2: ...<b>PAR1\n\nPAR2
18162  // ------------
18163  }
18164  }
18165 
18169  public function handleElement(&$token)
18170  {
18171  // We don't have to check if we're already in a <p> tag for block
18172  // tokens, because the tag would have been autoclosed by MakeWellFormed.
18173  if ($this->allowsElement('p')) {
18174  if (!empty($this->currentNesting)) {
18175  if ($this->_isInline($token)) {
18176  // State 1: <div>...<b>
18177  // ---
18178  // Check if this token is adjacent to the parent token
18179  // (seek backwards until token isn't whitespace)
18180  $i = null;
18181  $this->backward($i, $prev);
18182 
18183  if (!$prev instanceof HTMLPurifier_Token_Start) {
18184  // Token wasn't adjacent
18185  if ($prev instanceof HTMLPurifier_Token_Text &&
18186  substr($prev->data, -2) === "\n\n"
18187  ) {
18188  // State 1.1.4: <div><p>PAR1</p>\n\n<b>
18189  // ---
18190  // Quite frankly, this should be handled by splitText
18191  $token = array($this->_pStart(), $token);
18192  } else {
18193  // State 1.1.1: <div><p>PAR1</p><b>
18194  // ---
18195  // State 1.1.2: <div><br /><b>
18196  // ---
18197  // State 1.1.3: <div>PAR<b>
18198  // ---
18199  }
18200  } else {
18201  // State 1.2.1: <div><b>
18202  // ---
18203  // Lookahead to see if <p> is needed.
18204  if ($this->_pLookAhead()) {
18205  // State 1.3.1: <div><b>PAR1\n\nPAR2
18206  // ---
18207  $token = array($this->_pStart(), $token);
18208  } else {
18209  // State 1.3.2: <div><b>PAR1</b></div>
18210  // ---
18211 
18212  // State 1.3.3: <div><b>PAR1</b><div></div>\n\n</div>
18213  // ---
18214  }
18215  }
18216  } else {
18217  // State 2.3: ...<div>
18218  // -----
18219  }
18220  } else {
18221  if ($this->_isInline($token)) {
18222  // State 3.1: <b>
18223  // ---
18224  // This is where the {p} tag is inserted, not reflected in
18225  // inputTokens yet, however.
18226  $token = array($this->_pStart(), $token);
18227  } else {
18228  // State 3.2: <div>
18229  // -----
18230  }
18231 
18232  $i = null;
18233  if ($this->backward($i, $prev)) {
18234  if (!$prev instanceof HTMLPurifier_Token_Text) {
18235  // State 3.1.1: ...</p>{p}<b>
18236  // ---
18237  // State 3.2.1: ...</p><div>
18238  // -----
18239  if (!is_array($token)) {
18240  $token = array($token);
18241  }
18242  array_unshift($token, new HTMLPurifier_Token_Text("\n\n"));
18243  } else {
18244  // State 3.1.2: ...</p>\n\n{p}<b>
18245  // ---
18246  // State 3.2.2: ...</p>\n\n<div>
18247  // -----
18248  // Note: PAR<ELEM> cannot occur because PAR would have been
18249  // wrapped in <p> tags.
18250  }
18251  }
18252  }
18253  } else {
18254  // State 2.2: <ul><li>
18255  // ----
18256  // State 2.4: <p><b>
18257  // ---
18258  }
18259  }
18260 
18269  private function _splitText($data, &$result)
18270  {
18271  $raw_paragraphs = explode("\n\n", $data);
18272  $paragraphs = array(); // without empty paragraphs
18273  $needs_start = false;
18274  $needs_end = false;
18275 
18276  $c = count($raw_paragraphs);
18277  if ($c == 1) {
18278  // There were no double-newlines, abort quickly. In theory this
18279  // should never happen.
18280  $result[] = new HTMLPurifier_Token_Text($data);
18281  return;
18282  }
18283  for ($i = 0; $i < $c; $i++) {
18284  $par = $raw_paragraphs[$i];
18285  if (trim($par) !== '') {
18286  $paragraphs[] = $par;
18287  } else {
18288  if ($i == 0) {
18289  // Double newline at the front
18290  if (empty($result)) {
18291  // The empty result indicates that the AutoParagraph
18292  // injector did not add any start paragraph tokens.
18293  // This means that we have been in a paragraph for
18294  // a while, and the newline means we should start a new one.
18295  $result[] = new HTMLPurifier_Token_End('p');
18296  $result[] = new HTMLPurifier_Token_Text("\n\n");
18297  // However, the start token should only be added if
18298  // there is more processing to be done (i.e. there are
18299  // real paragraphs in here). If there are none, the
18300  // next start paragraph tag will be handled by the
18301  // next call to the injector
18302  $needs_start = true;
18303  } else {
18304  // We just started a new paragraph!
18305  // Reinstate a double-newline for presentation's sake, since
18306  // it was in the source code.
18307  array_unshift($result, new HTMLPurifier_Token_Text("\n\n"));
18308  }
18309  } elseif ($i + 1 == $c) {
18310  // Double newline at the end
18311  // There should be a trailing </p> when we're finally done.
18312  $needs_end = true;
18313  }
18314  }
18315  }
18316 
18317  // Check if this was just a giant blob of whitespace. Move this earlier,
18318  // perhaps?
18319  if (empty($paragraphs)) {
18320  return;
18321  }
18322 
18323  // Add the start tag indicated by \n\n at the beginning of $data
18324  if ($needs_start) {
18325  $result[] = $this->_pStart();
18326  }
18327 
18328  // Append the paragraphs onto the result
18329  foreach ($paragraphs as $par) {
18330  $result[] = new HTMLPurifier_Token_Text($par);
18331  $result[] = new HTMLPurifier_Token_End('p');
18332  $result[] = new HTMLPurifier_Token_Text("\n\n");
18333  $result[] = $this->_pStart();
18334  }
18335 
18336  // Remove trailing start token; Injector will handle this later if
18337  // it was indeed needed. This prevents from needing to do a lookahead,
18338  // at the cost of a lookbehind later.
18339  array_pop($result);
18340 
18341  // If there is no need for an end tag, remove all of it and let
18342  // MakeWellFormed close it later.
18343  if (!$needs_end) {
18344  array_pop($result); // removes \n\n
18345  array_pop($result); // removes </p>
18346  }
18347  }
18348 
18355  private function _isInline($token)
18356  {
18357  return isset($this->htmlDefinition->info['p']->child->elements[$token->name]);
18358  }
18359 
18365  private function _pLookAhead()
18366  {
18367  if ($this->currentToken instanceof HTMLPurifier_Token_Start) {
18368  $nesting = 1;
18369  } else {
18370  $nesting = 0;
18371  }
18372  $ok = false;
18373  $i = null;
18374  while ($this->forwardUntilEndToken($i, $current, $nesting)) {
18375  $result = $this->_checkNeedsP($current);
18376  if ($result !== null) {
18377  $ok = $result;
18378  break;
18379  }
18380  }
18381  return $ok;
18382  }
18383 
18390  private function _checkNeedsP($current)
18391  {
18392  if ($current instanceof HTMLPurifier_Token_Start) {
18393  if (!$this->_isInline($current)) {
18394  // <div>PAR1<div>
18395  // ----
18396  // Terminate early, since we hit a block element
18397  return false;
18398  }
18399  } elseif ($current instanceof HTMLPurifier_Token_Text) {
18400  if (strpos($current->data, "\n\n") !== false) {
18401  // <div>PAR1<b>PAR1\n\nPAR2
18402  // ----
18403  return true;
18404  } else {
18405  // <div>PAR1<b>PAR1...
18406  // ----
18407  }
18408  }
18409  return null;
18410  }
18411 }
18412 
18413 
18414 
18415 
18416 
18421 {
18425  public $name = 'DisplayLinkURI';
18426 
18430  public $needed = array('a');
18431 
18435  public function handleElement(&$token)
18436  {
18437  }
18438 
18442  public function handleEnd(&$token)
18443  {
18444  if (isset($token->start->attr['href'])) {
18445  $url = $token->start->attr['href'];
18446  unset($token->start->attr['href']);
18447  $token = array($token, new HTMLPurifier_Token_Text(" ($url)"));
18448  } else {
18449  // nothing to display
18450  }
18451  }
18452 }
18453 
18454 
18455 
18456 
18457 
18462 {
18466  public $name = 'Linkify';
18467 
18471  public $needed = array('a' => array('href'));
18472 
18476  public function handleText(&$token)
18477  {
18478  if (!$this->allowsElement('a')) {
18479  return;
18480  }
18481 
18482  if (strpos($token->data, '://') === false) {
18483  // our really quick heuristic failed, abort
18484  // this may not work so well if we want to match things like
18485  // "google.com", but then again, most people don't
18486  return;
18487  }
18488 
18489  // there is/are URL(s). Let's split the string.
18490  // We use this regex:
18491  // https://gist.github.com/gruber/249502
18492  // but with @cscott's backtracking fix and also
18493  // the Unicode characters un-Unicodified.
18494  $bits = preg_split(
18495  '/\\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',
18496  $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
18497 
18498 
18499  $token = array();
18500 
18501  // $i = index
18502  // $c = count
18503  // $l = is link
18504  for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
18505  if (!$l) {
18506  if ($bits[$i] === '') {
18507  continue;
18508  }
18509  $token[] = new HTMLPurifier_Token_Text($bits[$i]);
18510  } else {
18511  $token[] = new HTMLPurifier_Token_Start('a', array('href' => $bits[$i]));
18512  $token[] = new HTMLPurifier_Token_Text($bits[$i]);
18513  $token[] = new HTMLPurifier_Token_End('a');
18514  }
18515  }
18516  }
18517 }
18518 
18519 
18520 
18521 
18522 
18528 {
18532  public $name = 'PurifierLinkify';
18533 
18537  public $docURL;
18538 
18542  public $needed = array('a' => array('href'));
18543 
18549  public function prepare($config, $context)
18550  {
18551  $this->docURL = $config->get('AutoFormat.PurifierLinkify.DocURL');
18552  return parent::prepare($config, $context);
18553  }
18554 
18558  public function handleText(&$token)
18559  {
18560  if (!$this->allowsElement('a')) {
18561  return;
18562  }
18563  if (strpos($token->data, '%') === false) {
18564  return;
18565  }
18566 
18567  $bits = preg_split('#%([a-z0-9]+\.[a-z0-9]+)#Si', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
18568  $token = array();
18569 
18570  // $i = index
18571  // $c = count
18572  // $l = is link
18573  for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
18574  if (!$l) {
18575  if ($bits[$i] === '') {
18576  continue;
18577  }
18578  $token[] = new HTMLPurifier_Token_Text($bits[$i]);
18579  } else {
18580  $token[] = new HTMLPurifier_Token_Start(
18581  'a',
18582  array('href' => str_replace('%s', $bits[$i], $this->docURL))
18583  );
18584  $token[] = new HTMLPurifier_Token_Text('%' . $bits[$i]);
18585  $token[] = new HTMLPurifier_Token_End('a');
18586  }
18587  }
18588  }
18589 }
18590 
18591 
18592 
18593 
18594 
18596 {
18600  private $context;
18601 
18605  private $config;
18606 
18610  private $attrValidator;
18611 
18615  private $removeNbsp;
18616 
18620  private $removeNbspExceptions;
18621 
18626  private $exclude;
18627 
18633  public function prepare($config, $context)
18634  {
18635  parent::prepare($config, $context);
18636  $this->config = $config;
18637  $this->context = $context;
18638  $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
18639  $this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
18640  $this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
18641  foreach ($this->exclude as $key => $attrs) {
18642  if (!is_array($attrs)) {
18643  // HACK, see HTMLPurifier/Printer/ConfigForm.php
18644  $this->exclude[$key] = explode(';', $attrs);
18645  }
18646  }
18647  $this->attrValidator = new HTMLPurifier_AttrValidator();
18648  }
18649 
18653  public function handleElement(&$token)
18654  {
18655  if (!$token instanceof HTMLPurifier_Token_Start) {
18656  return;
18657  }
18658  $next = false;
18659  $deleted = 1; // the current tag
18660  for ($i = count($this->inputZipper->back) - 1; $i >= 0; $i--, $deleted++) {
18661  $next = $this->inputZipper->back[$i];
18662  if ($next instanceof HTMLPurifier_Token_Text) {
18663  if ($next->is_whitespace) {
18664  continue;
18665  }
18666  if ($this->removeNbsp && !isset($this->removeNbspExceptions[$token->name])) {
18667  $plain = str_replace("\xC2\xA0", "", $next->data);
18668  $isWsOrNbsp = $plain === '' || ctype_space($plain);
18669  if ($isWsOrNbsp) {
18670  continue;
18671  }
18672  }
18673  }
18674  break;
18675  }
18676  if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {
18677  $this->attrValidator->validateToken($token, $this->config, $this->context);
18678  $token->armor['ValidateAttributes'] = true;
18679  if (isset($this->exclude[$token->name])) {
18680  $r = true;
18681  foreach ($this->exclude[$token->name] as $elem) {
18682  if (!isset($token->attr[$elem])) $r = false;
18683  }
18684  if ($r) return;
18685  }
18686  if (isset($token->attr['id']) || isset($token->attr['name'])) {
18687  return;
18688  }
18689  $token = $deleted + 1;
18690  for ($b = 0, $c = count($this->inputZipper->front); $b < $c; $b++) {
18691  $prev = $this->inputZipper->front[$b];
18692  if ($prev instanceof HTMLPurifier_Token_Text && $prev->is_whitespace) {
18693  continue;
18694  }
18695  break;
18696  }
18697  // This is safe because we removed the token that triggered this.
18698  $this->rewindOffset($b+$deleted);
18699  return;
18700  }
18701  }
18702 }
18703 
18704 
18705 
18706 
18707 
18712 {
18716  public $name = 'RemoveSpansWithoutAttributes';
18717 
18721  public $needed = array('span');
18722 
18726  private $attrValidator;
18727 
18732  private $config;
18733 
18737  private $context;
18738 
18739  public function prepare($config, $context)
18740  {
18741  $this->attrValidator = new HTMLPurifier_AttrValidator();
18742  $this->config = $config;
18743  $this->context = $context;
18744  return parent::prepare($config, $context);
18745  }
18746 
18750  public function handleElement(&$token)
18751  {
18752  if ($token->name !== 'span' || !$token instanceof HTMLPurifier_Token_Start) {
18753  return;
18754  }
18755 
18756  // We need to validate the attributes now since this doesn't normally
18757  // happen until after MakeWellFormed. If all the attributes are removed
18758  // the span needs to be removed too.
18759  $this->attrValidator->validateToken($token, $this->config, $this->context);
18760  $token->armor['ValidateAttributes'] = true;
18761 
18762  if (!empty($token->attr)) {
18763  return;
18764  }
18765 
18766  $nesting = 0;
18767  while ($this->forwardUntilEndToken($i, $current, $nesting)) {
18768  }
18769 
18770  if ($current instanceof HTMLPurifier_Token_End && $current->name === 'span') {
18771  // Mark closing span tag for deletion
18772  $current->markForDeletion = true;
18773  // Delete open span tag
18774  $token = false;
18775  }
18776  }
18777 
18781  public function handleEnd(&$token)
18782  {
18783  if ($token->markForDeletion) {
18784  $token = false;
18785  }
18786  }
18787 }
18788 
18789 
18790 
18791 
18792 
18798 {
18802  public $name = 'SafeObject';
18803 
18807  public $needed = array('object', 'param');
18808 
18812  protected $objectStack = array();
18813 
18817  protected $paramStack = array();
18818 
18823  protected $addParam = array(
18824  'allowScriptAccess' => 'never',
18825  'allowNetworking' => 'internal',
18826  );
18827 
18832  protected $allowedParam = array(
18833  'wmode' => true,
18834  'movie' => true,
18835  'flashvars' => true,
18836  'src' => true,
18837  'allowfullscreen' => true, // if omitted, assume to be 'false'
18838  );
18839 
18845  public function prepare($config, $context)
18846  {
18847  parent::prepare($config, $context);
18848  }
18849 
18853  public function handleElement(&$token)
18854  {
18855  if ($token->name == 'object') {
18856  $this->objectStack[] = $token;
18857  $this->paramStack[] = array();
18858  $new = array($token);
18859  foreach ($this->addParam as $name => $value) {
18860  $new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value));
18861  }
18862  $token = $new;
18863  } elseif ($token->name == 'param') {
18864  $nest = count($this->currentNesting) - 1;
18865  if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') {
18866  $i = count($this->objectStack) - 1;
18867  if (!isset($token->attr['name'])) {
18868  $token = false;
18869  return;
18870  }
18871  $n = $token->attr['name'];
18872  // We need this fix because YouTube doesn't supply a data
18873  // attribute, which we need if a type is specified. This is
18874  // *very* Flash specific.
18875  if (!isset($this->objectStack[$i]->attr['data']) &&
18876  ($token->attr['name'] == 'movie' || $token->attr['name'] == 'src')
18877  ) {
18878  $this->objectStack[$i]->attr['data'] = $token->attr['value'];
18879  }
18880  // Check if the parameter is the correct value but has not
18881  // already been added
18882  if (!isset($this->paramStack[$i][$n]) &&
18883  isset($this->addParam[$n]) &&
18884  $token->attr['name'] === $this->addParam[$n]) {
18885  // keep token, and add to param stack
18886  $this->paramStack[$i][$n] = true;
18887  } elseif (isset($this->allowedParam[strtolower($n)])) {
18888  // keep token, don't do anything to it
18889  // (could possibly check for duplicates here)
18890  // Note: In principle, parameters should be case sensitive.
18891  // But it seems they are not really; so accept any case.
18892  } else {
18893  $token = false;
18894  }
18895  } else {
18896  // not directly inside an object, DENY!
18897  $token = false;
18898  }
18899  }
18900  }
18901 
18902  public function handleEnd(&$token)
18903  {
18904  // This is the WRONG way of handling the object and param stacks;
18905  // we should be inserting them directly on the relevant object tokens
18906  // so that the global stack handling handles it.
18907  if ($token->name == 'object') {
18908  array_pop($this->objectStack);
18909  array_pop($this->paramStack);
18910  }
18911  }
18912 }
18913 
18914 
18915 
18916 
18917 
18943 {
18944 
18948  private $factory;
18949 
18950  public function __construct()
18951  {
18952  // setup the factory
18953  parent::__construct();
18954  $this->factory = new HTMLPurifier_TokenFactory();
18955  }
18956 
18963  public function tokenizeHTML($html, $config, $context)
18964  {
18965  $html = $this->normalize($html, $config, $context);
18966 
18967  // attempt to armor stray angled brackets that cannot possibly
18968  // form tags and thus are probably being used as emoticons
18969  if ($config->get('Core.AggressivelyFixLt')) {
18970  $char = '[^a-z!\/]';
18971  $comment = "/<!--(.*?)(-->|\z)/is";
18972  $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html);
18973  do {
18974  $old = $html;
18975  $html = preg_replace("/<($char)/i", '&lt;\\1', $html);
18976  } while ($html !== $old);
18977  $html = preg_replace_callback($comment, array($this, 'callbackUndoCommentSubst'), $html); // fix comments
18978  }
18979 
18980  // preprocess html, essential for UTF-8
18981  $html = $this->wrapHTML($html, $config, $context);
18982 
18983  $doc = new DOMDocument();
18984  $doc->encoding = 'UTF-8'; // theoretically, the above has this covered
18985 
18986  $options = 0;
18987  if ($config->get('Core.AllowParseManyTags') && defined('LIBXML_PARSEHUGE')) {
18988  $options |= LIBXML_PARSEHUGE;
18989  }
18990 
18991  set_error_handler(array($this, 'muteErrorHandler'));
18992  // loadHTML() fails on PHP 5.3 when second parameter is given
18993  if ($options) {
18994  $doc->loadHTML($html, $options);
18995  } else {
18996  $doc->loadHTML($html);
18997  }
18998  restore_error_handler();
18999 
19000  $body = $doc->getElementsByTagName('html')->item(0)-> // <html>
19001  getElementsByTagName('body')->item(0); // <body>
19002 
19003  $div = $body->getElementsByTagName('div')->item(0); // <div>
19004  $tokens = array();
19005  $this->tokenizeDOM($div, $tokens, $config);
19006  // If the div has a sibling, that means we tripped across
19007  // a premature </div> tag. So remove the div we parsed,
19008  // and then tokenize the rest of body. We can't tokenize
19009  // the sibling directly as we'll lose the tags in that case.
19010  if ($div->nextSibling) {
19011  $body->removeChild($div);
19012  $this->tokenizeDOM($body, $tokens, $config);
19013  }
19014  return $tokens;
19015  }
19016 
19024  protected function tokenizeDOM($node, &$tokens, $config)
19025  {
19026  $level = 0;
19027  $nodes = array($level => new HTMLPurifier_Queue(array($node)));
19028  $closingNodes = array();
19029  do {
19030  while (!$nodes[$level]->isEmpty()) {
19031  $node = $nodes[$level]->shift(); // FIFO
19032  $collect = $level > 0 ? true : false;
19033  $needEndingTag = $this->createStartNode($node, $tokens, $collect, $config);
19034  if ($needEndingTag) {
19035  $closingNodes[$level][] = $node;
19036  }
19037  if ($node->childNodes && $node->childNodes->length) {
19038  $level++;
19039  $nodes[$level] = new HTMLPurifier_Queue();
19040  foreach ($node->childNodes as $childNode) {
19041  $nodes[$level]->push($childNode);
19042  }
19043  }
19044  }
19045  $level--;
19046  if ($level && isset($closingNodes[$level])) {
19047  while ($node = array_pop($closingNodes[$level])) {
19048  $this->createEndNode($node, $tokens);
19049  }
19050  }
19051  } while ($level > 0);
19052  }
19053 
19059  protected function getTagName($node)
19060  {
19061  if (isset($node->tagName)) {
19062  return $node->tagName;
19063  } else if (isset($node->nodeName)) {
19064  return $node->nodeName;
19065  } else if (isset($node->localName)) {
19066  return $node->localName;
19067  }
19068  return null;
19069  }
19070 
19076  protected function getData($node)
19077  {
19078  if (isset($node->data)) {
19079  return $node->data;
19080  } else if (isset($node->nodeValue)) {
19081  return $node->nodeValue;
19082  } else if (isset($node->textContent)) {
19083  return $node->textContent;
19084  }
19085  return null;
19086  }
19087 
19088 
19098  protected function createStartNode($node, &$tokens, $collect, $config)
19099  {
19100  // intercept non element nodes. WE MUST catch all of them,
19101  // but we're not getting the character reference nodes because
19102  // those should have been preprocessed
19103  if ($node->nodeType === XML_TEXT_NODE) {
19104  $data = $this->getData($node); // Handle variable data property
19105  if ($data !== null) {
19106  $tokens[] = $this->factory->createText($data);
19107  }
19108  return false;
19109  } elseif ($node->nodeType === XML_CDATA_SECTION_NODE) {
19110  // undo libxml's special treatment of <script> and <style> tags
19111  $last = end($tokens);
19112  $data = $node->data;
19113  // (note $node->tagname is already normalized)
19114  if ($last instanceof HTMLPurifier_Token_Start && ($last->name == 'script' || $last->name == 'style')) {
19115  $new_data = trim($data);
19116  if (substr($new_data, 0, 4) === '<!--') {
19117  $data = substr($new_data, 4);
19118  if (substr($data, -3) === '-->') {
19119  $data = substr($data, 0, -3);
19120  } else {
19121  // Highly suspicious! Not sure what to do...
19122  }
19123  }
19124  }
19125  $tokens[] = $this->factory->createText($this->parseText($data, $config));
19126  return false;
19127  } elseif ($node->nodeType === XML_COMMENT_NODE) {
19128  // this is code is only invoked for comments in script/style in versions
19129  // of libxml pre-2.6.28 (regular comments, of course, are still
19130  // handled regularly)
19131  $tokens[] = $this->factory->createComment($node->data);
19132  return false;
19133  } elseif ($node->nodeType !== XML_ELEMENT_NODE) {
19134  // not-well tested: there may be other nodes we have to grab
19135  return false;
19136  }
19137  $attr = $node->hasAttributes() ? $this->transformAttrToAssoc($node->attributes) : array();
19138  $tag_name = $this->getTagName($node); // Handle variable tagName property
19139  if (empty($tag_name)) {
19140  return (bool) $node->childNodes->length;
19141  }
19142  // We still have to make sure that the element actually IS empty
19143  if (!$node->childNodes->length) {
19144  if ($collect) {
19145  $tokens[] = $this->factory->createEmpty($tag_name, $attr);
19146  }
19147  return false;
19148  } else {
19149  if ($collect) {
19150  $tokens[] = $this->factory->createStart($tag_name, $attr);
19151  }
19152  return true;
19153  }
19154  }
19155 
19160  protected function createEndNode($node, &$tokens)
19161  {
19162  $tag_name = $this->getTagName($node); // Handle variable tagName property
19163  $tokens[] = $this->factory->createEnd($tag_name);
19164  }
19165 
19172  protected function transformAttrToAssoc($node_map)
19173  {
19174  // NamedNodeMap is documented very well, so we're using undocumented
19175  // features, namely, the fact that it implements Iterator and
19176  // has a ->length attribute
19177  if ($node_map->length === 0) {
19178  return array();
19179  }
19180  $array = array();
19181  foreach ($node_map as $attr) {
19182  $array[$attr->name] = $attr->value;
19183  }
19184  return $array;
19185  }
19186 
19192  public function muteErrorHandler($errno, $errstr)
19193  {
19194  }
19195 
19202  public function callbackUndoCommentSubst($matches)
19203  {
19204  return '<!--' . strtr($matches[1], array('&amp;' => '&', '&lt;' => '<')) . $matches[2];
19205  }
19206 
19213  public function callbackArmorCommentEntities($matches)
19214  {
19215  return '<!--' . str_replace('&', '&amp;', $matches[1]) . $matches[2];
19216  }
19217 
19225  protected function wrapHTML($html, $config, $context, $use_div = true)
19226  {
19227  $def = $config->getDefinition('HTML');
19228  $ret = '';
19229 
19230  if (!empty($def->doctype->dtdPublic) || !empty($def->doctype->dtdSystem)) {
19231  $ret .= '<!DOCTYPE html ';
19232  if (!empty($def->doctype->dtdPublic)) {
19233  $ret .= 'PUBLIC "' . $def->doctype->dtdPublic . '" ';
19234  }
19235  if (!empty($def->doctype->dtdSystem)) {
19236  $ret .= '"' . $def->doctype->dtdSystem . '" ';
19237  }
19238  $ret .= '>';
19239  }
19240 
19241  $ret .= '<html><head>';
19242  $ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
19243  // No protection if $html contains a stray </div>!
19244  $ret .= '</head><body>';
19245  if ($use_div) $ret .= '<div>';
19246  $ret .= $html;
19247  if ($use_div) $ret .= '</div>';
19248  $ret .= '</body></html>';
19249  return $ret;
19250  }
19251 }
19252 
19253 
19254 
19255 
19256 
19268 {
19272  public $tracksLineNumbers = true;
19273 
19278  protected $_whitespace = "\x20\x09\x0D\x0A";
19279 
19285  protected function scriptCallback($matches)
19286  {
19287  return $matches[1] . htmlspecialchars($matches[2], ENT_COMPAT, 'UTF-8') . $matches[3];
19288  }
19289 
19296  public function tokenizeHTML($html, $config, $context)
19297  {
19298  // special normalization for script tags without any armor
19299  // our "armor" heurstic is a < sign any number of whitespaces after
19300  // the first script tag
19301  if ($config->get('HTML.Trusted')) {
19302  $html = preg_replace_callback(
19303  '#(<script[^>]*>)(\s*[^<].+?)(</script>)#si',
19304  array($this, 'scriptCallback'),
19305  $html
19306  );
19307  }
19308 
19309  $html = $this->normalize($html, $config, $context);
19310 
19311  $cursor = 0; // our location in the text
19312  $inside_tag = false; // whether or not we're parsing the inside of a tag
19313  $array = array(); // result array
19314 
19315  // This is also treated to mean maintain *column* numbers too
19316  $maintain_line_numbers = $config->get('Core.MaintainLineNumbers');
19317 
19318  if ($maintain_line_numbers === null) {
19319  // automatically determine line numbering by checking
19320  // if error collection is on
19321  $maintain_line_numbers = $config->get('Core.CollectErrors');
19322  }
19323 
19324  if ($maintain_line_numbers) {
19325  $current_line = 1;
19326  $current_col = 0;
19327  $length = strlen($html);
19328  } else {
19329  $current_line = false;
19330  $current_col = false;
19331  $length = false;
19332  }
19333  $context->register('CurrentLine', $current_line);
19334  $context->register('CurrentCol', $current_col);
19335  $nl = "\n";
19336  // how often to manually recalculate. This will ALWAYS be right,
19337  // but it's pretty wasteful. Set to 0 to turn off
19338  $synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval');
19339 
19340  $e = false;
19341  if ($config->get('Core.CollectErrors')) {
19342  $e =& $context->get('ErrorCollector');
19343  }
19344 
19345  // for testing synchronization
19346  $loops = 0;
19347 
19348  while (++$loops) {
19349  // $cursor is either at the start of a token, or inside of
19350  // a tag (i.e. there was a < immediately before it), as indicated
19351  // by $inside_tag
19352 
19353  if ($maintain_line_numbers) {
19354  // $rcursor, however, is always at the start of a token.
19355  $rcursor = $cursor - (int)$inside_tag;
19356 
19357  // Column number is cheap, so we calculate it every round.
19358  // We're interested at the *end* of the newline string, so
19359  // we need to add strlen($nl) == 1 to $nl_pos before subtracting it
19360  // from our "rcursor" position.
19361  $nl_pos = strrpos($html, $nl, $rcursor - $length);
19362  $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1);
19363 
19364  // recalculate lines
19365  if ($synchronize_interval && // synchronization is on
19366  $cursor > 0 && // cursor is further than zero
19367  $loops % $synchronize_interval === 0) { // time to synchronize!
19368  $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor);
19369  }
19370  }
19371 
19372  $position_next_lt = strpos($html, '<', $cursor);
19373  $position_next_gt = strpos($html, '>', $cursor);
19374 
19375  // triggers on "<b>asdf</b>" but not "asdf <b></b>"
19376  // special case to set up context
19377  if ($position_next_lt === $cursor) {
19378  $inside_tag = true;
19379  $cursor++;
19380  }
19381 
19382  if (!$inside_tag && $position_next_lt !== false) {
19383  // We are not inside tag and there still is another tag to parse
19384  $token = new
19386  $this->parseText(
19387  substr(
19388  $html,
19389  $cursor,
19390  $position_next_lt - $cursor
19391  ), $config
19392  )
19393  );
19394  if ($maintain_line_numbers) {
19395  $token->rawPosition($current_line, $current_col);
19396  $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor);
19397  }
19398  $array[] = $token;
19399  $cursor = $position_next_lt + 1;
19400  $inside_tag = true;
19401  continue;
19402  } elseif (!$inside_tag) {
19403  // We are not inside tag but there are no more tags
19404  // If we're already at the end, break
19405  if ($cursor === strlen($html)) {
19406  break;
19407  }
19408  // Create Text of rest of string
19409  $token = new
19411  $this->parseText(
19412  substr(
19413  $html,
19414  $cursor
19415  ), $config
19416  )
19417  );
19418  if ($maintain_line_numbers) {
19419  $token->rawPosition($current_line, $current_col);
19420  }
19421  $array[] = $token;
19422  break;
19423  } elseif ($inside_tag && $position_next_gt !== false) {
19424  // We are in tag and it is well formed
19425  // Grab the internals of the tag
19426  $strlen_segment = $position_next_gt - $cursor;
19427 
19428  if ($strlen_segment < 1) {
19429  // there's nothing to process!
19430  $token = new HTMLPurifier_Token_Text('<');
19431  $cursor++;
19432  continue;
19433  }
19434 
19435  $segment = substr($html, $cursor, $strlen_segment);
19436 
19437  if ($segment === false) {
19438  // somehow, we attempted to access beyond the end of
19439  // the string, defense-in-depth, reported by Nate Abele
19440  break;
19441  }
19442 
19443  // Check if it's a comment
19444  if (substr($segment, 0, 3) === '!--') {
19445  // re-determine segment length, looking for -->
19446  $position_comment_end = strpos($html, '-->', $cursor);
19447  if ($position_comment_end === false) {
19448  // uh oh, we have a comment that extends to
19449  // infinity. Can't be helped: set comment
19450  // end position to end of string
19451  if ($e) {
19452  $e->send(E_WARNING, 'Lexer: Unclosed comment');
19453  }
19454  $position_comment_end = strlen($html);
19455  $end = true;
19456  } else {
19457  $end = false;
19458  }
19459  $strlen_segment = $position_comment_end - $cursor;
19460  $segment = substr($html, $cursor, $strlen_segment);
19461  $token = new
19463  substr(
19464  $segment,
19465  3,
19466  $strlen_segment - 3
19467  )
19468  );
19469  if ($maintain_line_numbers) {
19470  $token->rawPosition($current_line, $current_col);
19471  $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment);
19472  }
19473  $array[] = $token;
19474  $cursor = $end ? $position_comment_end : $position_comment_end + 3;
19475  $inside_tag = false;
19476  continue;
19477  }
19478 
19479  // Check if it's an end tag
19480  $is_end_tag = (strpos($segment, '/') === 0);
19481  if ($is_end_tag) {
19482  $type = substr($segment, 1);
19483  $token = new HTMLPurifier_Token_End($type);
19484  if ($maintain_line_numbers) {
19485  $token->rawPosition($current_line, $current_col);
19486  $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
19487  }
19488  $array[] = $token;
19489  $inside_tag = false;
19490  $cursor = $position_next_gt + 1;
19491  continue;
19492  }
19493 
19494  // Check leading character is alnum, if not, we may
19495  // have accidently grabbed an emoticon. Translate into
19496  // text and go our merry way
19497  if (!ctype_alpha($segment[0])) {
19498  // XML: $segment[0] !== '_' && $segment[0] !== ':'
19499  if ($e) {
19500  $e->send(E_NOTICE, 'Lexer: Unescaped lt');
19501  }
19502  $token = new HTMLPurifier_Token_Text('<');
19503  if ($maintain_line_numbers) {
19504  $token->rawPosition($current_line, $current_col);
19505  $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
19506  }
19507  $array[] = $token;
19508  $inside_tag = false;
19509  continue;
19510  }
19511 
19512  // Check if it is explicitly self closing, if so, remove
19513  // trailing slash. Remember, we could have a tag like <br>, so
19514  // any later token processing scripts must convert improperly
19515  // classified EmptyTags from StartTags.
19516  $is_self_closing = (strrpos($segment, '/') === $strlen_segment - 1);
19517  if ($is_self_closing) {
19518  $strlen_segment--;
19519  $segment = substr($segment, 0, $strlen_segment);
19520  }
19521 
19522  // Check if there are any attributes
19523  $position_first_space = strcspn($segment, $this->_whitespace);
19524 
19525  if ($position_first_space >= $strlen_segment) {
19526  if ($is_self_closing) {
19527  $token = new HTMLPurifier_Token_Empty($segment);
19528  } else {
19529  $token = new HTMLPurifier_Token_Start($segment);
19530  }
19531  if ($maintain_line_numbers) {
19532  $token->rawPosition($current_line, $current_col);
19533  $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
19534  }
19535  $array[] = $token;
19536  $inside_tag = false;
19537  $cursor = $position_next_gt + 1;
19538  continue;
19539  }
19540 
19541  // Grab out all the data
19542  $type = substr($segment, 0, $position_first_space);
19543  $attribute_string =
19544  trim(
19545  substr(
19546  $segment,
19547  $position_first_space
19548  )
19549  );
19550  if ($attribute_string) {
19551  $attr = $this->parseAttributeString(
19552  $attribute_string,
19553  $config,
19554  $context
19555  );
19556  } else {
19557  $attr = array();
19558  }
19559 
19560  if ($is_self_closing) {
19561  $token = new HTMLPurifier_Token_Empty($type, $attr);
19562  } else {
19563  $token = new HTMLPurifier_Token_Start($type, $attr);
19564  }
19565  if ($maintain_line_numbers) {
19566  $token->rawPosition($current_line, $current_col);
19567  $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
19568  }
19569  $array[] = $token;
19570  $cursor = $position_next_gt + 1;
19571  $inside_tag = false;
19572  continue;
19573  } else {
19574  // inside tag, but there's no ending > sign
19575  if ($e) {
19576  $e->send(E_WARNING, 'Lexer: Missing gt');
19577  }
19578  $token = new
19580  '<' .
19581  $this->parseText(
19582  substr($html, $cursor), $config
19583  )
19584  );
19585  if ($maintain_line_numbers) {
19586  $token->rawPosition($current_line, $current_col);
19587  }
19588  // no cursor scroll? Hmm...
19589  $array[] = $token;
19590  break;
19591  }
19592  break;
19593  }
19594 
19595  $context->destroy('CurrentLine');
19596  $context->destroy('CurrentCol');
19597  return $array;
19598  }
19599 
19608  protected function substrCount($haystack, $needle, $offset, $length)
19609  {
19610  static $oldVersion;
19611  if ($oldVersion === null) {
19612  $oldVersion = version_compare(PHP_VERSION, '5.1', '<');
19613  }
19614  if ($oldVersion) {
19615  $haystack = substr($haystack, $offset, $length);
19616  return substr_count($haystack, $needle);
19617  } else {
19618  return substr_count($haystack, $needle, $offset, $length);
19619  }
19620  }
19621 
19630  public function parseAttributeString($string, $config, $context)
19631  {
19632  $string = (string)$string; // quick typecast
19633 
19634  if ($string == '') {
19635  return array();
19636  } // no attributes
19637 
19638  $e = false;
19639  if ($config->get('Core.CollectErrors')) {
19640  $e =& $context->get('ErrorCollector');
19641  }
19642 
19643  // let's see if we can abort as quickly as possible
19644  // one equal sign, no spaces => one attribute
19645  $num_equal = substr_count($string, '=');
19646  $has_space = strpos($string, ' ');
19647  if ($num_equal === 0 && !$has_space) {
19648  // bool attribute
19649  return array($string => $string);
19650  } elseif ($num_equal === 1 && !$has_space) {
19651  // only one attribute
19652  list($key, $quoted_value) = explode('=', $string);
19653  $quoted_value = trim($quoted_value);
19654  if (!$key) {
19655  if ($e) {
19656  $e->send(E_ERROR, 'Lexer: Missing attribute key');
19657  }
19658  return array();
19659  }
19660  if (!$quoted_value) {
19661  return array($key => '');
19662  }
19663  $first_char = @$quoted_value[0];
19664  $last_char = @$quoted_value[strlen($quoted_value) - 1];
19665 
19666  $same_quote = ($first_char == $last_char);
19667  $open_quote = ($first_char == '"' || $first_char == "'");
19668 
19669  if ($same_quote && $open_quote) {
19670  // well behaved
19671  $value = substr($quoted_value, 1, strlen($quoted_value) - 2);
19672  } else {
19673  // not well behaved
19674  if ($open_quote) {
19675  if ($e) {
19676  $e->send(E_ERROR, 'Lexer: Missing end quote');
19677  }
19678  $value = substr($quoted_value, 1);
19679  } else {
19680  $value = $quoted_value;
19681  }
19682  }
19683  if ($value === false) {
19684  $value = '';
19685  }
19686  return array($key => $this->parseAttr($value, $config));
19687  }
19688 
19689  // setup loop environment
19690  $array = array(); // return assoc array of attributes
19691  $cursor = 0; // current position in string (moves forward)
19692  $size = strlen($string); // size of the string (stays the same)
19693 
19694  // if we have unquoted attributes, the parser expects a terminating
19695  // space, so let's guarantee that there's always a terminating space.
19696  $string .= ' ';
19697 
19698  $old_cursor = -1;
19699  while ($cursor < $size) {
19700  if ($old_cursor >= $cursor) {
19701  throw new Exception("Infinite loop detected");
19702  }
19703  $old_cursor = $cursor;
19704 
19705  $cursor += ($value = strspn($string, $this->_whitespace, $cursor));
19706  // grab the key
19707 
19708  $key_begin = $cursor; //we're currently at the start of the key
19709 
19710  // scroll past all characters that are the key (not whitespace or =)
19711  $cursor += strcspn($string, $this->_whitespace . '=', $cursor);
19712 
19713  $key_end = $cursor; // now at the end of the key
19714 
19715  $key = substr($string, $key_begin, $key_end - $key_begin);
19716 
19717  if (!$key) {
19718  if ($e) {
19719  $e->send(E_ERROR, 'Lexer: Missing attribute key');
19720  }
19721  $cursor += 1 + strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop
19722  continue; // empty key
19723  }
19724 
19725  // scroll past all whitespace
19726  $cursor += strspn($string, $this->_whitespace, $cursor);
19727 
19728  if ($cursor >= $size) {
19729  $array[$key] = $key;
19730  break;
19731  }
19732 
19733  // if the next character is an equal sign, we've got a regular
19734  // pair, otherwise, it's a bool attribute
19735  $first_char = @$string[$cursor];
19736 
19737  if ($first_char == '=') {
19738  // key="value"
19739 
19740  $cursor++;
19741  $cursor += strspn($string, $this->_whitespace, $cursor);
19742 
19743  if ($cursor === false) {
19744  $array[$key] = '';
19745  break;
19746  }
19747 
19748  // we might be in front of a quote right now
19749 
19750  $char = @$string[$cursor];
19751 
19752  if ($char == '"' || $char == "'") {
19753  // it's quoted, end bound is $char
19754  $cursor++;
19755  $value_begin = $cursor;
19756  $cursor = strpos($string, $char, $cursor);
19757  $value_end = $cursor;
19758  } else {
19759  // it's not quoted, end bound is whitespace
19760  $value_begin = $cursor;
19761  $cursor += strcspn($string, $this->_whitespace, $cursor);
19762  $value_end = $cursor;
19763  }
19764 
19765  // we reached a premature end
19766  if ($cursor === false) {
19767  $cursor = $size;
19768  $value_end = $cursor;
19769  }
19770 
19771  $value = substr($string, $value_begin, $value_end - $value_begin);
19772  if ($value === false) {
19773  $value = '';
19774  }
19775  $array[$key] = $this->parseAttr($value, $config);
19776  $cursor++;
19777  } else {
19778  // boolattr
19779  if ($key !== '') {
19780  $array[$key] = $key;
19781  } else {
19782  // purely theoretical
19783  if ($e) {
19784  $e->send(E_ERROR, 'Lexer: Missing attribute key');
19785  }
19786  }
19787  }
19788  }
19789  return $array;
19790  }
19791 }
19792 
19793 
19794 
19795 
19796 
19801 {
19806  public $data;
19807 
19811  public $is_whitespace = true;
19812 
19820  public function __construct($data, $line = null, $col = null)
19821  {
19822  $this->data = $data;
19823  $this->line = $line;
19824  $this->col = $col;
19825  }
19826 
19827  public function toTokenPair() {
19828  return array(new HTMLPurifier_Token_Comment($this->data, $this->line, $this->col), null);
19829  }
19830 }
19831 
19832 
19833 
19838 {
19847  public $name;
19848 
19853  public $attr = array();
19854 
19859  public $children = array();
19860 
19866  public $empty = false;
19867 
19868  public $endCol = null, $endLine = null, $endArmor = array();
19869 
19870  public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) {
19871  $this->name = $name;
19872  $this->attr = $attr;
19873  $this->line = $line;
19874  $this->col = $col;
19875  $this->armor = $armor;
19876  }
19877 
19878  public function toTokenPair() {
19879  // XXX inefficiency here, normalization is not necessary
19880  if ($this->empty) {
19881  return array(new HTMLPurifier_Token_Empty($this->name, $this->attr, $this->line, $this->col, $this->armor), null);
19882  } else {
19883  $start = new HTMLPurifier_Token_Start($this->name, $this->attr, $this->line, $this->col, $this->armor);
19884  $end = new HTMLPurifier_Token_End($this->name, array(), $this->endLine, $this->endCol, $this->endArmor);
19885  //$end->start = $start;
19886  return array($start, $end);
19887  }
19888  }
19889 }
19890 
19891 
19892 
19893 
19904 {
19905 
19911  public $name = '#PCDATA';
19912 
19916  public $data;
19923 
19932  public function __construct($data, $is_whitespace, $line = null, $col = null)
19933  {
19934  $this->data = $data;
19935  $this->is_whitespace = $is_whitespace;
19936  $this->line = $line;
19937  $this->col = $col;
19938  }
19939 
19940  public function toTokenPair() {
19941  return array(new HTMLPurifier_Token_Text($this->data, $this->line, $this->col), null);
19942  }
19943 }
19944 
19945 
19946 
19947 
19948 
19953 {
19954 
19959  protected $strategies = array();
19960 
19967  public function execute($tokens, $config, $context)
19968  {
19969  foreach ($this->strategies as $strategy) {
19970  $tokens = $strategy->execute($tokens, $config, $context);
19971  }
19972  return $tokens;
19973  }
19974 }
19975 
19976 
19977 
19978 
19979 
19984 {
19985  public function __construct()
19986  {
19987  $this->strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements();
19988  $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed();
19989  $this->strategies[] = new HTMLPurifier_Strategy_FixNesting();
19990  $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes();
19991  }
19992 }
19993 
19994 
19995 
19996 
19997 
20028 {
20029 
20036  public function execute($tokens, $config, $context)
20037  {
20038 
20039  //####################################################################//
20040  // Pre-processing
20041 
20042  // O(n) pass to convert to a tree, so that we can efficiently
20043  // refer to substrings
20044  $top_node = HTMLPurifier_Arborize::arborize($tokens, $config, $context);
20045 
20046  // get a copy of the HTML definition
20047  $definition = $config->getHTMLDefinition();
20048 
20049  $excludes_enabled = !$config->get('Core.DisableExcludes');
20050 
20051  // setup the context variable 'IsInline', for chameleon processing
20052  // is 'false' when we are not inline, 'true' when it must always
20053  // be inline, and an integer when it is inline for a certain
20054  // branch of the document tree
20055  $is_inline = $definition->info_parent_def->descendants_are_inline;
20056  $context->register('IsInline', $is_inline);
20057 
20058  // setup error collector
20059  $e =& $context->get('ErrorCollector', true);
20060 
20061  //####################################################################//
20062  // Loop initialization
20063 
20064  // stack that contains all elements that are excluded
20065  // it is organized by parent elements, similar to $stack,
20066  // but it is only populated when an element with exclusions is
20067  // processed, i.e. there won't be empty exclusions.
20068  $exclude_stack = array($definition->info_parent_def->excludes);
20069 
20070  // variable that contains the start token while we are processing
20071  // nodes. This enables error reporting to do its job
20072  $node = $top_node;
20073  // dummy token
20074  list($token, $d) = $node->toTokenPair();
20075  $context->register('CurrentNode', $node);
20076  $context->register('CurrentToken', $token);
20077 
20078  //####################################################################//
20079  // Loop
20080 
20081  // We need to implement a post-order traversal iteratively, to
20082  // avoid running into stack space limits. This is pretty tricky
20083  // to reason about, so we just manually stack-ify the recursive
20084  // variant:
20085  //
20086  // function f($node) {
20087  // foreach ($node->children as $child) {
20088  // f($child);
20089  // }
20090  // validate($node);
20091  // }
20092  //
20093  // Thus, we will represent a stack frame as array($node,
20094  // $is_inline, stack of children)
20095  // e.g. array_reverse($node->children) - already processed
20096  // children.
20097 
20098  $parent_def = $definition->info_parent_def;
20099  $stack = array(
20100  array($top_node,
20101  $parent_def->descendants_are_inline,
20102  $parent_def->excludes, // exclusions
20103  0)
20104  );
20105 
20106  while (!empty($stack)) {
20107  list($node, $is_inline, $excludes, $ix) = array_pop($stack);
20108  // recursive call
20109  $go = false;
20110  $def = empty($stack) ? $definition->info_parent_def : $definition->info[$node->name];
20111  while (isset($node->children[$ix])) {
20112  $child = $node->children[$ix++];
20113  if ($child instanceof HTMLPurifier_Node_Element) {
20114  $go = true;
20115  $stack[] = array($node, $is_inline, $excludes, $ix);
20116  $stack[] = array($child,
20117  // ToDo: I don't think it matters if it's def or
20118  // child_def, but double check this...
20119  $is_inline || $def->descendants_are_inline,
20120  empty($def->excludes) ? $excludes
20121  : array_merge($excludes, $def->excludes),
20122  0);
20123  break;
20124  }
20125  };
20126  if ($go) continue;
20127  list($token, $d) = $node->toTokenPair();
20128  // base case
20129  if ($excludes_enabled && isset($excludes[$node->name])) {
20130  $node->dead = true;
20131  if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded');
20132  } else {
20133  // XXX I suppose it would be slightly more efficient to
20134  // avoid the allocation here and have children
20135  // strategies handle it
20136  $children = array();
20137  foreach ($node->children as $child) {
20138  if (!$child->dead) $children[] = $child;
20139  }
20140  $result = $def->child->validateChildren($children, $config, $context);
20141  if ($result === true) {
20142  // nop
20143  $node->children = $children;
20144  } elseif ($result === false) {
20145  $node->dead = true;
20146  if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node removed');
20147  } else {
20148  $node->children = $result;
20149  if ($e) {
20150  // XXX This will miss mutations of internal nodes. Perhaps defer to the child validators
20151  if (empty($result) && !empty($children)) {
20152  $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed');
20153  } else if ($result != $children) {
20154  $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized');
20155  }
20156  }
20157  }
20158  }
20159  }
20160 
20161  //####################################################################//
20162  // Post-processing
20163 
20164  // remove context variables
20165  $context->destroy('IsInline');
20166  $context->destroy('CurrentNode');
20167  $context->destroy('CurrentToken');
20168 
20169  //####################################################################//
20170  // Return
20171 
20172  return HTMLPurifier_Arborize::flatten($node, $config, $context);
20173  }
20174 }
20175 
20176 
20177 
20178 
20179 
20192 {
20193 
20198  protected $tokens;
20199 
20204  protected $token;
20205 
20210  protected $zipper;
20211 
20216  protected $stack;
20217 
20222  protected $injectors;
20223 
20228  protected $config;
20229 
20234  protected $context;
20235 
20243  public function execute($tokens, $config, $context)
20244  {
20245  $definition = $config->getHTMLDefinition();
20246 
20247  // local variables
20248  $generator = new HTMLPurifier_Generator($config, $context);
20249  $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
20250  // used for autoclose early abortion
20251  $global_parent_allowed_elements = $definition->info_parent_def->child->getAllowedElements($config);
20252  $e = $context->get('ErrorCollector', true);
20253  $i = false; // injector index
20255  if ($token === NULL) {
20256  return array();
20257  }
20258  $reprocess = false; // whether or not to reprocess the same token
20259  $stack = array();
20260 
20261  // member variables
20262  $this->stack =& $stack;
20263  $this->tokens =& $tokens;
20264  $this->token =& $token;
20265  $this->zipper =& $zipper;
20266  $this->config = $config;
20267  $this->context = $context;
20268 
20269  // context variables
20270  $context->register('CurrentNesting', $stack);
20271  $context->register('InputZipper', $zipper);
20272  $context->register('CurrentToken', $token);
20273 
20274  // -- begin INJECTOR --
20275 
20276  $this->injectors = array();
20277 
20278  $injectors = $config->getBatch('AutoFormat');
20279  $def_injectors = $definition->info_injector;
20280  $custom_injectors = $injectors['Custom'];
20281  unset($injectors['Custom']); // special case
20282  foreach ($injectors as $injector => $b) {
20283  // XXX: Fix with a legitimate lookup table of enabled filters
20284  if (strpos($injector, '.') !== false) {
20285  continue;
20286  }
20287  $injector = "HTMLPurifier_Injector_$injector";
20288  if (!$b) {
20289  continue;
20290  }
20291  $this->injectors[] = new $injector;
20292  }
20293  foreach ($def_injectors as $injector) {
20294  // assumed to be objects
20295  $this->injectors[] = $injector;
20296  }
20297  foreach ($custom_injectors as $injector) {
20298  if (!$injector) {
20299  continue;
20300  }
20301  if (is_string($injector)) {
20302  $injector = "HTMLPurifier_Injector_$injector";
20303  $injector = new $injector;
20304  }
20305  $this->injectors[] = $injector;
20306  }
20307 
20308  // give the injectors references to the definition and context
20309  // variables for performance reasons
20310  foreach ($this->injectors as $ix => $injector) {
20311  $error = $injector->prepare($config, $context);
20312  if (!$error) {
20313  continue;
20314  }
20315  array_splice($this->injectors, $ix, 1); // rm the injector
20316  trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING);
20317  }
20318 
20319  // -- end INJECTOR --
20320 
20321  // a note on reprocessing:
20322  // In order to reduce code duplication, whenever some code needs
20323  // to make HTML changes in order to make things "correct", the
20324  // new HTML gets sent through the purifier, regardless of its
20325  // status. This means that if we add a start token, because it
20326  // was totally necessary, we don't have to update nesting; we just
20327  // punt ($reprocess = true; continue;) and it does that for us.
20328 
20329  // isset is in loop because $tokens size changes during loop exec
20330  for (;;
20331  // only increment if we don't need to reprocess
20332  $reprocess ? $reprocess = false : $token = $zipper->next($token)) {
20333 
20334  // check for a rewind
20335  if (is_int($i)) {
20336  // possibility: disable rewinding if the current token has a
20337  // rewind set on it already. This would offer protection from
20338  // infinite loop, but might hinder some advanced rewinding.
20339  $rewind_offset = $this->injectors[$i]->getRewindOffset();
20340  if (is_int($rewind_offset)) {
20341  for ($j = 0; $j < $rewind_offset; $j++) {
20342  if (empty($zipper->front)) break;
20343  $token = $zipper->prev($token);
20344  // indicate that other injectors should not process this token,
20345  // but we need to reprocess it. See Note [Injector skips]
20346  unset($token->skip[$i]);
20347  $token->rewind = $i;
20348  if ($token instanceof HTMLPurifier_Token_Start) {
20349  array_pop($this->stack);
20350  } elseif ($token instanceof HTMLPurifier_Token_End) {
20351  $this->stack[] = $token->start;
20352  }
20353  }
20354  }
20355  $i = false;
20356  }
20357 
20358  // handle case of document end
20359  if ($token === NULL) {
20360  // kill processing if stack is empty
20361  if (empty($this->stack)) {
20362  break;
20363  }
20364 
20365  // peek
20366  $top_nesting = array_pop($this->stack);
20367  $this->stack[] = $top_nesting;
20368 
20369  // send error [TagClosedSuppress]
20370  if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) {
20371  $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting);
20372  }
20373 
20374  // append, don't splice, since this is the end
20375  $token = new HTMLPurifier_Token_End($top_nesting->name);
20376 
20377  // punt!
20378  $reprocess = true;
20379  continue;
20380  }
20381 
20382  //echo '<br>'; printZipper($zipper, $token);//printTokens($this->stack);
20383  //flush();
20384 
20385  // quick-check: if it's not a tag, no need to process
20386  if (empty($token->is_tag)) {
20387  if ($token instanceof HTMLPurifier_Token_Text) {
20388  foreach ($this->injectors as $i => $injector) {
20389  if (isset($token->skip[$i])) {
20390  // See Note [Injector skips]
20391  continue;
20392  }
20393  if ($token->rewind !== null && $token->rewind !== $i) {
20394  continue;
20395  }
20396  // XXX fuckup
20397  $r = $token;
20398  $injector->handleText($r);
20399  $token = $this->processToken($r, $i);
20400  $reprocess = true;
20401  break;
20402  }
20403  }
20404  // another possibility is a comment
20405  continue;
20406  }
20407 
20408  if (isset($definition->info[$token->name])) {
20409  $type = $definition->info[$token->name]->child->type;
20410  } else {
20411  $type = false; // Type is unknown, treat accordingly
20412  }
20413 
20414  // quick tag checks: anything that's *not* an end tag
20415  $ok = false;
20416  if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) {
20417  // claims to be a start tag but is empty
20419  $token->name,
20420  $token->attr,
20421  $token->line,
20422  $token->col,
20423  $token->armor
20424  );
20425  $ok = true;
20426  } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) {
20427  // claims to be empty but really is a start tag
20428  // NB: this assignment is required
20429  $old_token = $token;
20430  $token = new HTMLPurifier_Token_End($token->name);
20431  $token = $this->insertBefore(
20432  new HTMLPurifier_Token_Start($old_token->name, $old_token->attr, $old_token->line, $old_token->col, $old_token->armor)
20433  );
20434  // punt (since we had to modify the input stream in a non-trivial way)
20435  $reprocess = true;
20436  continue;
20437  } elseif ($token instanceof HTMLPurifier_Token_Empty) {
20438  // real empty token
20439  $ok = true;
20440  } elseif ($token instanceof HTMLPurifier_Token_Start) {
20441  // start tag
20442 
20443  // ...unless they also have to close their parent
20444  if (!empty($this->stack)) {
20445 
20446  // Performance note: you might think that it's rather
20447  // inefficient, recalculating the autoclose information
20448  // for every tag that a token closes (since when we
20449  // do an autoclose, we push a new token into the
20450  // stream and then /process/ that, before
20451  // re-processing this token.) But this is
20452  // necessary, because an injector can make an
20453  // arbitrary transformations to the autoclosing
20454  // tokens we introduce, so things may have changed
20455  // in the meantime. Also, doing the inefficient thing is
20456  // "easy" to reason about (for certain perverse definitions
20457  // of "easy")
20458 
20459  $parent = array_pop($this->stack);
20460  $this->stack[] = $parent;
20461 
20462  $parent_def = null;
20463  $parent_elements = null;
20464  $autoclose = false;
20465  if (isset($definition->info[$parent->name])) {
20466  $parent_def = $definition->info[$parent->name];
20467  $parent_elements = $parent_def->child->getAllowedElements($config);
20468  $autoclose = !isset($parent_elements[$token->name]);
20469  }
20470 
20471  if ($autoclose && $definition->info[$token->name]->wrap) {
20472  // Check if an element can be wrapped by another
20473  // element to make it valid in a context (for
20474  // example, <ul><ul> needs a <li> in between)
20475  $wrapname = $definition->info[$token->name]->wrap;
20476  $wrapdef = $definition->info[$wrapname];
20477  $elements = $wrapdef->child->getAllowedElements($config);
20478  if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) {
20479  $newtoken = new HTMLPurifier_Token_Start($wrapname);
20480  $token = $this->insertBefore($newtoken);
20481  $reprocess = true;
20482  continue;
20483  }
20484  }
20485 
20486  $carryover = false;
20487  if ($autoclose && $parent_def->formatting) {
20488  $carryover = true;
20489  }
20490 
20491  if ($autoclose) {
20492  // check if this autoclose is doomed to fail
20493  // (this rechecks $parent, which his harmless)
20494  $autoclose_ok = isset($global_parent_allowed_elements[$token->name]);
20495  if (!$autoclose_ok) {
20496  foreach ($this->stack as $ancestor) {
20497  $elements = $definition->info[$ancestor->name]->child->getAllowedElements($config);
20498  if (isset($elements[$token->name])) {
20499  $autoclose_ok = true;
20500  break;
20501  }
20502  if ($definition->info[$token->name]->wrap) {
20503  $wrapname = $definition->info[$token->name]->wrap;
20504  $wrapdef = $definition->info[$wrapname];
20505  $wrap_elements = $wrapdef->child->getAllowedElements($config);
20506  if (isset($wrap_elements[$token->name]) && isset($elements[$wrapname])) {
20507  $autoclose_ok = true;
20508  break;
20509  }
20510  }
20511  }
20512  }
20513  if ($autoclose_ok) {
20514  // errors need to be updated
20515  $new_token = new HTMLPurifier_Token_End($parent->name);
20516  $new_token->start = $parent;
20517  // [TagClosedSuppress]
20518  if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) {
20519  if (!$carryover) {
20520  $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent);
20521  } else {
20522  $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent);
20523  }
20524  }
20525  if ($carryover) {
20526  $element = clone $parent;
20527  // [TagClosedAuto]
20528  $element->armor['MakeWellFormed_TagClosedError'] = true;
20529  $element->carryover = true;
20530  $token = $this->processToken(array($new_token, $token, $element));
20531  } else {
20532  $token = $this->insertBefore($new_token);
20533  }
20534  } else {
20535  $token = $this->remove();
20536  }
20537  $reprocess = true;
20538  continue;
20539  }
20540 
20541  }
20542  $ok = true;
20543  }
20544 
20545  if ($ok) {
20546  foreach ($this->injectors as $i => $injector) {
20547  if (isset($token->skip[$i])) {
20548  // See Note [Injector skips]
20549  continue;
20550  }
20551  if ($token->rewind !== null && $token->rewind !== $i) {
20552  continue;
20553  }
20554  $r = $token;
20555  $injector->handleElement($r);
20556  $token = $this->processToken($r, $i);
20557  $reprocess = true;
20558  break;
20559  }
20560  if (!$reprocess) {
20561  // ah, nothing interesting happened; do normal processing
20562  if ($token instanceof HTMLPurifier_Token_Start) {
20563  $this->stack[] = $token;
20564  } elseif ($token instanceof HTMLPurifier_Token_End) {
20565  throw new HTMLPurifier_Exception(
20566  'Improper handling of end tag in start code; possible error in MakeWellFormed'
20567  );
20568  }
20569  }
20570  continue;
20571  }
20572 
20573  // sanity check: we should be dealing with a closing tag
20574  if (!$token instanceof HTMLPurifier_Token_End) {
20575  throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier');
20576  }
20577 
20578  // make sure that we have something open
20579  if (empty($this->stack)) {
20580  if ($escape_invalid_tags) {
20581  if ($e) {
20582  $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');
20583  }
20584  $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token));
20585  } else {
20586  if ($e) {
20587  $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');
20588  }
20589  $token = $this->remove();
20590  }
20591  $reprocess = true;
20592  continue;
20593  }
20594 
20595  // first, check for the simplest case: everything closes neatly.
20596  // Eventually, everything passes through here; if there are problems
20597  // we modify the input stream accordingly and then punt, so that
20598  // the tokens get processed again.
20599  $current_parent = array_pop($this->stack);
20600  if ($current_parent->name == $token->name) {
20601  $token->start = $current_parent;
20602  foreach ($this->injectors as $i => $injector) {
20603  if (isset($token->skip[$i])) {
20604  // See Note [Injector skips]
20605  continue;
20606  }
20607  if ($token->rewind !== null && $token->rewind !== $i) {
20608  continue;
20609  }
20610  $r = $token;
20611  $injector->handleEnd($r);
20612  $token = $this->processToken($r, $i);
20613  $this->stack[] = $current_parent;
20614  $reprocess = true;
20615  break;
20616  }
20617  continue;
20618  }
20619 
20620  // okay, so we're trying to close the wrong tag
20621 
20622  // undo the pop previous pop
20623  $this->stack[] = $current_parent;
20624 
20625  // scroll back the entire nest, trying to find our tag.
20626  // (feature could be to specify how far you'd like to go)
20627  $size = count($this->stack);
20628  // -2 because -1 is the last element, but we already checked that
20629  $skipped_tags = false;
20630  for ($j = $size - 2; $j >= 0; $j--) {
20631  if ($this->stack[$j]->name == $token->name) {
20632  $skipped_tags = array_slice($this->stack, $j);
20633  break;
20634  }
20635  }
20636 
20637  // we didn't find the tag, so remove
20638  if ($skipped_tags === false) {
20639  if ($escape_invalid_tags) {
20640  if ($e) {
20641  $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');
20642  }
20643  $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token));
20644  } else {
20645  if ($e) {
20646  $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed');
20647  }
20648  $token = $this->remove();
20649  }
20650  $reprocess = true;
20651  continue;
20652  }
20653 
20654  // do errors, in REVERSE $j order: a,b,c with </a></b></c>
20655  $c = count($skipped_tags);
20656  if ($e) {
20657  for ($j = $c - 1; $j > 0; $j--) {
20658  // notice we exclude $j == 0, i.e. the current ending tag, from
20659  // the errors... [TagClosedSuppress]
20660  if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) {
20661  $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]);
20662  }
20663  }
20664  }
20665 
20666  // insert tags, in FORWARD $j order: c,b,a with </a></b></c>
20667  $replace = array($token);
20668  for ($j = 1; $j < $c; $j++) {
20669  // ...as well as from the insertions
20670  $new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name);
20671  $new_token->start = $skipped_tags[$j];
20672  array_unshift($replace, $new_token);
20673  if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) {
20674  // [TagClosedAuto]
20675  $element = clone $skipped_tags[$j];
20676  $element->carryover = true;
20677  $element->armor['MakeWellFormed_TagClosedError'] = true;
20678  $replace[] = $element;
20679  }
20680  }
20681  $token = $this->processToken($replace);
20682  $reprocess = true;
20683  continue;
20684  }
20685 
20686  $context->destroy('CurrentToken');
20687  $context->destroy('CurrentNesting');
20688  $context->destroy('InputZipper');
20689 
20690  unset($this->injectors, $this->stack, $this->tokens);
20691  return $zipper->toArray($token);
20692  }
20693 
20715  protected function processToken($token, $injector = -1)
20716  {
20717  // Zend OpCache miscompiles $token = array($token), so
20718  // avoid this pattern. See: https://github.com/ezyang/htmlpurifier/issues/108
20719 
20720  // normalize forms of token
20721  if (is_object($token)) {
20722  $tmp = $token;
20723  $token = array(1, $tmp);
20724  }
20725  if (is_int($token)) {
20726  $tmp = $token;
20727  $token = array($tmp);
20728  }
20729  if ($token === false) {
20730  $token = array(1);
20731  }
20732  if (!is_array($token)) {
20733  throw new HTMLPurifier_Exception('Invalid token type from injector');
20734  }
20735  if (!is_int($token[0])) {
20736  array_unshift($token, 1);
20737  }
20738  if ($token[0] === 0) {
20739  throw new HTMLPurifier_Exception('Deleting zero tokens is not valid');
20740  }
20741 
20742  // $token is now an array with the following form:
20743  // array(number nodes to delete, new node 1, new node 2, ...)
20744 
20745  $delete = array_shift($token);
20746  list($old, $r) = $this->zipper->splice($this->token, $delete, $token);
20747 
20748  if ($injector > -1) {
20749  // See Note [Injector skips]
20750  // Determine appropriate skips. Here's what the code does:
20751  // *If* we deleted one or more tokens, copy the skips
20752  // of those tokens into the skips of the new tokens (in $token).
20753  // Also, mark the newly inserted tokens as having come from
20754  // $injector.
20755  $oldskip = isset($old[0]) ? $old[0]->skip : array();
20756  foreach ($token as $object) {
20757  $object->skip = $oldskip;
20758  $object->skip[$injector] = true;
20759  }
20760  }
20761 
20762  return $r;
20763 
20764  }
20765 
20771  private function insertBefore($token)
20772  {
20773  // NB not $this->zipper->insertBefore(), due to positioning
20774  // differences
20775  $splice = $this->zipper->splice($this->token, 0, array($token));
20776 
20777  return $splice[1];
20778  }
20779 
20784  private function remove()
20785  {
20786  return $this->zipper->delete();
20787  }
20788 }
20789 
20790 // Note [Injector skips]
20791 // ~~~~~~~~~~~~~~~~~~~~~
20792 // When I originally designed this class, the idea behind the 'skip'
20793 // property of HTMLPurifier_Token was to help avoid infinite loops
20794 // in injector processing. For example, suppose you wrote an injector
20795 // that bolded swear words. Naively, you might write it so that
20796 // whenever you saw ****, you replaced it with <strong>****</strong>.
20797 //
20798 // When this happens, we will reprocess all of the tokens with the
20799 // other injectors. Now there is an opportunity for infinite loop:
20800 // if we rerun the swear-word injector on these tokens, we might
20801 // see **** and then reprocess again to get
20802 // <strong><strong>****</strong></strong> ad infinitum.
20803 //
20804 // Thus, the idea of a skip is that once we process a token with
20805 // an injector, we mark all of those tokens as having "come from"
20806 // the injector, and we never run the injector again on these
20807 // tokens.
20808 //
20809 // There were two more complications, however:
20810 //
20811 // - With HTMLPurifier_Injector_RemoveEmpty, we noticed that if
20812 // you had <b><i></i></b>, after you removed the <i></i>, you
20813 // really would like this injector to go back and reprocess
20814 // the <b> tag, discovering that it is now empty and can be
20815 // removed. So we reintroduced the possibility of infinite looping
20816 // by adding a "rewind" function, which let you go back to an
20817 // earlier point in the token stream and reprocess it with injectors.
20818 // Needless to say, we need to UN-skip the token so it gets
20819 // reprocessed.
20820 //
20821 // - Suppose that you successfuly process a token, replace it with
20822 // one with your skip mark, but now another injector wants to
20823 // process the skipped token with another token. Should you continue
20824 // to skip that new token, or reprocess it? If you reprocess,
20825 // you can end up with an infinite loop where one injector converts
20826 // <a> to <b>, and then another injector converts it back. So
20827 // we inherit the skips, but for some reason, I thought that we
20828 // should inherit the skip from the first token of the token
20829 // that we deleted. Why? Well, it seems to work OK.
20830 //
20831 // If I were to redesign this functionality, I would absolutely not
20832 // go about doing it this way: the semantics are just not very well
20833 // defined, and in any case you probably wanted to operate on trees,
20834 // not token streams.
20835 
20836 
20837 
20838 
20839 
20849 {
20850 
20857  public function execute($tokens, $config, $context)
20858  {
20859  $definition = $config->getHTMLDefinition();
20860  $generator = new HTMLPurifier_Generator($config, $context);
20861  $result = array();
20862 
20863  $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
20864  $remove_invalid_img = $config->get('Core.RemoveInvalidImg');
20865 
20866  // currently only used to determine if comments should be kept
20867  $trusted = $config->get('HTML.Trusted');
20868  $comment_lookup = $config->get('HTML.AllowedComments');
20869  $comment_regexp = $config->get('HTML.AllowedCommentsRegexp');
20870  $check_comments = $comment_lookup !== array() || $comment_regexp !== null;
20871 
20872  $remove_script_contents = $config->get('Core.RemoveScriptContents');
20873  $hidden_elements = $config->get('Core.HiddenElements');
20874 
20875  // remove script contents compatibility
20876  if ($remove_script_contents === true) {
20877  $hidden_elements['script'] = true;
20878  } elseif ($remove_script_contents === false && isset($hidden_elements['script'])) {
20879  unset($hidden_elements['script']);
20880  }
20881 
20882  $attr_validator = new HTMLPurifier_AttrValidator();
20883 
20884  // removes tokens until it reaches a closing tag with its value
20885  $remove_until = false;
20886 
20887  // converts comments into text tokens when this is equal to a tag name
20888  $textify_comments = false;
20889 
20890  $token = false;
20891  $context->register('CurrentToken', $token);
20892 
20893  $e = false;
20894  if ($config->get('Core.CollectErrors')) {
20895  $e =& $context->get('ErrorCollector');
20896  }
20897 
20898  foreach ($tokens as $token) {
20899  if ($remove_until) {
20900  if (empty($token->is_tag) || $token->name !== $remove_until) {
20901  continue;
20902  }
20903  }
20904  if (!empty($token->is_tag)) {
20905  // DEFINITION CALL
20906 
20907  // before any processing, try to transform the element
20908  if (isset($definition->info_tag_transform[$token->name])) {
20909  $original_name = $token->name;
20910  // there is a transformation for this tag
20911  // DEFINITION CALL
20912  $token = $definition->
20913  info_tag_transform[$token->name]->transform($token, $config, $context);
20914  if ($e) {
20915  $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name);
20916  }
20917  }
20918 
20919  if (isset($definition->info[$token->name])) {
20920  // mostly everything's good, but
20921  // we need to make sure required attributes are in order
20922  if (($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) &&
20923  $definition->info[$token->name]->required_attr &&
20924  ($token->name != 'img' || $remove_invalid_img) // ensure config option still works
20925  ) {
20926  $attr_validator->validateToken($token, $config, $context);
20927  $ok = true;
20928  foreach ($definition->info[$token->name]->required_attr as $name) {
20929  if (!isset($token->attr[$name])) {
20930  $ok = false;
20931  break;
20932  }
20933  }
20934  if (!$ok) {
20935  if ($e) {
20936  $e->send(
20937  E_ERROR,
20938  'Strategy_RemoveForeignElements: Missing required attribute',
20939  $name
20940  );
20941  }
20942  continue;
20943  }
20944  $token->armor['ValidateAttributes'] = true;
20945  }
20946 
20947  if (isset($hidden_elements[$token->name]) && $token instanceof HTMLPurifier_Token_Start) {
20948  $textify_comments = $token->name;
20949  } elseif ($token->name === $textify_comments && $token instanceof HTMLPurifier_Token_End) {
20950  $textify_comments = false;
20951  }
20952 
20953  } elseif ($escape_invalid_tags) {
20954  // invalid tag, generate HTML representation and insert in
20955  if ($e) {
20956  $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text');
20957  }
20958  $token = new HTMLPurifier_Token_Text(
20959  $generator->generateFromToken($token)
20960  );
20961  } else {
20962  // check if we need to destroy all of the tag's children
20963  // CAN BE GENERICIZED
20964  if (isset($hidden_elements[$token->name])) {
20965  if ($token instanceof HTMLPurifier_Token_Start) {
20966  $remove_until = $token->name;
20967  } elseif ($token instanceof HTMLPurifier_Token_Empty) {
20968  // do nothing: we're still looking
20969  } else {
20970  $remove_until = false;
20971  }
20972  if ($e) {
20973  $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed');
20974  }
20975  } else {
20976  if ($e) {
20977  $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed');
20978  }
20979  }
20980  continue;
20981  }
20982  } elseif ($token instanceof HTMLPurifier_Token_Comment) {
20983  // textify comments in script tags when they are allowed
20984  if ($textify_comments !== false) {
20985  $data = $token->data;
20986  $token = new HTMLPurifier_Token_Text($data);
20987  } elseif ($trusted || $check_comments) {
20988  // always cleanup comments
20989  $trailing_hyphen = false;
20990  if ($e) {
20991  // perform check whether or not there's a trailing hyphen
20992  if (substr($token->data, -1) == '-') {
20993  $trailing_hyphen = true;
20994  }
20995  }
20996  $token->data = rtrim($token->data, '-');
20997  $found_double_hyphen = false;
20998  while (strpos($token->data, '--') !== false) {
20999  $found_double_hyphen = true;
21000  $token->data = str_replace('--', '-', $token->data);
21001  }
21002  if ($trusted || !empty($comment_lookup[trim($token->data)]) ||
21003  ($comment_regexp !== null && preg_match($comment_regexp, trim($token->data)))) {
21004  // OK good
21005  if ($e) {
21006  if ($trailing_hyphen) {
21007  $e->send(
21008  E_NOTICE,
21009  'Strategy_RemoveForeignElements: Trailing hyphen in comment removed'
21010  );
21011  }
21012  if ($found_double_hyphen) {
21013  $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed');
21014  }
21015  }
21016  } else {
21017  if ($e) {
21018  $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
21019  }
21020  continue;
21021  }
21022  } else {
21023  // strip comments
21024  if ($e) {
21025  $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
21026  }
21027  continue;
21028  }
21029  } elseif ($token instanceof HTMLPurifier_Token_Text) {
21030  } else {
21031  continue;
21032  }
21033  $result[] = $token;
21034  }
21035  if ($remove_until && $e) {
21036  // we removed tokens until the end, throw error
21037  $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until);
21038  }
21039  $context->destroy('CurrentToken');
21040  return $result;
21041  }
21042 }
21043 
21044 
21045 
21046 
21047 
21053 {
21054 
21061  public function execute($tokens, $config, $context)
21062  {
21063  // setup validator
21064  $validator = new HTMLPurifier_AttrValidator();
21065 
21066  $token = false;
21067  $context->register('CurrentToken', $token);
21068 
21069  foreach ($tokens as $key => $token) {
21070 
21071  // only process tokens that have attributes,
21072  // namely start and empty tags
21073  if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) {
21074  continue;
21075  }
21076 
21077  // skip tokens that are armored
21078  if (!empty($token->armor['ValidateAttributes'])) {
21079  continue;
21080  }
21081 
21082  // note that we have no facilities here for removing tokens
21083  $validator->validateToken($token, $config, $context);
21084  }
21085  $context->destroy('CurrentToken');
21086  return $tokens;
21087  }
21088 }
21089 
21090 
21091 
21092 
21093 
21110 {
21114  public $transform_to = 'span';
21115 
21119  protected $_size_lookup = array(
21120  '0' => 'xx-small',
21121  '1' => 'xx-small',
21122  '2' => 'small',
21123  '3' => 'medium',
21124  '4' => 'large',
21125  '5' => 'x-large',
21126  '6' => 'xx-large',
21127  '7' => '300%',
21128  '-1' => 'smaller',
21129  '-2' => '60%',
21130  '+1' => 'larger',
21131  '+2' => '150%',
21132  '+3' => '200%',
21133  '+4' => '300%'
21134  );
21135 
21142  public function transform($tag, $config, $context)
21143  {
21144  if ($tag instanceof HTMLPurifier_Token_End) {
21145  $new_tag = clone $tag;
21146  $new_tag->name = $this->transform_to;
21147  return $new_tag;
21148  }
21149 
21150  $attr = $tag->attr;
21151  $prepend_style = '';
21152 
21153  // handle color transform
21154  if (isset($attr['color'])) {
21155  $prepend_style .= 'color:' . $attr['color'] . ';';
21156  unset($attr['color']);
21157  }
21158 
21159  // handle face transform
21160  if (isset($attr['face'])) {
21161  $prepend_style .= 'font-family:' . $attr['face'] . ';';
21162  unset($attr['face']);
21163  }
21164 
21165  // handle size transform
21166  if (isset($attr['size'])) {
21167  // normalize large numbers
21168  if ($attr['size'] !== '') {
21169  if ($attr['size'][0] == '+' || $attr['size'][0] == '-') {
21170  $size = (int)$attr['size'];
21171  if ($size < -2) {
21172  $attr['size'] = '-2';
21173  }
21174  if ($size > 4) {
21175  $attr['size'] = '+4';
21176  }
21177  } else {
21178  $size = (int)$attr['size'];
21179  if ($size > 7) {
21180  $attr['size'] = '7';
21181  }
21182  }
21183  }
21184  if (isset($this->_size_lookup[$attr['size']])) {
21185  $prepend_style .= 'font-size:' .
21186  $this->_size_lookup[$attr['size']] . ';';
21187  }
21188  unset($attr['size']);
21189  }
21190 
21191  if ($prepend_style) {
21192  $attr['style'] = isset($attr['style']) ?
21193  $prepend_style . $attr['style'] :
21194  $prepend_style;
21195  }
21196 
21197  $new_tag = clone $tag;
21198  $new_tag->name = $this->transform_to;
21199  $new_tag->attr = $attr;
21200 
21201  return $new_tag;
21202  }
21203 }
21204 
21205 
21206 
21207 
21208 
21215 {
21219  protected $style;
21220 
21225  public function __construct($transform_to, $style = null)
21226  {
21227  $this->transform_to = $transform_to;
21228  $this->style = $style;
21229  }
21230 
21237  public function transform($tag, $config, $context)
21238  {
21239  $new_tag = clone $tag;
21240  $new_tag->name = $this->transform_to;
21241  if (!is_null($this->style) &&
21242  ($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty)
21243  ) {
21244  $this->prependCSS($new_tag->attr, $this->style);
21245  }
21246  return $new_tag;
21247  }
21248 }
21249 
21250 
21251 
21252 
21253 
21258 {
21263  public $data;
21264 
21268  public $is_whitespace = true;
21269 
21277  public function __construct($data, $line = null, $col = null)
21278  {
21279  $this->data = $data;
21280  $this->line = $line;
21281  $this->col = $col;
21282  }
21283 
21284  public function toNode() {
21285  return new HTMLPurifier_Node_Comment($this->data, $this->line, $this->col);
21286  }
21287 }
21288 
21289 
21290 
21291 
21292 
21297 {
21305  public $is_tag = true;
21306 
21315  public $name;
21316 
21321  public $attr = array();
21322 
21332  public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array())
21333  {
21334  $this->name = ctype_lower($name) ? $name : strtolower($name);
21335  foreach ($attr as $key => $value) {
21336  // normalization only necessary when key is not lowercase
21337  if (!ctype_lower($key)) {
21338  $new_key = strtolower($key);
21339  if (!isset($attr[$new_key])) {
21340  $attr[$new_key] = $attr[$key];
21341  }
21342  if ($new_key !== $key) {
21343  unset($attr[$key]);
21344  }
21345  }
21346  }
21347  $this->attr = $attr;
21348  $this->line = $line;
21349  $this->col = $col;
21350  $this->armor = $armor;
21351  }
21352 
21353  public function toNode() {
21354  return new HTMLPurifier_Node_Element($this->name, $this->attr, $this->line, $this->col, $this->armor);
21355  }
21356 }
21357 
21358 
21359 
21360 
21361 
21366 {
21367  public function toNode() {
21368  $n = parent::toNode();
21369  $n->empty = true;
21370  return $n;
21371  }
21372 }
21373 
21374 
21375 
21376 
21377 
21386 {
21392  public $start;
21393 
21394  public function toNode() {
21395  throw new Exception("HTMLPurifier_Token_End->toNode not supported!");
21396  }
21397 }
21398 
21399 
21400 
21401 
21402 
21407 {
21408 }
21409 
21410 
21411 
21412 
21413 
21424 {
21425 
21429  public $name = '#PCDATA';
21435  public $data;
21442 
21451  public function __construct($data, $line = null, $col = null)
21452  {
21453  $this->data = $data;
21454  $this->is_whitespace = ctype_space($data);
21455  $this->line = $line;
21456  $this->col = $col;
21457  }
21458 
21459  public function toNode() {
21460  return new HTMLPurifier_Node_Text($this->data, $this->is_whitespace, $this->line, $this->col);
21461  }
21462 }
21463 
21464 
21465 
21466 
21467 
21469 {
21473  public $name = 'DisableExternal';
21474 
21478  protected $ourHostParts = false;
21479 
21484  public function prepare($config)
21485  {
21486  $our_host = $config->getDefinition('URI')->host;
21487  if ($our_host !== null) {
21488  $this->ourHostParts = array_reverse(explode('.', $our_host));
21489  }
21490  }
21491 
21498  public function filter(&$uri, $config, $context)
21499  {
21500  if (is_null($uri->host)) {
21501  return true;
21502  }
21503  if ($this->ourHostParts === false) {
21504  return false;
21505  }
21506  $host_parts = array_reverse(explode('.', $uri->host));
21507  foreach ($this->ourHostParts as $i => $x) {
21508  if (!isset($host_parts[$i])) {
21509  return false;
21510  }
21511  if ($host_parts[$i] != $this->ourHostParts[$i]) {
21512  return false;
21513  }
21514  }
21515  return true;
21516  }
21517 }
21518 
21519 
21520 
21521 
21522 
21524 {
21528  public $name = 'DisableExternalResources';
21529 
21536  public function filter(&$uri, $config, $context)
21537  {
21538  if (!$context->get('EmbeddedURI', true)) {
21539  return true;
21540  }
21541  return parent::filter($uri, $config, $context);
21542  }
21543 }
21544 
21545 
21546 
21547 
21548 
21550 {
21554  public $name = 'DisableResources';
21555 
21562  public function filter(&$uri, $config, $context)
21563  {
21564  return !$context->get('EmbeddedURI', true);
21565  }
21566 }
21567 
21568 
21569 
21570 
21571 
21572 // It's not clear to me whether or not Punycode means that hostnames
21573 // do not have canonical forms anymore. As far as I can tell, it's
21574 // not a problem (punycoding should be identity when no Unicode
21575 // points are involved), but I'm not 100% sure
21577 {
21581  public $name = 'HostBlacklist';
21582 
21586  protected $blacklist = array();
21587 
21592  public function prepare($config)
21593  {
21594  $this->blacklist = $config->get('URI.HostBlacklist');
21595  return true;
21596  }
21597 
21604  public function filter(&$uri, $config, $context)
21605  {
21606  foreach ($this->blacklist as $blacklisted_host_fragment) {
21607  if (strpos($uri->host, $blacklisted_host_fragment) !== false) {
21608  return false;
21609  }
21610  }
21611  return true;
21612  }
21613 }
21614 
21615 
21616 
21617 
21618 
21619 // does not support network paths
21620 
21622 {
21626  public $name = 'MakeAbsolute';
21627 
21631  protected $base;
21632 
21636  protected $basePathStack = array();
21637 
21642  public function prepare($config)
21643  {
21644  $def = $config->getDefinition('URI');
21645  $this->base = $def->base;
21646  if (is_null($this->base)) {
21647  trigger_error(
21648  'URI.MakeAbsolute is being ignored due to lack of ' .
21649  'value for URI.Base configuration',
21650  E_USER_WARNING
21651  );
21652  return false;
21653  }
21654  $this->base->fragment = null; // fragment is invalid for base URI
21655  $stack = explode('/', $this->base->path);
21656  array_pop($stack); // discard last segment
21657  $stack = $this->_collapseStack($stack); // do pre-parsing
21658  $this->basePathStack = $stack;
21659  return true;
21660  }
21661 
21668  public function filter(&$uri, $config, $context)
21669  {
21670  if (is_null($this->base)) {
21671  return true;
21672  } // abort early
21673  if ($uri->path === '' && is_null($uri->scheme) &&
21674  is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) {
21675  // reference to current document
21676  $uri = clone $this->base;
21677  return true;
21678  }
21679  if (!is_null($uri->scheme)) {
21680  // absolute URI already: don't change
21681  if (!is_null($uri->host)) {
21682  return true;
21683  }
21684  $scheme_obj = $uri->getSchemeObj($config, $context);
21685  if (!$scheme_obj) {
21686  // scheme not recognized
21687  return false;
21688  }
21689  if (!$scheme_obj->hierarchical) {
21690  // non-hierarchal URI with explicit scheme, don't change
21691  return true;
21692  }
21693  // special case: had a scheme but always is hierarchical and had no authority
21694  }
21695  if (!is_null($uri->host)) {
21696  // network path, don't bother
21697  return true;
21698  }
21699  if ($uri->path === '') {
21700  $uri->path = $this->base->path;
21701  } elseif ($uri->path[0] !== '/') {
21702  // relative path, needs more complicated processing
21703  $stack = explode('/', $uri->path);
21704  $new_stack = array_merge($this->basePathStack, $stack);
21705  if ($new_stack[0] !== '' && !is_null($this->base->host)) {
21706  array_unshift($new_stack, '');
21707  }
21708  $new_stack = $this->_collapseStack($new_stack);
21709  $uri->path = implode('/', $new_stack);
21710  } else {
21711  // absolute path, but still we should collapse
21712  $uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path)));
21713  }
21714  // re-combine
21715  $uri->scheme = $this->base->scheme;
21716  if (is_null($uri->userinfo)) {
21717  $uri->userinfo = $this->base->userinfo;
21718  }
21719  if (is_null($uri->host)) {
21720  $uri->host = $this->base->host;
21721  }
21722  if (is_null($uri->port)) {
21723  $uri->port = $this->base->port;
21724  }
21725  return true;
21726  }
21727 
21733  private function _collapseStack($stack)
21734  {
21735  $result = array();
21736  $is_folder = false;
21737  for ($i = 0; isset($stack[$i]); $i++) {
21738  $is_folder = false;
21739  // absorb an internally duplicated slash
21740  if ($stack[$i] == '' && $i && isset($stack[$i + 1])) {
21741  continue;
21742  }
21743  if ($stack[$i] == '..') {
21744  if (!empty($result)) {
21745  $segment = array_pop($result);
21746  if ($segment === '' && empty($result)) {
21747  // error case: attempted to back out too far:
21748  // restore the leading slash
21749  $result[] = '';
21750  } elseif ($segment === '..') {
21751  $result[] = '..'; // cannot remove .. with ..
21752  }
21753  } else {
21754  // relative path, preserve the double-dots
21755  $result[] = '..';
21756  }
21757  $is_folder = true;
21758  continue;
21759  }
21760  if ($stack[$i] == '.') {
21761  // silently absorb
21762  $is_folder = true;
21763  continue;
21764  }
21765  $result[] = $stack[$i];
21766  }
21767  if ($is_folder) {
21768  $result[] = '';
21769  }
21770  return $result;
21771  }
21772 }
21773 
21774 
21775 
21776 
21777 
21779 {
21783  public $name = 'Munge';
21784 
21788  public $post = true;
21789 
21793  private $target;
21794 
21798  private $parser;
21799 
21803  private $doEmbed;
21804 
21808  private $secretKey;
21809 
21813  protected $replace = array();
21814 
21819  public function prepare($config)
21820  {
21821  $this->target = $config->get('URI.' . $this->name);
21822  $this->parser = new HTMLPurifier_URIParser();
21823  $this->doEmbed = $config->get('URI.MungeResources');
21824  $this->secretKey = $config->get('URI.MungeSecretKey');
21825  if ($this->secretKey && !function_exists('hash_hmac')) {
21826  throw new Exception("Cannot use %URI.MungeSecretKey without hash_hmac support.");
21827  }
21828  return true;
21829  }
21830 
21837  public function filter(&$uri, $config, $context)
21838  {
21839  if ($context->get('EmbeddedURI', true) && !$this->doEmbed) {
21840  return true;
21841  }
21842 
21843  $scheme_obj = $uri->getSchemeObj($config, $context);
21844  if (!$scheme_obj) {
21845  return true;
21846  } // ignore unknown schemes, maybe another postfilter did it
21847  if (!$scheme_obj->browsable) {
21848  return true;
21849  } // ignore non-browseable schemes, since we can't munge those in a reasonable way
21850  if ($uri->isBenign($config, $context)) {
21851  return true;
21852  } // don't redirect if a benign URL
21853 
21854  $this->makeReplace($uri, $config, $context);
21855  $this->replace = array_map('rawurlencode', $this->replace);
21856 
21857  $new_uri = strtr($this->target, $this->replace);
21858  $new_uri = $this->parser->parse($new_uri);
21859  // don't redirect if the target host is the same as the
21860  // starting host
21861  if ($uri->host === $new_uri->host) {
21862  return true;
21863  }
21864  $uri = $new_uri; // overwrite
21865  return true;
21866  }
21867 
21873  protected function makeReplace($uri, $config, $context)
21874  {
21875  $string = $uri->toString();
21876  // always available
21877  $this->replace['%s'] = $string;
21878  $this->replace['%r'] = $context->get('EmbeddedURI', true);
21879  $token = $context->get('CurrentToken', true);
21880  $this->replace['%n'] = $token ? $token->name : null;
21881  $this->replace['%m'] = $context->get('CurrentAttr', true);
21882  $this->replace['%p'] = $context->get('CurrentCSSProperty', true);
21883  // not always available
21884  if ($this->secretKey) {
21885  $this->replace['%t'] = hash_hmac("sha256", $string, $this->secretKey);
21886  }
21887  }
21888 }
21889 
21890 
21891 
21892 
21893 
21901 {
21905  public $name = 'SafeIframe';
21906 
21910  public $always_load = true;
21911 
21915  protected $regexp = null;
21916 
21917  // XXX: The not so good bit about how this is all set up now is we
21918  // can't check HTML.SafeIframe in the 'prepare' step: we have to
21919  // defer till the actual filtering.
21924  public function prepare($config)
21925  {
21926  $this->regexp = $config->get('URI.SafeIframeRegexp');
21927  return true;
21928  }
21929 
21936  public function filter(&$uri, $config, $context)
21937  {
21938  // check if filter not applicable
21939  if (!$config->get('HTML.SafeIframe')) {
21940  return true;
21941  }
21942  // check if the filter should actually trigger
21943  if (!$context->get('EmbeddedURI', true)) {
21944  return true;
21945  }
21946  $token = $context->get('CurrentToken', true);
21947  if (!($token && $token->name == 'iframe')) {
21948  return true;
21949  }
21950  // check if we actually have some whitelists enabled
21951  if ($this->regexp === null) {
21952  return false;
21953  }
21954  // actually check the whitelists
21955  return preg_match($this->regexp, $uri->toString());
21956  }
21957 }
21958 
21959 
21960 
21961 
21962 
21967 {
21971  public $browsable = true;
21972 
21976  public $allowed_types = array(
21977  // you better write validation code for other types if you
21978  // decide to allow them
21979  'image/jpeg' => true,
21980  'image/gif' => true,
21981  'image/png' => true,
21982  );
21983  // this is actually irrelevant since we only write out the path
21984  // component
21988  public $may_omit_host = true;
21989 
21996  public function doValidate(&$uri, $config, $context)
21997  {
21998  $result = explode(',', $uri->path, 2);
21999  $is_base64 = false;
22000  $charset = null;
22001  $content_type = null;
22002  if (count($result) == 2) {
22003  list($metadata, $data) = $result;
22004  // do some legwork on the metadata
22005  $metas = explode(';', $metadata);
22006  while (!empty($metas)) {
22007  $cur = array_shift($metas);
22008  if ($cur == 'base64') {
22009  $is_base64 = true;
22010  break;
22011  }
22012  if (substr($cur, 0, 8) == 'charset=') {
22013  // doesn't match if there are arbitrary spaces, but
22014  // whatever dude
22015  if ($charset !== null) {
22016  continue;
22017  } // garbage
22018  $charset = substr($cur, 8); // not used
22019  } else {
22020  if ($content_type !== null) {
22021  continue;
22022  } // garbage
22023  $content_type = $cur;
22024  }
22025  }
22026  } else {
22027  $data = $result[0];
22028  }
22029  if ($content_type !== null && empty($this->allowed_types[$content_type])) {
22030  return false;
22031  }
22032  if ($charset !== null) {
22033  // error; we don't allow plaintext stuff
22034  $charset = null;
22035  }
22036  $data = rawurldecode($data);
22037  if ($is_base64) {
22038  $raw_data = base64_decode($data);
22039  } else {
22040  $raw_data = $data;
22041  }
22042  if ( strlen($raw_data) < 12 ) {
22043  // error; exif_imagetype throws exception with small files,
22044  // and this likely indicates a corrupt URI/failed parse anyway
22045  return false;
22046  }
22047  // XXX probably want to refactor this into a general mechanism
22048  // for filtering arbitrary content types
22049  if (function_exists('sys_get_temp_dir')) {
22050  $file = tempnam(sys_get_temp_dir(), "");
22051  } else {
22052  $file = tempnam("/tmp", "");
22053  }
22054  file_put_contents($file, $raw_data);
22055  if (function_exists('exif_imagetype')) {
22056  $image_code = exif_imagetype($file);
22057  unlink($file);
22058  } elseif (function_exists('getimagesize')) {
22059  set_error_handler(array($this, 'muteErrorHandler'));
22060  $info = getimagesize($file);
22061  restore_error_handler();
22062  unlink($file);
22063  if ($info == false) {
22064  return false;
22065  }
22066  $image_code = $info[2];
22067  } else {
22068  trigger_error("could not find exif_imagetype or getimagesize functions", E_USER_ERROR);
22069  }
22070  $real_content_type = image_type_to_mime_type($image_code);
22071  if ($real_content_type != $content_type) {
22072  // we're nice guys; if the content type is something else we
22073  // support, change it over
22074  if (empty($this->allowed_types[$real_content_type])) {
22075  return false;
22076  }
22077  $content_type = $real_content_type;
22078  }
22079  // ok, it's kosher, rewrite what we need
22080  $uri->userinfo = null;
22081  $uri->host = null;
22082  $uri->port = null;
22083  $uri->fragment = null;
22084  $uri->query = null;
22085  $uri->path = "$content_type;base64," . base64_encode($raw_data);
22086  return true;
22087  }
22088 
22093  public function muteErrorHandler($errno, $errstr)
22094  {
22095  }
22096 }
22097 
22098 
22099 
22104 {
22110  public $browsable = false;
22111 
22120  public $may_omit_host = true;
22121 
22128  public function doValidate(&$uri, $config, $context)
22129  {
22130  // Authentication method is not supported
22131  $uri->userinfo = null;
22132  // file:// makes no provisions for accessing the resource
22133  $uri->port = null;
22134  // While it seems to work on Firefox, the querystring has
22135  // no possible effect and is thus stripped.
22136  $uri->query = null;
22137  return true;
22138  }
22139 }
22140 
22141 
22142 
22143 
22144 
22149 {
22153  public $default_port = 21;
22154 
22158  public $browsable = true; // usually
22159 
22163  public $hierarchical = true;
22164 
22171  public function doValidate(&$uri, $config, $context)
22172  {
22173  $uri->query = null;
22174 
22175  // typecode check
22176  $semicolon_pos = strrpos($uri->path, ';'); // reverse
22177  if ($semicolon_pos !== false) {
22178  $type = substr($uri->path, $semicolon_pos + 1); // no semicolon
22179  $uri->path = substr($uri->path, 0, $semicolon_pos);
22180  $type_ret = '';
22181  if (strpos($type, '=') !== false) {
22182  // figure out whether or not the declaration is correct
22183  list($key, $typecode) = explode('=', $type, 2);
22184  if ($key !== 'type') {
22185  // invalid key, tack it back on encoded
22186  $uri->path .= '%3B' . $type;
22187  } elseif ($typecode === 'a' || $typecode === 'i' || $typecode === 'd') {
22188  $type_ret = ";type=$typecode";
22189  }
22190  } else {
22191  $uri->path .= '%3B' . $type;
22192  }
22193  $uri->path = str_replace(';', '%3B', $uri->path);
22194  $uri->path .= $type_ret;
22195  }
22196  return true;
22197  }
22198 }
22199 
22200 
22201 
22202 
22203 
22208 {
22212  public $default_port = 80;
22213 
22217  public $browsable = true;
22218 
22222  public $hierarchical = true;
22223 
22230  public function doValidate(&$uri, $config, $context)
22231  {
22232  $uri->userinfo = null;
22233  return true;
22234  }
22235 }
22236 
22237 
22238 
22239 
22240 
22245 {
22249  public $default_port = 443;
22253  public $secure = true;
22254 }
22255 
22256 
22257 
22258 
22259 
22260 // VERY RELAXED! Shouldn't cause problems, not even Firefox checks if the
22261 // email is valid, but be careful!
22262 
22270 {
22274  public $browsable = false;
22275 
22279  public $may_omit_host = true;
22280 
22287  public function doValidate(&$uri, $config, $context)
22288  {
22289  $uri->userinfo = null;
22290  $uri->host = null;
22291  $uri->port = null;
22292  // we need to validate path against RFC 2368's addr-spec
22293  return true;
22294  }
22295 }
22296 
22297 
22298 
22299 
22300 
22305 {
22309  public $browsable = false;
22310 
22314  public $may_omit_host = true;
22315 
22322  public function doValidate(&$uri, $config, $context)
22323  {
22324  $uri->userinfo = null;
22325  $uri->host = null;
22326  $uri->port = null;
22327  $uri->query = null;
22328  // typecode check needed on path
22329  return true;
22330  }
22331 }
22332 
22333 
22334 
22335 
22336 
22341 {
22345  public $default_port = 119;
22346 
22350  public $browsable = false;
22351 
22358  public function doValidate(&$uri, $config, $context)
22359  {
22360  $uri->userinfo = null;
22361  $uri->query = null;
22362  return true;
22363  }
22364 }
22365 
22366 
22367 
22368 
22369 
22380 {
22384  public $browsable = false;
22385 
22389  public $may_omit_host = true;
22390 
22397  public function doValidate(&$uri, $config, $context)
22398  {
22399  $uri->userinfo = null;
22400  $uri->host = null;
22401  $uri->port = null;
22402 
22403  // Delete all non-numeric characters, non-x characters
22404  // from phone number, EXCEPT for a leading plus sign.
22405  $uri->path = preg_replace('/(?!^\+)[^\dx]/', '',
22406  // Normalize e(x)tension to lower-case
22407  str_replace('X', 'x', $uri->path));
22408 
22409  return true;
22410  }
22411 }
22412 
22413 
22414 
22415 
22416 
22423 {
22431  protected function parseImplementation($var, $type, $allow_null)
22432  {
22433  if ($allow_null && $var === null) {
22434  return null;
22435  }
22436  switch ($type) {
22437  // Note: if code "breaks" from the switch, it triggers a generic
22438  // exception to be thrown. Specific errors can be specifically
22439  // done here.
22440  case self::C_MIXED:
22441  case self::ISTRING:
22442  case self::C_STRING:
22443  case self::TEXT:
22444  case self::ITEXT:
22445  return $var;
22446  case self::C_INT:
22447  if (is_string($var) && ctype_digit($var)) {
22448  $var = (int)$var;
22449  }
22450  return $var;
22451  case self::C_FLOAT:
22452  if ((is_string($var) && is_numeric($var)) || is_int($var)) {
22453  $var = (float)$var;
22454  }
22455  return $var;
22456  case self::C_BOOL:
22457  if (is_int($var) && ($var === 0 || $var === 1)) {
22458  $var = (bool)$var;
22459  } elseif (is_string($var)) {
22460  if ($var == 'on' || $var == 'true' || $var == '1') {
22461  $var = true;
22462  } elseif ($var == 'off' || $var == 'false' || $var == '0') {
22463  $var = false;
22464  } else {
22465  throw new HTMLPurifier_VarParserException("Unrecognized value '$var' for $type");
22466  }
22467  }
22468  return $var;
22469  case self::ALIST:
22470  case self::HASH:
22471  case self::LOOKUP:
22472  if (is_string($var)) {
22473  // special case: technically, this is an array with
22474  // a single empty string item, but having an empty
22475  // array is more intuitive
22476  if ($var == '') {
22477  return array();
22478  }
22479  if (strpos($var, "\n") === false && strpos($var, "\r") === false) {
22480  // simplistic string to array method that only works
22481  // for simple lists of tag names or alphanumeric characters
22482  $var = explode(',', $var);
22483  } else {
22484  $var = preg_split('/(,|[\n\r]+)/', $var);
22485  }
22486  // remove spaces
22487  foreach ($var as $i => $j) {
22488  $var[$i] = trim($j);
22489  }
22490  if ($type === self::HASH) {
22491  // key:value,key2:value2
22492  $nvar = array();
22493  foreach ($var as $keypair) {
22494  $c = explode(':', $keypair, 2);
22495  if (!isset($c[1])) {
22496  continue;
22497  }
22498  $nvar[trim($c[0])] = trim($c[1]);
22499  }
22500  $var = $nvar;
22501  }
22502  }
22503  if (!is_array($var)) {
22504  break;
22505  }
22506  $keys = array_keys($var);
22507  if ($keys === array_keys($keys)) {
22508  if ($type == self::ALIST) {
22509  return $var;
22510  } elseif ($type == self::LOOKUP) {
22511  $new = array();
22512  foreach ($var as $key) {
22513  $new[$key] = true;
22514  }
22515  return $new;
22516  } else {
22517  break;
22518  }
22519  }
22520  if ($type === self::ALIST) {
22521  trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING);
22522  return array_values($var);
22523  }
22524  if ($type === self::LOOKUP) {
22525  foreach ($var as $key => $value) {
22526  if ($value !== true) {
22527  trigger_error(
22528  "Lookup array has non-true value at key '$key'; " .
22529  "maybe your input array was not indexed numerically",
22530  E_USER_WARNING
22531  );
22532  }
22533  $var[$key] = true;
22534  }
22535  }
22536  return $var;
22537  default:
22538  $this->errorInconsistent(__CLASS__, $type);
22539  }
22540  $this->errorGeneric($var, $type);
22541  }
22542 }
22543 
22544 
22545 
22546 
22547 
22554 {
22555 
22562  protected function parseImplementation($var, $type, $allow_null)
22563  {
22564  return $this->evalExpression($var);
22565  }
22566 
22572  protected function evalExpression($expr)
22573  {
22574  $var = null;
22575  $result = eval("\$var = $expr;");
22576  if ($result === false) {
22577  throw new HTMLPurifier_VarParserException("Fatal error in evaluated code");
22578  }
22579  return $var;
22580  }
22581 }
22582 
22583 
22584 
HTMLPurifier_AttrDef_Clone\make
make($string)
Definition: HTMLPurifier.standalone.php:10474
HTMLPurifier_Encoder\ICONV_OK
const ICONV_OK
Definition: HTMLPurifier.standalone.php:4338
HTMLPurifier_HTMLModule_Tidy_Proprietary\makeFixes
makeFixes()
Definition: HTMLPurifier.standalone.php:17773
HTMLPurifier_ErrorStruct\addError
addError($severity, $message)
Definition: HTMLPurifier.standalone.php:5091
HTMLPurifier_AttrTransform_Input\$pixels
$pixels
Definition: HTMLPurifier.standalone.php:13957
HTMLPurifier_VarParserException
Definition: HTMLPurifier.standalone.php:10136
HTMLPurifier_HTMLModule_Object
Definition: HTMLPurifier.standalone.php:16778
HTMLPurifier_URI\isBenign
isBenign($config, $context)
Definition: HTMLPurifier.standalone.php:9155
HTMLPurifier_Strategy_Core
Definition: HTMLPurifier.standalone.php:19984
HTMLPurifier_ChildDef_List\$elements
$elements
Definition: HTMLPurifier.standalone.php:14751
HTMLPurifier_Language\formatMessage
formatMessage($key, $args=array())
Definition: HTMLPurifier.standalone.php:7192
HTMLPurifier_ContentSets\$values
$values
Definition: HTMLPurifier.standalone.php:2918
HTMLPurifier_HTMLModule_Iframe\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16380
HTMLPurifier_AttrDef\validate
validate($string, $config, $context)
HTMLPurifier_Language\$fallback
$fallback
Definition: HTMLPurifier.standalone.php:7064
HTMLPurifier_AttrDef_CSS_Font\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:11742
HTMLPurifier_Definition\$type
$type
Definition: HTMLPurifier.standalone.php:1178
HTMLPurifier_Node\$col
$col
Definition: HTMLPurifier.standalone.php:8028
HTMLPurifier_AttrTransform_EnumToCSS
Definition: HTMLPurifier.standalone.php:13774
HTMLPurifier_Config\$def
$def
Definition: HTMLPurifier.standalone.php:1849
HTMLPurifier_AttrDef_CSS_Color\validate
validate($color, $config, $context)
Definition: HTMLPurifier.standalone.php:11394
HTMLPurifier_AttrDef_HTML_Pixels\make
make($string)
Definition: HTMLPurifier.standalone.php:13071
HTMLPurifier_AttrDef_CSS_DenyElementDecorator\__construct
__construct($def, $element)
Definition: HTMLPurifier.standalone.php:11601
HTMLPurifier_UnitConverter\convert
convert($length, $to_unit)
Definition: HTMLPurifier.standalone.php:9704
HTMLPurifier_URIFilter_SafeIframe\$name
$name
Definition: HTMLPurifier.standalone.php:21905
HTMLPurifier_URIScheme_data\$may_omit_host
$may_omit_host
Definition: HTMLPurifier.standalone.php:21988
HTMLPurifier_HTMLDefinition\$doctype
$doctype
Definition: HTMLPurifier.standalone.php:5553
HTMLPurifier_Injector_Linkify\$name
$name
Definition: HTMLPurifier.standalone.php:18466
HTMLPurifier_LanguageFactory\$dir
$dir
Definition: HTMLPurifier.standalone.php:7288
HTMLPurifier_Token_Comment\toNode
toNode()
Definition: HTMLPurifier.standalone.php:21284
HTMLPurifier_DefinitionCache_Null\flush
flush($config)
Definition: HTMLPurifier.standalone.php:15496
HTMLPurifier_Node_Text\__construct
__construct($data, $is_whitespace, $line=null, $col=null)
Definition: HTMLPurifier.standalone.php:19932
HTMLPurifier_URIScheme\$default_port
$default_port
Definition: HTMLPurifier.standalone.php:9452
HTMLPurifier_ErrorCollector\$locale
$locale
Definition: HTMLPurifier.standalone.php:4815
HTMLPurifier_Config\maybeGetRawURIDefinition
maybeGetRawURIDefinition()
Definition: HTMLPurifier.standalone.php:2457
HTMLPurifier_ChildDef_Custom\$allow_empty
$allow_empty
Definition: HTMLPurifier.standalone.php:14605
$config
$config
Definition: Filter.ExtractStyleBlocks.txt:33
HTMLPurifier_URIFilter_Munge\makeReplace
makeReplace($uri, $config, $context)
Definition: HTMLPurifier.standalone.php:21873
HTMLPurifier_AttrDef_CSS_Length\$min
$min
Definition: HTMLPurifier.standalone.php:12202
HTMLPurifier_HTMLModule_TargetNoreferrer\$name
$name
Definition: HTMLPurifier.standalone.php:17370
HTMLPurifier_URIFilter_Munge\$post
$post
Definition: HTMLPurifier.standalone.php:21788
HTMLPurifier_HTMLDefinition\doSetup
doSetup($config)
Definition: HTMLPurifier.standalone.php:5648
HTMLPurifier_HTMLModule_Iframe\$name
$name
Definition: HTMLPurifier.standalone.php:16370
HTMLPurifier_ChildDef_StrictBlockquote\getAllowedElements
getAllowedElements($config)
Definition: HTMLPurifier.standalone.php:15025
HTMLPurifier_ContentSets\convertToLookup
convertToLookup($string)
Definition: HTMLPurifier.standalone.php:3046
HTMLPurifier_LanguageFactory\loadLanguage
loadLanguage($code)
Definition: HTMLPurifier.standalone.php:7395
HTMLPurifier_HTMLModuleManager\$attrTypes
$attrTypes
Definition: HTMLPurifier.standalone.php:6254
HTMLPurifier_HTMLModule_Tidy_Strict\$defines_child_def
$defines_child_def
Definition: HTMLPurifier.standalone.php:17997
HTMLPurifier_URIFilter_DisableExternal
Definition: HTMLPurifier.standalone.php:21469
HTMLPurifier_Token_Comment\$is_whitespace
$is_whitespace
Definition: HTMLPurifier.standalone.php:21268
HTMLPurifier_URIScheme_ftp
Definition: HTMLPurifier.standalone.php:22149
HTMLPurifier_AttrTransform\prependCSS
prependCSS(&$attr, $css)
Definition: HTMLPurifier.standalone.php:720
HTMLPurifier_AttrDef_URI_IPv6\validate
validate($aIP, $config, $context)
Definition: HTMLPurifier.standalone.php:13503
HTMLPurifier_AttrTransform_SafeParam
Definition: HTMLPurifier.standalone.php:14280
HTMLPurifier_AttrTransform_Input\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:13970
HTMLPurifier_AttrDef_HTML_Nmtokens\split
split($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12721
HTMLPurifier_Lexer_DOMLex\callbackArmorCommentEntities
callbackArmorCommentEntities($matches)
Definition: HTMLPurifier.standalone.php:19213
HTMLPurifier_AttrTransform_NameSync\__construct
__construct()
Definition: HTMLPurifier.standalone.php:14125
HTMLPurifier_HTMLModule_Tidy_Strict\$defaultLevel
$defaultLevel
Definition: HTMLPurifier.standalone.php:17982
HTMLPurifier_AttrDef_HTML_Bool\__construct
__construct($name=false)
Definition: HTMLPurifier.standalone.php:12655
HTMLPurifier_Injector\backward
backward(&$i, &$current)
Definition: HTMLPurifier.standalone.php:6996
HTMLPurifier_Lexer\create
static create($config)
Definition: HTMLPurifier.standalone.php:7691
HTMLPurifier_AttrDef_CSS_Border\__construct
__construct($config)
Definition: HTMLPurifier.standalone.php:11330
HTMLPurifier_VarParser_Native\evalExpression
evalExpression($expr)
Definition: HTMLPurifier.standalone.php:22572
HTMLPurifier_URIScheme_http\$browsable
$browsable
Definition: HTMLPurifier.standalone.php:22217
HTMLPurifier_HTMLModule_Edit\getChildDef
getChildDef($def)
Definition: HTMLPurifier.standalone.php:16111
HTMLPurifier_AttrDef_CSS_ListStyle\$info
$info
Definition: HTMLPurifier.standalone.php:12283
HTMLPurifier_AttrDef_HTML_Length\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:13103
HTMLPurifier_URIScheme\validate
validate(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:9500
HTMLPurifier_Lexer_DOMLex\transformAttrToAssoc
transformAttrToAssoc($node_map)
Definition: HTMLPurifier.standalone.php:19172
HTMLPurifier_Encoder\cleanUTF8
static cleanUTF8($str, $force_php=false)
Definition: HTMLPurifier.standalone.php:3959
HTMLPurifier_HTMLModule_Tidy\makeFixes
makeFixes()
Definition: HTMLPurifier.standalone.php:17693
HTMLPurifier_URIFilter_MakeAbsolute\prepare
prepare($config)
Definition: HTMLPurifier.standalone.php:21642
HTMLPurifier_AttrDef_URI_Host\__construct
__construct()
Definition: HTMLPurifier.standalone.php:13317
HTMLPurifier_Lexer\parseData
parseData($string, $is_attr, $config)
Definition: HTMLPurifier.standalone.php:7811
HTMLPurifier_HTMLModule_Tidy_Strict
Definition: HTMLPurifier.standalone.php:17973
HTMLPurifier_HTMLModule\addElement
addElement($element, $type, $contents, $attr_includes=array(), $attr=array())
Definition: HTMLPurifier.standalone.php:6093
HTMLPurifier_TokenFactory\createEnd
createEnd($name)
Definition: HTMLPurifier.standalone.php:8814
HTMLPurifier_AttrDef_URI_Host
Definition: HTMLPurifier.standalone.php:13303
HTMLPurifier_TagTransform\transform
transform($tag, $config, $context)
true
if(!defined("TRUE_VAL")) define("TRUE_VAL" true
Definition: constants.inc.php:8
HTMLPurifier
Definition: HTMLPurifier.standalone.php:75
HTMLPurifier_Zipper\fromArray
static fromArray($array)
Definition: HTMLPurifier.standalone.php:10177
HTMLPurifier_Lexer\escapeCommentedCDATA
static escapeCommentedCDATA($string)
Definition: HTMLPurifier.standalone.php:7880
HTMLPurifier_HTMLModule_Target
Definition: HTMLPurifier.standalone.php:17289
HTMLPurifier_Node_Comment\__construct
__construct($data, $line=null, $col=null)
Definition: HTMLPurifier.standalone.php:19820
HTMLPurifier_EntityParser\$_entity_lookup
$_entity_lookup
Definition: HTMLPurifier.standalone.php:4509
HTMLPurifier_AttrCollections
Definition: HTMLPurifier.standalone.php:397
HTMLPurifier_EntityParser\$_attrEntitiesRegex
$_attrEntitiesRegex
Definition: HTMLPurifier.standalone.php:4521
HTMLPurifier_DefinitionCache_Null
Definition: HTMLPurifier.standalone.php:15442
HTMLPurifier_AttrDef_URI_Email_SimpleCheck\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:13591
HTMLPurifier_Node_Text\toTokenPair
toTokenPair()
Definition: HTMLPurifier.standalone.php:19940
HTMLPurifier_ContentSets\$keys
$keys
Definition: HTMLPurifier.standalone.php:2913
HTMLPurifier_Config
Definition: HTMLPurifier.standalone.php:1807
HTMLPurifier_AttrDef_CSS_Border\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:11344
HTMLPurifier_URIScheme_ftp\doValidate
doValidate(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:22171
HTMLPurifier_PropertyListIterator\__construct
__construct(Iterator $iterator, $filter=null)
Definition: HTMLPurifier.standalone.php:8312
HTMLPurifier_HTMLModule_Tidy
Definition: HTMLPurifier.standalone.php:17477
HTMLPurifier_URIScheme_nntp\$browsable
$browsable
Definition: HTMLPurifier.standalone.php:22350
HTMLPurifier_Encoder\testEncodingSupportsASCII
static testEncodingSupportsASCII($encoding, $bypass=false)
Definition: HTMLPurifier.standalone.php:4396
HTMLPurifier_ErrorCollector\CHILDREN
const CHILDREN
Definition: HTMLPurifier.standalone.php:4795
HTMLPurifier_Strategy_MakeWellFormed
Definition: HTMLPurifier.standalone.php:20192
HTMLPurifier_AttrDef_Integer\$positive
$positive
Definition: HTMLPurifier.standalone.php:10584
HTMLPurifier\$context
$context
Definition: HTMLPurifier.standalone.php:122
HTMLPurifier_ChildDef_StrictBlockquote\$type
$type
Definition: HTMLPurifier.standalone.php:15012
HTMLPurifier_DefinitionCache\$type
$type
Definition: HTMLPurifier.standalone.php:3170
HTMLPurifier_URIDefinition\__construct
__construct()
Definition: HTMLPurifier.standalone.php:9203
HTMLPurifier_ConfigSchema\addAlias
addAlias($key, $new_key)
Definition: HTMLPurifier.standalone.php:2863
HTMLPurifier_AttrDef_URI_Email
Definition: HTMLPurifier.standalone.php:13279
HTMLPurifier_Injector_SafeObject\$paramStack
$paramStack
Definition: HTMLPurifier.standalone.php:18817
HTMLPurifier_ChildDef_Required\validateChildren
validateChildren($children, $config, $context)
Definition: HTMLPurifier.standalone.php:14879
doctypes
HTML SafeIframe such as URI otherwise it will fatally error This directive has no effect on strict doctypes
Definition: HTML.SafeIframe.txt:10
HTMLPurifier_Config\autoFinalize
autoFinalize()
Definition: HTMLPurifier.standalone.php:2648
HTMLPurifier_HTMLModule\getChildDef
getChildDef($def)
Definition: HTMLPurifier.standalone.php:6072
HTMLPurifier_AttrDef_CSS_FontFamily\$mask
$mask
Definition: HTMLPurifier.standalone.php:11889
HTMLPurifier_HTMLModuleManager\setup
setup($config)
Definition: HTMLPurifier.standalone.php:6466
HTMLPurifier_HTMLModule_Tidy\getFixesForLevel
getFixesForLevel($level)
Definition: HTMLPurifier.standalone.php:17543
HTMLPurifier_URIParser\__construct
__construct()
Definition: HTMLPurifier.standalone.php:9380
HTMLPurifier_AttrDef_CSS_Composite\$defs
$defs
Definition: HTMLPurifier.standalone.php:11551
HTMLPurifier_ChildDef_Chameleon\__construct
__construct($inline, $block)
Definition: HTMLPurifier.standalone.php:14554
HTMLPurifier_TagTransform\$transform_to
$transform_to
Definition: HTMLPurifier.standalone.php:8615
HTMLPurifier_AttrTypes\$info
$info
Definition: HTMLPurifier.standalone.php:756
HTMLPurifier_HTMLModule_Presentation
Definition: HTMLPurifier.standalone.php:16846
HTMLPurifier_UnitConverter
Definition: HTMLPurifier.standalone.php:9630
HTMLPurifier_AttrDef_CSS_ImportantDecorator\$allow
$allow
Definition: HTMLPurifier.standalone.php:12148
HTMLPurifier_EntityParser\$_semiOptionalPrefixRegex
$_semiOptionalPrefixRegex
Definition: HTMLPurifier.standalone.php:4526
HTMLPurifier_HTMLModule_NonXMLCommonAttributes\$attr_collections
$attr_collections
Definition: HTMLPurifier.standalone.php:16761
HTMLPurifier_AttrDef_CSS_Composite
Definition: HTMLPurifier.standalone.php:11544
HTMLPurifier\$config
$config
Definition: HTMLPurifier.standalone.php:92
HTMLPurifier_AttrDef_CSS_Font\$info
$info
Definition: HTMLPurifier.standalone.php:11720
HTMLPurifier_ConfigSchema\add
add($key, $default, $type, $allow_null)
Definition: HTMLPurifier.standalone.php:2816
HTMLPurifier_Config\$serial
$serial
Definition: HTMLPurifier.standalone.php:1835
HTMLPurifier_AttrDef_CSS_Number
Definition: HTMLPurifier.standalone.php:10929
HTMLPurifier_PercentEncoder\normalize
normalize($string)
Definition: HTMLPurifier.standalone.php:8135
HTMLPurifier_DefinitionCache_Decorator
Definition: HTMLPurifier.standalone.php:15326
HTMLPurifier_Config\getAll
getAll()
Definition: HTMLPurifier.standalone.php:2065
HTMLPurifier_URIFilter_MakeAbsolute
Definition: HTMLPurifier.standalone.php:21622
HTMLPurifier_AttrDef_CSS_Border
Definition: HTMLPurifier.standalone.php:11319
HTMLPurifier_ChildDef_StrictBlockquote\$init
$init
Definition: HTMLPurifier.standalone.php:15017
HTMLPurifier_StringHash
Definition: HTMLPurifier.standalone.php:8429
HTMLPurifier_ErrorCollector\$generator
$generator
Definition: HTMLPurifier.standalone.php:4820
HTMLPurifier_AttrDef_CSS_Filter\$intValidator
$intValidator
Definition: HTMLPurifier.standalone.php:11638
HTMLPurifier_Lexer_PH5P
Definition: PH5P.php:14
HTMLPurifier_AttrDef_CSS_TextDecoration\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12525
HTMLPurifier_Language\getErrorName
getErrorName($int)
Definition: HTMLPurifier.standalone.php:7151
$f
global $f
Definition: callback.php:13
HTMLPurifier_Lexer\extractBody
extractBody($html)
Definition: HTMLPurifier.standalone.php:7987
HTMLPurifier_Token_End\toNode
toNode()
Definition: HTMLPurifier.standalone.php:21394
HTMLPurifier_Injector_RemoveSpansWithoutAttributes\handleEnd
handleEnd(&$token)
Definition: HTMLPurifier.standalone.php:18781
HTMLPurifier_HTMLModuleManager\$elementLookup
$elementLookup
Definition: HTMLPurifier.standalone.php:6284
HTMLPurifier_URIFilter\$name
$name
Definition: HTMLPurifier.standalone.php:9325
HTMLPurifier_HTMLModule_Nofollow\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16740
HTMLPurifier_ChildDef_Table
Definition: HTMLPurifier.standalone.php:15130
HTMLPurifier_HTMLDefinition\addAttribute
addAttribute($element_name, $attr_name, $def)
Definition: HTMLPurifier.standalone.php:5568
HTMLPurifier_AttrDef_CSS_Percentage\$number_def
$number_def
Definition: HTMLPurifier.standalone.php:12466
HTMLPurifier_AttrDef_HTML_Class\split
split($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12767
HTMLPurifier_DefinitionCache_Decorator\cleanup
cleanup($config)
Definition: HTMLPurifier.standalone.php:15428
HTMLPurifier_ConfigSchema\__construct
__construct()
Definition: HTMLPurifier.standalone.php:2770
HTMLPurifier_Injector\getRewindOffset
getRewindOffset()
Definition: HTMLPurifier.standalone.php:6841
HTMLPurifier_ErrorStruct\getChild
getChild($type, $id)
Definition: HTMLPurifier.standalone.php:5078
HTMLPurifier_DefinitionCacheFactory\$caches
$caches
Definition: HTMLPurifier.standalone.php:3295
HTMLPurifier_Node\$dead
$dead
Definition: HTMLPurifier.standalone.php:8044
HTMLPurifier_AttrTransform_SafeEmbed\$name
$name
Definition: HTMLPurifier.standalone.php:14217
HTMLPurifier_URIDefinition\$type
$type
Definition: HTMLPurifier.standalone.php:9183
HTMLPurifier_ChildDef_StrictBlockquote\validateChildren
validateChildren($children, $config, $context)
Definition: HTMLPurifier.standalone.php:15037
HTMLPurifier_URIScheme\$hierarchical
$hierarchical
Definition: HTMLPurifier.standalone.php:9473
HTMLPurifier_AttrTransform_Background
Definition: HTMLPurifier.standalone.php:13612
HTMLPurifier_AttrDef_CSS_FontFamily\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:11935
set
$config set('HTML', 'DefinitionID', '1')
HTMLPurifier_ElementDef\$content_model_type
$content_model_type
Definition: HTMLPurifier.standalone.php:3690
HTMLPurifier_AttrDef_HTML_FrameTarget\$case_sensitive
$case_sensitive
Definition: HTMLPurifier.standalone.php:12871
HTMLPurifier_Injector\notifyEnd
notifyEnd($token)
Definition: HTMLPurifier.standalone.php:7038
HTMLPurifier_Injector\handleEnd
handleEnd(&$token)
Definition: HTMLPurifier.standalone.php:7027
HTMLPurifier_ElementDef\$attr_transform_post
$attr_transform_post
Definition: HTMLPurifier.standalone.php:3664
HTMLPurifier_Injector_PurifierLinkify\$name
$name
Definition: HTMLPurifier.standalone.php:18532
HTMLPurifier_URIFilter_DisableExternal\$ourHostParts
$ourHostParts
Definition: HTMLPurifier.standalone.php:21478
HTMLPurifier_URIScheme_mailto
Definition: HTMLPurifier.standalone.php:22270
HTMLPurifier_AttrDef_CSS_BackgroundPosition\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:11223
HTMLPurifier_DefinitionCache\isOld
isOld($key, $config)
Definition: HTMLPurifier.standalone.php:3200
name
Core AllowHostnameUnderscore underscores are not permitted in host most browsers do the right thing when faced with an underscore in the host name
Definition: Core.AllowHostnameUnderscore.txt:11
HTMLPurifier_AttrDef_CSS_Background
Definition: HTMLPurifier.standalone.php:11050
HTMLPurifier_HTMLModule_Nofollow\$name
$name
Definition: HTMLPurifier.standalone.php:16735
HTMLPurifier_ChildDef_Required
Definition: HTMLPurifier.standalone.php:14828
HTMLPurifier_URIDefinition\setupFilters
setupFilters($config)
Definition: HTMLPurifier.standalone.php:9236
HTMLPurifier_Lexer_DirectLex\tokenizeHTML
tokenizeHTML($html, $config, $context)
Definition: HTMLPurifier.standalone.php:19296
HTMLPurifier_AttrTransform_EnumToCSS\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:13813
HTMLPurifier_VarParser
Definition: HTMLPurifier.standalone.php:9938
HTMLPurifier_AttrTransform_Length\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14065
HTMLPurifier_ChildDef_List\validateChildren
validateChildren($children, $config, $context)
Definition: HTMLPurifier.standalone.php:14759
HTMLPurifier_Zipper\insertAfter
insertAfter($t)
Definition: HTMLPurifier.standalone.php:10259
HTMLPurifier_DefinitionCache_Decorator\flush
flush($config)
Definition: HTMLPurifier.standalone.php:15419
HTMLPurifier_Injector\forwardUntilEndToken
forwardUntilEndToken(&$i, &$current, &$nesting)
Definition: HTMLPurifier.standalone.php:6966
HTMLPurifier_HTMLModule_Iframe\$safe
$safe
Definition: HTMLPurifier.standalone.php:16375
HTMLPurifier_ChildDef_Custom\__construct
__construct($dtd_regex)
Definition: HTMLPurifier.standalone.php:14622
HTMLPurifier_CSSDefinition\setupConfigStuff
setupConfigStuff($config)
Definition: HTMLPurifier.standalone.php:1703
HTMLPurifier_URI\$port
$port
Definition: HTMLPurifier.standalone.php:8891
HTMLPurifier_HTMLModuleManager\addPrefix
addPrefix($prefix)
Definition: HTMLPurifier.standalone.php:6456
HTMLPurifier_Generator\__construct
__construct($config, $context)
Definition: HTMLPurifier.standalone.php:5235
HTMLPurifier_Node_Element\$children
$children
Definition: HTMLPurifier.standalone.php:19859
HTMLPurifier_PropertyList\__construct
__construct($parent=null)
Definition: HTMLPurifier.standalone.php:8196
HTMLPurifier_HTMLModule_Image\$name
$name
Definition: HTMLPurifier.standalone.php:16420
HTMLPurifier_Token_Start
Definition: HTMLPurifier.standalone.php:21407
HTMLPurifier_Lexer_DOMLex\createEndNode
createEndNode($node, &$tokens)
Definition: HTMLPurifier.standalone.php:19160
HTMLPurifier_AttrDef_Switch\$withTag
$withTag
Definition: HTMLPurifier.standalone.php:10751
HTMLPurifier_Token_Tag\__construct
__construct($name, $attr=array(), $line=null, $col=null, $armor=array())
Definition: HTMLPurifier.standalone.php:21332
HTMLPurifier_DefinitionCache_Null\cleanup
cleanup($config)
Definition: HTMLPurifier.standalone.php:15505
HTMLPurifier_Injector_AutoParagraph
Definition: HTMLPurifier.standalone.php:18067
schemes
Voluntary License Schemes The Licensor waives the right to collect whether individually in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes
Definition: license.txt:37
HTMLPurifier_AttrCollections\__construct
__construct($attr_types, $modules)
Definition: HTMLPurifier.standalone.php:412
HTMLPurifier_HTMLModule_Tidy_Name\$name
$name
Definition: HTMLPurifier.standalone.php:17731
HTMLPurifier_ConfigSchema\instance
static instance($prototype=null)
Definition: HTMLPurifier.standalone.php:2795
$ret
$ret
Definition: index.php:39
HTMLPurifier_ElementDef\create
static create($content_model, $content_model_type, $attr)
Definition: HTMLPurifier.standalone.php:3746
HTMLPurifier_AttrDef_Lang
Definition: HTMLPurifier.standalone.php:10655
HTMLPurifier_Language\getMessage
getMessage($key)
Definition: HTMLPurifier.standalone.php:7135
HTMLPurifier_AttrTransform_ImgSpace\$css
$css
Definition: HTMLPurifier.standalone.php:13899
HTMLPurifier_LanguageFactory\$keys
$keys
Definition: HTMLPurifier.standalone.php:7274
HTMLPurifier_Lexer\tokenizeHTML
tokenizeHTML($string, $config, $context)
Definition: HTMLPurifier.standalone.php:7856
HTMLPurifier_HTMLModule_XMLCommonAttributes\$name
$name
Definition: HTMLPurifier.standalone.php:17707
HTMLPurifier_HTMLModule_Tidy_XHTML\makeFixes
makeFixes()
Definition: HTMLPurifier.standalone.php:18048
HTMLPurifier_HTMLModule_StyleAttribute\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17199
HTMLPurifier_CSSDefinition\doSetupTricky
doSetupTricky($config)
Definition: HTMLPurifier.standalone.php:1639
HTMLPurifier_AttrDef_HTML_ID
Definition: HTMLPurifier.standalone.php:12906
HTMLPurifier_Token_End\$start
$start
Definition: HTMLPurifier.standalone.php:21392
HTMLPurifier_ElementDef\$standalone
$standalone
Definition: HTMLPurifier.standalone.php:3626
HTMLPurifier_Strategy_Core\__construct
__construct()
Definition: HTMLPurifier.standalone.php:19985
HTMLPurifier_AttrDef_Clone\validate
validate($v, $config, $context)
Definition: HTMLPurifier.standalone.php:10465
HTMLPurifier_DoctypeRegistry\make
make($config)
Definition: HTMLPurifier.standalone.php:3571
HTMLPurifier_AttrDef_CSS_Filter
Definition: HTMLPurifier.standalone.php:11634
HTMLPurifier_Token_Empty\toNode
toNode()
Definition: HTMLPurifier.standalone.php:21367
code
the intent is to exercise the right to control the distribution of derivative or collective works based on the Library In mere aggregation of another work not based on the Library with the you must alter all the notices that refer to this so that they refer to the ordinary GNU General Public instead of to this it is irreversible for that so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy This option is useful when you wish to copy part of the code of the Library into a program that is not a library You may copy and distribute the which must be distributed under the terms of Sections and above on a medium customarily used for software interchange If distribution of object code is made by offering access to copy from a designated then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code
Definition: license.txt:237
HTMLPurifier_Zipper\done
done()
Definition: HTMLPurifier.standalone.php:10243
HTMLPurifier_HTMLModule_Proprietary\$name
$name
Definition: HTMLPurifier.standalone.php:16887
HTMLPurifier_Injector_SafeObject\$addParam
$addParam
Definition: HTMLPurifier.standalone.php:18823
HTMLPurifier_HTMLModule\$info
$info
Definition: HTMLPurifier.standalone.php:5991
HTMLPurifier_Injector_Linkify
Definition: HTMLPurifier.standalone.php:18462
HTMLPurifier_HTMLModule_Ruby
Definition: HTMLPurifier.standalone.php:16924
HTMLPurifier_AttrDef_CSS_Percentage
Definition: HTMLPurifier.standalone.php:12460
HTMLPurifier_Token_Text\toNode
toNode()
Definition: HTMLPurifier.standalone.php:21459
HTMLPurifier_Config\getSerial
getSerial()
Definition: HTMLPurifier.standalone.php:2052
HTMLPurifier_AttrTransform_SafeObject\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14254
HTMLPurifier_URIFilter_MakeAbsolute\$name
$name
Definition: HTMLPurifier.standalone.php:21626
HTMLPurifier_ChildDef_List\$type
$type
Definition: HTMLPurifier.standalone.php:14745
HTMLPurifier_HTMLModule\$info_injector
$info_injector
Definition: HTMLPurifier.standalone.php:6038
HTMLPurifier_AttrTransform_ScriptRequired\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14358
HTMLPurifier_AttrTransform_SafeParam\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14303
HTMLPurifier_Exception
Definition: HTMLPurifier.standalone.php:5106
HTMLPurifier_Config\loadArray
loadArray($config_array)
Definition: HTMLPurifier.standalone.php:2468
HTMLPurifier_Injector_PurifierLinkify\handleText
handleText(&$token)
Definition: HTMLPurifier.standalone.php:18558
HTMLPurifier_HTMLModule_StyleAttribute\$name
$name
Definition: HTMLPurifier.standalone.php:17184
HTMLPurifier_DefinitionCacheFactory\instance
static instance($prototype=null)
Definition: HTMLPurifier.standalone.php:3320
HTMLPurifier_Length\getN
getN()
Definition: HTMLPurifier.standalone.php:7572
HTMLPurifier_Config\getURIDefinition
getURIDefinition($raw=false, $optimized=false)
Definition: HTMLPurifier.standalone.php:2240
HTMLPurifier_LanguageFactory\$validator
$validator
Definition: HTMLPurifier.standalone.php:7281
HTMLPurifier_HTMLModule_Tidy_Strict\getChildDef
getChildDef($def)
Definition: HTMLPurifier.standalone.php:18003
$fallback
$fallback
Definition: en-x-test.php:5
HTMLPurifier_Injector_RemoveSpansWithoutAttributes
Definition: HTMLPurifier.standalone.php:18712
HTMLPurifier_HTMLModule_Forms\$name
$name
Definition: HTMLPurifier.standalone.php:16133
HTMLPurifier_HTMLModule\parseContents
parseContents($contents)
Definition: HTMLPurifier.standalone.php:6161
HTMLPurifier_AttrTransform_EnumToCSS\$attr
$attr
Definition: HTMLPurifier.standalone.php:13779
HTMLPurifier_HTMLModule\$info_attr_transform_post
$info_attr_transform_post
Definition: HTMLPurifier.standalone.php:6029
HTMLPurifier_AttrTypes\__construct
__construct()
Definition: HTMLPurifier.standalone.php:762
HTMLPurifier_AttrDef_Enum\make
make($string)
Definition: HTMLPurifier.standalone.php:10541
HTMLPurifier_EntityLookup\setup
setup($file=false)
Definition: HTMLPurifier.standalone.php:4464
HTMLPurifier\purify
purify($html, $config=null)
Definition: HTMLPurifier.standalone.php:166
HTMLPurifier_Language\__construct
__construct($config, $context)
Definition: HTMLPurifier.standalone.php:7107
HTMLPurifier_HTMLModule_SafeEmbed\$name
$name
Definition: HTMLPurifier.standalone.php:16964
HTMLPurifier_Doctype\$modules
$modules
Definition: HTMLPurifier.standalone.php:3413
HTMLPurifier_AttrDef_CSS_Length
Definition: HTMLPurifier.standalone.php:12197
HTMLPurifier_Config\maybeGetRawDefinition
maybeGetRawDefinition($name)
Definition: HTMLPurifier.standalone.php:2433
HTMLPurifier_Queue\__construct
__construct($input=array())
Definition: HTMLPurifier.standalone.php:8357
HTMLPurifier_EntityParser\nonSpecialEntityCallback
nonSpecialEntityCallback($matches)
Definition: HTMLPurifier.standalone.php:4703
HTMLPurifier_Injector_SafeObject\$name
$name
Definition: HTMLPurifier.standalone.php:18802
HTMLPurifier_Bootstrap\autoload
static autoload($class)
Definition: HTMLPurifier.standalone.php:1059
HTMLPurifier_Node
Definition: HTMLPurifier.standalone.php:8017
HTMLPurifier_HTMLModule_Scripting\$elements
$elements
Definition: HTMLPurifier.standalone.php:17124
HTMLPurifier_Token_Tag\$is_tag
$is_tag
Definition: HTMLPurifier.standalone.php:21305
HTMLPurifier_Injector_RemoveSpansWithoutAttributes\$name
$name
Definition: HTMLPurifier.standalone.php:18716
HTMLPurifier_AttrDef_HTML_Bool\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12666
HTMLPurifier_URIFilter
Definition: HTMLPurifier.standalone.php:9319
HTMLPurifier_ConfigSchema\addValueAliases
addValueAliases($key, $aliases)
Definition: HTMLPurifier.standalone.php:2836
HTMLPurifier_LanguageFactory\instance
static instance($prototype=null)
Definition: HTMLPurifier.standalone.php:7308
HTMLPurifier_AttrTransform_TargetNoreferrer\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14472
HTMLPurifier_DefinitionCache\__construct
__construct($type)
Definition: HTMLPurifier.standalone.php:3176
$hash
$hash
Definition: Filter.ExtractStyleBlocks.txt:46
HTMLPurifier_HTMLModule_Text\$name
$name
Definition: HTMLPurifier.standalone.php:17400
HTMLPurifier_Strategy
Definition: HTMLPurifier.standalone.php:8403
HTMLPurifier_AttrDef_CSS_BackgroundPosition
Definition: HTMLPurifier.standalone.php:11199
HTMLPurifier_LanguageFactory\getFallbackFor
getFallbackFor($code)
Definition: HTMLPurifier.standalone.php:7385
HTMLPurifier_TagTransform_Font\transform
transform($tag, $config, $context)
Definition: HTMLPurifier.standalone.php:21142
HTMLPurifier_Node_Comment\$is_whitespace
$is_whitespace
Definition: HTMLPurifier.standalone.php:19811
HTMLPurifier_HTMLModule_Edit\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16084
HTMLPurifier_AttrDef
Definition: HTMLPurifier.standalone.php:552
HTMLPurifier_AttrDef_HTML_Color
Definition: HTMLPurifier.standalone.php:12809
HTMLPurifier_HTMLDefinition\$info_tag_transform
$info_tag_transform
Definition: HTMLPurifier.standalone.php:5522
HTMLPurifier_LanguageFactory\$cache
$cache
Definition: HTMLPurifier.standalone.php:7267
HTMLPurifier_AttrDef_CSS_Ident
Definition: HTMLPurifier.standalone.php:12107
HTMLPurifier_Queue
Definition: HTMLPurifier.standalone.php:8353
php
HTMLPurifier_StringHashParser\parseHandle
parseHandle($fh)
Definition: HTMLPurifier.standalone.php:8551
HTMLPurifier_HTMLModule_Name\$name
$name
Definition: HTMLPurifier.standalone.php:16703
HTMLPurifier_Length\make
static make($s)
Definition: HTMLPurifier.standalone.php:7513
HTMLPurifier_HTMLModule_Tidy\$levels
$levels
Definition: HTMLPurifier.standalone.php:17483
HTMLPurifier_AttrDef_CSS_Color\__construct
__construct()
Definition: HTMLPurifier.standalone.php:11383
$def
$def
Definition: HTML.DefinitionID.txt:17
HTMLPurifier_AttrTransform_ImgSpace\$attr
$attr
Definition: HTMLPurifier.standalone.php:13894
HTMLPurifier_AttrDef\$required
$required
Definition: HTMLPurifier.standalone.php:566
HTMLPurifier_HTMLModule_Hypertext\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16330
HTMLPurifier_URIDefinition\doSetup
doSetup($config)
Definition: HTMLPurifier.standalone.php:9230
HTMLPurifier_AttrTransform_Name
Definition: HTMLPurifier.standalone.php:14087
HTMLPurifier_AttrDef_HTML_Pixels\__construct
__construct($max=null)
Definition: HTMLPurifier.standalone.php:13024
HTMLPurifier_AttrTransform_Border
Definition: HTMLPurifier.standalone.php:13746
HTMLPurifier_URIFilter_DisableResources\filter
filter(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:21562
HTMLPurifier_IDAccumulator
Definition: HTMLPurifier.standalone.php:6712
HTMLPurifier_HTMLModule_Tidy_Proprietary\$defaultLevel
$defaultLevel
Definition: HTMLPurifier.standalone.php:17768
HTMLPurifier_AttrDef_URI_IPv4
Definition: HTMLPurifier.standalone.php:13447
HTMLPurifier_Node_Element\$attr
$attr
Definition: HTMLPurifier.standalone.php:19853
HTMLPurifier_ChildDef_Required\$allow_empty
$allow_empty
Definition: HTMLPurifier.standalone.php:14866
HTMLPurifier\$generator
$generator
Definition: HTMLPurifier.standalone.php:115
HTMLPurifier_Length\$allowedUnits
static $allowedUnits
Definition: HTMLPurifier.standalone.php:7491
HTMLPurifier_Lexer\removeIEConditional
static removeIEConditional($string)
Definition: HTMLPurifier.standalone.php:7894
HTMLPurifier_AttrDef_URI_IPv4\$ip4
$ip4
Definition: HTMLPurifier.standalone.php:13453
HTMLPurifier_Filter\preFilter
preFilter($html, $config, $context)
Definition: HTMLPurifier.standalone.php:5149
HTMLPurifier_AttrDef_CSS_Multiple\$single
$single
Definition: HTMLPurifier.standalone.php:12402
HTMLPurifier_URIDefinition
Definition: HTMLPurifier.standalone.php:9181
HTMLPurifier_DefinitionCache_Decorator_Memory\copy
copy()
Definition: HTMLPurifier.standalone.php:15926
HTMLPurifier_ChildDef_Custom\$type
$type
Definition: HTMLPurifier.standalone.php:14600
HTMLPurifier_AttrDef_CSS_BackgroundPosition\$length
$length
Definition: HTMLPurifier.standalone.php:11204
HTMLPurifier_URIFilter_DisableExternal\filter
filter(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:21498
HTMLPurifier_HTMLModule_SafeObject\$name
$name
Definition: HTMLPurifier.standalone.php:17008
HTMLPurifier_DefinitionCache\generateKey
generateKey($config)
Definition: HTMLPurifier.standalone.php:3186
HTMLPurifier_Doctype
Definition: HTMLPurifier.standalone.php:3401
HTMLPurifier_AttrDef_CSS_AlphaValue\__construct
__construct()
Definition: HTMLPurifier.standalone.php:11013
HTMLPurifier_DefinitionCache\add
add($def, $config)
HTMLPurifier_Queue\push
push($x)
Definition: HTMLPurifier.standalone.php:8379
HTMLPurifier_HTMLModuleManager\$modules
$modules
Definition: HTMLPurifier.standalone.php:6261
HTMLPurifier_HTMLModule_List\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16673
HTMLPurifier_URIFilter_HostBlacklist\$blacklist
$blacklist
Definition: HTMLPurifier.standalone.php:21586
HTMLPurifier_Lexer_DirectLex\scriptCallback
scriptCallback($matches)
Definition: HTMLPurifier.standalone.php:19285
HTMLPurifier_Injector_RemoveSpansWithoutAttributes\handleElement
handleElement(&$token)
Definition: HTMLPurifier.standalone.php:18750
HTMLPurifier_Node_Element\$endArmor
$endArmor
Definition: HTMLPurifier.standalone.php:19868
HTMLPurifier_HTMLModule_Name
Definition: HTMLPurifier.standalone.php:16699
HTMLPurifier_Lexer
Definition: HTMLPurifier.standalone.php:7665
HTMLPurifier_URIFilter\prepare
prepare($config)
Definition: HTMLPurifier.standalone.php:9347
HTMLPurifier_Filter\$name
$name
Definition: HTMLPurifier.standalone.php:5140
HTMLPurifier_Lexer_DOMLex\muteErrorHandler
muteErrorHandler($errno, $errstr)
Definition: HTMLPurifier.standalone.php:19192
HTMLPurifier_AttrDef_Text\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:10803
HTMLPurifier_HTMLModule_Bdo
Definition: HTMLPurifier.standalone.php:15997
context
you may still notice some empty particularly if a node had but those elements were later removed because they were not permitted in that context
Definition: AutoFormat.RemoveEmpty.txt:40
HTMLPurifier_Language\load
load()
Definition: HTMLPurifier.standalone.php:7117
HTMLPurifier_HTMLModuleManager\$userModules
$userModules
Definition: HTMLPurifier.standalone.php:6277
HTMLPurifier_HTMLModule\$name
$name
Definition: HTMLPurifier.standalone.php:5976
HTMLPurifier_URIScheme
Definition: HTMLPurifier.standalone.php:9444
HTMLPurifier_URIScheme_data\$allowed_types
$allowed_types
Definition: HTMLPurifier.standalone.php:21976
HTMLPurifier_HTMLModule
Definition: HTMLPurifier.standalone.php:5968
null
Attr AllowedClasses this is null
Definition: Attr.AllowedClasses.txt:6
HTMLPurifier_EntityParser
Definition: HTMLPurifier.standalone.php:4503
HTMLPurifier_StringHash\$accessed
$accessed
Definition: HTMLPurifier.standalone.php:8433
HTMLPurifier_AttrTransform_ImgRequired\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:13855
HTMLPurifier_ChildDef\$type
$type
Definition: HTMLPurifier.standalone.php:1749
HTMLPurifier_LanguageFactory\$mergeable_keys_list
$mergeable_keys_list
Definition: HTMLPurifier.standalone.php:7300
HTMLPurifier_HTMLModule\addBlankElement
addBlankElement($element)
Definition: HTMLPurifier.standalone.php:6123
HTMLPurifier_DefinitionCache_Decorator_Cleanup\copy
copy()
Definition: HTMLPurifier.standalone.php:15841
HTMLPurifier_CSSDefinition\doSetup
doSetup($config)
Definition: HTMLPurifier.standalone.php:1224
HTMLPurifier_UnitConverter\$outputPrecision
$outputPrecision
Definition: HTMLPurifier.standalone.php:9664
HTMLPurifier_Lexer_DOMLex\wrapHTML
wrapHTML($html, $config, $context, $use_div=true)
Definition: HTMLPurifier.standalone.php:19225
HTMLPurifier_URIScheme_https
Definition: HTMLPurifier.standalone.php:22245
HTMLPurifier_Injector
Definition: HTMLPurifier.standalone.php:6777
HTMLPurifier_AttrTransform_EnumToCSS\$enumToCSS
$enumToCSS
Definition: HTMLPurifier.standalone.php:13785
HTMLPurifier_AttrTransform_TargetNoopener
Definition: HTMLPurifier.standalone.php:14427
HTMLPurifier_Length\getUnit
getUnit()
Definition: HTMLPurifier.standalone.php:7581
HTMLPurifier_URIFilter_Munge
Definition: HTMLPurifier.standalone.php:21779
HTMLPurifier_AttrDef_HTML_FrameTarget\$valid_values
$valid_values
Definition: HTMLPurifier.standalone.php:12866
HTMLPurifier_CSSDefinition\$type
$type
Definition: HTMLPurifier.standalone.php:1212
HTMLPurifier_URIFilter_Munge\filter
filter(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:21837
HTMLPurifier_HTMLModule_Ruby\$name
$name
Definition: HTMLPurifier.standalone.php:16929
HTMLPurifier_URIFilter_HostBlacklist\prepare
prepare($config)
Definition: HTMLPurifier.standalone.php:21592
HTMLPurifier_TokenFactory\createComment
createComment($data)
Definition: HTMLPurifier.standalone.php:8851
HTMLPurifier_DefinitionCache_Decorator\__construct
__construct()
Definition: HTMLPurifier.standalone.php:15340
HTMLPurifier_Lexer\escapeCDATA
static escapeCDATA($string)
Definition: HTMLPurifier.standalone.php:7866
HTMLPurifier_HTMLModule_SafeObject
Definition: HTMLPurifier.standalone.php:17004
HTMLPurifier_PropertyList\$parent
$parent
Definition: HTMLPurifier.standalone.php:8185
HTMLPurifier_ChildDef_Table\$type
$type
Definition: HTMLPurifier.standalone.php:15139
HTMLPurifier_Lexer_DirectLex\substrCount
substrCount($haystack, $needle, $offset, $length)
Definition: HTMLPurifier.standalone.php:19608
HTMLPurifier_PropertyList\has
has($name)
Definition: HTMLPurifier.standalone.php:8233
HTMLPurifier_DefinitionCache_Serializer\generateDirectoryPath
generateDirectoryPath($config)
Definition: HTMLPurifier.standalone.php:15682
HTMLPurifier_Config\mergeArrayFromForm
mergeArrayFromForm($array, $index=false, $allowed=true, $mq_fix=true)
Definition: HTMLPurifier.standalone.php:2572
HTMLPurifier_AttrDef_CSS_Filter\__construct
__construct()
Definition: HTMLPurifier.standalone.php:11640
HTMLPurifier_AttrDef_HTML_Color\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12817
HTMLPurifier_AttrTransform_Textarea\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14504
HTMLPurifier_AttrDef_CSS_Color\$alpha
$alpha
Definition: HTMLPurifier.standalone.php:11381
HTMLPurifier_Injector_SafeObject
Definition: HTMLPurifier.standalone.php:18798
HTMLPurifier_AttrDef_CSS_Color
Definition: HTMLPurifier.standalone.php:11376
HTMLPurifier_Strategy_MakeWellFormed\$config
$config
Definition: HTMLPurifier.standalone.php:20228
HTMLPurifier_EntityParser\$_special_dec2str
$_special_dec2str
Definition: HTMLPurifier.standalone.php:4655
HTMLPurifier_Strategy_FixNesting
Definition: HTMLPurifier.standalone.php:20028
HTMLPurifier_AttrDef_CSS_Length\__construct
__construct($min=null, $max=null)
Definition: HTMLPurifier.standalone.php:12213
HTMLPurifier_ContentSets
Definition: HTMLPurifier.standalone.php:2894
HTMLPurifier_EntityParser\specialEntityCallback
specialEntityCallback($matches)
Definition: HTMLPurifier.standalone.php:4759
HTMLPurifier_URIParser\parse
parse($uri)
Definition: HTMLPurifier.standalone.php:9391
HTMLPurifier_HTMLModuleManager\$doctype
$doctype
Definition: HTMLPurifier.standalone.php:6249
HTMLPurifier_IDAccumulator\build
static build($config, $context)
Definition: HTMLPurifier.standalone.php:6726
HTMLPurifier_Context\exists
exists($name)
Definition: HTMLPurifier.standalone.php:3136
HTMLPurifier_AttrDef_Switch\$tag
$tag
Definition: HTMLPurifier.standalone.php:10746
HTMLPurifier_Node_Text\$is_whitespace
$is_whitespace
Definition: HTMLPurifier.standalone.php:19922
HTMLPurifier_VarParser\$stringTypes
static $stringTypes
Definition: HTMLPurifier.standalone.php:9974
HTMLPurifier_HTMLDefinition\parseTinyMCEAllowedList
parseTinyMCEAllowedList($list)
Definition: HTMLPurifier.standalone.php:5913
selector
Filter ExtractStyleBlocks Scope FilterParam ExtractStyleBlocksScope DESCRIPTION< p > If you would like users to be able to define external but only allow them to specify CSS declarations for a specific node and prevent them from fiddling with other use this directive It accepts any valid CSS selector
Definition: Filter.ExtractStyleBlocks.Scope.txt:12
HTMLPurifier_DefinitionCache\cleanup
cleanup($config)
HTMLPurifier_Strategy_Composite\$strategies
$strategies
Definition: HTMLPurifier.standalone.php:19959
HTMLPurifier_HTMLModule\mergeInAttrIncludes
mergeInAttrIncludes(&$attr, $attr_includes)
Definition: HTMLPurifier.standalone.php:6187
HTMLPurifier_ChildDef_Chameleon\$type
$type
Definition: HTMLPurifier.standalone.php:14548
HTMLPurifier_Generator\generateScriptFromToken
generateScriptFromToken($token)
Definition: HTMLPurifier.standalone.php:5361
HTMLPurifier_HTMLModule_StyleAttribute\$attr_collections
$attr_collections
Definition: HTMLPurifier.standalone.php:17189
HTMLPurifier_UnitConverter\getSigFigs
getSigFigs($n)
Definition: HTMLPurifier.standalone.php:9814
HTMLPurifier_AttrDef_CSS_Length\$max
$max
Definition: HTMLPurifier.standalone.php:12207
HTMLPurifier_UnitConverter\METRIC
const METRIC
Definition: HTMLPurifier.standalone.php:9633
HTMLPurifier_URIScheme\$browsable
$browsable
Definition: HTMLPurifier.standalone.php:9459
HTMLPurifier_URIDefinition\addFilter
addFilter($filter, $config)
Definition: HTMLPurifier.standalone.php:9219
$url
URI MungeSecretKey $url
Definition: URI.MungeSecretKey.txt:14
HTMLPurifier_Node_Element
Definition: HTMLPurifier.standalone.php:19838
HTMLPurifier_AttrDef_Enum\$valid_values
$valid_values
Definition: HTMLPurifier.standalone.php:10499
HTMLPurifier_ChildDef_Required\$whitespace
$whitespace
Definition: HTMLPurifier.standalone.php:14839
HTMLPurifier_HTMLModule_Tidy_Transitional\$defaultLevel
$defaultLevel
Definition: HTMLPurifier.standalone.php:18026
HTMLPurifier_URISchemeRegistry
Definition: HTMLPurifier.standalone.php:9547
HTMLPurifier_URIFilter_MakeAbsolute\$basePathStack
$basePathStack
Definition: HTMLPurifier.standalone.php:21636
HTMLPurifier_HTMLModule_SafeEmbed\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16969
HTMLPurifier_HTMLModule_Tidy_XHTML
Definition: HTMLPurifier.standalone.php:18034
HTMLPurifier_ConfigSchema\$info
$info
Definition: HTMLPurifier.standalone.php:2762
HTMLPurifier_Encoder\convertToASCIIDumbLossless
static convertToASCIIDumbLossless($str)
Definition: HTMLPurifier.standalone.php:4305
HTMLPurifier_Injector_AutoParagraph\$needed
$needed
Definition: HTMLPurifier.standalone.php:18076
HTMLPurifier_AttrDef_CSS_AlphaValue
Definition: HTMLPurifier.standalone.php:11011
HTMLPurifier_AttrDef_CSS_Background\$info
$info
Definition: HTMLPurifier.standalone.php:11057
HTMLPurifier_HTMLModule_Edit
Definition: HTMLPurifier.standalone.php:16074
HTMLPurifier_Strategy_MakeWellFormed\$zipper
$zipper
Definition: HTMLPurifier.standalone.php:20210
HTMLPurifier_StringHashParser
Definition: HTMLPurifier.standalone.php:8494
HTMLPurifier_URIScheme_mailto\doValidate
doValidate(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:22287
HTMLPurifier_URIScheme_mailto\$may_omit_host
$may_omit_host
Definition: HTMLPurifier.standalone.php:22279
HTMLPurifier_UnitConverter\DIGITAL
const DIGITAL
Definition: HTMLPurifier.standalone.php:9634
HTMLPurifier_ConfigSchema\makeFromSerial
static makeFromSerial()
Definition: HTMLPurifier.standalone.php:2779
HTMLPurifier_HTMLModule_List\$name
$name
Definition: HTMLPurifier.standalone.php:16654
HTMLPurifier_AttrDef_HTML_Bool\$name
$name
Definition: HTMLPurifier.standalone.php:12645
HTMLPurifier_AttrDef_CSS_DenyElementDecorator\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:11614
HTMLPurifier_Token_Comment\__construct
__construct($data, $line=null, $col=null)
Definition: HTMLPurifier.standalone.php:21277
HTMLPurifier_Token_Text\$name
$name
Definition: HTMLPurifier.standalone.php:21429
HTMLPurifier_VarParser\ITEXT
const ITEXT
Definition: HTMLPurifier.standalone.php:9943
HTMLPurifier_DefinitionCache_Decorator_Cleanup\replace
replace($def, $config)
Definition: HTMLPurifier.standalone.php:15879
HTMLPurifier_DefinitionCache_Null\add
add($def, $config)
Definition: HTMLPurifier.standalone.php:15449
HTMLPurifier_ContentSets\getChildDef
getChildDef($def, $module)
Definition: HTMLPurifier.standalone.php:3003
HTMLPurifier_PropertyListIterator\accept
accept()
Definition: HTMLPurifier.standalone.php:8322
HTMLPurifier_ChildDef_Optional\validateChildren
validateChildren($children, $config, $context)
Definition: HTMLPurifier.standalone.php:14968
HTMLPurifier_Lexer\normalize
normalize($html, $config, $context)
Definition: HTMLPurifier.standalone.php:7927
HTMLPurifier_ChildDef_StrictBlockquote
Definition: HTMLPurifier.standalone.php:14993
HTMLPurifier_URIFilter_MakeAbsolute\filter
filter(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:21668
HTMLPurifier_AttrDef_Clone\$clone
$clone
Definition: HTMLPurifier.standalone.php:10449
HTMLPurifier_ElementDef\$autoclose
$autoclose
Definition: HTMLPurifier.standalone.php:3726
HTMLPurifier_Language\$_loaded
$_loaded
Definition: HTMLPurifier.standalone.php:7091
HTMLPurifier_AttrDef_CSS_FontFamily
Definition: HTMLPurifier.standalone.php:11887
HTMLPurifier_HTMLModule_Edit\$defines_child_def
$defines_child_def
Definition: HTMLPurifier.standalone.php:16105
HTMLPurifier_Config\isFinalized
isFinalized($error=false)
Definition: HTMLPurifier.standalone.php:2636
HTMLPurifier_Injector_RemoveSpansWithoutAttributes\$needed
$needed
Definition: HTMLPurifier.standalone.php:18721
HTMLPurifier_HTMLDefinition\processModules
processModules($config)
Definition: HTMLPurifier.standalone.php:5665
HTMLPurifier_Injector_PurifierLinkify
Definition: HTMLPurifier.standalone.php:18528
HTMLPurifier_AttrTransform_TargetBlank
Definition: HTMLPurifier.standalone.php:14379
HTMLPurifier_Context
Definition: HTMLPurifier.standalone.php:3069
HTMLPurifier_Definition
Definition: HTMLPurifier.standalone.php:1154
HTMLPurifier_LanguageFactory\setup
setup()
Definition: HTMLPurifier.standalone.php:7324
HTMLPurifier_Config\inherit
static inherit(HTMLPurifier_Config $config)
Definition: HTMLPurifier.standalone.php:1934
HTMLPurifier_ChildDef_Optional\$allow_empty
$allow_empty
Definition: HTMLPurifier.standalone.php:14955
HTMLPurifier_HTMLModule_Tidy_Name
Definition: HTMLPurifier.standalone.php:17727
HTMLPurifier_URI\$scheme
$scheme
Definition: HTMLPurifier.standalone.php:8876
HTMLPurifier_HTMLModule_SafeObject\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17013
HTMLPurifier_URIDefinition\getDefaultScheme
getDefaultScheme($config, $context)
Definition: HTMLPurifier.standalone.php:9264
HTMLPurifier_AttrTransform_Lang
Definition: HTMLPurifier.standalone.php:14011
HTMLPurifier_URIFilter_HostBlacklist\$name
$name
Definition: HTMLPurifier.standalone.php:21581
HTMLPurifier_AttrTransform_BoolToCSS\$attr
$attr
Definition: HTMLPurifier.standalone.php:13703
HTMLPurifier_Length\$unit
$unit
Definition: HTMLPurifier.standalone.php:7479
HTMLPurifier_AttrValidator
Definition: HTMLPurifier.standalone.php:850
HTMLPurifier_Injector_AutoParagraph\$name
$name
Definition: HTMLPurifier.standalone.php:18071
HTMLPurifier_TokenFactory\createText
createText($data)
Definition: HTMLPurifier.standalone.php:8839
HTMLPurifier_PropertyList\setParent
setParent($plist)
Definition: HTMLPurifier.standalone.php:8283
HTMLPurifier_AttrTransform\confiscateAttr
confiscateAttr(&$attr, $key)
Definition: HTMLPurifier.standalone.php:732
HTMLPurifier_Token_Comment\$data
$data
Definition: HTMLPurifier.standalone.php:21263
HTMLPurifier_Config\createDefault
static createDefault()
Definition: HTMLPurifier.standalone.php:1943
HTMLPurifier_Token_Tag\$name
$name
Definition: HTMLPurifier.standalone.php:21315
HTMLPurifier_AttrDef_CSS_AlphaValue\validate
validate($number, $config, $context)
Definition: HTMLPurifier.standalone.php:11024
HTMLPurifier_HTMLModule_Target\$name
$name
Definition: HTMLPurifier.standalone.php:17293
HTMLPurifier_DefinitionCacheFactory\addDecorator
addDecorator($decorator)
Definition: HTMLPurifier.standalone.php:3380
HTMLPurifier_Token\rawPosition
rawPosition($l, $c)
Definition: HTMLPurifier.standalone.php:8725
HTMLPurifier_ConfigSchema\$singleton
static $singleton
Definition: HTMLPurifier.standalone.php:2768
HTMLPurifier_Config\loadIni
loadIni($filename)
Definition: HTMLPurifier.standalone.php:2620
HTMLPurifier_HTMLDefinition\$info_attr_transform_pre
$info_attr_transform_pre
Definition: HTMLPurifier.standalone.php:5528
HTMLPurifier_HTMLModule_Tidy_Strict\$name
$name
Definition: HTMLPurifier.standalone.php:17977
HTMLPurifier_Doctype\$aliases
$aliases
Definition: HTMLPurifier.standalone.php:3431
HTMLPurifier_URISchemeRegistry\getScheme
getScheme($scheme, $config, $context)
Definition: HTMLPurifier.standalone.php:9581
HTMLPurifier_HTMLModuleManager\processModule
processModule($module)
Definition: HTMLPurifier.standalone.php:6572
HTMLPurifier_ElementDef\$attr
$attr
Definition: HTMLPurifier.standalone.php:3640
HTMLPurifier_AttrDef_CSS_Number\$non_negative
$non_negative
Definition: HTMLPurifier.standalone.php:10935
HTMLPurifier_ErrorCollector\$lines
$lines
Definition: HTMLPurifier.standalone.php:4830
HTMLPurifier\VERSION
const VERSION
Definition: HTMLPurifier.standalone.php:86
HTMLPurifier_ChildDef_StrictBlockquote\$real_elements
$real_elements
Definition: HTMLPurifier.standalone.php:14997
HTMLPurifier_Node_Text\$name
$name
Definition: HTMLPurifier.standalone.php:19911
HTMLPurifier_AttrDef_CSS_Ident\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12115
HTMLPurifier_AttrTransform_ImgSpace
Definition: HTMLPurifier.standalone.php:13890
HTMLPurifier_HTMLModule_Tidy_Name\makeFixes
makeFixes()
Definition: HTMLPurifier.standalone.php:17741
HTMLPurifier_Generator
Definition: HTMLPurifier.standalone.php:5179
HTMLPurifier_URIFilter_HostBlacklist\filter
filter(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:21604
HTMLPurifier_Injector_SafeObject\prepare
prepare($config, $context)
Definition: HTMLPurifier.standalone.php:18845
HTMLPurifier_AttrDef_HTML_MultiLength\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:13232
HTMLPurifier_Encoder\ICONV_TRUNCATES
const ICONV_TRUNCATES
Definition: HTMLPurifier.standalone.php:4342
HTMLPurifier_VarParser_Flexible
Definition: HTMLPurifier.standalone.php:22423
HTMLPurifier_Config\__construct
__construct($definition, $parent=null)
Definition: HTMLPurifier.standalone.php:1895
HTMLPurifier_Doctype\$name
$name
Definition: HTMLPurifier.standalone.php:3406
HTMLPurifier_URIScheme_file\doValidate
doValidate(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:22128
HTMLPurifier_UnitConverter\__construct
__construct($output_precision=4, $internal_precision=10, $force_no_bcmath=false)
Definition: HTMLPurifier.standalone.php:9678
HTMLPurifier_HTMLModule_Legacy
Definition: HTMLPurifier.standalone.php:16476
HTMLPurifier_HTMLModule_Image
Definition: HTMLPurifier.standalone.php:16415
HTMLPurifier_Lexer_DirectLex\$_whitespace
$_whitespace
Definition: HTMLPurifier.standalone.php:19278
HTMLPurifier_AttrDef_URI
Definition: HTMLPurifier.standalone.php:10818
HTMLPurifier_StringHash\offsetGet
offsetGet($index)
Definition: HTMLPurifier.standalone.php:8440
HTMLPurifier_ChildDef_List
Definition: HTMLPurifier.standalone.php:14741
HTMLPurifier_Generator\generateAttributes
generateAttributes($assoc_array_of_attributes, $element='')
Definition: HTMLPurifier.standalone.php:5379
HTMLPurifier_URIFilter_Munge\prepare
prepare($config)
Definition: HTMLPurifier.standalone.php:21819
HTMLPurifier_Token\$col
$col
Definition: HTMLPurifier.standalone.php:8658
HTMLPurifier_HTMLModule_XMLCommonAttributes\$attr_collections
$attr_collections
Definition: HTMLPurifier.standalone.php:17712
HTMLPurifier_AttrValidator\validateToken
validateToken($token, $config, $context)
Definition: HTMLPurifier.standalone.php:859
HTMLPurifier_AttrDef_HTML_MultiLength
Definition: HTMLPurifier.standalone.php:13224
HTMLPurifier_CSSDefinition
Definition: HTMLPurifier.standalone.php:1210
HTMLPurifier_HTMLModule\$elements
$elements
Definition: HTMLPurifier.standalone.php:5983
HTMLPurifier_Token_Tag\$attr
$attr
Definition: HTMLPurifier.standalone.php:21321
HTMLPurifier_AttrTransform_Nofollow\__construct
__construct()
Definition: HTMLPurifier.standalone.php:14172
HTMLPurifier_AttrDef_URI_Email\unpack
unpack($string)
Definition: HTMLPurifier.standalone.php:13286
HTMLPurifier_AttrDef_HTML_Pixels
Definition: HTMLPurifier.standalone.php:13014
HTMLPurifier_Config\$version
$version
Definition: HTMLPurifier.standalone.php:1813
HTMLPurifier_ConfigSchema\postProcess
postProcess()
Definition: HTMLPurifier.standalone.php:2874
HTMLPurifier_HTMLModuleManager\__construct
__construct()
Definition: HTMLPurifier.standalone.php:6308
HTMLPurifier_AttrDef_Clone\__construct
__construct($clone)
Definition: HTMLPurifier.standalone.php:10454
and
and
Definition: license.txt:18
HTMLPurifier_URIFilter_Munge\$replace
$replace
Definition: HTMLPurifier.standalone.php:21813
HTMLPurifier_AttrDef_HTML_Class
Definition: HTMLPurifier.standalone.php:12760
HTMLPurifier_VarParser\C_STRING
const C_STRING
Definition: HTMLPurifier.standalone.php:9940
HTMLPurifier_DoctypeRegistry\getDoctypeFromConfig
getDoctypeFromConfig($config)
Definition: HTMLPurifier.standalone.php:3581
HTMLPurifier_HTMLModule_Tidy_Strict\makeFixes
makeFixes()
Definition: HTMLPurifier.standalone.php:17987
HTMLPurifier_VarParser\HASH
const HASH
Definition: HTMLPurifier.standalone.php:9949
HTMLPurifier_URIScheme_data\muteErrorHandler
muteErrorHandler($errno, $errstr)
Definition: HTMLPurifier.standalone.php:22093
HTMLPurifier_HTMLModule_SafeScripting\$name
$name
Definition: HTMLPurifier.standalone.php:17069
HTMLPurifier_HTMLModule_Edit\$name
$name
Definition: HTMLPurifier.standalone.php:16079
HTMLPurifier_Lexer_DOMLex
Definition: HTMLPurifier.standalone.php:18943
HTMLPurifier_EntityParser\$_substituteEntitiesRegex
$_substituteEntitiesRegex
Definition: HTMLPurifier.standalone.php:4647
HTMLPurifier_Strategy_FixNesting\execute
execute($tokens, $config, $context)
Definition: HTMLPurifier.standalone.php:20036
HTMLPurifier_AttrDef_HTML_Pixels\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:13035
HTMLPurifier_Lexer_DOMLex\__construct
__construct()
Definition: HTMLPurifier.standalone.php:18950
HTMLPurifier_DoctypeRegistry
Definition: HTMLPurifier.standalone.php:3469
tag
you may still notice some empty particularly if a node had but those elements were later removed because they were not permitted in that or tags after being auto closed by another tag
Definition: AutoFormat.RemoveEmpty.txt:41
HTMLPurifier_ChildDef_Empty\__construct
__construct()
Definition: HTMLPurifier.standalone.php:14711
HTMLPurifier_URIScheme_https\$secure
$secure
Definition: HTMLPurifier.standalone.php:22253
HTMLPurifier_ConfigSchema\$defaultPlist
$defaultPlist
Definition: HTMLPurifier.standalone.php:2729
HTMLPurifier_TokenFactory
Definition: HTMLPurifier.standalone.php:8756
elements
you may still notice some empty particularly if a node had elements
Definition: AutoFormat.RemoveEmpty.txt:39
HTMLPurifier_Zipper\splice
splice($t, $delete, $replacement)
Definition: HTMLPurifier.standalone.php:10283
HTMLPurifier_Doctype\__construct
__construct( $name=null, $xml=true, $modules=array(), $tidyModules=array(), $aliases=array(), $dtd_public=null, $dtd_system=null)
Definition: HTMLPurifier.standalone.php:3445
HTMLPurifier_HTMLModule_Tidy_XHTML\$name
$name
Definition: HTMLPurifier.standalone.php:18038
HTMLPurifier_AttrDef_URI\$embedsResource
$embedsResource
Definition: HTMLPurifier.standalone.php:10828
HTMLPurifier_DefinitionCache_Serializer\cleanup
cleanup($config)
Definition: HTMLPurifier.standalone.php:15635
HTMLPurifier_AttrDef_URI\validate
validate($uri, $config, $context)
Definition: HTMLPurifier.standalone.php:10855
HTMLPurifier_HTMLDefinition
Definition: HTMLPurifier.standalone.php:5482
HTMLPurifier_AttrDef_HTML_ID\$selector
$selector
Definition: HTMLPurifier.standalone.php:12916
HTMLPurifier_HTMLDefinition\$info_block_wrapper
$info_block_wrapper
Definition: HTMLPurifier.standalone.php:5516
HTMLPurifier_VarParser\LOOKUP
const LOOKUP
Definition: HTMLPurifier.standalone.php:9947
xml
if(!defined("CONTENTS_TYPE_XML")) define("CONTENTS_TYPE_XML" xml
Definition: constants.inc.php:13
HTMLPurifier_Zipper\toArray
toArray($t=NULL)
Definition: HTMLPurifier.standalone.php:10188
HTMLPurifier_Injector\handleElement
handleElement(&$token)
Definition: HTMLPurifier.standalone.php:7020
HTMLPurifier_Bootstrap
Definition: HTMLPurifier.standalone.php:1052
HTMLPurifier_ErrorCollector
Definition: HTMLPurifier.standalone.php:4786
HTMLPurifier_EntityParser\entityCallback
entityCallback($matches)
Definition: HTMLPurifier.standalone.php:4608
HTMLPurifier_Node_Comment\toTokenPair
toTokenPair()
Definition: HTMLPurifier.standalone.php:19827
HTMLPurifier_Injector_SafeObject\$needed
$needed
Definition: HTMLPurifier.standalone.php:18807
HTMLPurifier_HTMLModule_CommonAttributes\$name
$name
Definition: HTMLPurifier.standalone.php:16042
HTMLPurifier_Generator\$config
$config
Definition: HTMLPurifier.standalone.php:5229
HTMLPurifier_AttrCollections\performInclusions
performInclusions(&$attr)
Definition: HTMLPurifier.standalone.php:452
HTMLPurifier_HTMLModule_TargetBlank
Definition: HTMLPurifier.standalone.php:17319
HTMLPurifier_EntityParser\$_textEntitiesRegex
$_textEntitiesRegex
Definition: HTMLPurifier.standalone.php:4515
HTMLPurifier_PropertyList
Definition: HTMLPurifier.standalone.php:8174
HTMLPurifier_DefinitionCache_Decorator_Memory\add
add($def, $config)
Definition: HTMLPurifier.standalone.php:15936
HTMLPurifier_Config\$parser
$parser
Definition: HTMLPurifier.standalone.php:1841
HTMLPurifier_DoctypeRegistry\$doctypes
$doctypes
Definition: HTMLPurifier.standalone.php:3475
HTMLPurifier_ErrorCollector\$_stacks
$_stacks
Definition: HTMLPurifier.standalone.php:4810
table
and distribute a copy of this License along with the Library You may charge a fee for the physical act of transferring a and you may at your option offer warranty protection in exchange for a fee You may modify your copy or copies of the Library or any portion of thus forming a work based on the and copy and distribute such modifications or work under the terms of Section provided that you also meet all of these other than as an argument passed when the facility is then you must make a good faith effort to ensure in the event an application does not supply such function or table
Definition: license.txt:180
HTMLPurifier_AttrTransform_TargetBlank\__construct
__construct()
Definition: HTMLPurifier.standalone.php:14385
HTMLPurifier_Node_Element\__construct
__construct($name, $attr=array(), $line=null, $col=null, $armor=array())
Definition: HTMLPurifier.standalone.php:19870
HTMLPurifier_Token_Tag
Definition: HTMLPurifier.standalone.php:21297
HTMLPurifier_ChildDef_Optional
Definition: HTMLPurifier.standalone.php:14951
HTMLPurifier_HTMLModule_SafeScripting\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17074
HTMLPurifier_Config\loadArrayFromForm
static loadArrayFromForm($array, $index=false, $allowed=true, $mq_fix=true, $schema=null)
Definition: HTMLPurifier.standalone.php:2557
HTMLPurifier_URIScheme\$may_omit_host
$may_omit_host
Definition: HTMLPurifier.standalone.php:9481
HTMLPurifier\$strategy
$strategy
Definition: HTMLPurifier.standalone.php:110
HTMLPurifier_AttrTransform_BdoDir
Definition: HTMLPurifier.standalone.php:13643
HTMLPurifier_ConfigSchema
Definition: HTMLPurifier.standalone.php:2717
HTMLPurifier_HTMLModule_Forms\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16151
HTMLPurifier_HTMLModule_Object\$name
$name
Definition: HTMLPurifier.standalone.php:16782
HTMLPurifier_Config\$chatty
$chatty
Definition: HTMLPurifier.standalone.php:1881
HTMLPurifier_DefinitionCacheFactory
Definition: HTMLPurifier.standalone.php:3291
HTMLPurifier_HTMLModule_Tables\$name
$name
Definition: HTMLPurifier.standalone.php:17217
HTMLPurifier_AttrDef_CSS_Background\__construct
__construct($config)
Definition: HTMLPurifier.standalone.php:11062
HTMLPurifier_Node\$armor
$armor
Definition: HTMLPurifier.standalone.php:8035
HTMLPurifier_Encoder\unsafeIconv
static unsafeIconv($in, $out, $text)
Definition: HTMLPurifier.standalone.php:3857
HTMLPurifier_Arborize\arborize
static arborize($tokens, $config, $context)
Definition: HTMLPurifier.standalone.php:329
HTMLPurifier_Config\create
static create($config, $schema=null)
Definition: HTMLPurifier.standalone.php:1912
HTMLPurifier_AttrTransform_Border\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:13753
HTMLPurifier_Strategy_ValidateAttributes\execute
execute($tokens, $config, $context)
Definition: HTMLPurifier.standalone.php:21061
HTMLPurifier_Queue\isEmpty
isEmpty()
Definition: HTMLPurifier.standalone.php:8386
HTMLPurifier_Node_Comment
Definition: HTMLPurifier.standalone.php:19801
HTMLPurifier_Strategy_RemoveForeignElements
Definition: HTMLPurifier.standalone.php:20849
HTMLPurifier_HTMLModule\makeLookup
makeLookup($list)
Definition: HTMLPurifier.standalone.php:6207
HTMLPurifier_URI\$userinfo
$userinfo
Definition: HTMLPurifier.standalone.php:8881
HTMLPurifier_HTMLDefinition\$info_injector
$info_injector
Definition: HTMLPurifier.standalone.php:5547
HTMLPurifier_HTMLModule_Hypertext\$name
$name
Definition: HTMLPurifier.standalone.php:16325
HTMLPurifier_Context\destroy
destroy($name)
Definition: HTMLPurifier.standalone.php:3119
HTMLPurifier_AttrTransform_Background\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:13619
HTMLPurifier_HTMLModule_TargetNoopener\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17353
HTMLPurifier_URIScheme_nntp\doValidate
doValidate(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:22358
HTMLPurifier_URIFilter_SafeIframe\filter
filter(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:21936
HTMLPurifier_AttrDef_CSS_Number\__construct
__construct($non_negative=false)
Definition: HTMLPurifier.standalone.php:10940
HTMLPurifier_EntityLookup\instance
static instance($prototype=false)
Definition: HTMLPurifier.standalone.php:4477
HTMLPurifier_AttrDef_CSS_ListStyle
Definition: HTMLPurifier.standalone.php:12276
HTMLPurifier_VarParser\C_BOOL
const C_BOOL
Definition: HTMLPurifier.standalone.php:9946
HTMLPurifier_URI\$fragment
$fragment
Definition: HTMLPurifier.standalone.php:8906
HTMLPurifier_StringHashParser\$default
$default
Definition: HTMLPurifier.standalone.php:8499
HTMLPurifier_URIScheme_tel
Definition: HTMLPurifier.standalone.php:22380
HTMLPurifier_Token\toNode
toNode()
HTMLPurifier_HTMLModule_Text
Definition: HTMLPurifier.standalone.php:17396
HTMLPurifier_HTMLModule_Legacy\$name
$name
Definition: HTMLPurifier.standalone.php:16480
HTMLPurifier_HTMLModule_XMLCommonAttributes
Definition: HTMLPurifier.standalone.php:17703
HTMLPurifier_ChildDef_Table\__construct
__construct()
Definition: HTMLPurifier.standalone.php:15154
HTMLPurifier_Filter\postFilter
postFilter($html, $config, $context)
Definition: HTMLPurifier.standalone.php:5161
HTMLPurifier_URIFilter_HostBlacklist
Definition: HTMLPurifier.standalone.php:21577
HTMLPurifier_Filter
Definition: HTMLPurifier.standalone.php:5134
HTMLPurifier_AttrDef_URI_IPv4\validate
validate($aIP, $config, $context)
Definition: HTMLPurifier.standalone.php:13461
HTMLPurifier_ChildDef_Custom\_compileRegex
_compileRegex()
Definition: HTMLPurifier.standalone.php:14631
HTMLPurifier_ElementDef\$attr_transform_pre
$attr_transform_pre
Definition: HTMLPurifier.standalone.php:3658
HTMLPurifier_HTMLModule_Text\$content_sets
$content_sets
Definition: HTMLPurifier.standalone.php:17405
HTMLPurifier_TagTransform_Simple\__construct
__construct($transform_to, $style=null)
Definition: HTMLPurifier.standalone.php:21225
HTMLPurifier_Length\toString
toString()
Definition: HTMLPurifier.standalone.php:7560
HTMLPurifier_Config\$finalized
$finalized
Definition: HTMLPurifier.standalone.php:1861
HTMLPurifier_AttrDef_CSS_Number\validate
validate($number, $config, $context)
Definition: HTMLPurifier.standalone.php:10953
HTMLPurifier_VarParser\ISTRING
const ISTRING
Definition: HTMLPurifier.standalone.php:9941
HTMLPurifier_Node_Comment\$data
$data
Definition: HTMLPurifier.standalone.php:19806
HTMLPurifier_HTMLModule_Bdo\$name
$name
Definition: HTMLPurifier.standalone.php:16002
HTMLPurifier_AttrDef_URI_Email_SimpleCheck
Definition: HTMLPurifier.standalone.php:13583
HTMLPurifier_AttrDef_CSS_BackgroundPosition\__construct
__construct()
Definition: HTMLPurifier.standalone.php:11211
HTMLPurifier_Lexer_DOMLex\createStartNode
createStartNode($node, &$tokens, $collect, $config)
Definition: HTMLPurifier.standalone.php:19098
HTMLPurifier_HTMLModule\$safe
$safe
Definition: HTMLPurifier.standalone.php:6062
HTMLPurifier_AttrTransform_EnumToCSS\__construct
__construct($attr, $enum_to_css, $case_sensitive=false)
Definition: HTMLPurifier.standalone.php:13800
HTMLPurifier_URIParser
Definition: HTMLPurifier.standalone.php:9373
HTMLPurifier_UnitConverter\ENGLISH
const ENGLISH
Definition: HTMLPurifier.standalone.php:9632
HTMLPurifier_DefinitionCache_Null\replace
replace($def, $config)
Definition: HTMLPurifier.standalone.php:15469
HTMLPurifier_DefinitionCache_Decorator_Cleanup\$name
$name
Definition: HTMLPurifier.standalone.php:15836
HTMLPurifier_EntityParser\substituteSpecialEntities
substituteSpecialEntities($string)
Definition: HTMLPurifier.standalone.php:4740
HTMLPurifier_URIScheme_data
Definition: HTMLPurifier.standalone.php:21967
HTMLPurifier_Strategy_Composite
Definition: HTMLPurifier.standalone.php:19953
HTMLPurifier_DefinitionCacheFactory\setup
setup()
Definition: HTMLPurifier.standalone.php:3310
HTMLPurifier_Strategy\execute
execute($tokens, $config, $context)
type
if(!defined("USER_STATUS_TYPE")) define("USER_STATUS_TYPE" type
Definition: constants.inc.php:13
HTMLPurifier_DefinitionCache_Decorator_Memory\replace
replace($def, $config)
Definition: HTMLPurifier.standalone.php:15964
HTMLPurifier_HTMLModuleManager\getElement
getElement($name, $trusted=null)
Definition: HTMLPurifier.standalone.php:6621
HTMLPurifier_Injector_SafeObject\$objectStack
$objectStack
Definition: HTMLPurifier.standalone.php:18812
HTMLPurifier_AttrDef_Clone
Definition: HTMLPurifier.standalone.php:10444
HTMLPurifier_URIFilter_Munge\$name
$name
Definition: HTMLPurifier.standalone.php:21783
defaults
</pre >< p > In the above the configuration is still at the defaults
Definition: HTML.DefinitionID.txt:21
$path
$path
Definition: header.inc.php:12
HTMLPurifier_EntityParser\substituteTextEntities
substituteTextEntities($string)
Definition: HTMLPurifier.standalone.php:4574
HTMLPurifier_AttrTransform_TargetNoreferrer
Definition: HTMLPurifier.standalone.php:14465
HTMLPurifier_ErrorCollector\__construct
__construct($context)
Definition: HTMLPurifier.standalone.php:4835
HTMLPurifier_AttrTransform_Nofollow\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14183
HTMLPurifier_ChildDef_Chameleon\$inline
$inline
Definition: HTMLPurifier.standalone.php:14537
HTMLPurifier_Config\getHTMLDefinition
getHTMLDefinition($raw=false, $optimized=false)
Definition: HTMLPurifier.standalone.php:2202
HTMLPurifier_Token_End
Definition: HTMLPurifier.standalone.php:21386
HTMLPurifier_AttrTransform_BdoDir\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:13651
HTMLPurifier_URI\$host
$host
Definition: HTMLPurifier.standalone.php:8886
HTMLPurifier_URIFilter_SafeIframe\$regexp
$regexp
Definition: HTMLPurifier.standalone.php:21915
HTMLPurifier_URIFilter\$post
$post
Definition: HTMLPurifier.standalone.php:9331
HTMLPurifier_VarParser\TEXT
const TEXT
Definition: HTMLPurifier.standalone.php:9942
HTMLPurifier_AttrDef_URI_Host\$ipv4
$ipv4
Definition: HTMLPurifier.standalone.php:13309
HTMLPurifier_AttrDef_Switch
Definition: HTMLPurifier.standalone.php:10741
HTMLPurifier_HTMLModule_Bdo\$attr_collections
$attr_collections
Definition: HTMLPurifier.standalone.php:16007
HTMLPurifier_HTMLModule_Tidy_Proprietary\$name
$name
Definition: HTMLPurifier.standalone.php:17763
HTMLPurifier_AttrDef_CSS_Multiple\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12426
HTMLPurifier_URIScheme\$secure
$secure
Definition: HTMLPurifier.standalone.php:9466
HTMLPurifier_HTMLModule_Forms\$safe
$safe
Definition: HTMLPurifier.standalone.php:16138
HTMLPurifier_URIScheme_tel\$browsable
$browsable
Definition: HTMLPurifier.standalone.php:22384
HTMLPurifier_AttrTransform_SafeObject\$name
$name
Definition: HTMLPurifier.standalone.php:14246
HTMLPurifier_VarParser\error
error($msg)
Definition: HTMLPurifier.standalone.php:10077
HTMLPurifier_AttrTransform_NameSync\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14136
HTMLPurifier_URI\$path
$path
Definition: HTMLPurifier.standalone.php:8896
HTMLPurifier_AttrCollections\$info
$info
Definition: HTMLPurifier.standalone.php:403
HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4
Definition: HTMLPurifier.standalone.php:17793
HTMLPurifier_HTMLDefinition\getAnonymousModule
getAnonymousModule()
Definition: HTMLPurifier.standalone.php:5614
HTMLPurifier_ErrorStruct\ATTR
const ATTR
Definition: HTMLPurifier.standalone.php:5040
HTMLPurifier_Config\getCSSDefinition
getCSSDefinition($raw=false, $optimized=false)
Definition: HTMLPurifier.standalone.php:2221
HTMLPurifier_HTMLModule_List\$content_sets
$content_sets
Definition: HTMLPurifier.standalone.php:16668
HTMLPurifier_Token_Text\$data
$data
Definition: HTMLPurifier.standalone.php:21435
HTMLPurifier_URI\validate
validate($config, $context)
Definition: HTMLPurifier.standalone.php:8968
HTMLPurifier_ErrorCollector\$errors
$errors
Definition: HTMLPurifier.standalone.php:4800
HTMLPurifier_AttrTransform_SafeParam\$name
$name
Definition: HTMLPurifier.standalone.php:14284
HTMLPurifier_ElementDef\mergeIn
mergeIn($def)
Definition: HTMLPurifier.standalone.php:3761
HTMLPurifier_TagTransform_Simple\transform
transform($tag, $config, $context)
Definition: HTMLPurifier.standalone.php:21237
HTMLPurifier_ErrorCollector\SEVERITY
const SEVERITY
Definition: HTMLPurifier.standalone.php:4793
HTMLPurifier_HTMLDefinition\$info_parent
$info_parent
Definition: HTMLPurifier.standalone.php:5502
HTMLPurifier_Injector\$currentToken
$currentToken
Definition: HTMLPurifier.standalone.php:6801
HTMLPurifier_PropertyListIterator
Definition: HTMLPurifier.standalone.php:8297
HTMLPurifier_Node_Text\$data
$data
Definition: HTMLPurifier.standalone.php:19916
HTMLPurifier_Node_Text
Definition: HTMLPurifier.standalone.php:19904
HTMLPurifier_URIDefinition\$base
$base
Definition: HTMLPurifier.standalone.php:9191
HTMLPurifier_URI\__construct
__construct($scheme, $userinfo, $host, $port, $path, $query, $fragment)
Definition: HTMLPurifier.standalone.php:8918
HTMLPurifier_Generator\generateFromTokens
generateFromTokens($tokens)
Definition: HTMLPurifier.standalone.php:5251
HTMLPurifier_ElementDef\$required_attr
$required_attr
Definition: HTMLPurifier.standalone.php:3706
HTMLPurifier_Injector\$htmlDefinition
$htmlDefinition
Definition: HTMLPurifier.standalone.php:6788
HTMLPurifier_EntityLookup\$table
$table
Definition: HTMLPurifier.standalone.php:4455
HTMLPurifier_StringHash\getAccessed
getAccessed()
Definition: HTMLPurifier.standalone.php:8450
HTMLPurifier_DefinitionCache_Serializer\replace
replace($def, $config)
Definition: HTMLPurifier.standalone.php:15560
HTMLPurifier_TagTransform_Font\$transform_to
$transform_to
Definition: HTMLPurifier.standalone.php:21114
HTMLPurifier_AttrDef_CSS\validate
validate($css, $config, $context)
Definition: HTMLPurifier.standalone.php:10322
HTMLPurifier_VarParser_Flexible\parseImplementation
parseImplementation($var, $type, $allow_null)
Definition: HTMLPurifier.standalone.php:22431
HTMLPurifier_ErrorCollector\MESSAGE
const MESSAGE
Definition: HTMLPurifier.standalone.php:4794
HTMLPurifier_URIScheme_nntp\$default_port
$default_port
Definition: HTMLPurifier.standalone.php:22345
HTMLPurifier_Lexer_DirectLex
Definition: HTMLPurifier.standalone.php:19268
HTMLPurifier_HTMLModule_TargetNoopener
Definition: HTMLPurifier.standalone.php:17344
HTMLPurifier_Length
Definition: HTMLPurifier.standalone.php:7467
HTMLPurifier_HTMLModule_Scripting
Definition: HTMLPurifier.standalone.php:17115
HTMLPurifier_Encoder\iconvAvailable
static iconvAvailable()
Definition: HTMLPurifier.standalone.php:4187
HTMLPurifier_AttrDef\parseCDATA
parseCDATA($string)
Definition: HTMLPurifier.standalone.php:598
HTMLPurifier_Definition\setup
setup($config)
Definition: HTMLPurifier.standalone.php:1191
HTMLPurifier_HTMLDefinition\addBlankElement
addBlankElement($element_name)
Definition: HTMLPurifier.standalone.php:5601
HTMLPurifier_Lexer\CDATACallback
static CDATACallback($matches)
Definition: HTMLPurifier.standalone.php:7912
HTMLPurifier_HTMLModule_Tidy\makeFixesForLevel
makeFixesForLevel($fixes)
Definition: HTMLPurifier.standalone.php:17577
HTMLPurifier_HTMLModule_Scripting\$name
$name
Definition: HTMLPurifier.standalone.php:17119
HTMLPurifier_ContentSets\$info
$info
Definition: HTMLPurifier.standalone.php:2900
HTMLPurifier_HTMLModule\addElementToContentSet
addElementToContentSet($element, $type)
Definition: HTMLPurifier.standalone.php:6141
HTMLPurifier_VarParser\errorInconsistent
errorInconsistent($class, $type)
Definition: HTMLPurifier.standalone.php:10091
HTMLPurifier_URIFilter_DisableExternalResources\$name
$name
Definition: HTMLPurifier.standalone.php:21528
HTMLPurifier_AttrDef_URI\$parser
$parser
Definition: HTMLPurifier.standalone.php:10823
HTMLPurifier_AttrDef_HTML_ID\__construct
__construct($selector=false)
Definition: HTMLPurifier.standalone.php:12921
HTMLPurifier_HTMLModule_Tidy\$defaultLevel
$defaultLevel
Definition: HTMLPurifier.standalone.php:17490
HTMLPurifier_Bootstrap\registerAutoload
static registerAutoload()
Definition: HTMLPurifier.standalone.php:1100
HTMLPurifier_HTMLModule\$content_sets
$content_sets
Definition: HTMLPurifier.standalone.php:6000
HTMLPurifier_URIFilter_SafeIframe\prepare
prepare($config)
Definition: HTMLPurifier.standalone.php:21924
HTMLPurifier_URIScheme_http\$default_port
$default_port
Definition: HTMLPurifier.standalone.php:22212
HTMLPurifier_Doctype\$dtdPublic
$dtdPublic
Definition: HTMLPurifier.standalone.php:3437
HTMLPurifier_URIScheme_http\$hierarchical
$hierarchical
Definition: HTMLPurifier.standalone.php:22222
HTMLPurifier_Encoder\convertFromUTF8
static convertFromUTF8($str, $config, $context)
Definition: HTMLPurifier.standalone.php:4251
HTMLPurifier_AttrTransform\transform
transform($attr, $config, $context)
HTMLPurifier_AttrDef_CSS_Filter\validate
validate($value, $config, $context)
Definition: HTMLPurifier.standalone.php:11651
HTMLPurifier_Definition\$optimized
$optimized
Definition: HTMLPurifier.standalone.php:1172
HTMLPurifier_AttrDef_HTML_FrameTarget
Definition: HTMLPurifier.standalone.php:12861
HTMLPurifier_HTMLModule\$info_tag_transform
$info_tag_transform
Definition: HTMLPurifier.standalone.php:6017
HTMLPurifier_URI\isLocal
isLocal($config, $context)
Definition: HTMLPurifier.standalone.php:9133
HTMLPurifier_URIFilter_DisableResources\$name
$name
Definition: HTMLPurifier.standalone.php:21554
HTMLPurifier_ElementDef\$excludes
$excludes
Definition: HTMLPurifier.standalone.php:3720
$last
$last
Definition: index.php:25
HTMLPurifier_HTMLModule_SafeScripting
Definition: HTMLPurifier.standalone.php:17065
HTMLPurifier_ChildDef_Required\__construct
__construct($elements)
Definition: HTMLPurifier.standalone.php:14844
HTMLPurifier_HTMLDefinition\$info_attr_transform_post
$info_attr_transform_post
Definition: HTMLPurifier.standalone.php:5534
HTMLPurifier_Injector\checkNeeded
checkNeeded($config)
Definition: HTMLPurifier.standalone.php:6880
HTMLPurifier_HTMLModule_Forms
Definition: HTMLPurifier.standalone.php:16129
HTMLPurifier_PropertyList\squash
squash($force=false)
Definition: HTMLPurifier.standalone.php:8258
HTMLPurifier_ChildDef
Definition: HTMLPurifier.standalone.php:1743
HTMLPurifier_ErrorCollector\send
send($severity, $msg)
Definition: HTMLPurifier.standalone.php:4848
HTMLPurifier_VarParser\C_INT
const C_INT
Definition: HTMLPurifier.standalone.php:9944
HTMLPurifier_Language\listify
listify($array)
Definition: HTMLPurifier.standalone.php:7167
HTMLPurifier_URIScheme_nntp
Definition: HTMLPurifier.standalone.php:22341
HTMLPurifier_Injector_RemoveSpansWithoutAttributes\prepare
prepare($config, $context)
Definition: HTMLPurifier.standalone.php:18739
HTMLPurifier_AttrDef_CSS_TextDecoration
Definition: HTMLPurifier.standalone.php:12517
HTMLPurifier_URIDefinition\$postFilters
$postFilters
Definition: HTMLPurifier.standalone.php:9185
HTMLPurifier_Config\maybeGetRawCSSDefinition
maybeGetRawCSSDefinition()
Definition: HTMLPurifier.standalone.php:2449
HTMLPurifier_Injector_SafeObject\handleElement
handleElement(&$token)
Definition: HTMLPurifier.standalone.php:18853
HTMLPurifier_URIDefinition\$filters
$filters
Definition: HTMLPurifier.standalone.php:9184
HTMLPurifier_URIDefinition\$host
$host
Definition: HTMLPurifier.standalone.php:9196
HTMLPurifier_Encoder\iconv
static iconv($in, $out, $text, $max_chunk_size=8000)
Definition: HTMLPurifier.standalone.php:3873
HTMLPurifier_Config\$autoFinalize
$autoFinalize
Definition: HTMLPurifier.standalone.php:1820
HTMLPurifier_DoctypeRegistry\$aliases
$aliases
Definition: HTMLPurifier.standalone.php:3481
HTMLPurifier_AttrTransform_ScriptRequired
Definition: HTMLPurifier.standalone.php:14351
HTMLPurifier_HTMLDefinition\$type
$type
Definition: HTMLPurifier.standalone.php:5630
HTMLPurifier_Doctype\$dtdSystem
$dtdSystem
Definition: HTMLPurifier.standalone.php:3443
HTMLPurifier_VarParser\getTypeName
static getTypeName($type)
Definition: HTMLPurifier.standalone.php:10114
HTMLPurifier_AttrDef_HTML_Class\filter
filter($tokens, $config, $context)
Definition: HTMLPurifier.standalone.php:12784
HTMLPurifier_HTMLModule_StyleAttribute
Definition: HTMLPurifier.standalone.php:17180
HTMLPurifier_AttrTransform_Textarea
Definition: HTMLPurifier.standalone.php:14497
HTMLPurifier_AttrDef_Text
Definition: HTMLPurifier.standalone.php:10795
HTMLPurifier_CSSDefinition\doSetupProprietary
doSetupProprietary($config)
Definition: HTMLPurifier.standalone.php:1591
HTMLPurifier_AttrTransform_Input
Definition: HTMLPurifier.standalone.php:13953
HTMLPurifier_Token\$skip
$skip
Definition: HTMLPurifier.standalone.php:8672
HTMLPurifier_Encoder\ICONV_UNUSABLE
const ICONV_UNUSABLE
Definition: HTMLPurifier.standalone.php:4346
HTMLPurifier_Lexer\parseAttr
parseAttr($string, $config)
Definition: HTMLPurifier.standalone.php:7798
HTMLPurifier\$version
$version
Definition: HTMLPurifier.standalone.php:81
HTMLPurifier_Encoder\unichr
static unichr($code)
Definition: HTMLPurifier.standalone.php:4140
HTMLPurifier_ErrorCollector\LINENO
const LINENO
Definition: HTMLPurifier.standalone.php:4792
HTMLPurifier_ContentSets\__construct
__construct($modules)
Definition: HTMLPurifier.standalone.php:2925
HTMLPurifier_Language\$messages
$messages
Definition: HTMLPurifier.standalone.php:7070
HTMLPurifier_Lexer\$tracksLineNumbers
$tracksLineNumbers
Definition: HTMLPurifier.standalone.php:7671
HTMLPurifier_HTMLModule_Forms\$content_sets
$content_sets
Definition: HTMLPurifier.standalone.php:16143
HTMLPurifier_DefinitionCache_Decorator_Memory\$definitions
$definitions
Definition: HTMLPurifier.standalone.php:15916
HTMLPurifier_AttrDef_CSS_Composite\__construct
__construct($defs)
Definition: HTMLPurifier.standalone.php:11556
HTMLPurifier_Injector_SafeObject\handleEnd
handleEnd(&$token)
Definition: HTMLPurifier.standalone.php:18902
HTMLPurifier_HTMLModule_Presentation\$name
$name
Definition: HTMLPurifier.standalone.php:16851
HTMLPurifier_Injector_SafeObject\$allowedParam
$allowedParam
Definition: HTMLPurifier.standalone.php:18832
HTMLPurifier_DefinitionCache_Serializer\generateBaseDirectoryPath
generateBaseDirectoryPath($config)
Definition: HTMLPurifier.standalone.php:15695
HTMLPurifier_Config\$definitions
$definitions
Definition: HTMLPurifier.standalone.php:1855
HTMLPurifier_UnitConverter\$units
static $units
Definition: HTMLPurifier.standalone.php:9645
HTMLPurifier_URIScheme_mailto\$browsable
$browsable
Definition: HTMLPurifier.standalone.php:22274
HTMLPurifier_HTMLDefinition\addElement
addElement($element_name, $type, $contents, $attr_collections, $attributes=array())
Definition: HTMLPurifier.standalone.php:5584
HTMLPurifier_Token_Text
Definition: HTMLPurifier.standalone.php:21424
HTMLPurifier_Config\$plist
$plist
Definition: HTMLPurifier.standalone.php:1867
HTMLPurifier_URIScheme_data\doValidate
doValidate(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:21996
HTMLPurifier_AttrDef_HTML_Bool\$minimized
$minimized
Definition: HTMLPurifier.standalone.php:12650
HTMLPurifier_Token\$carryover
$carryover
Definition: HTMLPurifier.standalone.php:8682
HTMLPurifier_Strategy_ValidateAttributes
Definition: HTMLPurifier.standalone.php:21053
HTMLPurifier_Node_Element\toTokenPair
toTokenPair()
Definition: HTMLPurifier.standalone.php:19878
HTMLPurifier_Lexer_DOMLex\getData
getData($node)
Definition: HTMLPurifier.standalone.php:19076
HTMLPurifier_AttrDef_HTML_Bool\make
make($string)
Definition: HTMLPurifier.standalone.php:12675
HTMLPurifier_Token_Tag\toNode
toNode()
Definition: HTMLPurifier.standalone.php:21353
HTMLPurifier_HTMLDefinition\$info_parent_def
$info_parent_def
Definition: HTMLPurifier.standalone.php:5509
HTMLPurifier_URIFilter_DisableResources
Definition: HTMLPurifier.standalone.php:21550
HTMLPurifier_TagTransform\prependCSS
prependCSS(&$attr, $css)
Definition: HTMLPurifier.standalone.php:8632
HTMLPurifier_DefinitionCache_Serializer
Definition: HTMLPurifier.standalone.php:15516
HTMLPurifier_AttrTransform_Input\__construct
__construct()
Definition: HTMLPurifier.standalone.php:13959
HTMLPurifier_HTMLModuleManager
Definition: HTMLPurifier.standalone.php:6238
HTMLPurifier_ChildDef_Required\$type
$type
Definition: HTMLPurifier.standalone.php:14871
HTMLPurifier_Injector_Linkify\handleText
handleText(&$token)
Definition: HTMLPurifier.standalone.php:18476
HTMLPurifier_AttrDef_CSS
Definition: HTMLPurifier.standalone.php:10314
HTMLPurifier_Config\serialize
serialize()
Definition: HTMLPurifier.standalone.php:2699
HTMLPurifier_StringHash\resetAccessed
resetAccessed()
Definition: HTMLPurifier.standalone.php:8458
HTMLPurifier_Encoder\testIconvTruncateBug
static testIconvTruncateBug()
Definition: HTMLPurifier.standalone.php:4362
HTMLPurifier_Arborize
Definition: HTMLPurifier.standalone.php:328
HTMLPurifier_URI
Definition: HTMLPurifier.standalone.php:8872
HTMLPurifier_VarParser\parse
parse($var, $type, $allow_null=false)
Definition: HTMLPurifier.standalone.php:9991
HTMLPurifier_ElementDef\$wrap
$wrap
Definition: HTMLPurifier.standalone.php:3734
HTMLPurifier_AttrDef_CSS_ImportantDecorator\__construct
__construct($def, $allow=false)
Definition: HTMLPurifier.standalone.php:12154
HTMLPurifier_URISchemeRegistry\$schemes
$schemes
Definition: HTMLPurifier.standalone.php:9572
HTMLPurifier_AttrTransform_Length
Definition: HTMLPurifier.standalone.php:14041
HTMLPurifier_URIDefinition\$registeredFilters
$registeredFilters
Definition: HTMLPurifier.standalone.php:9186
HTMLPurifier_VarParser\errorGeneric
errorGeneric($var, $type)
Definition: HTMLPurifier.standalone.php:10104
HTMLPurifier_URIFilter_SafeIframe\$always_load
$always_load
Definition: HTMLPurifier.standalone.php:21910
HTMLPurifier_URISchemeRegistry\instance
static instance($prototype=null)
Definition: HTMLPurifier.standalone.php:9557
HTMLPurifier_HTMLModule_SafeEmbed
Definition: HTMLPurifier.standalone.php:16960
HTMLPurifier_HTMLModule\$attr_collections
$attr_collections
Definition: HTMLPurifier.standalone.php:6011
HTMLPurifier_URIFilter_DisableExternalResources
Definition: HTMLPurifier.standalone.php:21524
HTMLPurifier_Bootstrap\getPath
static getPath($class)
Definition: HTMLPurifier.standalone.php:1079
HTMLPurifier_HTMLModuleManager\$prefixes
$prefixes
Definition: HTMLPurifier.standalone.php:6290
HTMLPurifier_AttrTransform_BoolToCSS\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:13727
HTMLPurifier_Node\toTokenPair
toTokenPair()
HTMLPurifier_AttrTransform_ImgRequired
Definition: HTMLPurifier.standalone.php:13847
HTMLPurifier_URIFilter_DisableExternalResources\filter
filter(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:21536
HTMLPurifier_HTMLModule_Scripting\$content_sets
$content_sets
Definition: HTMLPurifier.standalone.php:17129
HTMLPurifier_AttrTransform_SafeParam\__construct
__construct()
Definition: HTMLPurifier.standalone.php:14291
HTMLPurifier_AttrDef_Switch\$withoutTag
$withoutTag
Definition: HTMLPurifier.standalone.php:10756
HTMLPurifier_Strategy_MakeWellFormed\$stack
$stack
Definition: HTMLPurifier.standalone.php:20216
HTMLPurifier_HTMLModule\$defines_child_def
$defines_child_def
Definition: HTMLPurifier.standalone.php:6047
HTMLPurifier_ElementDef
Definition: HTMLPurifier.standalone.php:3620
HTMLPurifier_AttrDef\make
make($string)
Definition: HTMLPurifier.standalone.php:610
HTMLPurifier_HTMLModule_CommonAttributes
Definition: HTMLPurifier.standalone.php:16038
HTMLPurifier_Length\$isValid
$isValid
Definition: HTMLPurifier.standalone.php:7485
HTMLPurifier_ElementDef\$child
$child
Definition: HTMLPurifier.standalone.php:3670
HTMLPurifier_DefinitionCache_Decorator\decorate
decorate(&$cache)
Definition: HTMLPurifier.standalone.php:15349
HTMLPurifier_HTMLModule_Tidy\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17511
HTMLPurifier_Lexer_DOMLex\callbackUndoCommentSubst
callbackUndoCommentSubst($matches)
Definition: HTMLPurifier.standalone.php:19202
HTMLPurifier_ChildDef_Custom\$dtd_regex
$dtd_regex
Definition: HTMLPurifier.standalone.php:14611
HTMLPurifier_Injector\rewindOffset
rewindOffset($offset)
Definition: HTMLPurifier.standalone.php:6832
HTMLPurifier_EntityLookup
Definition: HTMLPurifier.standalone.php:4450
HTMLPurifier_AttrDef_HTML_Nmtokens\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12697
HTMLPurifier_ErrorCollector\$_current
$_current
Definition: HTMLPurifier.standalone.php:4805
HTMLPurifier\__construct
__construct($config=null)
Definition: HTMLPurifier.standalone.php:134
HTMLPurifier_Strategy_MakeWellFormed\$context
$context
Definition: HTMLPurifier.standalone.php:20234
HTMLPurifier_AttrDef_HTML_FrameTarget\__construct
__construct()
Definition: HTMLPurifier.standalone.php:12873
HTMLPurifier_ChildDef_Custom
Definition: HTMLPurifier.standalone.php:14596
HTMLPurifier_LanguageFactory\create
create($config, $context, $code=false)
Definition: HTMLPurifier.standalone.php:7337
HTMLPurifier_URIScheme_ftp\$browsable
$browsable
Definition: HTMLPurifier.standalone.php:22158
HTMLPurifier_AttrDef_HTML_Bool
Definition: HTMLPurifier.standalone.php:12640
HTMLPurifier_URIScheme_file\$may_omit_host
$may_omit_host
Definition: HTMLPurifier.standalone.php:22120
HTMLPurifier_IDAccumulator\load
load($array_of_ids)
Definition: HTMLPurifier.standalone.php:6751
HTMLPurifier_AttrTransform_BoolToCSS\__construct
__construct($attr, $css)
Definition: HTMLPurifier.standalone.php:13715
HTMLPurifier_HTMLDefinition\$info_global_attr
$info_global_attr
Definition: HTMLPurifier.standalone.php:5496
HTMLPurifier_TagTransform_Font\$_size_lookup
$_size_lookup
Definition: HTMLPurifier.standalone.php:21119
HTMLPurifier_PropertyList\getParent
getParent()
Definition: HTMLPurifier.standalone.php:8274
HTMLPurifier_AttrDef_CSS_Multiple
Definition: HTMLPurifier.standalone.php:12396
HTMLPurifier_Encoder\convertToUTF8
static convertToUTF8($str, $config, $context)
Definition: HTMLPurifier.standalone.php:4203
HTMLPurifier_Token\$rewind
$rewind
Definition: HTMLPurifier.standalone.php:8677
HTMLPurifier_EntityParser\__construct
__construct()
Definition: HTMLPurifier.standalone.php:4528
HTMLPurifier_AttrCollections\doConstruct
doConstruct($attr_types, $modules)
Definition: HTMLPurifier.standalone.php:417
HTMLPurifier_Lexer_DOMLex\tokenizeHTML
tokenizeHTML($html, $config, $context)
Definition: HTMLPurifier.standalone.php:18963
HTMLPurifier_Queue\shift
shift()
Definition: HTMLPurifier.standalone.php:8365
HTMLPurifier_HTMLModule_Presentation\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16856
HTMLPurifier_Injector_AutoParagraph\handleElement
handleElement(&$token)
Definition: HTMLPurifier.standalone.php:18169
HTMLPurifier_Lexer_DirectLex\parseAttributeString
parseAttributeString($string, $config, $context)
Definition: HTMLPurifier.standalone.php:19630
HTMLPurifier_PropertyListIterator\$filter
$filter
Definition: HTMLPurifier.standalone.php:8306
HTMLPurifier_DefinitionCache
Definition: HTMLPurifier.standalone.php:3166
HTMLPurifier_AttrDef_CSS_Percentage\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12482
HTMLPurifier_HTMLModule_TargetNoreferrer\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17375
HTMLPurifier_Injector_AutoParagraph\handleText
handleText(&$token)
Definition: HTMLPurifier.standalone.php:18091
HTMLPurifier_ElementDef\$descendants_are_inline
$descendants_are_inline
Definition: HTMLPurifier.standalone.php:3699
HTMLPurifier_ChildDef_Chameleon\validateChildren
validateChildren($children, $config, $context)
Definition: HTMLPurifier.standalone.php:14567
HTMLPurifier_HTMLModule_Proprietary
Definition: HTMLPurifier.standalone.php:16883
HTMLPurifier_AttrTypes
Definition: HTMLPurifier.standalone.php:751
HTMLPurifier_PropertyList\$data
$data
Definition: HTMLPurifier.standalone.php:8179
HTMLPurifier_ContentSets\generateChildDef
generateChildDef(&$def, $module)
Definition: HTMLPurifier.standalone.php:2970
HTMLPurifier_HTMLModule_TargetNoopener\$name
$name
Definition: HTMLPurifier.standalone.php:17348
$s
$s
Definition: embed.php:13
HTMLPurifier_Encoder\muteErrorHandler
static muteErrorHandler()
Definition: HTMLPurifier.standalone.php:3846
HTMLPurifier_DefinitionCacheFactory\$implementations
$implementations
Definition: HTMLPurifier.standalone.php:3300
HTMLPurifier_HTMLModuleManager\$trusted
$trusted
Definition: HTMLPurifier.standalone.php:6306
HTMLPurifier_AttrTransform_Lang\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14019
HTMLPurifier_VarParser\C_MIXED
const C_MIXED
Definition: HTMLPurifier.standalone.php:9950
HTMLPurifier_AttrDef_CSS_URI\__construct
__construct()
Definition: HTMLPurifier.standalone.php:12570
HTMLPurifier_HTMLModule_Legacy\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16485
HTMLPurifier_HTMLModule_Image\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16425
HTMLPurifier_Strategy_MakeWellFormed\$injectors
$injectors
Definition: HTMLPurifier.standalone.php:20222
HTMLPurifier_URIScheme_http\doValidate
doValidate(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:22230
HTMLPurifier_ChildDef\$allow_empty
$allow_empty
Definition: HTMLPurifier.standalone.php:1758
HTMLPurifier_Generator\generateFromToken
generateFromToken($token)
Definition: HTMLPurifier.standalone.php:5307
HTMLPurifier_ErrorCollector\getHTMLFormatted
getHTMLFormatted($config, $errors=null)
Definition: HTMLPurifier.standalone.php:4952
HTMLPurifier_HTMLModule_Iframe
Definition: HTMLPurifier.standalone.php:16365
HTMLPurifier_DefinitionCacheFactory\$decorators
$decorators
Definition: HTMLPurifier.standalone.php:3305
HTMLPurifier_URIScheme\doValidate
doValidate(&$uri, $config, $context)
HTMLPurifier_ChildDef_Chameleon\$block
$block
Definition: HTMLPurifier.standalone.php:14543
HTMLPurifier_AttrTransform_SafeEmbed
Definition: HTMLPurifier.standalone.php:14213
HTMLPurifier_HTMLModule_Name\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16708
HTMLPurifier_Config\getBatchSerial
getBatchSerial($namespace)
Definition: HTMLPurifier.standalone.php:2036
HTMLPurifier_AttrDef_Enum
Definition: HTMLPurifier.standalone.php:10492
HTMLPurifier_ChildDef_Empty\validateChildren
validateChildren($children, $config, $context)
Definition: HTMLPurifier.standalone.php:14721
HTMLPurifier_TagTransform_Simple
Definition: HTMLPurifier.standalone.php:21215
HTMLPurifier_ElementDef\$content_model
$content_model
Definition: HTMLPurifier.standalone.php:3680
HTMLPurifier_ChildDef\validateChildren
validateChildren($children, $config, $context)
HTMLPurifier_URIScheme_data\$browsable
$browsable
Definition: HTMLPurifier.standalone.php:21971
HTMLPurifier_ChildDef_Table\$elements
$elements
Definition: HTMLPurifier.standalone.php:15144
HTMLPurifier_DefinitionCache_Serializer\generateFilePath
generateFilePath($config)
Definition: HTMLPurifier.standalone.php:15669
HTMLPurifier_URIScheme_news\$may_omit_host
$may_omit_host
Definition: HTMLPurifier.standalone.php:22314
HTMLPurifier_HTMLModuleManager\$contentSets
$contentSets
Definition: HTMLPurifier.standalone.php:6295
HTMLPurifier_URIParser\$percentEncoder
$percentEncoder
Definition: HTMLPurifier.standalone.php:9378
HTMLPurifier_Strategy_MakeWellFormed\$tokens
$tokens
Definition: HTMLPurifier.standalone.php:20198
HTMLPurifier_Language\$context
$context
Definition: HTMLPurifier.standalone.php:7101
HTMLPurifier_VarParser_Native
Definition: HTMLPurifier.standalone.php:22554
HTMLPurifier_ChildDef_Chameleon
Definition: HTMLPurifier.standalone.php:14531
HTMLPurifier_AttrDef_CSS_Composite\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:11567
HTMLPurifier_Strategy_RemoveForeignElements\execute
execute($tokens, $config, $context)
Definition: HTMLPurifier.standalone.php:20857
HTMLPurifier_UnitConverter\$internalPrecision
$internalPrecision
Definition: HTMLPurifier.standalone.php:9670
HTMLPurifier_ChildDef\$elements
$elements
Definition: HTMLPurifier.standalone.php:1764
HTMLPurifier_HTMLModule_Tidy\$fixesForLevel
$fixesForLevel
Definition: HTMLPurifier.standalone.php:17498
HTMLPurifier_AttrDef_HTML_Nmtokens
Definition: HTMLPurifier.standalone.php:12689
HTMLPurifier_TagTransform_Simple\$style
$style
Definition: HTMLPurifier.standalone.php:21219
HTMLPurifier_Definition\doSetup
doSetup($config)
HTMLPurifier_AttrDef_HTML_Nmtokens\filter
filter($tokens, $config, $context)
Definition: HTMLPurifier.standalone.php:12746
HTMLPurifier_URIScheme_tel\doValidate
doValidate(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:22397
HTMLPurifier_HTMLModule_Nofollow
Definition: HTMLPurifier.standalone.php:16730
HTMLPurifier_HTMLModule_List
Definition: HTMLPurifier.standalone.php:16650
HTMLPurifier_AttrDef_URI\__construct
__construct($embeds_resource=false)
Definition: HTMLPurifier.standalone.php:10833
HTMLPurifier_AttrDef_CSS_URI\validate
validate($uri_string, $config, $context)
Definition: HTMLPurifier.standalone.php:12581
HTMLPurifier_Injector\prepare
prepare($config, $context)
Definition: HTMLPurifier.standalone.php:6857
HTMLPurifier_HTMLModule_Tables\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17222
HTMLPurifier_TagTransform_Font
Definition: HTMLPurifier.standalone.php:21110
HTMLPurifier_DefinitionCache_Serializer\flush
flush($config)
Definition: HTMLPurifier.standalone.php:15605
HTMLPurifier_Injector\handleText
handleText(&$token)
Definition: HTMLPurifier.standalone.php:7013
HTMLPurifier_Injector_RemoveEmpty\prepare
prepare($config, $context)
Definition: HTMLPurifier.standalone.php:18633
HTMLPurifier_DefinitionCache_Decorator_Cleanup
Definition: HTMLPurifier.standalone.php:15832
HTMLPurifier_Strategy_MakeWellFormed\processToken
processToken($token, $injector=-1)
Definition: HTMLPurifier.standalone.php:20715
HTMLPurifier_AttrTransform_Nofollow
Definition: HTMLPurifier.standalone.php:14166
HTMLPurifier_URIFilter\filter
filter(&$uri, $config, $context)
HTMLPurifier_AttrTransform_NameSync
Definition: HTMLPurifier.standalone.php:14123
HTMLPurifier_HTMLModuleManager\$registeredModules
$registeredModules
Definition: HTMLPurifier.standalone.php:6269
HTMLPurifier_PercentEncoder
Definition: HTMLPurifier.standalone.php:8067
HTMLPurifier_ChildDef_Required\$elements
$elements
Definition: HTMLPurifier.standalone.php:14833
HTMLPurifier_AttrDef_CSS_DenyElementDecorator\$def
$def
Definition: HTMLPurifier.standalone.php:11591
modules
HTML AllowedModules you can quickly activate or disable these modules by specifying which modules you wish to allow with this directive This is most useful for unit testing specific modules
Definition: HTML.AllowedModules.txt:12
HTMLPurifier_AttrTransform_Length\$cssName
$cssName
Definition: HTMLPurifier.standalone.php:14051
HTMLPurifier_AttrDef_CSS_Background\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:11078
HTMLPurifier_ErrorStruct\$value
$value
Definition: HTMLPurifier.standalone.php:5057
HTMLPurifier_AttrDef_CSS_Border\$info
$info
Definition: HTMLPurifier.standalone.php:11325
HTMLPurifier_Config\getBatch
getBatch($namespace)
Definition: HTMLPurifier.standalone.php:2009
HTMLPurifier_URIDefinition\$defaultScheme
$defaultScheme
Definition: HTMLPurifier.standalone.php:9201
HTMLPurifier_Context\loadArray
loadArray($context_array)
Definition: HTMLPurifier.standalone.php:3145
HTMLPurifier_HTMLModule_Tidy\getFixType
getFixType($name)
Definition: HTMLPurifier.standalone.php:17650
HTMLPurifier_Node_Element\$endLine
$endLine
Definition: HTMLPurifier.standalone.php:19868
HTMLPurifier_AttrTransform_Length\__construct
__construct($name, $css_name=null)
Definition: HTMLPurifier.standalone.php:14053
HTMLPurifier_AttrTransform_ImgSpace\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:13921
HTMLPurifier_Arborize\flatten
static flatten($node, $config, $context)
Definition: HTMLPurifier.standalone.php:356
HTMLPurifier_HTMLModule_Tidy_Proprietary
Definition: HTMLPurifier.standalone.php:17758
HTMLPurifier_Config\maybeGetRawHTMLDefinition
maybeGetRawHTMLDefinition()
Definition: HTMLPurifier.standalone.php:2441
HTMLPurifier_TokenFactory\createEmpty
createEmpty($name, $attr=array())
Definition: HTMLPurifier.standalone.php:8827
HTMLPurifier_HTMLModule_Tidy_Name\$defaultLevel
$defaultLevel
Definition: HTMLPurifier.standalone.php:17736
HTMLPurifier_AttrTransform_Name\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14095
HTMLPurifier_ErrorStruct
Definition: HTMLPurifier.standalone.php:5033
HTMLPurifier_AttrDef_Integer\validate
validate($integer, $config, $context)
Definition: HTMLPurifier.standalone.php:10604
HTMLPurifier_AttrDef_CSS_ImportantDecorator
Definition: HTMLPurifier.standalone.php:12140
HTMLPurifier_AttrDef_CSS_ImportantDecorator\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12167
HTMLPurifier_Injector_PurifierLinkify\$needed
$needed
Definition: HTMLPurifier.standalone.php:18542
HTMLPurifier_Node_Element\$endCol
$endCol
Definition: HTMLPurifier.standalone.php:19868
HTMLPurifier_AttrDef_URI_Host\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:13329
HTMLPurifier_AttrDef_URI_IPv4\_loadRegex
_loadRegex()
Definition: HTMLPurifier.standalone.php:13477
HTMLPurifier_HTMLModule_Object\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16792
HTMLPurifier_Token\position
position($l=null, $c=null)
Definition: HTMLPurifier.standalone.php:8714
HTMLPurifier_Injector\$inputZipper
$inputZipper
Definition: HTMLPurifier.standalone.php:6807
HTMLPurifier_AttrTransform
Definition: HTMLPurifier.standalone.php:701
HTMLPurifier_AttrTransform_BgColor
Definition: HTMLPurifier.standalone.php:13669
HTMLPurifier_HTMLModule_Hypertext
Definition: HTMLPurifier.standalone.php:16320
HTMLPurifier_AttrTransform_SafeEmbed\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14225
HTMLPurifier_URIScheme_https\$default_port
$default_port
Definition: HTMLPurifier.standalone.php:22249
HTMLPurifier_ChildDef_Empty\$allow_empty
$allow_empty
Definition: HTMLPurifier.standalone.php:14704
HTMLPurifier_Language\$error
$error
Definition: HTMLPurifier.standalone.php:7084
HTMLPurifier_CSSDefinition\$info
$info
Definition: HTMLPurifier.standalone.php:1218
HTMLPurifier_Injector_PurifierLinkify\$docURL
$docURL
Definition: HTMLPurifier.standalone.php:18537
HTMLPurifier_Injector\allowsElement
allowsElement($name)
Definition: HTMLPurifier.standalone.php:6907
HTMLPurifier_HTMLModule_TargetBlank\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17328
HTMLPurifier_AttrDef_CSS_Multiple\$max
$max
Definition: HTMLPurifier.standalone.php:12408
HTMLPurifier_HTMLModule_TargetBlank\$name
$name
Definition: HTMLPurifier.standalone.php:17323
HTMLPurifier_ErrorStruct\$children
$children
Definition: HTMLPurifier.standalone.php:5071
HTMLPurifier_ContentSets\$lookup
$lookup
Definition: HTMLPurifier.standalone.php:2907
HTMLPurifier_Length\validate
validate()
Definition: HTMLPurifier.standalone.php:7531
HTMLPurifier_Length\$n
$n
Definition: HTMLPurifier.standalone.php:7473
HTMLPurifier_Length\compareTo
compareTo($l)
Definition: HTMLPurifier.standalone.php:7605
HTMLPurifier_AttrDef_CSS_Font
Definition: HTMLPurifier.standalone.php:11710
HTMLPurifier_AttrTransform_TargetNoopener\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14434
HTMLPurifier_URIFilter_DisableExternal\$name
$name
Definition: HTMLPurifier.standalone.php:21473
HTMLPurifier_Zipper\next
next($t)
Definition: HTMLPurifier.standalone.php:10202
HTMLPurifier_HTMLDefinition\$info
$info
Definition: HTMLPurifier.standalone.php:5490
HTMLPurifier_Strategy_MakeWellFormed\execute
execute($tokens, $config, $context)
Definition: HTMLPurifier.standalone.php:20243
HTMLPurifier_DefinitionCache\flush
flush($config)
HTMLPurifier_ChildDef_StrictBlockquote\$allow_empty
$allow_empty
Definition: HTMLPurifier.standalone.php:15007
HTMLPurifier_Injector_Linkify\$needed
$needed
Definition: HTMLPurifier.standalone.php:18471
HTMLPurifier_Doctype\$tidyModules
$tidyModules
Definition: HTMLPurifier.standalone.php:3419
HTMLPurifier_ElementDef\$formatting
$formatting
Definition: HTMLPurifier.standalone.php:3741
HTMLPurifier_ChildDef_Optional\$type
$type
Definition: HTMLPurifier.standalone.php:14960
HTMLPurifier_AttrTransform_BoolToCSS
Definition: HTMLPurifier.standalone.php:13698
HTMLPurifier_Token_Comment
Definition: HTMLPurifier.standalone.php:21258
HTMLPurifier_AttrDef_CSS_ImportantDecorator\$def
$def
Definition: HTMLPurifier.standalone.php:12144
HTMLPurifier_TagTransform
Definition: HTMLPurifier.standalone.php:8609
HTMLPurifier_AttrDef_Enum\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:10523
HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4\makeFixes
makeFixes()
Definition: HTMLPurifier.standalone.php:17798
HTMLPurifier\getInstance
static getInstance($prototype=null)
Definition: HTMLPurifier.standalone.php:310
HTMLPurifier_AttrDef_HTML_FrameTarget\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12883
HTMLPurifier_DefinitionCache_Serializer\add
add($def, $config)
Definition: HTMLPurifier.standalone.php:15523
HTMLPurifier_HTMLModule_Bdo\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16014
HTMLPurifier_URI\$query
$query
Definition: HTMLPurifier.standalone.php:8901
HTMLPurifier_HTMLModule_Tidy_Transitional
Definition: HTMLPurifier.standalone.php:18017
HTMLPurifier_HTMLModule_NonXMLCommonAttributes
Definition: HTMLPurifier.standalone.php:16752
HTMLPurifier_AttrCollections\expandIdentifiers
expandIdentifiers(&$attr, $attr_types)
Definition: HTMLPurifier.standalone.php:489
HTMLPurifier_Injector\$needed
$needed
Definition: HTMLPurifier.standalone.php:6815
HTMLPurifier_AttrDef_Integer\$zero
$zero
Definition: HTMLPurifier.standalone.php:10578
HTMLPurifier_Zipper\$back
$back
Definition: HTMLPurifier.standalone.php:10164
HTMLPurifier_AttrDef_HTML_ID\validate
validate($id, $config, $context)
Definition: HTMLPurifier.standalone.php:12932
HTMLPurifier_IDAccumulator\$ids
$ids
Definition: HTMLPurifier.standalone.php:6718
HTMLPurifier_AttrDef_URI_IPv6
Definition: HTMLPurifier.standalone.php:13495
HTMLPurifier_Token_Text\$is_whitespace
$is_whitespace
Definition: HTMLPurifier.standalone.php:21441
HTMLPurifier_Zipper
Definition: HTMLPurifier.standalone.php:10163
HTMLPurifier_ChildDef_Empty
Definition: HTMLPurifier.standalone.php:14700
HTMLPurifier_LanguageFactory
Definition: HTMLPurifier.standalone.php:7260
HTMLPurifier_HTMLModule_Tidy\populate
populate($fixes)
Definition: HTMLPurifier.standalone.php:17597
HTMLPurifier_URIScheme_news\$browsable
$browsable
Definition: HTMLPurifier.standalone.php:22309
HTMLPurifier_HTMLModule_Text\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17412
HTMLPurifier_HTMLModule_Ruby\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16934
HTMLPurifier_Injector\$name
$name
Definition: HTMLPurifier.standalone.php:6783
HTMLPurifier_HTMLModule_CommonAttributes\$attr_collections
$attr_collections
Definition: HTMLPurifier.standalone.php:16047
HTMLPurifier_HTMLModuleManager\$attrCollections
$attrCollections
Definition: HTMLPurifier.standalone.php:6300
HTMLPurifier_Node_Element\$empty
$empty
Definition: HTMLPurifier.standalone.php:19866
HTMLPurifier_VarParser\ALIST
const ALIST
Definition: HTMLPurifier.standalone.php:9948
HTMLPurifier_ChildDef_StrictBlockquote\$fake_elements
$fake_elements
Definition: HTMLPurifier.standalone.php:15002
HTMLPurifier_URIScheme_file
Definition: HTMLPurifier.standalone.php:22104
HTMLPurifier_Lexer_DOMLex\getTagName
getTagName($node)
Definition: HTMLPurifier.standalone.php:19059
HTMLPurifier_AttrDef_CSS_Font\__construct
__construct($config)
Definition: HTMLPurifier.standalone.php:11725
HTMLPurifier_HTMLModule_Tables
Definition: HTMLPurifier.standalone.php:17213
HTMLPurifier_StringHashParser\parseMultiFile
parseMultiFile($file)
Definition: HTMLPurifier.standalone.php:8525
HTMLPurifier_ChildDef_Table\validateChildren
validateChildren($children, $config, $context)
Definition: HTMLPurifier.standalone.php:15164
HTMLPurifier_Language\$config
$config
Definition: HTMLPurifier.standalone.php:7096
HTMLPurifier_Language
Definition: HTMLPurifier.standalone.php:7052
HTMLPurifier_HTMLModule_TargetNoreferrer
Definition: HTMLPurifier.standalone.php:17366
HTMLPurifier_URIScheme_tel\$may_omit_host
$may_omit_host
Definition: HTMLPurifier.standalone.php:22389
HTMLPurifier_DefinitionCache_Decorator_Cleanup\add
add($def, $config)
Definition: HTMLPurifier.standalone.php:15851
HTMLPurifier_Zipper\__construct
__construct($front, $back)
Definition: HTMLPurifier.standalone.php:10166
HTMLPurifier_StringHashParser\parseFile
parseFile($file)
Definition: HTMLPurifier.standalone.php:8506
HTMLPurifier_Language\$code
$code
Definition: HTMLPurifier.standalone.php:7058
HTMLPurifier_Token\$armor
$armor
Definition: HTMLPurifier.standalone.php:8666
HTMLPurifier_Injector_RemoveEmpty\handleElement
handleElement(&$token)
Definition: HTMLPurifier.standalone.php:18653
HTMLPurifier_Token_Text\__construct
__construct($data, $line=null, $col=null)
Definition: HTMLPurifier.standalone.php:21451
HTMLPurifier_Injector_RemoveEmpty
Definition: HTMLPurifier.standalone.php:18596
HTMLPurifier_HTMLModule_Object\$safe
$safe
Definition: HTMLPurifier.standalone.php:16787
HTMLPurifier_PropertyListIterator\$l
$l
Definition: HTMLPurifier.standalone.php:8302
HTMLPurifier_Doctype\$xml
$xml
Definition: HTMLPurifier.standalone.php:3425
HTMLPurifier_EntityParser\substituteNonSpecialEntities
substituteNonSpecialEntities($string)
Definition: HTMLPurifier.standalone.php:4684
HTMLPurifier_HTMLModule\setup
setup($config)
Definition: HTMLPurifier.standalone.php:6228
HTMLPurifier_AttrDef_CSS_BackgroundPosition\$percentage
$percentage
Definition: HTMLPurifier.standalone.php:11209
HTMLPurifier_Injector\$currentNesting
$currentNesting
Definition: HTMLPurifier.standalone.php:6795
HTMLPurifier_HTMLModule_Target\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17298
HTMLPurifier_LanguageFactory\$mergeable_keys_map
$mergeable_keys_map
Definition: HTMLPurifier.standalone.php:7294
HTMLPurifier_URIFilter_SafeIframe
Definition: HTMLPurifier.standalone.php:21901
HTMLPurifier_PercentEncoder\$preserve
$preserve
Definition: HTMLPurifier.standalone.php:8073
HTMLPurifier_URIScheme_http
Definition: HTMLPurifier.standalone.php:22208
HTMLPurifier_VarParser\C_FLOAT
const C_FLOAT
Definition: HTMLPurifier.standalone.php:9945
$html
$html
Definition: Filter.ExtractStyleBlocks.txt:37
HTMLPurifier_URIScheme_news
Definition: HTMLPurifier.standalone.php:22305
HTMLPurifier_HTMLModule\$info_attr_transform_pre
$info_attr_transform_pre
Definition: HTMLPurifier.standalone.php:6023
HTMLPurifier_Zipper\insertBefore
insertBefore($t)
Definition: HTMLPurifier.standalone.php:10251
empty
Attr AllowedRel this is empty
Definition: Attr.AllowedRel.txt:7
HTMLPurifier_PropertyList\reset
reset($name=null)
Definition: HTMLPurifier.standalone.php:8243
$o
$o
Definition: cmd.php:193
HTMLPurifier_AttrDef\expandCSSEscape
expandCSSEscape($string)
Definition: HTMLPurifier.standalone.php:640
HTMLPurifier_Strategy_Composite\execute
execute($tokens, $config, $context)
Definition: HTMLPurifier.standalone.php:19967
HTMLPurifier_AttrDef_CSS_FontFamily\__construct
__construct()
Definition: HTMLPurifier.standalone.php:11891
HTMLPurifier_URIScheme_ftp\$default_port
$default_port
Definition: HTMLPurifier.standalone.php:22153
HTMLPurifier_Zipper\advance
advance($t, $n)
Definition: HTMLPurifier.standalone.php:10213
HTMLPurifier_ErrorStruct\TOKEN
const TOKEN
Definition: HTMLPurifier.standalone.php:5039
HTMLPurifier_URIFilter_MakeAbsolute\$base
$base
Definition: HTMLPurifier.standalone.php:21631
HTMLPurifier_HTMLModule_Proprietary\setup
setup($config)
Definition: HTMLPurifier.standalone.php:16892
HTMLPurifier_Config\finalize
finalize()
Definition: HTMLPurifier.standalone.php:2660
HTMLPurifier_Length\__construct
__construct($n='0', $u=false)
Definition: HTMLPurifier.standalone.php:7502
HTMLPurifier_ChildDef_Table\$allow_empty
$allow_empty
Definition: HTMLPurifier.standalone.php:15134
HTMLPurifier_HTMLModuleManager\$doctypes
$doctypes
Definition: HTMLPurifier.standalone.php:6243
HTMLPurifier_AttrDef_Integer\__construct
__construct($negative=true, $zero=true, $positive=true)
Definition: HTMLPurifier.standalone.php:10591
HTMLPurifier_ChildDef_Custom\validateChildren
validateChildren($children, $config, $context)
Definition: HTMLPurifier.standalone.php:14667
HTMLPurifier_Config\getAllowedDirectivesForForm
static getAllowedDirectivesForForm($allowed, $schema=null)
Definition: HTMLPurifier.standalone.php:2497
HTMLPurifier_TokenFactory\createStart
createStart($name, $attr=array())
Definition: HTMLPurifier.standalone.php:8802
HTMLPurifier_DefinitionCache\checkDefType
checkDefType($def)
Definition: HTMLPurifier.standalone.php:3225
HTMLPurifier_Node\$line
$line
Definition: HTMLPurifier.standalone.php:8022
HTMLPurifier_VarParser\$types
static $types
Definition: HTMLPurifier.standalone.php:9956
HTMLPurifier_ErrorStruct\$type
$type
Definition: HTMLPurifier.standalone.php:5047
HTMLPurifier_Zipper\$front
$front
Definition: HTMLPurifier.standalone.php:10164
HTMLPurifier_Strategy_MakeWellFormed\$token
$token
Definition: HTMLPurifier.standalone.php:20204
HTMLPurifier_Lexer\parseText
parseText($string, $config)
Definition: HTMLPurifier.standalone.php:7794
HTMLPurifier_ErrorStruct\CSSPROP
const CSSPROP
Definition: HTMLPurifier.standalone.php:5041
HTMLPurifier_Lexer_DOMLex\tokenizeDOM
tokenizeDOM($node, &$tokens, $config)
Definition: HTMLPurifier.standalone.php:19024
HTMLPurifier_URIDefinition\postFilter
postFilter(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:9278
HTMLPurifier_EntityParser\$_special_ent2dec
$_special_ent2dec
Definition: HTMLPurifier.standalone.php:4668
HTMLPurifier_Language\$errorNames
$errorNames
Definition: HTMLPurifier.standalone.php:7076
HTMLPurifier_HTMLModule_Scripting\$safe
$safe
Definition: HTMLPurifier.standalone.php:17134
HTMLPurifier_AttrDef_CSS_ListStyle\__construct
__construct($config)
Definition: HTMLPurifier.standalone.php:12288
as
as
Definition: Filter.ExtractStyleBlocks.Escaping.txt:10
HTMLPurifier_AttrDef_HTML_Pixels\$max
$max
Definition: HTMLPurifier.standalone.php:13019
HTMLPurifier_AttrDef_URI\make
make($string)
Definition: HTMLPurifier.standalone.php:10843
HTMLPurifier_HTMLDefinition\__construct
__construct()
Definition: HTMLPurifier.standalone.php:5640
HTMLPurifier_URIFilter\$always_load
$always_load
Definition: HTMLPurifier.standalone.php:9339
HTMLPurifier_HTMLModule_NonXMLCommonAttributes\$name
$name
Definition: HTMLPurifier.standalone.php:16756
HTMLPurifier_AttrDef_Enum\$case_sensitive
$case_sensitive
Definition: HTMLPurifier.standalone.php:10505
HTMLPurifier_DefinitionCache_Decorator\$cache
$cache
Definition: HTMLPurifier.standalone.php:15332
HTMLPurifier_Config\getDefinition
getDefinition($type, $raw=false, $optimized=false)
Definition: HTMLPurifier.standalone.php:2262
HTMLPurifier_AttrDef_Switch\__construct
__construct($tag, $with_tag, $without_tag)
Definition: HTMLPurifier.standalone.php:10763
HTMLPurifier_Injector_PurifierLinkify\prepare
prepare($config, $context)
Definition: HTMLPurifier.standalone.php:18549
HTMLPurifier_DefinitionCache_Decorator_Memory\$name
$name
Definition: HTMLPurifier.standalone.php:15921
HTMLPurifier_URI\toString
toString()
Definition: HTMLPurifier.standalone.php:9079
HTMLPurifier_ChildDef_Empty\$type
$type
Definition: HTMLPurifier.standalone.php:14709
HTMLPurifier_URIDefinition\registerFilter
registerFilter($filter)
Definition: HTMLPurifier.standalone.php:9214
HTMLPurifier_Lexer\$_special_entity2str
$_special_entity2str
Definition: HTMLPurifier.standalone.php:7783
HTMLPurifier_AttrDef_HTML_Length
Definition: HTMLPurifier.standalone.php:13095
HTMLPurifier_VarParser_Native\parseImplementation
parseImplementation($var, $type, $allow_null)
Definition: HTMLPurifier.standalone.php:22562
HTMLPurifier_AttrDef_CSS_ListStyle\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12302
HTMLPurifier_HTMLDefinition\setupConfigStuff
setupConfigStuff($config)
Definition: HTMLPurifier.standalone.php:5716
HTMLPurifier_AttrTransform_TargetBlank\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:14396
HTMLPurifier_VarParser\parseImplementation
parseImplementation($var, $type, $allow_null)
Definition: HTMLPurifier.standalone.php:10068
HTMLPurifier_ContentSets\generateChildDefCallback
generateChildDefCallback($matches)
Definition: HTMLPurifier.standalone.php:2989
HTMLPurifier_AttrDef_CSS_URI
Definition: HTMLPurifier.standalone.php:12568
HTMLPurifier_Token\__get
__get($n)
Definition: HTMLPurifier.standalone.php:8688
HTMLPurifier_AttrDef\$minimized
$minimized
Definition: HTMLPurifier.standalone.php:559
HTMLPurifier_Lexer_DirectLex\$tracksLineNumbers
$tracksLineNumbers
Definition: HTMLPurifier.standalone.php:19272
HTMLPurifier_ConfigSchema\$defaults
$defaults
Definition: HTMLPurifier.standalone.php:2723
HTMLPurifier_AttrDef_Switch\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:10776
HTMLPurifier_ErrorStruct\$errors
$errors
Definition: HTMLPurifier.standalone.php:5063
HTMLPurifier\purifyArray
purifyArray($array_of_html, $config=null)
Definition: HTMLPurifier.standalone.php:260
HTMLPurifier_AttrDef_Integer
Definition: HTMLPurifier.standalone.php:10566
HTMLPurifier_HTMLDefinition\$manager
$manager
Definition: HTMLPurifier.standalone.php:5635
HTMLPurifier_AttrDef_CSS_DenyElementDecorator\$element
$element
Definition: HTMLPurifier.standalone.php:11595
HTMLPurifier_ErrorCollector\$context
$context
Definition: HTMLPurifier.standalone.php:4825
HTMLPurifier_DefinitionCache_Decorator\$name
$name
Definition: HTMLPurifier.standalone.php:15338
HTMLPurifier_Length\isValid
isValid()
Definition: HTMLPurifier.standalone.php:7590
HTMLPurifier_Zipper\prev
prev($t)
Definition: HTMLPurifier.standalone.php:10225
HTMLPurifier_AttrDef_CSS_DenyElementDecorator
Definition: HTMLPurifier.standalone.php:11587
HTMLPurifier_PropertyList\$cache
$cache
Definition: HTMLPurifier.standalone.php:8191
HTMLPurifier\instance
static instance($prototype=null)
Definition: HTMLPurifier.standalone.php:285
HTMLPurifier_DefinitionCache_Decorator\add
add($def, $config)
Definition: HTMLPurifier.standalone.php:15372
false
if(!defined("FALSE_VAL")) define("FALSE_VAL" false
Definition: constants.inc.php:9
HTMLPurifier_PercentEncoder\__construct
__construct($preserve=false)
Definition: HTMLPurifier.standalone.php:8079
HTMLPurifier_Definition\$setup
$setup
Definition: HTMLPurifier.standalone.php:1160
HTMLPurifier_HTMLDefinition\$info_content_sets
$info_content_sets
Definition: HTMLPurifier.standalone.php:5541
HTMLPurifier_EntityParser\substituteAttrEntities
substituteAttrEntities($string)
Definition: HTMLPurifier.standalone.php:4590
HTMLPurifier_Lexer\__construct
__construct()
Definition: HTMLPurifier.standalone.php:7774
HTMLPurifier_TokenFactory\__construct
__construct()
Definition: HTMLPurifier.standalone.php:8787
HTMLPurifier_AttrDef_Integer\$negative
$negative
Definition: HTMLPurifier.standalone.php:10572
HTMLPurifier_Node_Element\$name
$name
Definition: HTMLPurifier.standalone.php:19847
HTMLPurifier_PercentEncoder\encode
encode($string)
Definition: HTMLPurifier.standalone.php:8114
HTMLPurifier_AttrDef_URI_Host\$ipv6
$ipv6
Definition: HTMLPurifier.standalone.php:13315
HTMLPurifier_AttrDef_Enum\__construct
__construct($valid_values=array(), $case_sensitive=false)
Definition: HTMLPurifier.standalone.php:10511
HTMLPurifier_AttrDef_CSS_Multiple\__construct
__construct($single, $max=4)
Definition: HTMLPurifier.standalone.php:12414
HTMLPurifier_HTMLModuleManager\addModule
addModule($module)
Definition: HTMLPurifier.standalone.php:6443
HTMLPurifier_AttrDef_CSS_Percentage\__construct
__construct($non_negative=false)
Definition: HTMLPurifier.standalone.php:12471
HTMLPurifier_URIFilter_DisableExternal\prepare
prepare($config)
Definition: HTMLPurifier.standalone.php:21484
HTMLPurifier_DefinitionCache\replace
replace($def, $config)
HTMLPurifier_Token
Definition: HTMLPurifier.standalone.php:8647
HTMLPurifier\addFilter
addFilter($filter)
Definition: HTMLPurifier.standalone.php:145
or
Voluntary License Schemes The Licensor waives the right to collect whether individually or
Definition: license.txt:37
HTMLPurifier_Config\triggerError
triggerError($msg, $no)
Definition: HTMLPurifier.standalone.php:2673
HTMLPurifier_Generator\escape
escape($string, $quote=null)
Definition: HTMLPurifier.standalone.php:5443
HTMLPurifier_AttrTransform_SafeObject
Definition: HTMLPurifier.standalone.php:14242
HTMLPurifier_Config\$serials
$serials
Definition: HTMLPurifier.standalone.php:1829
HTMLPurifier_ChildDef\getAllowedElements
getAllowedElements($config)
Definition: HTMLPurifier.standalone.php:1772
HTMLPurifier_CSSDefinition\doSetupTrusted
doSetupTrusted($config)
Definition: HTMLPurifier.standalone.php:1673
HTMLPurifier_DefinitionCache_Decorator\replace
replace($def, $config)
Definition: HTMLPurifier.standalone.php:15392
HTMLPurifier_HTMLModuleManager\getElements
getElements()
Definition: HTMLPurifier.standalone.php:6584
HTMLPurifier_URIScheme_news\doValidate
doValidate(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:22322
HTMLPurifier_ErrorCollector\getRaw
getRaw()
Definition: HTMLPurifier.standalone.php:4941
HTMLPurifier_HTMLModule_Tidy_Transitional\$name
$name
Definition: HTMLPurifier.standalone.php:18021
HTMLPurifier_HTMLModule_Tidy_XHTML\$defaultLevel
$defaultLevel
Definition: HTMLPurifier.standalone.php:18043
HTMLPurifier_Token\$line
$line
Definition: HTMLPurifier.standalone.php:8652
HTMLPurifier_DefinitionCache_Decorator_Memory
Definition: HTMLPurifier.standalone.php:15912
$dir
$dir
Definition: config.php:10
HTMLPurifier_URIScheme_file\$browsable
$browsable
Definition: HTMLPurifier.standalone.php:22110
HTMLPurifier_AttrDef\mungeRgb
mungeRgb($string)
Definition: HTMLPurifier.standalone.php:625
HTMLPurifier_AttrTransform_EnumToCSS\$caseSensitive
$caseSensitive
Definition: HTMLPurifier.standalone.php:13793
HTMLPurifier_AttrTransform_BoolToCSS\$css
$css
Definition: HTMLPurifier.standalone.php:13709
HTMLPurifier_HTMLModule_Scripting\setup
setup($config)
Definition: HTMLPurifier.standalone.php:17139
HTMLPurifier_Encoder
Definition: HTMLPurifier.standalone.php:3833
HTMLPurifier_AttrTransform_ImgSpace\__construct
__construct($attr)
Definition: HTMLPurifier.standalone.php:13907
HTMLPurifier_DefinitionCache_Decorator\copy
copy()
Definition: HTMLPurifier.standalone.php:15362
HTMLPurifier_AttrDef_Lang\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:10663
HTMLPurifier_URIScheme_ftp\$hierarchical
$hierarchical
Definition: HTMLPurifier.standalone.php:22163
HTMLPurifier_Injector\forward
forward(&$i, &$current)
Definition: HTMLPurifier.standalone.php:6942
HTMLPurifier_AttrTransform_BgColor\transform
transform($attr, $config, $context)
Definition: HTMLPurifier.standalone.php:13676
HTMLPurifier_DefinitionCacheFactory\create
create($type, $config)
Definition: HTMLPurifier.standalone.php:3348
HTMLPurifier_URIDefinition\filter
filter(&$uri, $config, $context)
Definition: HTMLPurifier.standalone.php:9269
HTMLPurifier_ConfigSchema\addAllowedValues
addAllowedValues($key, $allowed)
Definition: HTMLPurifier.standalone.php:2853
HTMLPurifier_Config\prepareArrayFromForm
static prepareArrayFromForm($array, $index=false, $allowed=true, $mq_fix=true, $schema=null)
Definition: HTMLPurifier.standalone.php:2590
HTMLPurifier_Token_Empty
Definition: HTMLPurifier.standalone.php:21366
HTMLPurifier_AttrDef_CSS_Length\validate
validate($string, $config, $context)
Definition: HTMLPurifier.standalone.php:12225
HTMLPurifier_URI\getSchemeObj
getSchemeObj($config, $context)
Definition: HTMLPurifier.standalone.php:8935
HTMLPurifier_URIDefinition\setupMemberVariables
setupMemberVariables($config)
Definition: HTMLPurifier.standalone.php:9251
HTMLPurifier_HTMLModuleManager\registerModule
registerModule($module, $overload=false)
Definition: HTMLPurifier.standalone.php:6404
HTMLPurifier_Injector\$rewindOffset
$rewindOffset
Definition: HTMLPurifier.standalone.php:6821
HTMLPurifier_AttrTransform_Length\$name
$name
Definition: HTMLPurifier.standalone.php:14046
HTMLPurifier_IDAccumulator\add
add($id)
Definition: HTMLPurifier.standalone.php:6738