| 1: | <?php | 
| 2: |  | 
| 3: | class HTMLPurifier_HTMLModuleManager | 
| 4: | { | 
| 5: |  | 
| 6: |  | 
| 7: |  | 
| 8: |  | 
| 9: | public $doctypes; | 
| 10: |  | 
| 11: |  | 
| 12: |  | 
| 13: |  | 
| 14: |  | 
| 15: | public $doctype; | 
| 16: |  | 
| 17: |  | 
| 18: |  | 
| 19: |  | 
| 20: | public $attrTypes; | 
| 21: |  | 
| 22: |  | 
| 23: |  | 
| 24: |  | 
| 25: |  | 
| 26: |  | 
| 27: | public $modules = array(); | 
| 28: |  | 
| 29: |  | 
| 30: |  | 
| 31: |  | 
| 32: |  | 
| 33: |  | 
| 34: |  | 
| 35: | public $registeredModules = array(); | 
| 36: |  | 
| 37: |  | 
| 38: |  | 
| 39: |  | 
| 40: |  | 
| 41: |  | 
| 42: |  | 
| 43: | public $userModules = array(); | 
| 44: |  | 
| 45: |  | 
| 46: |  | 
| 47: |  | 
| 48: |  | 
| 49: |  | 
| 50: | public $elementLookup = array(); | 
| 51: |  | 
| 52: |  | 
| 53: |  | 
| 54: |  | 
| 55: |  | 
| 56: | public $prefixes = array('HTMLPurifier_HTMLModule_'); | 
| 57: |  | 
| 58: |  | 
| 59: |  | 
| 60: |  | 
| 61: | public $contentSets; | 
| 62: |  | 
| 63: |  | 
| 64: |  | 
| 65: |  | 
| 66: | public $attrCollections; | 
| 67: |  | 
| 68: |  | 
| 69: |  | 
| 70: |  | 
| 71: |  | 
| 72: | public $trusted = false; | 
| 73: |  | 
| 74: | public function __construct() | 
| 75: | { | 
| 76: |  | 
| 77: | $this->attrTypes = new HTMLPurifier_AttrTypes(); | 
| 78: | $this->doctypes  = new HTMLPurifier_DoctypeRegistry(); | 
| 79: |  | 
| 80: |  | 
| 81: | $common = array( | 
| 82: | 'CommonAttributes', 'Text', 'Hypertext', 'List', | 
| 83: | 'Presentation', 'Edit', 'Bdo', 'Tables', 'Image', | 
| 84: | 'StyleAttribute', | 
| 85: |  | 
| 86: | 'Scripting', 'Object', 'Forms', | 
| 87: |  | 
| 88: | 'Name', | 
| 89: | ); | 
| 90: | $transitional = array('Legacy', 'Target', 'Iframe'); | 
| 91: | $xml = array('XMLCommonAttributes'); | 
| 92: | $non_xml = array('NonXMLCommonAttributes'); | 
| 93: |  | 
| 94: |  | 
| 95: | $this->doctypes->register( | 
| 96: | 'HTML 4.01 Transitional', | 
| 97: | false, | 
| 98: | array_merge($common, $transitional, $non_xml), | 
| 99: | array('Tidy_Transitional', 'Tidy_Proprietary'), | 
| 100: | array(), | 
| 101: | '-//W3C//DTD HTML 4.01 Transitional//EN', | 
| 102: | 'http://www.w3.org/TR/html4/loose.dtd' | 
| 103: | ); | 
| 104: |  | 
| 105: | $this->doctypes->register( | 
| 106: | 'HTML 4.01 Strict', | 
| 107: | false, | 
| 108: | array_merge($common, $non_xml), | 
| 109: | array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'), | 
| 110: | array(), | 
| 111: | '-//W3C//DTD HTML 4.01//EN', | 
| 112: | 'http://www.w3.org/TR/html4/strict.dtd' | 
| 113: | ); | 
| 114: |  | 
| 115: | $this->doctypes->register( | 
| 116: | 'XHTML 1.0 Transitional', | 
| 117: | true, | 
| 118: | array_merge($common, $transitional, $xml, $non_xml), | 
| 119: | array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'), | 
| 120: | array(), | 
| 121: | '-//W3C//DTD XHTML 1.0 Transitional//EN', | 
| 122: | 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd' | 
| 123: | ); | 
| 124: |  | 
| 125: | $this->doctypes->register( | 
| 126: | 'XHTML 1.0 Strict', | 
| 127: | true, | 
| 128: | array_merge($common, $xml, $non_xml), | 
| 129: | array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'), | 
| 130: | array(), | 
| 131: | '-//W3C//DTD XHTML 1.0 Strict//EN', | 
| 132: | 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd' | 
| 133: | ); | 
| 134: |  | 
| 135: | $this->doctypes->register( | 
| 136: | 'XHTML 1.1', | 
| 137: | true, | 
| 138: |  | 
| 139: |  | 
| 140: | array_merge($common, $xml, array('Ruby', 'Iframe')), | 
| 141: | array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'), | 
| 142: | array(), | 
| 143: | '-//W3C//DTD XHTML 1.1//EN', | 
| 144: | 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd' | 
| 145: | ); | 
| 146: |  | 
| 147: | } | 
| 148: |  | 
| 149: |  | 
| 150: |  | 
| 151: |  | 
| 152: |  | 
| 153: |  | 
| 154: |  | 
| 155: |  | 
| 156: |  | 
| 157: |  | 
| 158: |  | 
| 159: |  | 
| 160: |  | 
| 161: |  | 
| 162: |  | 
| 163: |  | 
| 164: |  | 
| 165: |  | 
| 166: |  | 
| 167: |  | 
| 168: |  | 
| 169: |  | 
| 170: | public function registerModule($module, $overload = false) | 
| 171: | { | 
| 172: | if (is_string($module)) { | 
| 173: |  | 
| 174: | $original_module = $module; | 
| 175: | $ok = false; | 
| 176: | foreach ($this->prefixes as $prefix) { | 
| 177: | $module = $prefix . $original_module; | 
| 178: | if (class_exists($module)) { | 
| 179: | $ok = true; | 
| 180: | break; | 
| 181: | } | 
| 182: | } | 
| 183: | if (!$ok) { | 
| 184: | $module = $original_module; | 
| 185: | if (!class_exists($module)) { | 
| 186: | trigger_error( | 
| 187: | $original_module . ' module does not exist', | 
| 188: | E_USER_ERROR | 
| 189: | ); | 
| 190: | return; | 
| 191: | } | 
| 192: | } | 
| 193: | $module = new $module(); | 
| 194: | } | 
| 195: | if (empty($module->name)) { | 
| 196: | trigger_error('Module instance of ' . get_class($module) . ' must have name'); | 
| 197: | return; | 
| 198: | } | 
| 199: | if (!$overload && isset($this->registeredModules[$module->name])) { | 
| 200: | trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING); | 
| 201: | } | 
| 202: | $this->registeredModules[$module->name] = $module; | 
| 203: | } | 
| 204: |  | 
| 205: |  | 
| 206: |  | 
| 207: |  | 
| 208: |  | 
| 209: | public function addModule($module) | 
| 210: | { | 
| 211: | $this->registerModule($module); | 
| 212: | if (is_object($module)) { | 
| 213: | $module = $module->name; | 
| 214: | } | 
| 215: | $this->userModules[] = $module; | 
| 216: | } | 
| 217: |  | 
| 218: |  | 
| 219: |  | 
| 220: |  | 
| 221: |  | 
| 222: | public function addPrefix($prefix) | 
| 223: | { | 
| 224: | $this->prefixes[] = $prefix; | 
| 225: | } | 
| 226: |  | 
| 227: |  | 
| 228: |  | 
| 229: |  | 
| 230: |  | 
| 231: |  | 
| 232: | public function setup($config) | 
| 233: | { | 
| 234: | $this->trusted = $config->get('HTML.Trusted'); | 
| 235: |  | 
| 236: |  | 
| 237: | $this->doctype = $this->doctypes->make($config); | 
| 238: | $modules = $this->doctype->modules; | 
| 239: |  | 
| 240: |  | 
| 241: | $lookup = $config->get('HTML.AllowedModules'); | 
| 242: | $special_cases = $config->get('HTML.CoreModules'); | 
| 243: |  | 
| 244: | if (is_array($lookup)) { | 
| 245: | foreach ($modules as $k => $m) { | 
| 246: | if (isset($special_cases[$m])) { | 
| 247: | continue; | 
| 248: | } | 
| 249: | if (!isset($lookup[$m])) { | 
| 250: | unset($modules[$k]); | 
| 251: | } | 
| 252: | } | 
| 253: | } | 
| 254: |  | 
| 255: |  | 
| 256: | if ($config->get('HTML.Proprietary')) { | 
| 257: | $modules[] = 'Proprietary'; | 
| 258: | } | 
| 259: | if ($config->get('HTML.SafeObject')) { | 
| 260: | $modules[] = 'SafeObject'; | 
| 261: | } | 
| 262: | if ($config->get('HTML.SafeEmbed')) { | 
| 263: | $modules[] = 'SafeEmbed'; | 
| 264: | } | 
| 265: | if ($config->get('HTML.SafeScripting') !== array()) { | 
| 266: | $modules[] = 'SafeScripting'; | 
| 267: | } | 
| 268: | if ($config->get('HTML.Nofollow')) { | 
| 269: | $modules[] = 'Nofollow'; | 
| 270: | } | 
| 271: | if ($config->get('HTML.TargetBlank')) { | 
| 272: | $modules[] = 'TargetBlank'; | 
| 273: | } | 
| 274: |  | 
| 275: |  | 
| 276: | if ($config->get('HTML.TargetNoreferrer')) { | 
| 277: | $modules[] = 'TargetNoreferrer'; | 
| 278: | } | 
| 279: | if ($config->get('HTML.TargetNoopener')) { | 
| 280: | $modules[] = 'TargetNoopener'; | 
| 281: | } | 
| 282: |  | 
| 283: |  | 
| 284: | $modules = array_merge($modules, $this->userModules); | 
| 285: |  | 
| 286: | foreach ($modules as $module) { | 
| 287: | $this->processModule($module); | 
| 288: | $this->modules[$module]->setup($config); | 
| 289: | } | 
| 290: |  | 
| 291: | foreach ($this->doctype->tidyModules as $module) { | 
| 292: | $this->processModule($module); | 
| 293: | $this->modules[$module]->setup($config); | 
| 294: | } | 
| 295: |  | 
| 296: |  | 
| 297: | foreach ($this->modules as $module) { | 
| 298: | $n = array(); | 
| 299: | foreach ($module->info_injector as $injector) { | 
| 300: | if (!is_object($injector)) { | 
| 301: | $class = "HTMLPurifier_Injector_$injector"; | 
| 302: | $injector = new $class; | 
| 303: | } | 
| 304: | $n[$injector->name] = $injector; | 
| 305: | } | 
| 306: | $module->info_injector = $n; | 
| 307: | } | 
| 308: |  | 
| 309: |  | 
| 310: | foreach ($this->modules as $module) { | 
| 311: | foreach ($module->info as $name => $def) { | 
| 312: | if (!isset($this->elementLookup[$name])) { | 
| 313: | $this->elementLookup[$name] = array(); | 
| 314: | } | 
| 315: | $this->elementLookup[$name][] = $module->name; | 
| 316: | } | 
| 317: | } | 
| 318: |  | 
| 319: |  | 
| 320: | $this->contentSets = new HTMLPurifier_ContentSets( | 
| 321: |  | 
| 322: |  | 
| 323: | $this->modules | 
| 324: | ); | 
| 325: | $this->attrCollections = new HTMLPurifier_AttrCollections( | 
| 326: | $this->attrTypes, | 
| 327: |  | 
| 328: |  | 
| 329: |  | 
| 330: | $this->modules | 
| 331: | ); | 
| 332: | } | 
| 333: |  | 
| 334: |  | 
| 335: |  | 
| 336: |  | 
| 337: |  | 
| 338: | public function processModule($module) | 
| 339: | { | 
| 340: | if (!isset($this->registeredModules[$module]) || is_object($module)) { | 
| 341: | $this->registerModule($module); | 
| 342: | } | 
| 343: | $this->modules[$module] = $this->registeredModules[$module]; | 
| 344: | } | 
| 345: |  | 
| 346: |  | 
| 347: |  | 
| 348: |  | 
| 349: |  | 
| 350: | public function getElements() | 
| 351: | { | 
| 352: | $elements = array(); | 
| 353: | foreach ($this->modules as $module) { | 
| 354: | if (!$this->trusted && !$module->safe) { | 
| 355: | continue; | 
| 356: | } | 
| 357: | foreach ($module->info as $name => $v) { | 
| 358: | if (isset($elements[$name])) { | 
| 359: | continue; | 
| 360: | } | 
| 361: | $elements[$name] = $this->getElement($name); | 
| 362: | } | 
| 363: | } | 
| 364: |  | 
| 365: |  | 
| 366: |  | 
| 367: | foreach ($elements as $n => $v) { | 
| 368: | if ($v === false) { | 
| 369: | unset($elements[$n]); | 
| 370: | } | 
| 371: | } | 
| 372: |  | 
| 373: | return $elements; | 
| 374: |  | 
| 375: | } | 
| 376: |  | 
| 377: |  | 
| 378: |  | 
| 379: |  | 
| 380: |  | 
| 381: |  | 
| 382: |  | 
| 383: |  | 
| 384: |  | 
| 385: |  | 
| 386: |  | 
| 387: | public function getElement($name, $trusted = null) | 
| 388: | { | 
| 389: | if (!isset($this->elementLookup[$name])) { | 
| 390: | return false; | 
| 391: | } | 
| 392: |  | 
| 393: |  | 
| 394: | $def = false; | 
| 395: | if ($trusted === null) { | 
| 396: | $trusted = $this->trusted; | 
| 397: | } | 
| 398: |  | 
| 399: |  | 
| 400: |  | 
| 401: | foreach ($this->elementLookup[$name] as $module_name) { | 
| 402: | $module = $this->modules[$module_name]; | 
| 403: |  | 
| 404: |  | 
| 405: |  | 
| 406: | if (!$trusted && !$module->safe) { | 
| 407: | continue; | 
| 408: | } | 
| 409: |  | 
| 410: |  | 
| 411: |  | 
| 412: |  | 
| 413: | $new_def = clone $module->info[$name]; | 
| 414: |  | 
| 415: | if (!$def && $new_def->standalone) { | 
| 416: | $def = $new_def; | 
| 417: | } elseif ($def) { | 
| 418: |  | 
| 419: |  | 
| 420: | $def->mergeIn($new_def); | 
| 421: | } else { | 
| 422: |  | 
| 423: |  | 
| 424: |  | 
| 425: |  | 
| 426: |  | 
| 427: |  | 
| 428: |  | 
| 429: |  | 
| 430: |  | 
| 431: |  | 
| 432: | continue; | 
| 433: | } | 
| 434: |  | 
| 435: |  | 
| 436: | $this->attrCollections->performInclusions($def->attr); | 
| 437: | $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes); | 
| 438: |  | 
| 439: |  | 
| 440: | if (is_string($def->content_model) && | 
| 441: | strpos($def->content_model, 'Inline') !== false) { | 
| 442: | if ($name != 'del' && $name != 'ins') { | 
| 443: |  | 
| 444: | $def->descendants_are_inline = true; | 
| 445: | } | 
| 446: | } | 
| 447: |  | 
| 448: | $this->contentSets->generateChildDef($def, $module); | 
| 449: | } | 
| 450: |  | 
| 451: |  | 
| 452: |  | 
| 453: | if (!$def) { | 
| 454: | return false; | 
| 455: | } | 
| 456: |  | 
| 457: |  | 
| 458: | foreach ($def->attr as $attr_name => $attr_def) { | 
| 459: | if ($attr_def->required) { | 
| 460: | $def->required_attr[] = $attr_name; | 
| 461: | } | 
| 462: | } | 
| 463: | return $def; | 
| 464: | } | 
| 465: | } | 
| 466: |  | 
| 467: |  | 
| 468: |  |