File indexing completed on 2025-01-26 05:29:06
0001 <?php 0002 0003 /** 0004 * Validates the HTML attribute style, otherwise known as CSS. 0005 * @note We don't implement the whole CSS specification, so it might be 0006 * difficult to reuse this component in the context of validating 0007 * actual stylesheet declarations. 0008 * @note If we were really serious about validating the CSS, we would 0009 * tokenize the styles and then parse the tokens. Obviously, we 0010 * are not doing that. Doing that could seriously harm performance, 0011 * but would make these components a lot more viable for a CSS 0012 * filtering solution. 0013 */ 0014 class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef 0015 { 0016 0017 /** 0018 * @param string $css 0019 * @param HTMLPurifier_Config $config 0020 * @param HTMLPurifier_Context $context 0021 * @return bool|string 0022 */ 0023 public function validate($css, $config, $context) 0024 { 0025 $css = $this->parseCDATA($css); 0026 0027 $definition = $config->getCSSDefinition(); 0028 $allow_duplicates = $config->get("CSS.AllowDuplicates"); 0029 0030 0031 // According to the CSS2.1 spec, the places where a 0032 // non-delimiting semicolon can appear are in strings 0033 // escape sequences. So here is some dumb hack to 0034 // handle quotes. 0035 $len = strlen($css); 0036 $accum = ""; 0037 $declarations = array(); 0038 $quoted = false; 0039 for ($i = 0; $i < $len; $i++) { 0040 $c = strcspn($css, ";'\"", $i); 0041 $accum .= substr($css, $i, $c); 0042 $i += $c; 0043 if ($i == $len) break; 0044 $d = $css[$i]; 0045 if ($quoted) { 0046 $accum .= $d; 0047 if ($d == $quoted) { 0048 $quoted = false; 0049 } 0050 } else { 0051 if ($d == ";") { 0052 $declarations[] = $accum; 0053 $accum = ""; 0054 } else { 0055 $accum .= $d; 0056 $quoted = $d; 0057 } 0058 } 0059 } 0060 if ($accum != "") $declarations[] = $accum; 0061 0062 $propvalues = array(); 0063 $new_declarations = ''; 0064 0065 /** 0066 * Name of the current CSS property being validated. 0067 */ 0068 $property = false; 0069 $context->register('CurrentCSSProperty', $property); 0070 0071 foreach ($declarations as $declaration) { 0072 if (!$declaration) { 0073 continue; 0074 } 0075 if (!strpos($declaration, ':')) { 0076 continue; 0077 } 0078 list($property, $value) = explode(':', $declaration, 2); 0079 $property = trim($property); 0080 $value = trim($value); 0081 $ok = false; 0082 do { 0083 if (isset($definition->info[$property])) { 0084 $ok = true; 0085 break; 0086 } 0087 if (ctype_lower($property)) { 0088 break; 0089 } 0090 $property = strtolower($property); 0091 if (isset($definition->info[$property])) { 0092 $ok = true; 0093 break; 0094 } 0095 } while (0); 0096 if (!$ok) { 0097 continue; 0098 } 0099 // inefficient call, since the validator will do this again 0100 if (strtolower(trim($value)) !== 'inherit') { 0101 // inherit works for everything (but only on the base property) 0102 $result = $definition->info[$property]->validate( 0103 $value, 0104 $config, 0105 $context 0106 ); 0107 } else { 0108 $result = 'inherit'; 0109 } 0110 if ($result === false) { 0111 continue; 0112 } 0113 if ($allow_duplicates) { 0114 $new_declarations .= "$property:$result;"; 0115 } else { 0116 $propvalues[$property] = $result; 0117 } 0118 } 0119 0120 $context->destroy('CurrentCSSProperty'); 0121 0122 // procedure does not write the new CSS simultaneously, so it's 0123 // slightly inefficient, but it's the only way of getting rid of 0124 // duplicates. Perhaps config to optimize it, but not now. 0125 0126 foreach ($propvalues as $prop => $value) { 0127 $new_declarations .= "$prop:$value;"; 0128 } 0129 0130 return $new_declarations ? $new_declarations : false; 0131 0132 } 0133 0134 } 0135 0136 // vim: et sw=4 sts=4