1 <?php 2 3 /** 4 * Validates the HTML attribute style, otherwise known as CSS. 5 * @note We don't implement the whole CSS specification, so it might be 6 * difficult to reuse this component in the context of validating 7 * actual stylesheet declarations. 8 * @note If we were really serious about validating the CSS, we would 9 * tokenize the styles and then parse the tokens. Obviously, we 10 * are not doing that. Doing that could seriously harm performance, 11 * but would make these components a lot more viable for a CSS 12 * filtering solution. 13 */ 14 class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef 15 { 16 17 /** 18 * @param string $css 19 * @param HTMLPurifier_Config $config 20 * @param HTMLPurifier_Context $context 21 * @return bool|string 22 */ 23 public function validate($css, $config, $context) 24 { 25 $css = $this->parseCDATA($css); 26 27 $definition = $config->getCSSDefinition(); 28 $allow_duplicates = $config->get("CSS.AllowDuplicates"); 29 30 31 // According to the CSS2.1 spec, the places where a 32 // non-delimiting semicolon can appear are in strings 33 // escape sequences. So here is some dumb hack to 34 // handle quotes. 35 $len = strlen($css); 36 $accum = ""; 37 $declarations = array(); 38 $quoted = false; 39 for ($i = 0; $i < $len; $i++) { 40 $c = strcspn($css, ";'\"", $i); 41 $accum .= substr($css, $i, $c); 42 $i += $c; 43 if ($i == $len) break; 44 $d = $css[$i]; 45 if ($quoted) { 46 $accum .= $d; 47 if ($d == $quoted) { 48 $quoted = false; 49 } 50 } else { 51 if ($d == ";") { 52 $declarations[] = $accum; 53 $accum = ""; 54 } else { 55 $accum .= $d; 56 $quoted = $d; 57 } 58 } 59 } 60 if ($accum != "") $declarations[] = $accum; 61 62 $propvalues = array(); 63 $new_declarations = ''; 64 65 /** 66 * Name of the current CSS property being validated. 67 */ 68 $property = false; 69 $context->register('CurrentCSSProperty', $property); 70 71 foreach ($declarations as $declaration) { 72 if (!$declaration) { 73 continue; 74 } 75 if (!strpos($declaration, ':')) { 76 continue; 77 } 78 list($property, $value) = explode(':', $declaration, 2); 79 $property = trim($property); 80 $value = trim($value); 81 $ok = false; 82 do { 83 if (isset($definition->info[$property])) { 84 $ok = true; 85 break; 86 } 87 if (ctype_lower($property)) { 88 break; 89 } 90 $property = strtolower($property); 91 if (isset($definition->info[$property])) { 92 $ok = true; 93 break; 94 } 95 } while (0); 96 if (!$ok) { 97 continue; 98 } 99 // inefficient call, since the validator will do this again 100 if (strtolower(trim($value)) !== 'inherit') { 101 // inherit works for everything (but only on the base property) 102 $result = $definition->info[$property]->validate( 103 $value, 104 $config, 105 $context 106 ); 107 } else { 108 $result = 'inherit'; 109 } 110 if ($result === false) { 111 continue; 112 } 113 if ($allow_duplicates) { 114 $new_declarations .= "$property:$result;"; 115 } else { 116 $propvalues[$property] = $result; 117 } 118 } 119 120 $context->destroy('CurrentCSSProperty'); 121 122 // procedure does not write the new CSS simultaneously, so it's 123 // slightly inefficient, but it's the only way of getting rid of 124 // duplicates. Perhaps config to optimize it, but not now. 125 126 foreach ($propvalues as $prop => $value) { 127 $new_declarations .= "$prop:$value;"; 128 } 129 130 return $new_declarations ? $new_declarations : false; 131 132 } 133 134 } 135 136 // vim: et sw=4 sts=4
title
Description
Body
title
Description
Body
title
Description
Body
title
Body