@@ -6,72 +6,72 @@ |
||
6 | 6 | class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef |
7 | 7 | { |
8 | 8 | |
9 | - public function validate($color, $config, $context) { |
|
9 | + public function validate($color, $config, $context) { |
|
10 | 10 | |
11 | - static $colors = null; |
|
12 | - if ($colors === null) $colors = $config->get('Core.ColorKeywords'); |
|
11 | + static $colors = null; |
|
12 | + if ($colors === null) $colors = $config->get('Core.ColorKeywords'); |
|
13 | 13 | |
14 | - $color = trim($color); |
|
15 | - if ($color === '') return false; |
|
14 | + $color = trim($color); |
|
15 | + if ($color === '') return false; |
|
16 | 16 | |
17 | - $lower = strtolower($color); |
|
18 | - if (isset($colors[$lower])) return $colors[$lower]; |
|
17 | + $lower = strtolower($color); |
|
18 | + if (isset($colors[$lower])) return $colors[$lower]; |
|
19 | 19 | |
20 | - if (strpos($color, 'rgb(') !== false) { |
|
21 | - // rgb literal handling |
|
22 | - $length = strlen($color); |
|
23 | - if (strpos($color, ')') !== $length - 1) return false; |
|
24 | - $triad = substr($color, 4, $length - 4 - 1); |
|
25 | - $parts = explode(',', $triad); |
|
26 | - if (count($parts) !== 3) return false; |
|
27 | - $type = false; // to ensure that they're all the same type |
|
28 | - $new_parts = array(); |
|
29 | - foreach ($parts as $part) { |
|
30 | - $part = trim($part); |
|
31 | - if ($part === '') return false; |
|
32 | - $length = strlen($part); |
|
33 | - if ($part[$length - 1] === '%') { |
|
34 | - // handle percents |
|
35 | - if (!$type) { |
|
36 | - $type = 'percentage'; |
|
37 | - } elseif ($type !== 'percentage') { |
|
38 | - return false; |
|
39 | - } |
|
40 | - $num = (float) substr($part, 0, $length - 1); |
|
41 | - if ($num < 0) $num = 0; |
|
42 | - if ($num > 100) $num = 100; |
|
43 | - $new_parts[] = "$num%"; |
|
44 | - } else { |
|
45 | - // handle integers |
|
46 | - if (!$type) { |
|
47 | - $type = 'integer'; |
|
48 | - } elseif ($type !== 'integer') { |
|
49 | - return false; |
|
50 | - } |
|
51 | - $num = (int) $part; |
|
52 | - if ($num < 0) $num = 0; |
|
53 | - if ($num > 255) $num = 255; |
|
54 | - $new_parts[] = (string) $num; |
|
55 | - } |
|
56 | - } |
|
57 | - $new_triad = implode(',', $new_parts); |
|
58 | - $color = "rgb($new_triad)"; |
|
59 | - } else { |
|
60 | - // hexadecimal handling |
|
61 | - if ($color[0] === '#') { |
|
62 | - $hex = substr($color, 1); |
|
63 | - } else { |
|
64 | - $hex = $color; |
|
65 | - $color = '#' . $color; |
|
66 | - } |
|
67 | - $length = strlen($hex); |
|
68 | - if ($length !== 3 && $length !== 6) return false; |
|
69 | - if (!ctype_xdigit($hex)) return false; |
|
70 | - } |
|
20 | + if (strpos($color, 'rgb(') !== false) { |
|
21 | + // rgb literal handling |
|
22 | + $length = strlen($color); |
|
23 | + if (strpos($color, ')') !== $length - 1) return false; |
|
24 | + $triad = substr($color, 4, $length - 4 - 1); |
|
25 | + $parts = explode(',', $triad); |
|
26 | + if (count($parts) !== 3) return false; |
|
27 | + $type = false; // to ensure that they're all the same type |
|
28 | + $new_parts = array(); |
|
29 | + foreach ($parts as $part) { |
|
30 | + $part = trim($part); |
|
31 | + if ($part === '') return false; |
|
32 | + $length = strlen($part); |
|
33 | + if ($part[$length - 1] === '%') { |
|
34 | + // handle percents |
|
35 | + if (!$type) { |
|
36 | + $type = 'percentage'; |
|
37 | + } elseif ($type !== 'percentage') { |
|
38 | + return false; |
|
39 | + } |
|
40 | + $num = (float) substr($part, 0, $length - 1); |
|
41 | + if ($num < 0) $num = 0; |
|
42 | + if ($num > 100) $num = 100; |
|
43 | + $new_parts[] = "$num%"; |
|
44 | + } else { |
|
45 | + // handle integers |
|
46 | + if (!$type) { |
|
47 | + $type = 'integer'; |
|
48 | + } elseif ($type !== 'integer') { |
|
49 | + return false; |
|
50 | + } |
|
51 | + $num = (int) $part; |
|
52 | + if ($num < 0) $num = 0; |
|
53 | + if ($num > 255) $num = 255; |
|
54 | + $new_parts[] = (string) $num; |
|
55 | + } |
|
56 | + } |
|
57 | + $new_triad = implode(',', $new_parts); |
|
58 | + $color = "rgb($new_triad)"; |
|
59 | + } else { |
|
60 | + // hexadecimal handling |
|
61 | + if ($color[0] === '#') { |
|
62 | + $hex = substr($color, 1); |
|
63 | + } else { |
|
64 | + $hex = $color; |
|
65 | + $color = '#' . $color; |
|
66 | + } |
|
67 | + $length = strlen($hex); |
|
68 | + if ($length !== 3 && $length !== 6) return false; |
|
69 | + if (!ctype_xdigit($hex)) return false; |
|
70 | + } |
|
71 | 71 | |
72 | - return $color; |
|
72 | + return $color; |
|
73 | 73 | |
74 | - } |
|
74 | + } |
|
75 | 75 | |
76 | 76 | } |
77 | 77 |
@@ -9,26 +9,38 @@ discard block |
||
9 | 9 | public function validate($color, $config, $context) { |
10 | 10 | |
11 | 11 | static $colors = null; |
12 | - if ($colors === null) $colors = $config->get('Core.ColorKeywords'); |
|
12 | + if ($colors === null) { |
|
13 | + $colors = $config->get('Core.ColorKeywords'); |
|
14 | + } |
|
13 | 15 | |
14 | 16 | $color = trim($color); |
15 | - if ($color === '') return false; |
|
17 | + if ($color === '') { |
|
18 | + return false; |
|
19 | + } |
|
16 | 20 | |
17 | 21 | $lower = strtolower($color); |
18 | - if (isset($colors[$lower])) return $colors[$lower]; |
|
22 | + if (isset($colors[$lower])) { |
|
23 | + return $colors[$lower]; |
|
24 | + } |
|
19 | 25 | |
20 | 26 | if (strpos($color, 'rgb(') !== false) { |
21 | 27 | // rgb literal handling |
22 | 28 | $length = strlen($color); |
23 | - if (strpos($color, ')') !== $length - 1) return false; |
|
29 | + if (strpos($color, ')') !== $length - 1) { |
|
30 | + return false; |
|
31 | + } |
|
24 | 32 | $triad = substr($color, 4, $length - 4 - 1); |
25 | 33 | $parts = explode(',', $triad); |
26 | - if (count($parts) !== 3) return false; |
|
34 | + if (count($parts) !== 3) { |
|
35 | + return false; |
|
36 | + } |
|
27 | 37 | $type = false; // to ensure that they're all the same type |
28 | 38 | $new_parts = array(); |
29 | 39 | foreach ($parts as $part) { |
30 | 40 | $part = trim($part); |
31 | - if ($part === '') return false; |
|
41 | + if ($part === '') { |
|
42 | + return false; |
|
43 | + } |
|
32 | 44 | $length = strlen($part); |
33 | 45 | if ($part[$length - 1] === '%') { |
34 | 46 | // handle percents |
@@ -38,8 +50,12 @@ discard block |
||
38 | 50 | return false; |
39 | 51 | } |
40 | 52 | $num = (float) substr($part, 0, $length - 1); |
41 | - if ($num < 0) $num = 0; |
|
42 | - if ($num > 100) $num = 100; |
|
53 | + if ($num < 0) { |
|
54 | + $num = 0; |
|
55 | + } |
|
56 | + if ($num > 100) { |
|
57 | + $num = 100; |
|
58 | + } |
|
43 | 59 | $new_parts[] = "$num%"; |
44 | 60 | } else { |
45 | 61 | // handle integers |
@@ -49,8 +65,12 @@ discard block |
||
49 | 65 | return false; |
50 | 66 | } |
51 | 67 | $num = (int) $part; |
52 | - if ($num < 0) $num = 0; |
|
53 | - if ($num > 255) $num = 255; |
|
68 | + if ($num < 0) { |
|
69 | + $num = 0; |
|
70 | + } |
|
71 | + if ($num > 255) { |
|
72 | + $num = 255; |
|
73 | + } |
|
54 | 74 | $new_parts[] = (string) $num; |
55 | 75 | } |
56 | 76 | } |
@@ -65,8 +85,12 @@ discard block |
||
65 | 85 | $color = '#' . $color; |
66 | 86 | } |
67 | 87 | $length = strlen($hex); |
68 | - if ($length !== 3 && $length !== 6) return false; |
|
69 | - if (!ctype_xdigit($hex)) return false; |
|
88 | + if ($length !== 3 && $length !== 6) { |
|
89 | + return false; |
|
90 | + } |
|
91 | + if (!ctype_xdigit($hex)) { |
|
92 | + return false; |
|
93 | + } |
|
70 | 94 | } |
71 | 95 | |
72 | 96 | return $color; |
@@ -62,7 +62,7 @@ |
||
62 | 62 | $hex = substr($color, 1); |
63 | 63 | } else { |
64 | 64 | $hex = $color; |
65 | - $color = '#' . $color; |
|
65 | + $color = '#'.$color; |
|
66 | 66 | } |
67 | 67 | $length = strlen($hex); |
68 | 68 | if ($length !== 3 && $length !== 6) return false; |
@@ -12,26 +12,26 @@ |
||
12 | 12 | class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef |
13 | 13 | { |
14 | 14 | |
15 | - /** |
|
16 | - * List of HTMLPurifier_AttrDef objects that may process strings |
|
17 | - * @todo Make protected |
|
18 | - */ |
|
19 | - public $defs; |
|
15 | + /** |
|
16 | + * List of HTMLPurifier_AttrDef objects that may process strings |
|
17 | + * @todo Make protected |
|
18 | + */ |
|
19 | + public $defs; |
|
20 | 20 | |
21 | - /** |
|
22 | - * @param $defs List of HTMLPurifier_AttrDef objects |
|
23 | - */ |
|
24 | - public function __construct($defs) { |
|
25 | - $this->defs = $defs; |
|
26 | - } |
|
21 | + /** |
|
22 | + * @param $defs List of HTMLPurifier_AttrDef objects |
|
23 | + */ |
|
24 | + public function __construct($defs) { |
|
25 | + $this->defs = $defs; |
|
26 | + } |
|
27 | 27 | |
28 | - public function validate($string, $config, $context) { |
|
29 | - foreach ($this->defs as $i => $def) { |
|
30 | - $result = $this->defs[$i]->validate($string, $config, $context); |
|
31 | - if ($result !== false) return $result; |
|
32 | - } |
|
33 | - return false; |
|
34 | - } |
|
28 | + public function validate($string, $config, $context) { |
|
29 | + foreach ($this->defs as $i => $def) { |
|
30 | + $result = $this->defs[$i]->validate($string, $config, $context); |
|
31 | + if ($result !== false) return $result; |
|
32 | + } |
|
33 | + return false; |
|
34 | + } |
|
35 | 35 | |
36 | 36 | } |
37 | 37 |
@@ -28,7 +28,9 @@ |
||
28 | 28 | public function validate($string, $config, $context) { |
29 | 29 | foreach ($this->defs as $i => $def) { |
30 | 30 | $result = $this->defs[$i]->validate($string, $config, $context); |
31 | - if ($result !== false) return $result; |
|
31 | + if ($result !== false) { |
|
32 | + return $result; |
|
33 | + } |
|
32 | 34 | } |
33 | 35 | return false; |
34 | 36 | } |
@@ -5,24 +5,24 @@ |
||
5 | 5 | */ |
6 | 6 | class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef |
7 | 7 | { |
8 | - public $def, $element; |
|
8 | + public $def, $element; |
|
9 | 9 | |
10 | - /** |
|
11 | - * @param $def Definition to wrap |
|
12 | - * @param $element Element to deny |
|
13 | - */ |
|
14 | - public function __construct($def, $element) { |
|
15 | - $this->def = $def; |
|
16 | - $this->element = $element; |
|
17 | - } |
|
18 | - /** |
|
19 | - * Checks if CurrentToken is set and equal to $this->element |
|
20 | - */ |
|
21 | - public function validate($string, $config, $context) { |
|
22 | - $token = $context->get('CurrentToken', true); |
|
23 | - if ($token && $token->name == $this->element) return false; |
|
24 | - return $this->def->validate($string, $config, $context); |
|
25 | - } |
|
10 | + /** |
|
11 | + * @param $def Definition to wrap |
|
12 | + * @param $element Element to deny |
|
13 | + */ |
|
14 | + public function __construct($def, $element) { |
|
15 | + $this->def = $def; |
|
16 | + $this->element = $element; |
|
17 | + } |
|
18 | + /** |
|
19 | + * Checks if CurrentToken is set and equal to $this->element |
|
20 | + */ |
|
21 | + public function validate($string, $config, $context) { |
|
22 | + $token = $context->get('CurrentToken', true); |
|
23 | + if ($token && $token->name == $this->element) return false; |
|
24 | + return $this->def->validate($string, $config, $context); |
|
25 | + } |
|
26 | 26 | } |
27 | 27 | |
28 | 28 | // vim: et sw=4 sts=4 |
@@ -20,7 +20,9 @@ |
||
20 | 20 | */ |
21 | 21 | public function validate($string, $config, $context) { |
22 | 22 | $token = $context->get('CurrentToken', true); |
23 | - if ($token && $token->name == $this->element) return false; |
|
23 | + if ($token && $token->name == $this->element) { |
|
24 | + return false; |
|
25 | + } |
|
24 | 26 | return $this->def->validate($string, $config, $context); |
25 | 27 | } |
26 | 28 | } |
@@ -8,46 +8,46 @@ |
||
8 | 8 | class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef |
9 | 9 | { |
10 | 10 | |
11 | - protected $intValidator; |
|
11 | + protected $intValidator; |
|
12 | 12 | |
13 | - public function __construct() { |
|
14 | - $this->intValidator = new HTMLPurifier_AttrDef_Integer(); |
|
15 | - } |
|
13 | + public function __construct() { |
|
14 | + $this->intValidator = new HTMLPurifier_AttrDef_Integer(); |
|
15 | + } |
|
16 | 16 | |
17 | - public function validate($value, $config, $context) { |
|
18 | - $value = $this->parseCDATA($value); |
|
19 | - if ($value === 'none') return $value; |
|
20 | - // if we looped this we could support multiple filters |
|
21 | - $function_length = strcspn($value, '('); |
|
22 | - $function = trim(substr($value, 0, $function_length)); |
|
23 | - if ($function !== 'alpha' && |
|
24 | - $function !== 'Alpha' && |
|
25 | - $function !== 'progid:DXImageTransform.Microsoft.Alpha' |
|
26 | - ) return false; |
|
27 | - $cursor = $function_length + 1; |
|
28 | - $parameters_length = strcspn($value, ')', $cursor); |
|
29 | - $parameters = substr($value, $cursor, $parameters_length); |
|
30 | - $params = explode(',', $parameters); |
|
31 | - $ret_params = array(); |
|
32 | - $lookup = array(); |
|
33 | - foreach ($params as $param) { |
|
34 | - list($key, $value) = explode('=', $param); |
|
35 | - $key = trim($key); |
|
36 | - $value = trim($value); |
|
37 | - if (isset($lookup[$key])) continue; |
|
38 | - if ($key !== 'opacity') continue; |
|
39 | - $value = $this->intValidator->validate($value, $config, $context); |
|
40 | - if ($value === false) continue; |
|
41 | - $int = (int) $value; |
|
42 | - if ($int > 100) $value = '100'; |
|
43 | - if ($int < 0) $value = '0'; |
|
44 | - $ret_params[] = "$key=$value"; |
|
45 | - $lookup[$key] = true; |
|
46 | - } |
|
47 | - $ret_parameters = implode(',', $ret_params); |
|
48 | - $ret_function = "$function($ret_parameters)"; |
|
49 | - return $ret_function; |
|
50 | - } |
|
17 | + public function validate($value, $config, $context) { |
|
18 | + $value = $this->parseCDATA($value); |
|
19 | + if ($value === 'none') return $value; |
|
20 | + // if we looped this we could support multiple filters |
|
21 | + $function_length = strcspn($value, '('); |
|
22 | + $function = trim(substr($value, 0, $function_length)); |
|
23 | + if ($function !== 'alpha' && |
|
24 | + $function !== 'Alpha' && |
|
25 | + $function !== 'progid:DXImageTransform.Microsoft.Alpha' |
|
26 | + ) return false; |
|
27 | + $cursor = $function_length + 1; |
|
28 | + $parameters_length = strcspn($value, ')', $cursor); |
|
29 | + $parameters = substr($value, $cursor, $parameters_length); |
|
30 | + $params = explode(',', $parameters); |
|
31 | + $ret_params = array(); |
|
32 | + $lookup = array(); |
|
33 | + foreach ($params as $param) { |
|
34 | + list($key, $value) = explode('=', $param); |
|
35 | + $key = trim($key); |
|
36 | + $value = trim($value); |
|
37 | + if (isset($lookup[$key])) continue; |
|
38 | + if ($key !== 'opacity') continue; |
|
39 | + $value = $this->intValidator->validate($value, $config, $context); |
|
40 | + if ($value === false) continue; |
|
41 | + $int = (int) $value; |
|
42 | + if ($int > 100) $value = '100'; |
|
43 | + if ($int < 0) $value = '0'; |
|
44 | + $ret_params[] = "$key=$value"; |
|
45 | + $lookup[$key] = true; |
|
46 | + } |
|
47 | + $ret_parameters = implode(',', $ret_params); |
|
48 | + $ret_function = "$function($ret_parameters)"; |
|
49 | + return $ret_function; |
|
50 | + } |
|
51 | 51 | |
52 | 52 | } |
53 | 53 |
@@ -16,14 +16,18 @@ discard block |
||
16 | 16 | |
17 | 17 | public function validate($value, $config, $context) { |
18 | 18 | $value = $this->parseCDATA($value); |
19 | - if ($value === 'none') return $value; |
|
19 | + if ($value === 'none') { |
|
20 | + return $value; |
|
21 | + } |
|
20 | 22 | // if we looped this we could support multiple filters |
21 | 23 | $function_length = strcspn($value, '('); |
22 | 24 | $function = trim(substr($value, 0, $function_length)); |
23 | 25 | if ($function !== 'alpha' && |
24 | 26 | $function !== 'Alpha' && |
25 | 27 | $function !== 'progid:DXImageTransform.Microsoft.Alpha' |
26 | - ) return false; |
|
28 | + ) { |
|
29 | + return false; |
|
30 | + } |
|
27 | 31 | $cursor = $function_length + 1; |
28 | 32 | $parameters_length = strcspn($value, ')', $cursor); |
29 | 33 | $parameters = substr($value, $cursor, $parameters_length); |
@@ -34,13 +38,23 @@ discard block |
||
34 | 38 | list($key, $value) = explode('=', $param); |
35 | 39 | $key = trim($key); |
36 | 40 | $value = trim($value); |
37 | - if (isset($lookup[$key])) continue; |
|
38 | - if ($key !== 'opacity') continue; |
|
41 | + if (isset($lookup[$key])) { |
|
42 | + continue; |
|
43 | + } |
|
44 | + if ($key !== 'opacity') { |
|
45 | + continue; |
|
46 | + } |
|
39 | 47 | $value = $this->intValidator->validate($value, $config, $context); |
40 | - if ($value === false) continue; |
|
48 | + if ($value === false) { |
|
49 | + continue; |
|
50 | + } |
|
41 | 51 | $int = (int) $value; |
42 | - if ($int > 100) $value = '100'; |
|
43 | - if ($int < 0) $value = '0'; |
|
52 | + if ($int > 100) { |
|
53 | + $value = '100'; |
|
54 | + } |
|
55 | + if ($int < 0) { |
|
56 | + $value = '0'; |
|
57 | + } |
|
44 | 58 | $ret_params[] = "$key=$value"; |
45 | 59 | $lookup[$key] = true; |
46 | 60 | } |
@@ -6,143 +6,143 @@ |
||
6 | 6 | class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef |
7 | 7 | { |
8 | 8 | |
9 | - /** |
|
10 | - * Local copy of component validators. |
|
11 | - * |
|
12 | - * @note If we moved specific CSS property definitions to their own |
|
13 | - * classes instead of having them be assembled at run time by |
|
14 | - * CSSDefinition, this wouldn't be necessary. We'd instantiate |
|
15 | - * our own copies. |
|
16 | - */ |
|
17 | - protected $info = array(); |
|
9 | + /** |
|
10 | + * Local copy of component validators. |
|
11 | + * |
|
12 | + * @note If we moved specific CSS property definitions to their own |
|
13 | + * classes instead of having them be assembled at run time by |
|
14 | + * CSSDefinition, this wouldn't be necessary. We'd instantiate |
|
15 | + * our own copies. |
|
16 | + */ |
|
17 | + protected $info = array(); |
|
18 | 18 | |
19 | - public function __construct($config) { |
|
20 | - $def = $config->getCSSDefinition(); |
|
21 | - $this->info['font-style'] = $def->info['font-style']; |
|
22 | - $this->info['font-variant'] = $def->info['font-variant']; |
|
23 | - $this->info['font-weight'] = $def->info['font-weight']; |
|
24 | - $this->info['font-size'] = $def->info['font-size']; |
|
25 | - $this->info['line-height'] = $def->info['line-height']; |
|
26 | - $this->info['font-family'] = $def->info['font-family']; |
|
27 | - } |
|
19 | + public function __construct($config) { |
|
20 | + $def = $config->getCSSDefinition(); |
|
21 | + $this->info['font-style'] = $def->info['font-style']; |
|
22 | + $this->info['font-variant'] = $def->info['font-variant']; |
|
23 | + $this->info['font-weight'] = $def->info['font-weight']; |
|
24 | + $this->info['font-size'] = $def->info['font-size']; |
|
25 | + $this->info['line-height'] = $def->info['line-height']; |
|
26 | + $this->info['font-family'] = $def->info['font-family']; |
|
27 | + } |
|
28 | 28 | |
29 | - public function validate($string, $config, $context) { |
|
29 | + public function validate($string, $config, $context) { |
|
30 | 30 | |
31 | - static $system_fonts = array( |
|
32 | - 'caption' => true, |
|
33 | - 'icon' => true, |
|
34 | - 'menu' => true, |
|
35 | - 'message-box' => true, |
|
36 | - 'small-caption' => true, |
|
37 | - 'status-bar' => true |
|
38 | - ); |
|
31 | + static $system_fonts = array( |
|
32 | + 'caption' => true, |
|
33 | + 'icon' => true, |
|
34 | + 'menu' => true, |
|
35 | + 'message-box' => true, |
|
36 | + 'small-caption' => true, |
|
37 | + 'status-bar' => true |
|
38 | + ); |
|
39 | 39 | |
40 | - // regular pre-processing |
|
41 | - $string = $this->parseCDATA($string); |
|
42 | - if ($string === '') return false; |
|
40 | + // regular pre-processing |
|
41 | + $string = $this->parseCDATA($string); |
|
42 | + if ($string === '') return false; |
|
43 | 43 | |
44 | - // check if it's one of the keywords |
|
45 | - $lowercase_string = strtolower($string); |
|
46 | - if (isset($system_fonts[$lowercase_string])) { |
|
47 | - return $lowercase_string; |
|
48 | - } |
|
44 | + // check if it's one of the keywords |
|
45 | + $lowercase_string = strtolower($string); |
|
46 | + if (isset($system_fonts[$lowercase_string])) { |
|
47 | + return $lowercase_string; |
|
48 | + } |
|
49 | 49 | |
50 | - $bits = explode(' ', $string); // bits to process |
|
51 | - $stage = 0; // this indicates what we're looking for |
|
52 | - $caught = array(); // which stage 0 properties have we caught? |
|
53 | - $stage_1 = array('font-style', 'font-variant', 'font-weight'); |
|
54 | - $final = ''; // output |
|
50 | + $bits = explode(' ', $string); // bits to process |
|
51 | + $stage = 0; // this indicates what we're looking for |
|
52 | + $caught = array(); // which stage 0 properties have we caught? |
|
53 | + $stage_1 = array('font-style', 'font-variant', 'font-weight'); |
|
54 | + $final = ''; // output |
|
55 | 55 | |
56 | - for ($i = 0, $size = count($bits); $i < $size; $i++) { |
|
57 | - if ($bits[$i] === '') continue; |
|
58 | - switch ($stage) { |
|
56 | + for ($i = 0, $size = count($bits); $i < $size; $i++) { |
|
57 | + if ($bits[$i] === '') continue; |
|
58 | + switch ($stage) { |
|
59 | 59 | |
60 | - // attempting to catch font-style, font-variant or font-weight |
|
61 | - case 0: |
|
62 | - foreach ($stage_1 as $validator_name) { |
|
63 | - if (isset($caught[$validator_name])) continue; |
|
64 | - $r = $this->info[$validator_name]->validate( |
|
65 | - $bits[$i], $config, $context); |
|
66 | - if ($r !== false) { |
|
67 | - $final .= $r . ' '; |
|
68 | - $caught[$validator_name] = true; |
|
69 | - break; |
|
70 | - } |
|
71 | - } |
|
72 | - // all three caught, continue on |
|
73 | - if (count($caught) >= 3) $stage = 1; |
|
74 | - if ($r !== false) break; |
|
60 | + // attempting to catch font-style, font-variant or font-weight |
|
61 | + case 0: |
|
62 | + foreach ($stage_1 as $validator_name) { |
|
63 | + if (isset($caught[$validator_name])) continue; |
|
64 | + $r = $this->info[$validator_name]->validate( |
|
65 | + $bits[$i], $config, $context); |
|
66 | + if ($r !== false) { |
|
67 | + $final .= $r . ' '; |
|
68 | + $caught[$validator_name] = true; |
|
69 | + break; |
|
70 | + } |
|
71 | + } |
|
72 | + // all three caught, continue on |
|
73 | + if (count($caught) >= 3) $stage = 1; |
|
74 | + if ($r !== false) break; |
|
75 | 75 | |
76 | - // attempting to catch font-size and perhaps line-height |
|
77 | - case 1: |
|
78 | - $found_slash = false; |
|
79 | - if (strpos($bits[$i], '/') !== false) { |
|
80 | - list($font_size, $line_height) = |
|
81 | - explode('/', $bits[$i]); |
|
82 | - if ($line_height === '') { |
|
83 | - // ooh, there's a space after the slash! |
|
84 | - $line_height = false; |
|
85 | - $found_slash = true; |
|
86 | - } |
|
87 | - } else { |
|
88 | - $font_size = $bits[$i]; |
|
89 | - $line_height = false; |
|
90 | - } |
|
91 | - $r = $this->info['font-size']->validate( |
|
92 | - $font_size, $config, $context); |
|
93 | - if ($r !== false) { |
|
94 | - $final .= $r; |
|
95 | - // attempt to catch line-height |
|
96 | - if ($line_height === false) { |
|
97 | - // we need to scroll forward |
|
98 | - for ($j = $i + 1; $j < $size; $j++) { |
|
99 | - if ($bits[$j] === '') continue; |
|
100 | - if ($bits[$j] === '/') { |
|
101 | - if ($found_slash) { |
|
102 | - return false; |
|
103 | - } else { |
|
104 | - $found_slash = true; |
|
105 | - continue; |
|
106 | - } |
|
107 | - } |
|
108 | - $line_height = $bits[$j]; |
|
109 | - break; |
|
110 | - } |
|
111 | - } else { |
|
112 | - // slash already found |
|
113 | - $found_slash = true; |
|
114 | - $j = $i; |
|
115 | - } |
|
116 | - if ($found_slash) { |
|
117 | - $i = $j; |
|
118 | - $r = $this->info['line-height']->validate( |
|
119 | - $line_height, $config, $context); |
|
120 | - if ($r !== false) { |
|
121 | - $final .= '/' . $r; |
|
122 | - } |
|
123 | - } |
|
124 | - $final .= ' '; |
|
125 | - $stage = 2; |
|
126 | - break; |
|
127 | - } |
|
128 | - return false; |
|
76 | + // attempting to catch font-size and perhaps line-height |
|
77 | + case 1: |
|
78 | + $found_slash = false; |
|
79 | + if (strpos($bits[$i], '/') !== false) { |
|
80 | + list($font_size, $line_height) = |
|
81 | + explode('/', $bits[$i]); |
|
82 | + if ($line_height === '') { |
|
83 | + // ooh, there's a space after the slash! |
|
84 | + $line_height = false; |
|
85 | + $found_slash = true; |
|
86 | + } |
|
87 | + } else { |
|
88 | + $font_size = $bits[$i]; |
|
89 | + $line_height = false; |
|
90 | + } |
|
91 | + $r = $this->info['font-size']->validate( |
|
92 | + $font_size, $config, $context); |
|
93 | + if ($r !== false) { |
|
94 | + $final .= $r; |
|
95 | + // attempt to catch line-height |
|
96 | + if ($line_height === false) { |
|
97 | + // we need to scroll forward |
|
98 | + for ($j = $i + 1; $j < $size; $j++) { |
|
99 | + if ($bits[$j] === '') continue; |
|
100 | + if ($bits[$j] === '/') { |
|
101 | + if ($found_slash) { |
|
102 | + return false; |
|
103 | + } else { |
|
104 | + $found_slash = true; |
|
105 | + continue; |
|
106 | + } |
|
107 | + } |
|
108 | + $line_height = $bits[$j]; |
|
109 | + break; |
|
110 | + } |
|
111 | + } else { |
|
112 | + // slash already found |
|
113 | + $found_slash = true; |
|
114 | + $j = $i; |
|
115 | + } |
|
116 | + if ($found_slash) { |
|
117 | + $i = $j; |
|
118 | + $r = $this->info['line-height']->validate( |
|
119 | + $line_height, $config, $context); |
|
120 | + if ($r !== false) { |
|
121 | + $final .= '/' . $r; |
|
122 | + } |
|
123 | + } |
|
124 | + $final .= ' '; |
|
125 | + $stage = 2; |
|
126 | + break; |
|
127 | + } |
|
128 | + return false; |
|
129 | 129 | |
130 | - // attempting to catch font-family |
|
131 | - case 2: |
|
132 | - $font_family = |
|
133 | - implode(' ', array_slice($bits, $i, $size - $i)); |
|
134 | - $r = $this->info['font-family']->validate( |
|
135 | - $font_family, $config, $context); |
|
136 | - if ($r !== false) { |
|
137 | - $final .= $r . ' '; |
|
138 | - // processing completed successfully |
|
139 | - return rtrim($final); |
|
140 | - } |
|
141 | - return false; |
|
142 | - } |
|
143 | - } |
|
144 | - return false; |
|
145 | - } |
|
130 | + // attempting to catch font-family |
|
131 | + case 2: |
|
132 | + $font_family = |
|
133 | + implode(' ', array_slice($bits, $i, $size - $i)); |
|
134 | + $r = $this->info['font-family']->validate( |
|
135 | + $font_family, $config, $context); |
|
136 | + if ($r !== false) { |
|
137 | + $final .= $r . ' '; |
|
138 | + // processing completed successfully |
|
139 | + return rtrim($final); |
|
140 | + } |
|
141 | + return false; |
|
142 | + } |
|
143 | + } |
|
144 | + return false; |
|
145 | + } |
|
146 | 146 | |
147 | 147 | } |
148 | 148 |
@@ -39,7 +39,9 @@ discard block |
||
39 | 39 | |
40 | 40 | // regular pre-processing |
41 | 41 | $string = $this->parseCDATA($string); |
42 | - if ($string === '') return false; |
|
42 | + if ($string === '') { |
|
43 | + return false; |
|
44 | + } |
|
43 | 45 | |
44 | 46 | // check if it's one of the keywords |
45 | 47 | $lowercase_string = strtolower($string); |
@@ -54,13 +56,17 @@ discard block |
||
54 | 56 | $final = ''; // output |
55 | 57 | |
56 | 58 | for ($i = 0, $size = count($bits); $i < $size; $i++) { |
57 | - if ($bits[$i] === '') continue; |
|
59 | + if ($bits[$i] === '') { |
|
60 | + continue; |
|
61 | + } |
|
58 | 62 | switch ($stage) { |
59 | 63 | |
60 | 64 | // attempting to catch font-style, font-variant or font-weight |
61 | 65 | case 0: |
62 | 66 | foreach ($stage_1 as $validator_name) { |
63 | - if (isset($caught[$validator_name])) continue; |
|
67 | + if (isset($caught[$validator_name])) { |
|
68 | + continue; |
|
69 | + } |
|
64 | 70 | $r = $this->info[$validator_name]->validate( |
65 | 71 | $bits[$i], $config, $context); |
66 | 72 | if ($r !== false) { |
@@ -70,8 +76,12 @@ discard block |
||
70 | 76 | } |
71 | 77 | } |
72 | 78 | // all three caught, continue on |
73 | - if (count($caught) >= 3) $stage = 1; |
|
74 | - if ($r !== false) break; |
|
79 | + if (count($caught) >= 3) { |
|
80 | + $stage = 1; |
|
81 | + } |
|
82 | + if ($r !== false) { |
|
83 | + break; |
|
84 | + } |
|
75 | 85 | |
76 | 86 | // attempting to catch font-size and perhaps line-height |
77 | 87 | case 1: |
@@ -96,7 +106,9 @@ discard block |
||
96 | 106 | if ($line_height === false) { |
97 | 107 | // we need to scroll forward |
98 | 108 | for ($j = $i + 1; $j < $size; $j++) { |
99 | - if ($bits[$j] === '') continue; |
|
109 | + if ($bits[$j] === '') { |
|
110 | + continue; |
|
111 | + } |
|
100 | 112 | if ($bits[$j] === '/') { |
101 | 113 | if ($found_slash) { |
102 | 114 | return false; |
@@ -64,7 +64,7 @@ discard block |
||
64 | 64 | $r = $this->info[$validator_name]->validate( |
65 | 65 | $bits[$i], $config, $context); |
66 | 66 | if ($r !== false) { |
67 | - $final .= $r . ' '; |
|
67 | + $final .= $r.' '; |
|
68 | 68 | $caught[$validator_name] = true; |
69 | 69 | break; |
70 | 70 | } |
@@ -118,7 +118,7 @@ discard block |
||
118 | 118 | $r = $this->info['line-height']->validate( |
119 | 119 | $line_height, $config, $context); |
120 | 120 | if ($r !== false) { |
121 | - $final .= '/' . $r; |
|
121 | + $final .= '/'.$r; |
|
122 | 122 | } |
123 | 123 | } |
124 | 124 | $final .= ' '; |
@@ -134,7 +134,7 @@ discard block |
||
134 | 134 | $r = $this->info['font-family']->validate( |
135 | 135 | $font_family, $config, $context); |
136 | 136 | if ($r !== false) { |
137 | - $final .= $r . ' '; |
|
137 | + $final .= $r.' '; |
|
138 | 138 | // processing completed successfully |
139 | 139 | return rtrim($final); |
140 | 140 | } |
@@ -6,22 +6,22 @@ discard block |
||
6 | 6 | class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef |
7 | 7 | { |
8 | 8 | |
9 | - protected $mask = null; |
|
10 | - |
|
11 | - public function __construct() { |
|
12 | - $this->mask = '- '; |
|
13 | - for ($c = 'a'; $c <= 'z'; $c++) $this->mask .= $c; |
|
14 | - for ($c = 'A'; $c <= 'Z'; $c++) $this->mask .= $c; |
|
15 | - for ($c = '0'; $c <= '9'; $c++) $this->mask .= $c; // cast-y, but should be fine |
|
16 | - // special bytes used by UTF-8 |
|
17 | - for ($i = 0x80; $i <= 0xFF; $i++) { |
|
18 | - // We don't bother excluding invalid bytes in this range, |
|
19 | - // because the our restriction of well-formed UTF-8 will |
|
20 | - // prevent these from ever occurring. |
|
21 | - $this->mask .= chr($i); |
|
22 | - } |
|
23 | - |
|
24 | - /* |
|
9 | + protected $mask = null; |
|
10 | + |
|
11 | + public function __construct() { |
|
12 | + $this->mask = '- '; |
|
13 | + for ($c = 'a'; $c <= 'z'; $c++) $this->mask .= $c; |
|
14 | + for ($c = 'A'; $c <= 'Z'; $c++) $this->mask .= $c; |
|
15 | + for ($c = '0'; $c <= '9'; $c++) $this->mask .= $c; // cast-y, but should be fine |
|
16 | + // special bytes used by UTF-8 |
|
17 | + for ($i = 0x80; $i <= 0xFF; $i++) { |
|
18 | + // We don't bother excluding invalid bytes in this range, |
|
19 | + // because the our restriction of well-formed UTF-8 will |
|
20 | + // prevent these from ever occurring. |
|
21 | + $this->mask .= chr($i); |
|
22 | + } |
|
23 | + |
|
24 | + /* |
|
25 | 25 | PHP's internal strcspn implementation is |
26 | 26 | O(length of string * length of mask), making it inefficient |
27 | 27 | for large masks. However, it's still faster than |
@@ -36,161 +36,161 @@ discard block |
||
36 | 36 | c = *++p; |
37 | 37 | } |
38 | 38 | */ |
39 | - // possible optimization: invert the mask. |
|
40 | - } |
|
41 | - |
|
42 | - public function validate($string, $config, $context) { |
|
43 | - static $generic_names = array( |
|
44 | - 'serif' => true, |
|
45 | - 'sans-serif' => true, |
|
46 | - 'monospace' => true, |
|
47 | - 'fantasy' => true, |
|
48 | - 'cursive' => true |
|
49 | - ); |
|
50 | - $allowed_fonts = $config->get('CSS.AllowedFonts'); |
|
51 | - |
|
52 | - // assume that no font names contain commas in them |
|
53 | - $fonts = explode(',', $string); |
|
54 | - $final = ''; |
|
55 | - foreach($fonts as $font) { |
|
56 | - $font = trim($font); |
|
57 | - if ($font === '') continue; |
|
58 | - // match a generic name |
|
59 | - if (isset($generic_names[$font])) { |
|
60 | - if ($allowed_fonts === null || isset($allowed_fonts[$font])) { |
|
61 | - $final .= $font . ', '; |
|
62 | - } |
|
63 | - continue; |
|
64 | - } |
|
65 | - // match a quoted name |
|
66 | - if ($font[0] === '"' || $font[0] === "'") { |
|
67 | - $length = strlen($font); |
|
68 | - if ($length <= 2) continue; |
|
69 | - $quote = $font[0]; |
|
70 | - if ($font[$length - 1] !== $quote) continue; |
|
71 | - $font = substr($font, 1, $length - 2); |
|
72 | - } |
|
73 | - |
|
74 | - $font = $this->expandCSSEscape($font); |
|
75 | - |
|
76 | - // $font is a pure representation of the font name |
|
77 | - |
|
78 | - if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) { |
|
79 | - continue; |
|
80 | - } |
|
81 | - |
|
82 | - if (ctype_alnum($font) && $font !== '') { |
|
83 | - // very simple font, allow it in unharmed |
|
84 | - $final .= $font . ', '; |
|
85 | - continue; |
|
86 | - } |
|
87 | - |
|
88 | - // bugger out on whitespace. form feed (0C) really |
|
89 | - // shouldn't show up regardless |
|
90 | - $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font); |
|
91 | - |
|
92 | - // Here, there are various classes of characters which need |
|
93 | - // to be treated differently: |
|
94 | - // - Alphanumeric characters are essentially safe. We |
|
95 | - // handled these above. |
|
96 | - // - Spaces require quoting, though most parsers will do |
|
97 | - // the right thing if there aren't any characters that |
|
98 | - // can be misinterpreted |
|
99 | - // - Dashes rarely occur, but they fairly unproblematic |
|
100 | - // for parsing/rendering purposes. |
|
101 | - // The above characters cover the majority of Western font |
|
102 | - // names. |
|
103 | - // - Arbitrary Unicode characters not in ASCII. Because |
|
104 | - // most parsers give little thought to Unicode, treatment |
|
105 | - // of these codepoints is basically uniform, even for |
|
106 | - // punctuation-like codepoints. These characters can |
|
107 | - // show up in non-Western pages and are supported by most |
|
108 | - // major browsers, for example: "MS 明朝" is a |
|
109 | - // legitimate font-name |
|
110 | - // <http://ja.wikipedia.org/wiki/MS_明朝>. See |
|
111 | - // the CSS3 spec for more examples: |
|
112 | - // <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png> |
|
113 | - // You can see live samples of these on the Internet: |
|
114 | - // <http://www.google.co.jp/search?q=font-family+MS+明朝|ゴシック> |
|
115 | - // However, most of these fonts have ASCII equivalents: |
|
116 | - // for example, 'MS Mincho', and it's considered |
|
117 | - // professional to use ASCII font names instead of |
|
118 | - // Unicode font names. Thanks Takeshi Terada for |
|
119 | - // providing this information. |
|
120 | - // The following characters, to my knowledge, have not been |
|
121 | - // used to name font names. |
|
122 | - // - Single quote. While theoretically you might find a |
|
123 | - // font name that has a single quote in its name (serving |
|
124 | - // as an apostrophe, e.g. Dave's Scribble), I haven't |
|
125 | - // been able to find any actual examples of this. |
|
126 | - // Internet Explorer's cssText translation (which I |
|
127 | - // believe is invoked by innerHTML) normalizes any |
|
128 | - // quoting to single quotes, and fails to escape single |
|
129 | - // quotes. (Note that this is not IE's behavior for all |
|
130 | - // CSS properties, just some sort of special casing for |
|
131 | - // font-family). So a single quote *cannot* be used |
|
132 | - // safely in the font-family context if there will be an |
|
133 | - // innerHTML/cssText translation. Note that Firefox 3.x |
|
134 | - // does this too. |
|
135 | - // - Double quote. In IE, these get normalized to |
|
136 | - // single-quotes, no matter what the encoding. (Fun |
|
137 | - // fact, in IE8, the 'content' CSS property gained |
|
138 | - // support, where they special cased to preserve encoded |
|
139 | - // double quotes, but still translate unadorned double |
|
140 | - // quotes into single quotes.) So, because their |
|
141 | - // fixpoint behavior is identical to single quotes, they |
|
142 | - // cannot be allowed either. Firefox 3.x displays |
|
143 | - // single-quote style behavior. |
|
144 | - // - Backslashes are reduced by one (so \\ -> \) every |
|
145 | - // iteration, so they cannot be used safely. This shows |
|
146 | - // up in IE7, IE8 and FF3 |
|
147 | - // - Semicolons, commas and backticks are handled properly. |
|
148 | - // - The rest of the ASCII punctuation is handled properly. |
|
149 | - // We haven't checked what browsers do to unadorned |
|
150 | - // versions, but this is not important as long as the |
|
151 | - // browser doesn't /remove/ surrounding quotes (as IE does |
|
152 | - // for HTML). |
|
153 | - // |
|
154 | - // With these results in hand, we conclude that there are |
|
155 | - // various levels of safety: |
|
156 | - // - Paranoid: alphanumeric, spaces and dashes(?) |
|
157 | - // - International: Paranoid + non-ASCII Unicode |
|
158 | - // - Edgy: Everything except quotes, backslashes |
|
159 | - // - NoJS: Standards compliance, e.g. sod IE. Note that |
|
160 | - // with some judicious character escaping (since certain |
|
161 | - // types of escaping doesn't work) this is theoretically |
|
162 | - // OK as long as innerHTML/cssText is not called. |
|
163 | - // We believe that international is a reasonable default |
|
164 | - // (that we will implement now), and once we do more |
|
165 | - // extensive research, we may feel comfortable with dropping |
|
166 | - // it down to edgy. |
|
167 | - |
|
168 | - // Edgy: alphanumeric, spaces, dashes and Unicode. Use of |
|
169 | - // str(c)spn assumes that the string was already well formed |
|
170 | - // Unicode (which of course it is). |
|
171 | - if (strspn($font, $this->mask) !== strlen($font)) { |
|
172 | - continue; |
|
173 | - } |
|
174 | - |
|
175 | - // Historical: |
|
176 | - // In the absence of innerHTML/cssText, these ugly |
|
177 | - // transforms don't pose a security risk (as \\ and \" |
|
178 | - // might--these escapes are not supported by most browsers). |
|
179 | - // We could try to be clever and use single-quote wrapping |
|
180 | - // when there is a double quote present, but I have choosen |
|
181 | - // not to implement that. (NOTE: you can reduce the amount |
|
182 | - // of escapes by one depending on what quoting style you use) |
|
183 | - // $font = str_replace('\\', '\\5C ', $font); |
|
184 | - // $font = str_replace('"', '\\22 ', $font); |
|
185 | - // $font = str_replace("'", '\\27 ', $font); |
|
186 | - |
|
187 | - // font possibly with spaces, requires quoting |
|
188 | - $final .= "'$font', "; |
|
189 | - } |
|
190 | - $final = rtrim($final, ', '); |
|
191 | - if ($final === '') return false; |
|
192 | - return $final; |
|
193 | - } |
|
39 | + // possible optimization: invert the mask. |
|
40 | + } |
|
41 | + |
|
42 | + public function validate($string, $config, $context) { |
|
43 | + static $generic_names = array( |
|
44 | + 'serif' => true, |
|
45 | + 'sans-serif' => true, |
|
46 | + 'monospace' => true, |
|
47 | + 'fantasy' => true, |
|
48 | + 'cursive' => true |
|
49 | + ); |
|
50 | + $allowed_fonts = $config->get('CSS.AllowedFonts'); |
|
51 | + |
|
52 | + // assume that no font names contain commas in them |
|
53 | + $fonts = explode(',', $string); |
|
54 | + $final = ''; |
|
55 | + foreach($fonts as $font) { |
|
56 | + $font = trim($font); |
|
57 | + if ($font === '') continue; |
|
58 | + // match a generic name |
|
59 | + if (isset($generic_names[$font])) { |
|
60 | + if ($allowed_fonts === null || isset($allowed_fonts[$font])) { |
|
61 | + $final .= $font . ', '; |
|
62 | + } |
|
63 | + continue; |
|
64 | + } |
|
65 | + // match a quoted name |
|
66 | + if ($font[0] === '"' || $font[0] === "'") { |
|
67 | + $length = strlen($font); |
|
68 | + if ($length <= 2) continue; |
|
69 | + $quote = $font[0]; |
|
70 | + if ($font[$length - 1] !== $quote) continue; |
|
71 | + $font = substr($font, 1, $length - 2); |
|
72 | + } |
|
73 | + |
|
74 | + $font = $this->expandCSSEscape($font); |
|
75 | + |
|
76 | + // $font is a pure representation of the font name |
|
77 | + |
|
78 | + if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) { |
|
79 | + continue; |
|
80 | + } |
|
81 | + |
|
82 | + if (ctype_alnum($font) && $font !== '') { |
|
83 | + // very simple font, allow it in unharmed |
|
84 | + $final .= $font . ', '; |
|
85 | + continue; |
|
86 | + } |
|
87 | + |
|
88 | + // bugger out on whitespace. form feed (0C) really |
|
89 | + // shouldn't show up regardless |
|
90 | + $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font); |
|
91 | + |
|
92 | + // Here, there are various classes of characters which need |
|
93 | + // to be treated differently: |
|
94 | + // - Alphanumeric characters are essentially safe. We |
|
95 | + // handled these above. |
|
96 | + // - Spaces require quoting, though most parsers will do |
|
97 | + // the right thing if there aren't any characters that |
|
98 | + // can be misinterpreted |
|
99 | + // - Dashes rarely occur, but they fairly unproblematic |
|
100 | + // for parsing/rendering purposes. |
|
101 | + // The above characters cover the majority of Western font |
|
102 | + // names. |
|
103 | + // - Arbitrary Unicode characters not in ASCII. Because |
|
104 | + // most parsers give little thought to Unicode, treatment |
|
105 | + // of these codepoints is basically uniform, even for |
|
106 | + // punctuation-like codepoints. These characters can |
|
107 | + // show up in non-Western pages and are supported by most |
|
108 | + // major browsers, for example: "MS 明朝" is a |
|
109 | + // legitimate font-name |
|
110 | + // <http://ja.wikipedia.org/wiki/MS_明朝>. See |
|
111 | + // the CSS3 spec for more examples: |
|
112 | + // <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png> |
|
113 | + // You can see live samples of these on the Internet: |
|
114 | + // <http://www.google.co.jp/search?q=font-family+MS+明朝|ゴシック> |
|
115 | + // However, most of these fonts have ASCII equivalents: |
|
116 | + // for example, 'MS Mincho', and it's considered |
|
117 | + // professional to use ASCII font names instead of |
|
118 | + // Unicode font names. Thanks Takeshi Terada for |
|
119 | + // providing this information. |
|
120 | + // The following characters, to my knowledge, have not been |
|
121 | + // used to name font names. |
|
122 | + // - Single quote. While theoretically you might find a |
|
123 | + // font name that has a single quote in its name (serving |
|
124 | + // as an apostrophe, e.g. Dave's Scribble), I haven't |
|
125 | + // been able to find any actual examples of this. |
|
126 | + // Internet Explorer's cssText translation (which I |
|
127 | + // believe is invoked by innerHTML) normalizes any |
|
128 | + // quoting to single quotes, and fails to escape single |
|
129 | + // quotes. (Note that this is not IE's behavior for all |
|
130 | + // CSS properties, just some sort of special casing for |
|
131 | + // font-family). So a single quote *cannot* be used |
|
132 | + // safely in the font-family context if there will be an |
|
133 | + // innerHTML/cssText translation. Note that Firefox 3.x |
|
134 | + // does this too. |
|
135 | + // - Double quote. In IE, these get normalized to |
|
136 | + // single-quotes, no matter what the encoding. (Fun |
|
137 | + // fact, in IE8, the 'content' CSS property gained |
|
138 | + // support, where they special cased to preserve encoded |
|
139 | + // double quotes, but still translate unadorned double |
|
140 | + // quotes into single quotes.) So, because their |
|
141 | + // fixpoint behavior is identical to single quotes, they |
|
142 | + // cannot be allowed either. Firefox 3.x displays |
|
143 | + // single-quote style behavior. |
|
144 | + // - Backslashes are reduced by one (so \\ -> \) every |
|
145 | + // iteration, so they cannot be used safely. This shows |
|
146 | + // up in IE7, IE8 and FF3 |
|
147 | + // - Semicolons, commas and backticks are handled properly. |
|
148 | + // - The rest of the ASCII punctuation is handled properly. |
|
149 | + // We haven't checked what browsers do to unadorned |
|
150 | + // versions, but this is not important as long as the |
|
151 | + // browser doesn't /remove/ surrounding quotes (as IE does |
|
152 | + // for HTML). |
|
153 | + // |
|
154 | + // With these results in hand, we conclude that there are |
|
155 | + // various levels of safety: |
|
156 | + // - Paranoid: alphanumeric, spaces and dashes(?) |
|
157 | + // - International: Paranoid + non-ASCII Unicode |
|
158 | + // - Edgy: Everything except quotes, backslashes |
|
159 | + // - NoJS: Standards compliance, e.g. sod IE. Note that |
|
160 | + // with some judicious character escaping (since certain |
|
161 | + // types of escaping doesn't work) this is theoretically |
|
162 | + // OK as long as innerHTML/cssText is not called. |
|
163 | + // We believe that international is a reasonable default |
|
164 | + // (that we will implement now), and once we do more |
|
165 | + // extensive research, we may feel comfortable with dropping |
|
166 | + // it down to edgy. |
|
167 | + |
|
168 | + // Edgy: alphanumeric, spaces, dashes and Unicode. Use of |
|
169 | + // str(c)spn assumes that the string was already well formed |
|
170 | + // Unicode (which of course it is). |
|
171 | + if (strspn($font, $this->mask) !== strlen($font)) { |
|
172 | + continue; |
|
173 | + } |
|
174 | + |
|
175 | + // Historical: |
|
176 | + // In the absence of innerHTML/cssText, these ugly |
|
177 | + // transforms don't pose a security risk (as \\ and \" |
|
178 | + // might--these escapes are not supported by most browsers). |
|
179 | + // We could try to be clever and use single-quote wrapping |
|
180 | + // when there is a double quote present, but I have choosen |
|
181 | + // not to implement that. (NOTE: you can reduce the amount |
|
182 | + // of escapes by one depending on what quoting style you use) |
|
183 | + // $font = str_replace('\\', '\\5C ', $font); |
|
184 | + // $font = str_replace('"', '\\22 ', $font); |
|
185 | + // $font = str_replace("'", '\\27 ', $font); |
|
186 | + |
|
187 | + // font possibly with spaces, requires quoting |
|
188 | + $final .= "'$font', "; |
|
189 | + } |
|
190 | + $final = rtrim($final, ', '); |
|
191 | + if ($final === '') return false; |
|
192 | + return $final; |
|
193 | + } |
|
194 | 194 | |
195 | 195 | } |
196 | 196 |
@@ -10,9 +10,16 @@ discard block |
||
10 | 10 | |
11 | 11 | public function __construct() { |
12 | 12 | $this->mask = '- '; |
13 | - for ($c = 'a'; $c <= 'z'; $c++) $this->mask .= $c; |
|
14 | - for ($c = 'A'; $c <= 'Z'; $c++) $this->mask .= $c; |
|
15 | - for ($c = '0'; $c <= '9'; $c++) $this->mask .= $c; // cast-y, but should be fine |
|
13 | + for ($c = 'a'; $c <= 'z'; $c++) { |
|
14 | + $this->mask .= $c; |
|
15 | + } |
|
16 | + for ($c = 'A'; $c <= 'Z'; $c++) { |
|
17 | + $this->mask .= $c; |
|
18 | + } |
|
19 | + for ($c = '0'; $c <= '9'; $c++) { |
|
20 | + $this->mask .= $c; |
|
21 | + } |
|
22 | + // cast-y, but should be fine |
|
16 | 23 | // special bytes used by UTF-8 |
17 | 24 | for ($i = 0x80; $i <= 0xFF; $i++) { |
18 | 25 | // We don't bother excluding invalid bytes in this range, |
@@ -54,7 +61,9 @@ discard block |
||
54 | 61 | $final = ''; |
55 | 62 | foreach($fonts as $font) { |
56 | 63 | $font = trim($font); |
57 | - if ($font === '') continue; |
|
64 | + if ($font === '') { |
|
65 | + continue; |
|
66 | + } |
|
58 | 67 | // match a generic name |
59 | 68 | if (isset($generic_names[$font])) { |
60 | 69 | if ($allowed_fonts === null || isset($allowed_fonts[$font])) { |
@@ -65,9 +74,13 @@ discard block |
||
65 | 74 | // match a quoted name |
66 | 75 | if ($font[0] === '"' || $font[0] === "'") { |
67 | 76 | $length = strlen($font); |
68 | - if ($length <= 2) continue; |
|
77 | + if ($length <= 2) { |
|
78 | + continue; |
|
79 | + } |
|
69 | 80 | $quote = $font[0]; |
70 | - if ($font[$length - 1] !== $quote) continue; |
|
81 | + if ($font[$length - 1] !== $quote) { |
|
82 | + continue; |
|
83 | + } |
|
71 | 84 | $font = substr($font, 1, $length - 2); |
72 | 85 | } |
73 | 86 | |
@@ -188,7 +201,9 @@ discard block |
||
188 | 201 | $final .= "'$font', "; |
189 | 202 | } |
190 | 203 | $final = rtrim($final, ', '); |
191 | - if ($final === '') return false; |
|
204 | + if ($final === '') { |
|
205 | + return false; |
|
206 | + } |
|
192 | 207 | return $final; |
193 | 208 | } |
194 | 209 |
@@ -52,13 +52,13 @@ discard block |
||
52 | 52 | // assume that no font names contain commas in them |
53 | 53 | $fonts = explode(',', $string); |
54 | 54 | $final = ''; |
55 | - foreach($fonts as $font) { |
|
55 | + foreach ($fonts as $font) { |
|
56 | 56 | $font = trim($font); |
57 | 57 | if ($font === '') continue; |
58 | 58 | // match a generic name |
59 | 59 | if (isset($generic_names[$font])) { |
60 | 60 | if ($allowed_fonts === null || isset($allowed_fonts[$font])) { |
61 | - $final .= $font . ', '; |
|
61 | + $final .= $font.', '; |
|
62 | 62 | } |
63 | 63 | continue; |
64 | 64 | } |
@@ -81,7 +81,7 @@ discard block |
||
81 | 81 | |
82 | 82 | if (ctype_alnum($font) && $font !== '') { |
83 | 83 | // very simple font, allow it in unharmed |
84 | - $final .= $font . ', '; |
|
84 | + $final .= $font.', '; |
|
85 | 85 | continue; |
86 | 86 | } |
87 | 87 |
@@ -6,18 +6,18 @@ |
||
6 | 6 | class HTMLPurifier_AttrDef_CSS_Ident extends HTMLPurifier_AttrDef |
7 | 7 | { |
8 | 8 | |
9 | - public function validate($string, $config, $context) { |
|
9 | + public function validate($string, $config, $context) { |
|
10 | 10 | |
11 | - $string = trim($string); |
|
11 | + $string = trim($string); |
|
12 | 12 | |
13 | - // early abort: '' and '0' (strings that convert to false) are invalid |
|
14 | - if (!$string) return false; |
|
13 | + // early abort: '' and '0' (strings that convert to false) are invalid |
|
14 | + if (!$string) return false; |
|
15 | 15 | |
16 | - $pattern = '/^(-?[A-Za-z_][A-Za-z_\-0-9]*)$/'; |
|
17 | - if (!preg_match($pattern, $string)) return false; |
|
18 | - return $string; |
|
16 | + $pattern = '/^(-?[A-Za-z_][A-Za-z_\-0-9]*)$/'; |
|
17 | + if (!preg_match($pattern, $string)) return false; |
|
18 | + return $string; |
|
19 | 19 | |
20 | - } |
|
20 | + } |
|
21 | 21 | |
22 | 22 | } |
23 | 23 |
@@ -11,10 +11,14 @@ |
||
11 | 11 | $string = trim($string); |
12 | 12 | |
13 | 13 | // early abort: '' and '0' (strings that convert to false) are invalid |
14 | - if (!$string) return false; |
|
14 | + if (!$string) { |
|
15 | + return false; |
|
16 | + } |
|
15 | 17 | |
16 | 18 | $pattern = '/^(-?[A-Za-z_][A-Za-z_\-0-9]*)$/'; |
17 | - if (!preg_match($pattern, $string)) return false; |
|
19 | + if (!preg_match($pattern, $string)) { |
|
20 | + return false; |
|
21 | + } |
|
18 | 22 | return $string; |
19 | 23 | |
20 | 24 | } |
@@ -5,36 +5,36 @@ |
||
5 | 5 | */ |
6 | 6 | class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef |
7 | 7 | { |
8 | - public $def, $allow; |
|
8 | + public $def, $allow; |
|
9 | 9 | |
10 | - /** |
|
11 | - * @param $def Definition to wrap |
|
12 | - * @param $allow Whether or not to allow !important |
|
13 | - */ |
|
14 | - public function __construct($def, $allow = false) { |
|
15 | - $this->def = $def; |
|
16 | - $this->allow = $allow; |
|
17 | - } |
|
18 | - /** |
|
19 | - * Intercepts and removes !important if necessary |
|
20 | - */ |
|
21 | - public function validate($string, $config, $context) { |
|
22 | - // test for ! and important tokens |
|
23 | - $string = trim($string); |
|
24 | - $is_important = false; |
|
25 | - // :TODO: optimization: test directly for !important and ! important |
|
26 | - if (strlen($string) >= 9 && substr($string, -9) === 'important') { |
|
27 | - $temp = rtrim(substr($string, 0, -9)); |
|
28 | - // use a temp, because we might want to restore important |
|
29 | - if (strlen($temp) >= 1 && substr($temp, -1) === '!') { |
|
30 | - $string = rtrim(substr($temp, 0, -1)); |
|
31 | - $is_important = true; |
|
32 | - } |
|
33 | - } |
|
34 | - $string = $this->def->validate($string, $config, $context); |
|
35 | - if ($this->allow && $is_important) $string .= ' !important'; |
|
36 | - return $string; |
|
37 | - } |
|
10 | + /** |
|
11 | + * @param $def Definition to wrap |
|
12 | + * @param $allow Whether or not to allow !important |
|
13 | + */ |
|
14 | + public function __construct($def, $allow = false) { |
|
15 | + $this->def = $def; |
|
16 | + $this->allow = $allow; |
|
17 | + } |
|
18 | + /** |
|
19 | + * Intercepts and removes !important if necessary |
|
20 | + */ |
|
21 | + public function validate($string, $config, $context) { |
|
22 | + // test for ! and important tokens |
|
23 | + $string = trim($string); |
|
24 | + $is_important = false; |
|
25 | + // :TODO: optimization: test directly for !important and ! important |
|
26 | + if (strlen($string) >= 9 && substr($string, -9) === 'important') { |
|
27 | + $temp = rtrim(substr($string, 0, -9)); |
|
28 | + // use a temp, because we might want to restore important |
|
29 | + if (strlen($temp) >= 1 && substr($temp, -1) === '!') { |
|
30 | + $string = rtrim(substr($temp, 0, -1)); |
|
31 | + $is_important = true; |
|
32 | + } |
|
33 | + } |
|
34 | + $string = $this->def->validate($string, $config, $context); |
|
35 | + if ($this->allow && $is_important) $string .= ' !important'; |
|
36 | + return $string; |
|
37 | + } |
|
38 | 38 | } |
39 | 39 | |
40 | 40 | // vim: et sw=4 sts=4 |
@@ -32,7 +32,9 @@ |
||
32 | 32 | } |
33 | 33 | } |
34 | 34 | $string = $this->def->validate($string, $config, $context); |
35 | - if ($this->allow && $is_important) $string .= ' !important'; |
|
35 | + if ($this->allow && $is_important) { |
|
36 | + $string .= ' !important'; |
|
37 | + } |
|
36 | 38 | return $string; |
37 | 39 | } |
38 | 40 | } |
@@ -7,71 +7,71 @@ |
||
7 | 7 | class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef |
8 | 8 | { |
9 | 9 | |
10 | - /** |
|
11 | - * Local copy of component validators. |
|
12 | - * @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl. |
|
13 | - */ |
|
14 | - protected $info; |
|
15 | - |
|
16 | - public function __construct($config) { |
|
17 | - $def = $config->getCSSDefinition(); |
|
18 | - $this->info['list-style-type'] = $def->info['list-style-type']; |
|
19 | - $this->info['list-style-position'] = $def->info['list-style-position']; |
|
20 | - $this->info['list-style-image'] = $def->info['list-style-image']; |
|
21 | - } |
|
22 | - |
|
23 | - public function validate($string, $config, $context) { |
|
24 | - |
|
25 | - // regular pre-processing |
|
26 | - $string = $this->parseCDATA($string); |
|
27 | - if ($string === '') return false; |
|
28 | - |
|
29 | - // assumes URI doesn't have spaces in it |
|
30 | - $bits = explode(' ', strtolower($string)); // bits to process |
|
31 | - |
|
32 | - $caught = array(); |
|
33 | - $caught['type'] = false; |
|
34 | - $caught['position'] = false; |
|
35 | - $caught['image'] = false; |
|
36 | - |
|
37 | - $i = 0; // number of catches |
|
38 | - $none = false; |
|
39 | - |
|
40 | - foreach ($bits as $bit) { |
|
41 | - if ($i >= 3) return; // optimization bit |
|
42 | - if ($bit === '') continue; |
|
43 | - foreach ($caught as $key => $status) { |
|
44 | - if ($status !== false) continue; |
|
45 | - $r = $this->info['list-style-' . $key]->validate($bit, $config, $context); |
|
46 | - if ($r === false) continue; |
|
47 | - if ($r === 'none') { |
|
48 | - if ($none) continue; |
|
49 | - else $none = true; |
|
50 | - if ($key == 'image') continue; |
|
51 | - } |
|
52 | - $caught[$key] = $r; |
|
53 | - $i++; |
|
54 | - break; |
|
55 | - } |
|
56 | - } |
|
57 | - |
|
58 | - if (!$i) return false; |
|
59 | - |
|
60 | - $ret = array(); |
|
61 | - |
|
62 | - // construct type |
|
63 | - if ($caught['type']) $ret[] = $caught['type']; |
|
64 | - |
|
65 | - // construct image |
|
66 | - if ($caught['image']) $ret[] = $caught['image']; |
|
67 | - |
|
68 | - // construct position |
|
69 | - if ($caught['position']) $ret[] = $caught['position']; |
|
70 | - |
|
71 | - if (empty($ret)) return false; |
|
72 | - return implode(' ', $ret); |
|
73 | - |
|
74 | - } |
|
10 | + /** |
|
11 | + * Local copy of component validators. |
|
12 | + * @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl. |
|
13 | + */ |
|
14 | + protected $info; |
|
15 | + |
|
16 | + public function __construct($config) { |
|
17 | + $def = $config->getCSSDefinition(); |
|
18 | + $this->info['list-style-type'] = $def->info['list-style-type']; |
|
19 | + $this->info['list-style-position'] = $def->info['list-style-position']; |
|
20 | + $this->info['list-style-image'] = $def->info['list-style-image']; |
|
21 | + } |
|
22 | + |
|
23 | + public function validate($string, $config, $context) { |
|
24 | + |
|
25 | + // regular pre-processing |
|
26 | + $string = $this->parseCDATA($string); |
|
27 | + if ($string === '') return false; |
|
28 | + |
|
29 | + // assumes URI doesn't have spaces in it |
|
30 | + $bits = explode(' ', strtolower($string)); // bits to process |
|
31 | + |
|
32 | + $caught = array(); |
|
33 | + $caught['type'] = false; |
|
34 | + $caught['position'] = false; |
|
35 | + $caught['image'] = false; |
|
36 | + |
|
37 | + $i = 0; // number of catches |
|
38 | + $none = false; |
|
39 | + |
|
40 | + foreach ($bits as $bit) { |
|
41 | + if ($i >= 3) return; // optimization bit |
|
42 | + if ($bit === '') continue; |
|
43 | + foreach ($caught as $key => $status) { |
|
44 | + if ($status !== false) continue; |
|
45 | + $r = $this->info['list-style-' . $key]->validate($bit, $config, $context); |
|
46 | + if ($r === false) continue; |
|
47 | + if ($r === 'none') { |
|
48 | + if ($none) continue; |
|
49 | + else $none = true; |
|
50 | + if ($key == 'image') continue; |
|
51 | + } |
|
52 | + $caught[$key] = $r; |
|
53 | + $i++; |
|
54 | + break; |
|
55 | + } |
|
56 | + } |
|
57 | + |
|
58 | + if (!$i) return false; |
|
59 | + |
|
60 | + $ret = array(); |
|
61 | + |
|
62 | + // construct type |
|
63 | + if ($caught['type']) $ret[] = $caught['type']; |
|
64 | + |
|
65 | + // construct image |
|
66 | + if ($caught['image']) $ret[] = $caught['image']; |
|
67 | + |
|
68 | + // construct position |
|
69 | + if ($caught['position']) $ret[] = $caught['position']; |
|
70 | + |
|
71 | + if (empty($ret)) return false; |
|
72 | + return implode(' ', $ret); |
|
73 | + |
|
74 | + } |
|
75 | 75 | |
76 | 76 | } |
77 | 77 |
@@ -24,7 +24,9 @@ discard block |
||
24 | 24 | |
25 | 25 | // regular pre-processing |
26 | 26 | $string = $this->parseCDATA($string); |
27 | - if ($string === '') return false; |
|
27 | + if ($string === '') { |
|
28 | + return false; |
|
29 | + } |
|
28 | 30 | |
29 | 31 | // assumes URI doesn't have spaces in it |
30 | 32 | $bits = explode(' ', strtolower($string)); // bits to process |
@@ -38,16 +40,30 @@ discard block |
||
38 | 40 | $none = false; |
39 | 41 | |
40 | 42 | foreach ($bits as $bit) { |
41 | - if ($i >= 3) return; // optimization bit |
|
42 | - if ($bit === '') continue; |
|
43 | + if ($i >= 3) { |
|
44 | + return; |
|
45 | + } |
|
46 | + // optimization bit |
|
47 | + if ($bit === '') { |
|
48 | + continue; |
|
49 | + } |
|
43 | 50 | foreach ($caught as $key => $status) { |
44 | - if ($status !== false) continue; |
|
51 | + if ($status !== false) { |
|
52 | + continue; |
|
53 | + } |
|
45 | 54 | $r = $this->info['list-style-' . $key]->validate($bit, $config, $context); |
46 | - if ($r === false) continue; |
|
55 | + if ($r === false) { |
|
56 | + continue; |
|
57 | + } |
|
47 | 58 | if ($r === 'none') { |
48 | - if ($none) continue; |
|
49 | - else $none = true; |
|
50 | - if ($key == 'image') continue; |
|
59 | + if ($none) { |
|
60 | + continue; |
|
61 | + } else { |
|
62 | + $none = true; |
|
63 | + } |
|
64 | + if ($key == 'image') { |
|
65 | + continue; |
|
66 | + } |
|
51 | 67 | } |
52 | 68 | $caught[$key] = $r; |
53 | 69 | $i++; |
@@ -55,20 +71,30 @@ discard block |
||
55 | 71 | } |
56 | 72 | } |
57 | 73 | |
58 | - if (!$i) return false; |
|
74 | + if (!$i) { |
|
75 | + return false; |
|
76 | + } |
|
59 | 77 | |
60 | 78 | $ret = array(); |
61 | 79 | |
62 | 80 | // construct type |
63 | - if ($caught['type']) $ret[] = $caught['type']; |
|
81 | + if ($caught['type']) { |
|
82 | + $ret[] = $caught['type']; |
|
83 | + } |
|
64 | 84 | |
65 | 85 | // construct image |
66 | - if ($caught['image']) $ret[] = $caught['image']; |
|
86 | + if ($caught['image']) { |
|
87 | + $ret[] = $caught['image']; |
|
88 | + } |
|
67 | 89 | |
68 | 90 | // construct position |
69 | - if ($caught['position']) $ret[] = $caught['position']; |
|
91 | + if ($caught['position']) { |
|
92 | + $ret[] = $caught['position']; |
|
93 | + } |
|
70 | 94 | |
71 | - if (empty($ret)) return false; |
|
95 | + if (empty($ret)) { |
|
96 | + return false; |
|
97 | + } |
|
72 | 98 | return implode(' ', $ret); |
73 | 99 | |
74 | 100 | } |
@@ -42,7 +42,7 @@ |
||
42 | 42 | if ($bit === '') continue; |
43 | 43 | foreach ($caught as $key => $status) { |
44 | 44 | if ($status !== false) continue; |
45 | - $r = $this->info['list-style-' . $key]->validate($bit, $config, $context); |
|
45 | + $r = $this->info['list-style-'.$key]->validate($bit, $config, $context); |
|
46 | 46 | if ($r === false) continue; |
47 | 47 | if ($r === 'none') { |
48 | 48 | if ($none) continue; |