PageRenderTime 678ms CodeModel.GetById 161ms app.highlight 168ms RepoModel.GetById 113ms app.codeStats 6ms

/yii/vendors/htmlpurifier/HTMLPurifier.standalone.php

https://bitbucket.org/Crisu83/webgames
PHP | 14492 lines | 7496 code | 2503 blank | 4493 comment | 1442 complexity | 7d678df995783a3e3f130996946c003d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2
   3/**
   4 * @file
   5 * This file was auto-generated by generate-includes.php and includes all of
   6 * the core files required by HTML Purifier. Use this if performance is a
   7 * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
   8 * FILE, changes will be overwritten the next time the script is run.
   9 *
  10 * @version 4.4.0
  11 *
  12 * @warning
  13 *      You must *not* include any other HTML Purifier files before this file,
  14 *      because 'require' not 'require_once' is used.
  15 *
  16 * @warning
  17 *      This file requires that the include path contains the HTML Purifier
  18 *      library directory; this is not auto-set.
  19 */
  20
  21
  22
  23/*! @mainpage
  24 *
  25 * HTML Purifier is an HTML filter that will take an arbitrary snippet of
  26 * HTML and rigorously test, validate and filter it into a version that
  27 * is safe for output onto webpages. It achieves this by:
  28 *
  29 *  -# Lexing (parsing into tokens) the document,
  30 *  -# Executing various strategies on the tokens:
  31 *      -# Removing all elements not in the whitelist,
  32 *      -# Making the tokens well-formed,
  33 *      -# Fixing the nesting of the nodes, and
  34 *      -# Validating attributes of the nodes; and
  35 *  -# Generating HTML from the purified tokens.
  36 *
  37 * However, most users will only need to interface with the HTMLPurifier
  38 * and HTMLPurifier_Config.
  39 */
  40
  41/*
  42    HTML Purifier 4.4.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
  60/**
  61 * Facade that coordinates HTML Purifier's subsystems in order to purify HTML.
  62 *
  63 * @note There are several points in which configuration can be specified
  64 *       for HTML Purifier.  The precedence of these (from lowest to
  65 *       highest) is as follows:
  66 *          -# Instance: new HTMLPurifier($config)
  67 *          -# Invocation: purify($html, $config)
  68 *       These configurations are entirely independent of each other and
  69 *       are *not* merged (this behavior may change in the future).
  70 *
  71 * @todo We need an easier way to inject strategies using the configuration
  72 *       object.
  73 */
  74class HTMLPurifier
  75{
  76
  77    /** Version of HTML Purifier */
  78    public $version = '4.4.0';
  79
  80    /** Constant with version of HTML Purifier */
  81    const VERSION = '4.4.0';
  82
  83    /** Global configuration object */
  84    public $config;
  85
  86    /** Array of extra HTMLPurifier_Filter objects to run on HTML, for backwards compatibility */
  87    private $filters = array();
  88
  89    /** Single instance of HTML Purifier */
  90    private static $instance;
  91
  92    protected $strategy, $generator;
  93
  94    /**
  95     * Resultant HTMLPurifier_Context of last run purification. Is an array
  96     * of contexts if the last called method was purifyArray().
  97     */
  98    public $context;
  99
 100    /**
 101     * Initializes the purifier.
 102     * @param $config Optional HTMLPurifier_Config object for all instances of
 103     *                the purifier, if omitted, a default configuration is
 104     *                supplied (which can be overridden on a per-use basis).
 105     *                The parameter can also be any type that
 106     *                HTMLPurifier_Config::create() supports.
 107     */
 108    public function __construct($config = null) {
 109
 110        $this->config = HTMLPurifier_Config::create($config);
 111
 112        $this->strategy     = new HTMLPurifier_Strategy_Core();
 113
 114    }
 115
 116    /**
 117     * Adds a filter to process the output. First come first serve
 118     * @param $filter HTMLPurifier_Filter object
 119     */
 120    public function addFilter($filter) {
 121        trigger_error('HTMLPurifier->addFilter() is deprecated, use configuration directives in the Filter namespace or Filter.Custom', E_USER_WARNING);
 122        $this->filters[] = $filter;
 123    }
 124
 125    /**
 126     * Filters an HTML snippet/document to be XSS-free and standards-compliant.
 127     *
 128     * @param $html String of HTML to purify
 129     * @param $config HTMLPurifier_Config object for this operation, if omitted,
 130     *                defaults to the config object specified during this
 131     *                object's construction. The parameter can also be any type
 132     *                that HTMLPurifier_Config::create() supports.
 133     * @return Purified HTML
 134     */
 135    public function purify($html, $config = null) {
 136
 137        // :TODO: make the config merge in, instead of replace
 138        $config = $config ? HTMLPurifier_Config::create($config) : $this->config;
 139
 140        // implementation is partially environment dependant, partially
 141        // configuration dependant
 142        $lexer = HTMLPurifier_Lexer::create($config);
 143
 144        $context = new HTMLPurifier_Context();
 145
 146        // setup HTML generator
 147        $this->generator = new HTMLPurifier_Generator($config, $context);
 148        $context->register('Generator', $this->generator);
 149
 150        // set up global context variables
 151        if ($config->get('Core.CollectErrors')) {
 152            // may get moved out if other facilities use it
 153            $language_factory = HTMLPurifier_LanguageFactory::instance();
 154            $language = $language_factory->create($config, $context);
 155            $context->register('Locale', $language);
 156
 157            $error_collector = new HTMLPurifier_ErrorCollector($context);
 158            $context->register('ErrorCollector', $error_collector);
 159        }
 160
 161        // setup id_accumulator context, necessary due to the fact that
 162        // AttrValidator can be called from many places
 163        $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
 164        $context->register('IDAccumulator', $id_accumulator);
 165
 166        $html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context);
 167
 168        // setup filters
 169        $filter_flags = $config->getBatch('Filter');
 170        $custom_filters = $filter_flags['Custom'];
 171        unset($filter_flags['Custom']);
 172        $filters = array();
 173        foreach ($filter_flags as $filter => $flag) {
 174            if (!$flag) continue;
 175            if (strpos($filter, '.') !== false) continue;
 176            $class = "HTMLPurifier_Filter_$filter";
 177            $filters[] = new $class;
 178        }
 179        foreach ($custom_filters as $filter) {
 180            // maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat
 181            $filters[] = $filter;
 182        }
 183        $filters = array_merge($filters, $this->filters);
 184        // maybe prepare(), but later
 185
 186        for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) {
 187            $html = $filters[$i]->preFilter($html, $config, $context);
 188        }
 189
 190        // purified HTML
 191        $html =
 192            $this->generator->generateFromTokens(
 193                // list of tokens
 194                $this->strategy->execute(
 195                    // list of un-purified tokens
 196                    $lexer->tokenizeHTML(
 197                        // un-purified HTML
 198                        $html, $config, $context
 199                    ),
 200                    $config, $context
 201                )
 202            );
 203
 204        for ($i = $filter_size - 1; $i >= 0; $i--) {
 205            $html = $filters[$i]->postFilter($html, $config, $context);
 206        }
 207
 208        $html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context);
 209        $this->context =& $context;
 210        return $html;
 211    }
 212
 213    /**
 214     * Filters an array of HTML snippets
 215     * @param $config Optional HTMLPurifier_Config object for this operation.
 216     *                See HTMLPurifier::purify() for more details.
 217     * @return Array of purified HTML
 218     */
 219    public function purifyArray($array_of_html, $config = null) {
 220        $context_array = array();
 221        foreach ($array_of_html as $key => $html) {
 222            $array_of_html[$key] = $this->purify($html, $config);
 223            $context_array[$key] = $this->context;
 224        }
 225        $this->context = $context_array;
 226        return $array_of_html;
 227    }
 228
 229    /**
 230     * Singleton for enforcing just one HTML Purifier in your system
 231     * @param $prototype Optional prototype HTMLPurifier instance to
 232     *                   overload singleton with, or HTMLPurifier_Config
 233     *                   instance to configure the generated version with.
 234     */
 235    public static function instance($prototype = null) {
 236        if (!self::$instance || $prototype) {
 237            if ($prototype instanceof HTMLPurifier) {
 238                self::$instance = $prototype;
 239            } elseif ($prototype) {
 240                self::$instance = new HTMLPurifier($prototype);
 241            } else {
 242                self::$instance = new HTMLPurifier();
 243            }
 244        }
 245        return self::$instance;
 246    }
 247
 248    /**
 249     * @note Backwards compatibility, see instance()
 250     */
 251    public static function getInstance($prototype = null) {
 252        return HTMLPurifier::instance($prototype);
 253    }
 254
 255}
 256
 257
 258
 259
 260
 261/**
 262 * Defines common attribute collections that modules reference
 263 */
 264
 265class HTMLPurifier_AttrCollections
 266{
 267
 268    /**
 269     * Associative array of attribute collections, indexed by name
 270     */
 271    public $info = array();
 272
 273    /**
 274     * Performs all expansions on internal data for use by other inclusions
 275     * It also collects all attribute collection extensions from
 276     * modules
 277     * @param $attr_types HTMLPurifier_AttrTypes instance
 278     * @param $modules Hash array of HTMLPurifier_HTMLModule members
 279     */
 280    public function __construct($attr_types, $modules) {
 281        // load extensions from the modules
 282        foreach ($modules as $module) {
 283            foreach ($module->attr_collections as $coll_i => $coll) {
 284                if (!isset($this->info[$coll_i])) {
 285                    $this->info[$coll_i] = array();
 286                }
 287                foreach ($coll as $attr_i => $attr) {
 288                    if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
 289                        // merge in includes
 290                        $this->info[$coll_i][$attr_i] = array_merge(
 291                            $this->info[$coll_i][$attr_i], $attr);
 292                        continue;
 293                    }
 294                    $this->info[$coll_i][$attr_i] = $attr;
 295                }
 296            }
 297        }
 298        // perform internal expansions and inclusions
 299        foreach ($this->info as $name => $attr) {
 300            // merge attribute collections that include others
 301            $this->performInclusions($this->info[$name]);
 302            // replace string identifiers with actual attribute objects
 303            $this->expandIdentifiers($this->info[$name], $attr_types);
 304        }
 305    }
 306
 307    /**
 308     * Takes a reference to an attribute associative array and performs
 309     * all inclusions specified by the zero index.
 310     * @param &$attr Reference to attribute array
 311     */
 312    public function performInclusions(&$attr) {
 313        if (!isset($attr[0])) return;
 314        $merge = $attr[0];
 315        $seen  = array(); // recursion guard
 316        // loop through all the inclusions
 317        for ($i = 0; isset($merge[$i]); $i++) {
 318            if (isset($seen[$merge[$i]])) continue;
 319            $seen[$merge[$i]] = true;
 320            // foreach attribute of the inclusion, copy it over
 321            if (!isset($this->info[$merge[$i]])) continue;
 322            foreach ($this->info[$merge[$i]] as $key => $value) {
 323                if (isset($attr[$key])) continue; // also catches more inclusions
 324                $attr[$key] = $value;
 325            }
 326            if (isset($this->info[$merge[$i]][0])) {
 327                // recursion
 328                $merge = array_merge($merge, $this->info[$merge[$i]][0]);
 329            }
 330        }
 331        unset($attr[0]);
 332    }
 333
 334    /**
 335     * Expands all string identifiers in an attribute array by replacing
 336     * them with the appropriate values inside HTMLPurifier_AttrTypes
 337     * @param &$attr Reference to attribute array
 338     * @param $attr_types HTMLPurifier_AttrTypes instance
 339     */
 340    public function expandIdentifiers(&$attr, $attr_types) {
 341
 342        // because foreach will process new elements we add, make sure we
 343        // skip duplicates
 344        $processed = array();
 345
 346        foreach ($attr as $def_i => $def) {
 347            // skip inclusions
 348            if ($def_i === 0) continue;
 349
 350            if (isset($processed[$def_i])) continue;
 351
 352            // determine whether or not attribute is required
 353            if ($required = (strpos($def_i, '*') !== false)) {
 354                // rename the definition
 355                unset($attr[$def_i]);
 356                $def_i = trim($def_i, '*');
 357                $attr[$def_i] = $def;
 358            }
 359
 360            $processed[$def_i] = true;
 361
 362            // if we've already got a literal object, move on
 363            if (is_object($def)) {
 364                // preserve previous required
 365                $attr[$def_i]->required = ($required || $attr[$def_i]->required);
 366                continue;
 367            }
 368
 369            if ($def === false) {
 370                unset($attr[$def_i]);
 371                continue;
 372            }
 373
 374            if ($t = $attr_types->get($def)) {
 375                $attr[$def_i] = $t;
 376                $attr[$def_i]->required = $required;
 377            } else {
 378                unset($attr[$def_i]);
 379            }
 380        }
 381
 382    }
 383
 384}
 385
 386
 387
 388
 389
 390/**
 391 * Base class for all validating attribute definitions.
 392 *
 393 * This family of classes forms the core for not only HTML attribute validation,
 394 * but also any sort of string that needs to be validated or cleaned (which
 395 * means CSS properties and composite definitions are defined here too).
 396 * Besides defining (through code) what precisely makes the string valid,
 397 * subclasses are also responsible for cleaning the code if possible.
 398 */
 399
 400abstract class HTMLPurifier_AttrDef
 401{
 402
 403    /**
 404     * Tells us whether or not an HTML attribute is minimized. Has no
 405     * meaning in other contexts.
 406     */
 407    public $minimized = false;
 408
 409    /**
 410     * Tells us whether or not an HTML attribute is required. Has no
 411     * meaning in other contexts
 412     */
 413    public $required = false;
 414
 415    /**
 416     * Validates and cleans passed string according to a definition.
 417     *
 418     * @param $string String to be validated and cleaned.
 419     * @param $config Mandatory HTMLPurifier_Config object.
 420     * @param $context Mandatory HTMLPurifier_AttrContext object.
 421     */
 422    abstract public function validate($string, $config, $context);
 423
 424    /**
 425     * Convenience method that parses a string as if it were CDATA.
 426     *
 427     * This method process a string in the manner specified at
 428     * <http://www.w3.org/TR/html4/types.html#h-6.2> by removing
 429     * leading and trailing whitespace, ignoring line feeds, and replacing
 430     * carriage returns and tabs with spaces.  While most useful for HTML
 431     * attributes specified as CDATA, it can also be applied to most CSS
 432     * values.
 433     *
 434     * @note This method is not entirely standards compliant, as trim() removes
 435     *       more types of whitespace than specified in the spec. In practice,
 436     *       this is rarely a problem, as those extra characters usually have
 437     *       already been removed by HTMLPurifier_Encoder.
 438     *
 439     * @warning This processing is inconsistent with XML's whitespace handling
 440     *          as specified by section 3.3.3 and referenced XHTML 1.0 section
 441     *          4.7.  However, note that we are NOT necessarily
 442     *          parsing XML, thus, this behavior may still be correct. We
 443     *          assume that newlines have been normalized.
 444     */
 445    public function parseCDATA($string) {
 446        $string = trim($string);
 447        $string = str_replace(array("\n", "\t", "\r"), ' ', $string);
 448        return $string;
 449    }
 450
 451    /**
 452     * Factory method for creating this class from a string.
 453     * @param $string String construction info
 454     * @return Created AttrDef object corresponding to $string
 455     */
 456    public function make($string) {
 457        // default implementation, return a flyweight of this object.
 458        // If $string has an effect on the returned object (i.e. you
 459        // need to overload this method), it is best
 460        // to clone or instantiate new copies. (Instantiation is safer.)
 461        return $this;
 462    }
 463
 464    /**
 465     * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work
 466     * properly. THIS IS A HACK!
 467     */
 468    protected function mungeRgb($string) {
 469        return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
 470    }
 471
 472    /**
 473     * Parses a possibly escaped CSS string and returns the "pure" 
 474     * version of it.
 475     */
 476    protected function expandCSSEscape($string) {
 477        // flexibly parse it
 478        $ret = '';
 479        for ($i = 0, $c = strlen($string); $i < $c; $i++) {
 480            if ($string[$i] === '\\') {
 481                $i++;
 482                if ($i >= $c) {
 483                    $ret .= '\\';
 484                    break;
 485                }
 486                if (ctype_xdigit($string[$i])) {
 487                    $code = $string[$i];
 488                    for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
 489                        if (!ctype_xdigit($string[$i])) break;
 490                        $code .= $string[$i];
 491                    }
 492                    // We have to be extremely careful when adding
 493                    // new characters, to make sure we're not breaking
 494                    // the encoding.
 495                    $char = HTMLPurifier_Encoder::unichr(hexdec($code));
 496                    if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue;
 497                    $ret .= $char;
 498                    if ($i < $c && trim($string[$i]) !== '') $i--;
 499                    continue;
 500                }
 501                if ($string[$i] === "\n") continue;
 502            }
 503            $ret .= $string[$i];
 504        }
 505        return $ret;
 506    }
 507
 508}
 509
 510
 511
 512
 513
 514/**
 515 * Processes an entire attribute array for corrections needing multiple values.
 516 *
 517 * Occasionally, a certain attribute will need to be removed and popped onto
 518 * another value.  Instead of creating a complex return syntax for
 519 * HTMLPurifier_AttrDef, we just pass the whole attribute array to a
 520 * specialized object and have that do the special work.  That is the
 521 * family of HTMLPurifier_AttrTransform.
 522 *
 523 * An attribute transformation can be assigned to run before or after
 524 * HTMLPurifier_AttrDef validation.  See HTMLPurifier_HTMLDefinition for
 525 * more details.
 526 */
 527
 528abstract class HTMLPurifier_AttrTransform
 529{
 530
 531    /**
 532     * Abstract: makes changes to the attributes dependent on multiple values.
 533     *
 534     * @param $attr Assoc array of attributes, usually from
 535     *              HTMLPurifier_Token_Tag::$attr
 536     * @param $config Mandatory HTMLPurifier_Config object.
 537     * @param $context Mandatory HTMLPurifier_Context object
 538     * @returns Processed attribute array.
 539     */
 540    abstract public function transform($attr, $config, $context);
 541
 542    /**
 543     * Prepends CSS properties to the style attribute, creating the
 544     * attribute if it doesn't exist.
 545     * @param $attr Attribute array to process (passed by reference)
 546     * @param $css CSS to prepend
 547     */
 548    public function prependCSS(&$attr, $css) {
 549        $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
 550        $attr['style'] = $css . $attr['style'];
 551    }
 552
 553    /**
 554     * Retrieves and removes an attribute
 555     * @param $attr Attribute array to process (passed by reference)
 556     * @param $key Key of attribute to confiscate
 557     */
 558    public function confiscateAttr(&$attr, $key) {
 559        if (!isset($attr[$key])) return null;
 560        $value = $attr[$key];
 561        unset($attr[$key]);
 562        return $value;
 563    }
 564
 565}
 566
 567
 568
 569
 570
 571/**
 572 * Provides lookup array of attribute types to HTMLPurifier_AttrDef objects
 573 */
 574class HTMLPurifier_AttrTypes
 575{
 576    /**
 577     * Lookup array of attribute string identifiers to concrete implementations
 578     */
 579    protected $info = array();
 580
 581    /**
 582     * Constructs the info array, supplying default implementations for attribute
 583     * types.
 584     */
 585    public function __construct() {
 586        // XXX This is kind of poor, since we don't actually /clone/
 587        // instances; instead, we use the supplied make() attribute. So,
 588        // the underlying class must know how to deal with arguments.
 589        // With the old implementation of Enum, that ignored its
 590        // arguments when handling a make dispatch, the IAlign
 591        // definition wouldn't work.
 592
 593        // pseudo-types, must be instantiated via shorthand
 594        $this->info['Enum']    = new HTMLPurifier_AttrDef_Enum();
 595        $this->info['Bool']    = new HTMLPurifier_AttrDef_HTML_Bool();
 596
 597        $this->info['CDATA']    = new HTMLPurifier_AttrDef_Text();
 598        $this->info['ID']       = new HTMLPurifier_AttrDef_HTML_ID();
 599        $this->info['Length']   = new HTMLPurifier_AttrDef_HTML_Length();
 600        $this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength();
 601        $this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens();
 602        $this->info['Pixels']   = new HTMLPurifier_AttrDef_HTML_Pixels();
 603        $this->info['Text']     = new HTMLPurifier_AttrDef_Text();
 604        $this->info['URI']      = new HTMLPurifier_AttrDef_URI();
 605        $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang();
 606        $this->info['Color']    = new HTMLPurifier_AttrDef_HTML_Color();
 607        $this->info['IAlign']   = self::makeEnum('top,middle,bottom,left,right');
 608        $this->info['LAlign']   = self::makeEnum('top,bottom,left,right');
 609        $this->info['FrameTarget'] = new HTMLPurifier_AttrDef_HTML_FrameTarget();
 610
 611        // unimplemented aliases
 612        $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text();
 613        $this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text();
 614        $this->info['Charsets'] = new HTMLPurifier_AttrDef_Text();
 615        $this->info['Character'] = new HTMLPurifier_AttrDef_Text();
 616
 617        // "proprietary" types
 618        $this->info['Class'] = new HTMLPurifier_AttrDef_HTML_Class();
 619
 620        // number is really a positive integer (one or more digits)
 621        // FIXME: ^^ not always, see start and value of list items
 622        $this->info['Number']   = new HTMLPurifier_AttrDef_Integer(false, false, true);
 623    }
 624
 625    private static function makeEnum($in) {
 626        return new HTMLPurifier_AttrDef_Clone(new HTMLPurifier_AttrDef_Enum(explode(',', $in)));
 627    }
 628
 629    /**
 630     * Retrieves a type
 631     * @param $type String type name
 632     * @return Object AttrDef for type
 633     */
 634    public function get($type) {
 635
 636        // determine if there is any extra info tacked on
 637        if (strpos($type, '#') !== false) list($type, $string) = explode('#', $type, 2);
 638        else $string = '';
 639
 640        if (!isset($this->info[$type])) {
 641            trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR);
 642            return;
 643        }
 644
 645        return $this->info[$type]->make($string);
 646
 647    }
 648
 649    /**
 650     * Sets a new implementation for a type
 651     * @param $type String type name
 652     * @param $impl Object AttrDef for type
 653     */
 654    public function set($type, $impl) {
 655        $this->info[$type] = $impl;
 656    }
 657}
 658
 659
 660
 661
 662
 663/**
 664 * Validates the attributes of a token. Doesn't manage required attributes
 665 * very well. The only reason we factored this out was because RemoveForeignElements
 666 * also needed it besides ValidateAttributes.
 667 */
 668class HTMLPurifier_AttrValidator
 669{
 670
 671    /**
 672     * Validates the attributes of a token, returning a modified token
 673     * that has valid tokens
 674     * @param $token Reference to token to validate. We require a reference
 675     *     because the operation this class performs on the token are
 676     *     not atomic, so the context CurrentToken to be updated
 677     *     throughout
 678     * @param $config Instance of HTMLPurifier_Config
 679     * @param $context Instance of HTMLPurifier_Context
 680     */
 681    public function validateToken(&$token, &$config, $context) {
 682
 683        $definition = $config->getHTMLDefinition();
 684        $e =& $context->get('ErrorCollector', true);
 685
 686        // initialize IDAccumulator if necessary
 687        $ok =& $context->get('IDAccumulator', true);
 688        if (!$ok) {
 689            $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
 690            $context->register('IDAccumulator', $id_accumulator);
 691        }
 692
 693        // initialize CurrentToken if necessary
 694        $current_token =& $context->get('CurrentToken', true);
 695        if (!$current_token) $context->register('CurrentToken', $token);
 696
 697        if (
 698            !$token instanceof HTMLPurifier_Token_Start &&
 699            !$token instanceof HTMLPurifier_Token_Empty
 700        ) return $token;
 701
 702        // create alias to global definition array, see also $defs
 703        // DEFINITION CALL
 704        $d_defs = $definition->info_global_attr;
 705
 706        // don't update token until the very end, to ensure an atomic update
 707        $attr = $token->attr;
 708
 709        // do global transformations (pre)
 710        // nothing currently utilizes this
 711        foreach ($definition->info_attr_transform_pre as $transform) {
 712            $attr = $transform->transform($o = $attr, $config, $context);
 713            if ($e) {
 714                if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
 715            }
 716        }
 717
 718        // do local transformations only applicable to this element (pre)
 719        // ex. <p align="right"> to <p style="text-align:right;">
 720        foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
 721            $attr = $transform->transform($o = $attr, $config, $context);
 722            if ($e) {
 723                if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
 724            }
 725        }
 726
 727        // create alias to this element's attribute definition array, see
 728        // also $d_defs (global attribute definition array)
 729        // DEFINITION CALL
 730        $defs = $definition->info[$token->name]->attr;
 731
 732        $attr_key = false;
 733        $context->register('CurrentAttr', $attr_key);
 734
 735        // iterate through all the attribute keypairs
 736        // Watch out for name collisions: $key has previously been used
 737        foreach ($attr as $attr_key => $value) {
 738
 739            // call the definition
 740            if ( isset($defs[$attr_key]) ) {
 741                // there is a local definition defined
 742                if ($defs[$attr_key] === false) {
 743                    // We've explicitly been told not to allow this element.
 744                    // This is usually when there's a global definition
 745                    // that must be overridden.
 746                    // Theoretically speaking, we could have a
 747                    // AttrDef_DenyAll, but this is faster!
 748                    $result = false;
 749                } else {
 750                    // validate according to the element's definition
 751                    $result = $defs[$attr_key]->validate(
 752                                    $value, $config, $context
 753                               );
 754                }
 755            } elseif ( isset($d_defs[$attr_key]) ) {
 756                // there is a global definition defined, validate according
 757                // to the global definition
 758                $result = $d_defs[$attr_key]->validate(
 759                                $value, $config, $context
 760                           );
 761            } else {
 762                // system never heard of the attribute? DELETE!
 763                $result = false;
 764            }
 765
 766            // put the results into effect
 767            if ($result === false || $result === null) {
 768                // this is a generic error message that should replaced
 769                // with more specific ones when possible
 770                if ($e) $e->send(E_ERROR, 'AttrValidator: Attribute removed');
 771
 772                // remove the attribute
 773                unset($attr[$attr_key]);
 774            } elseif (is_string($result)) {
 775                // generally, if a substitution is happening, there
 776                // was some sort of implicit correction going on. We'll
 777                // delegate it to the attribute classes to say exactly what.
 778
 779                // simple substitution
 780                $attr[$attr_key] = $result;
 781            } else {
 782                // nothing happens
 783            }
 784
 785            // we'd also want slightly more complicated substitution
 786            // involving an array as the return value,
 787            // although we're not sure how colliding attributes would
 788            // resolve (certain ones would be completely overriden,
 789            // others would prepend themselves).
 790        }
 791
 792        $context->destroy('CurrentAttr');
 793
 794        // post transforms
 795
 796        // global (error reporting untested)
 797        foreach ($definition->info_attr_transform_post as $transform) {
 798            $attr = $transform->transform($o = $attr, $config, $context);
 799            if ($e) {
 800                if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
 801            }
 802        }
 803
 804        // local (error reporting untested)
 805        foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
 806            $attr = $transform->transform($o = $attr, $config, $context);
 807            if ($e) {
 808                if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
 809            }
 810        }
 811
 812        $token->attr = $attr;
 813
 814        // destroy CurrentToken if we made it ourselves
 815        if (!$current_token) $context->destroy('CurrentToken');
 816
 817    }
 818
 819
 820}
 821
 822
 823
 824
 825
 826// constants are slow, so we use as few as possible
 827if (!defined('HTMLPURIFIER_PREFIX')) {
 828    define('HTMLPURIFIER_PREFIX', dirname(__FILE__) . '/standalone');
 829    set_include_path(HTMLPURIFIER_PREFIX . PATH_SEPARATOR . get_include_path());
 830}
 831
 832// accomodations for versions earlier than 5.0.2
 833// borrowed from PHP_Compat, LGPL licensed, by Aidan Lister <aidan@php.net>
 834if (!defined('PHP_EOL')) {
 835    switch (strtoupper(substr(PHP_OS, 0, 3))) {
 836        case 'WIN':
 837            define('PHP_EOL', "\r\n");
 838            break;
 839        case 'DAR':
 840            define('PHP_EOL', "\r");
 841            break;
 842        default:
 843            define('PHP_EOL', "\n");
 844    }
 845}
 846
 847/**
 848 * Bootstrap class that contains meta-functionality for HTML Purifier such as
 849 * the autoload function.
 850 *
 851 * @note
 852 *      This class may be used without any other files from HTML Purifier.
 853 */
 854class HTMLPurifier_Bootstrap
 855{
 856
 857    /**
 858     * Autoload function for HTML Purifier
 859     * @param $class Class to load
 860     */
 861    public static function autoload($class) {
 862        $file = HTMLPurifier_Bootstrap::getPath($class);
 863        if (!$file) return false;
 864        // Technically speaking, it should be ok and more efficient to
 865        // just do 'require', but Antonio Parraga reports that with
 866        // Zend extensions such as Zend debugger and APC, this invariant
 867        // may be broken.  Since we have efficient alternatives, pay
 868        // the cost here and avoid the bug.
 869        require_once HTMLPURIFIER_PREFIX . '/' . $file;
 870        return true;
 871    }
 872
 873    /**
 874     * Returns the path for a specific class.
 875     */
 876    public static function getPath($class) {
 877        if (strncmp('HTMLPurifier', $class, 12) !== 0) return false;
 878        // Custom implementations
 879        if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
 880            $code = str_replace('_', '-', substr($class, 22));
 881            $file = 'HTMLPurifier/Language/classes/' . $code . '.php';
 882        } else {
 883            $file = str_replace('_', '/', $class) . '.php';
 884        }
 885        if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) return false;
 886        return $file;
 887    }
 888
 889    /**
 890     * "Pre-registers" our autoloader on the SPL stack.
 891     */
 892    public static function registerAutoload() {
 893        $autoload = array('HTMLPurifier_Bootstrap', 'autoload');
 894        if ( ($funcs = spl_autoload_functions()) === false ) {
 895            spl_autoload_register($autoload);
 896        } elseif (function_exists('spl_autoload_unregister')) {
 897            $buggy  = version_compare(PHP_VERSION, '5.2.11', '<');
 898            $compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&
 899                      version_compare(PHP_VERSION, '5.1.0', '>=');
 900            foreach ($funcs as $func) {
 901                if ($buggy && is_array($func)) {
 902                    // :TRICKY: There are some compatibility issues and some
 903                    // places where we need to error out
 904                    $reflector = new ReflectionMethod($func[0], $func[1]);
 905                    if (!$reflector->isStatic()) {
 906                        throw new Exception('
 907                            HTML Purifier autoloader registrar is not compatible
 908                            with non-static object methods due to PHP Bug #44144;
 909                            Please do not use HTMLPurifier.autoload.php (or any
 910                            file that includes this file); instead, place the code:
 911                            spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\'))
 912                            after your own autoloaders.
 913                        ');
 914                    }
 915                    // Suprisingly, spl_autoload_register supports the
 916                    // Class::staticMethod callback format, although call_user_func doesn't
 917                    if ($compat) $func = implode('::', $func);
 918                }
 919                spl_autoload_unregister($func);
 920            }
 921            spl_autoload_register($autoload);
 922            foreach ($funcs as $func) spl_autoload_register($func);
 923        }
 924    }
 925
 926}
 927
 928
 929
 930
 931
 932/**
 933 * Super-class for definition datatype objects, implements serialization
 934 * functions for the class.
 935 */
 936abstract class HTMLPurifier_Definition
 937{
 938
 939    /**
 940     * Has setup() been called yet?
 941     */
 942    public $setup = false;
 943
 944    /**
 945     * If true, write out the final definition object to the cache after
 946     * setup.  This will be true only if all invocations to get a raw
 947     * definition object are also optimized.  This does not cause file
 948     * system thrashing because on subsequent calls the cached object
 949     * is used and any writes to the raw definition object are short
 950     * circuited.  See enduser-customize.html for the high-level
 951     * picture.
 952     */
 953    public $optimized = null;
 954
 955    /**
 956     * What type of definition is it?
 957     */
 958    public $type;
 959
 960    /**
 961     * Sets up the definition object into the final form, something
 962     * not done by the constructor
 963     * @param $config HTMLPurifier_Config instance
 964     */
 965    abstract protected function doSetup($config);
 966
 967    /**
 968     * Setup function that aborts if already setup
 969     * @param $config HTMLPurifier_Config instance
 970     */
 971    public function setup($config) {
 972        if ($this->setup) return;
 973        $this->setup = true;
 974        $this->doSetup($config);
 975    }
 976
 977}
 978
 979
 980
 981
 982
 983/**
 984 * Defines allowed CSS attributes and what their values are.
 985 * @see HTMLPurifier_HTMLDefinition
 986 */
 987class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
 988{
 989
 990    public $type = 'CSS';
 991
 992    /**
 993     * Assoc array of attribute name to definition object.
 994     */
 995    public $info = array();
 996
 997    /**
 998     * Constructs the info array.  The meat of this class.
 999     */
1000    protected function doSetup($config) {
1001
1002        $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
1003            array('left', 'right', 'center', 'justify'), false);
1004
1005        $border_style =
1006        $this->info['border-bottom-style'] =
1007        $this->info['border-right-style'] =
1008        $this->info['border-left-style'] =
1009        $this->info['border-top-style'] =  new HTMLPurifier_AttrDef_Enum(
1010            array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
1011            'groove', 'ridge', 'inset', 'outset'), false);
1012
1013        $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
1014
1015        $this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
1016            array('none', 'left', 'right', 'both'), false);
1017        $this->info['float'] = new HTMLPurifier_AttrDef_Enum(
1018            array('none', 'left', 'right'), false);
1019        $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
1020            array('normal', 'italic', 'oblique'), false);
1021        $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
1022            array('normal', 'small-caps'), false);
1023
1024        $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
1025            array(
1026                new HTMLPurifier_AttrDef_Enum(array('none')),
1027                new HTMLPurifier_AttrDef_CSS_URI()
1028            )
1029        );
1030
1031        $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
1032            array('inside', 'outside'), false);
1033        $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
1034            array('disc', 'circle', 'square', 'decimal', 'lower-roman',
1035            'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false);
1036        $this->info['list-style-image'] = $uri_or_none;
1037
1038        $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
1039
1040        $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
1041            array('capitalize', 'uppercase', 'lowercase', 'none'), false);
1042        $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
1043
1044        $this->info['background-image'] = $uri_or_none;
1045        $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
1046            array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
1047        );
1048        $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
1049            array('scroll', 'fixed')
1050        );
1051        $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
1052
1053        $border_color =
1054        $this->info['border-top-color'] =
1055        $this->info['border-bottom-color'] =
1056        $this->info['border-left-color'] =
1057        $this->info['border-right-color'] =
1058        $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1059            new HTMLPurifier_AttrDef_Enum(array('transparent')),
1060            new HTMLPurifier_AttrDef_CSS_Color()
1061        ));
1062
1063        $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
1064
1065        $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
1066
1067        $border_width =
1068        $this->info['border-top-width'] =
1069        $this->info['border-bottom-width'] =
1070        $this->info['border-left-width'] =
1071        $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1072            new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
1073            new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
1074        ));
1075
1076        $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
1077
1078        $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1079            new HTMLPurifier_AttrDef_Enum(array('normal')),
1080            new HTMLPurifier_AttrDef_CSS_Length()
1081        ));
1082
1083        $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1084            new HTMLPurifier_AttrDef_Enum(array('normal')),
1085            new HTMLPurifier_AttrDef_CSS_Length()
1086        ));
1087
1088        $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1089            new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small',
1090                'small', 'medium', 'large', 'x-large', 'xx-large',
1091                'larger', 'smaller')),
1092            new HTMLPurifier_AttrDef_CSS_Percentage(),
1093            new HTMLPurifier_AttrDef_CSS_Length()
1094        ));
1095
1096        $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1097            new HTMLPurifier_AttrDef_Enum(array('normal')),
1098            new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
1099            new HTMLPurifier_AttrDef_CSS_Length('0'),
1100            new HTMLPurifier_AttrDef_CSS_Percentage(true)
1101        ));
1102
1103        $margin =
1104        $this->info['margin-top'] =
1105        $this->info['margin-bottom'] =
1106        $this->info['margin-left'] =
1107        $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1108            new HTMLPurifier_AttrDef_CSS_Length(),
1109            new HTMLPurifier_AttrDef_CSS_Percentage(),
1110            new HTMLPurifier_AttrDef_Enum(array('auto'))
1111        ));
1112
1113        $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
1114
1115        // non-negative
1116        $padding =
1117        $this->info['padding-top'] =
1118        $this->info['padding-bottom'] =
1119        $this->info['padding-left'] =
1120        $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1121            new HTMLPurifier_AttrDef_CSS_Length('0'),
1122            new HTMLPurifier_AttrDef_CSS_Percentage(true)
1123        ));
1124
1125        $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
1126
1127        $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1128            new HTMLPurifier_AttrDef_CSS_Length(),
1129            new HTMLPurifier_AttrDef_CSS_Percentage()
1130        ));
1131
1132        $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array(
1133            new HTMLPurifier_AttrDef_CSS_Length('0'),
1134            new HTMLPurifier_AttrDef_CSS_Percentage(true),
1135            new HTMLPurifier_AttrDef_Enum(array('auto'))
1136        ));
1137        $max = $config->get('CSS.MaxImgLength');
1138
1139        $this->info['width'] =
1140        $this->info['height'] =
1141            $max === null ?
1142            $trusted_wh :
1143            new HTMLPurifier_AttrDef_Switch('img',
1144                // For img tags:
1145                new HTMLPurifier_AttrDef_CSS_Composite(array(
1146                    new HTMLPurifier_AttrDef_CSS_Length('0', $max),
1147                    new HTMLPurifier_AttrDef_Enum(array('auto'))
1148                )),
1149                // For everyone else:
1150                $trusted_wh
1151            );
1152
1153        $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
1154
1155        $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
1156
1157        // this could use specialized code
1158        $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
1159            array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300',
1160            '400', '500', '600', '700', '800', '900'), false);
1161
1162        // MUST be called after other font properties, as it references
1163        // a CSSDefinition object
1164        $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
1165
1166        // same here
1167        $this->info['border'] =
1168        $this->info['border-bottom'] =
1169        $this->info['border-top'] =
1170        $this->info['border-left'] =
1171        $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
1172
1173        $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array(
1174            'collapse', 'separate'));
1175
1176        $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array(
1177            'top', 'bottom'));
1178
1179        $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array(
1180            'auto', 'fixed'));
1181
1182        $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1183            new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super',
1184                'top', 'text-top', 'middle', 'bottom', 'text-bottom')),
1185            new HTMLPurifier_AttrDef_CSS_Length(),
1186            new HTMLPurifier_AttrDef_CSS_Percentage()
1187        ));
1188
1189        $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
1190
1191        // partial support
1192        $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap'));
1193
1194        if ($config->get('CSS.Proprietary')) {
1195            $this->doSetupProprietary($config);
1196        }
1197
1198        if ($config->get('CSS.AllowTricky')) {
1199            $this->doSetupTricky($config);
1200        }
1201
1202        if ($config->get('CSS.Trusted')) {
1203            $this->doSetupTrusted($config);
1204        }
1205
1206        $allow_important = $config->get('CSS.AllowImportant');
1207        // wrap all attr-defs with decorator that handles !important
1208        foreach ($this->info as $k => $v) {
1209            $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
1210        }
1211
1212        $this->setupConfigStuff($config);
1213    }
1214
1215    protected function doSetupProprietary($config) {
1216        // Internet Explorer only scrollbar colors
1217        $this->info['scrollbar-arrow-color']        = new HTMLPurifier_AttrDef_CSS_Color();
1218        $this->info['scrollbar-base-color']         = new HTMLPurifier_AttrDef_CSS_Color();
1219        $this->info['scrollbar-darkshadow-color']   = new HTMLPurifier_AttrDef_CSS_Color();
1220        $this->info['scrollbar-face-color']         = new HTMLPurifier_AttrDef_CSS_Color();
1221        $this->info['scrollbar-highlight-color']    = new HTMLPurifier_AttrDef_CSS_Color();
1222        $this->info['scrollbar-shadow-color']       = new HTMLPurifier_AttrDef_CSS_Color();
1223
1224        // technically not proprietary, but CSS3, and no one supports it
1225        $this->info['opacity']          = new HTMLPurifier_AttrDef_CSS_AlphaValue();
1226        $this->info['-moz-opacity']     = new HTMLPurifier_AttrDef_CSS_AlphaValue();
1227        $this->info['-khtml-opacity']   = new HTMLPurifier_AttrDef_CSS_AlphaValue();
1228
1229        // only opacity, for now
1230        $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
1231
1232    }
1233
1234    protected function doSetupTricky($config) {
1235        $this->info['display'] = new HTMLPurifier_AttrDef_Enum(array(
1236            'inline', 'block', 'list-item', 'run-in', 'compact',
1237            'marker', 'table', 'inline-table', 'table-row-group',
1238            'table-header-group', 'table-footer-group', 'table-row',
1239            'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none'
1240        ));
1241        $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array(
1242            'visible', 'hidden', 'collapse'
1243        ));
1244        $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
1245    }
1246
1247    protected function doSetupTrusted($config) {
1248        $this->info['position'] = new HTMLPurifier_AttrDef_Enum(array(
1249            'static', 'relative', 'absolute', 'fixed'
1250        ));
1251        $this->info['top'] =
1252        $this->info['left'] =
1253        $this->info['right'] =
1254        $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1255            new HTMLPurifier_AttrDef_CSS_Length(),
1256            new HTMLPurifier_AttrDef_CSS_Percentage(),
1257            new HTMLPurifier_AttrDef_Enum(array('auto')),
1258        ));
1259        $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
1260            new HTMLPurifier_AttrDef_Integer(),
1261            new HTMLPurifier_AttrDef_Enum(array('auto')),
1262        ));
1263    }
1264
1265    /**
1266     * Performs extra config-based processing. Based off of
1267     * HTMLPurifier_HTMLDefinition.
1268     * @todo Refactor duplicate elements into common class (probably using
1269     *       composition, not inheritance).
1270     */
1271    protected function setupConfigStuff($config) {
1272
1273        // setup allowed elements
1274        $support = "(for information on implementing this, see the ".
1275                   "support forums) ";
1276        $allowed_properties = $config->get('CSS.AllowedProperties');
1277        if ($allowed_properties !== null) {
1278            foreach ($this->info as $name => $d) {
1279                if(!isset($allowed_properties[$name])) unset($this->info[$name]);
1280                unset($allowed_properties[$name]);
1281            }
1282            // emit errors
1283            foreach ($allowed_properties as $name => $d) {
1284                // :TODO: Is this htmlspecialchars() call really necessary?
1285                $name = htmlspecialchars($name);
1286                trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
1287            }
1288        }
1289
1290        $forbidden_properties = $config->get('CSS.ForbiddenProperties');
1291        if ($forbidden_properties !== null) {
1292            foreach ($this->info as $name => $d) {
1293                if (isset($forbidden_properties[$name])) {
1294                    unset($this->info[$name]);
1295                }
1296            }
1297        }
1298

Large files files are truncated, but you can click here to view the full file