1: <?php
2:
3: /**
4: * Validates Color as defined by CSS.
5: */
6: class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
7: {
8:
9: /**
10: * @type HTMLPurifier_AttrDef_CSS_AlphaValue
11: */
12: protected $alpha;
13:
14: public function __construct()
15: {
16: $this->alpha = new HTMLPurifier_AttrDef_CSS_AlphaValue();
17: }
18:
19: /**
20: * @param string $color
21: * @param HTMLPurifier_Config $config
22: * @param HTMLPurifier_Context $context
23: * @return bool|string
24: */
25: public function validate($color, $config, $context)
26: {
27: static $colors = null;
28: if ($colors === null) {
29: $colors = $config->get('Core.ColorKeywords');
30: }
31:
32: $color = trim($color);
33: if ($color === '') {
34: return false;
35: }
36:
37: $lower = strtolower($color);
38: if (isset($colors[$lower])) {
39: return $colors[$lower];
40: }
41:
42: if (preg_match('#(rgb|rgba|hsl|hsla)\(#', $color, $matches) === 1) {
43: $length = strlen($color);
44: if (strpos($color, ')') !== $length - 1) {
45: return false;
46: }
47:
48: // get used function : rgb, rgba, hsl or hsla
49: $function = $matches[1];
50:
51: $parameters_size = 3;
52: $alpha_channel = false;
53: if (substr($function, -1) === 'a') {
54: $parameters_size = 4;
55: $alpha_channel = true;
56: }
57:
58: /*
59: * Allowed types for values :
60: * parameter_position => [type => max_value]
61: */
62: $allowed_types = array(
63: 1 => array('percentage' => 100, 'integer' => 255),
64: 2 => array('percentage' => 100, 'integer' => 255),
65: 3 => array('percentage' => 100, 'integer' => 255),
66: );
67: $allow_different_types = false;
68:
69: if (strpos($function, 'hsl') !== false) {
70: $allowed_types = array(
71: 1 => array('integer' => 360),
72: 2 => array('percentage' => 100),
73: 3 => array('percentage' => 100),
74: );
75: $allow_different_types = true;
76: }
77:
78: $values = trim(str_replace($function, '', $color), ' ()');
79:
80: $parts = explode(',', $values);
81: if (count($parts) !== $parameters_size) {
82: return false;
83: }
84:
85: $type = false;
86: $new_parts = array();
87: $i = 0;
88:
89: foreach ($parts as $part) {
90: $i++;
91: $part = trim($part);
92:
93: if ($part === '') {
94: return false;
95: }
96:
97: // different check for alpha channel
98: if ($alpha_channel === true && $i === count($parts)) {
99: $result = $this->alpha->validate($part, $config, $context);
100:
101: if ($result === false) {
102: return false;
103: }
104:
105: $new_parts[] = (string)$result;
106: continue;
107: }
108:
109: if (substr($part, -1) === '%') {
110: $current_type = 'percentage';
111: } else {
112: $current_type = 'integer';
113: }
114:
115: if (!array_key_exists($current_type, $allowed_types[$i])) {
116: return false;
117: }
118:
119: if (!$type) {
120: $type = $current_type;
121: }
122:
123: if ($allow_different_types === false && $type != $current_type) {
124: return false;
125: }
126:
127: $max_value = $allowed_types[$i][$current_type];
128:
129: if ($current_type == 'integer') {
130: // Return value between range 0 -> $max_value
131: $new_parts[] = (int)max(min($part, $max_value), 0);
132: } elseif ($current_type == 'percentage') {
133: $new_parts[] = (float)max(min(rtrim($part, '%'), $max_value), 0) . '%';
134: }
135: }
136:
137: $new_values = implode(',', $new_parts);
138:
139: $color = $function . '(' . $new_values . ')';
140: } else {
141: // hexadecimal handling
142: if ($color[0] === '#') {
143: $hex = substr($color, 1);
144: } else {
145: $hex = $color;
146: $color = '#' . $color;
147: }
148: $length = strlen($hex);
149: if ($length !== 3 && $length !== 6) {
150: return false;
151: }
152: if (!ctype_xdigit($hex)) {
153: return false;
154: }
155: }
156: return $color;
157: }
158:
159: }
160:
161: // vim: et sw=4 sts=4
162: