diff options
Diffstat (limited to 'inc/3rdparty/htmlpurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php')
-rw-r--r-- | inc/3rdparty/htmlpurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/inc/3rdparty/htmlpurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php b/inc/3rdparty/htmlpurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php new file mode 100644 index 00000000..f9af36d7 --- /dev/null +++ b/inc/3rdparty/htmlpurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php | |||
@@ -0,0 +1,219 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Validates a font family list according to CSS spec | ||
5 | */ | ||
6 | class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef | ||
7 | { | ||
8 | |||
9 | protected $mask = null; | ||
10 | |||
11 | public function __construct() | ||
12 | { | ||
13 | $this->mask = '_- '; | ||
14 | for ($c = 'a'; $c <= 'z'; $c++) { | ||
15 | $this->mask .= $c; | ||
16 | } | ||
17 | for ($c = 'A'; $c <= 'Z'; $c++) { | ||
18 | $this->mask .= $c; | ||
19 | } | ||
20 | for ($c = '0'; $c <= '9'; $c++) { | ||
21 | $this->mask .= $c; | ||
22 | } // cast-y, but should be fine | ||
23 | // special bytes used by UTF-8 | ||
24 | for ($i = 0x80; $i <= 0xFF; $i++) { | ||
25 | // We don't bother excluding invalid bytes in this range, | ||
26 | // because the our restriction of well-formed UTF-8 will | ||
27 | // prevent these from ever occurring. | ||
28 | $this->mask .= chr($i); | ||
29 | } | ||
30 | |||
31 | /* | ||
32 | PHP's internal strcspn implementation is | ||
33 | O(length of string * length of mask), making it inefficient | ||
34 | for large masks. However, it's still faster than | ||
35 | preg_match 8) | ||
36 | for (p = s1;;) { | ||
37 | spanp = s2; | ||
38 | do { | ||
39 | if (*spanp == c || p == s1_end) { | ||
40 | return p - s1; | ||
41 | } | ||
42 | } while (spanp++ < (s2_end - 1)); | ||
43 | c = *++p; | ||
44 | } | ||
45 | */ | ||
46 | // possible optimization: invert the mask. | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * @param string $string | ||
51 | * @param HTMLPurifier_Config $config | ||
52 | * @param HTMLPurifier_Context $context | ||
53 | * @return bool|string | ||
54 | */ | ||
55 | public function validate($string, $config, $context) | ||
56 | { | ||
57 | static $generic_names = array( | ||
58 | 'serif' => true, | ||
59 | 'sans-serif' => true, | ||
60 | 'monospace' => true, | ||
61 | 'fantasy' => true, | ||
62 | 'cursive' => true | ||
63 | ); | ||
64 | $allowed_fonts = $config->get('CSS.AllowedFonts'); | ||
65 | |||
66 | // assume that no font names contain commas in them | ||
67 | $fonts = explode(',', $string); | ||
68 | $final = ''; | ||
69 | foreach ($fonts as $font) { | ||
70 | $font = trim($font); | ||
71 | if ($font === '') { | ||
72 | continue; | ||
73 | } | ||
74 | // match a generic name | ||
75 | if (isset($generic_names[$font])) { | ||
76 | if ($allowed_fonts === null || isset($allowed_fonts[$font])) { | ||
77 | $final .= $font . ', '; | ||
78 | } | ||
79 | continue; | ||
80 | } | ||
81 | // match a quoted name | ||
82 | if ($font[0] === '"' || $font[0] === "'") { | ||
83 | $length = strlen($font); | ||
84 | if ($length <= 2) { | ||
85 | continue; | ||
86 | } | ||
87 | $quote = $font[0]; | ||
88 | if ($font[$length - 1] !== $quote) { | ||
89 | continue; | ||
90 | } | ||
91 | $font = substr($font, 1, $length - 2); | ||
92 | } | ||
93 | |||
94 | $font = $this->expandCSSEscape($font); | ||
95 | |||
96 | // $font is a pure representation of the font name | ||
97 | |||
98 | if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) { | ||
99 | continue; | ||
100 | } | ||
101 | |||
102 | if (ctype_alnum($font) && $font !== '') { | ||
103 | // very simple font, allow it in unharmed | ||
104 | $final .= $font . ', '; | ||
105 | continue; | ||
106 | } | ||
107 | |||
108 | // bugger out on whitespace. form feed (0C) really | ||
109 | // shouldn't show up regardless | ||
110 | $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font); | ||
111 | |||
112 | // Here, there are various classes of characters which need | ||
113 | // to be treated differently: | ||
114 | // - Alphanumeric characters are essentially safe. We | ||
115 | // handled these above. | ||
116 | // - Spaces require quoting, though most parsers will do | ||
117 | // the right thing if there aren't any characters that | ||
118 | // can be misinterpreted | ||
119 | // - Dashes rarely occur, but they fairly unproblematic | ||
120 | // for parsing/rendering purposes. | ||
121 | // The above characters cover the majority of Western font | ||
122 | // names. | ||
123 | // - Arbitrary Unicode characters not in ASCII. Because | ||
124 | // most parsers give little thought to Unicode, treatment | ||
125 | // of these codepoints is basically uniform, even for | ||
126 | // punctuation-like codepoints. These characters can | ||
127 | // show up in non-Western pages and are supported by most | ||
128 | // major browsers, for example: "MS 明朝" is a | ||
129 | // legitimate font-name | ||
130 | // <http://ja.wikipedia.org/wiki/MS_明朝>. See | ||
131 | // the CSS3 spec for more examples: | ||
132 | // <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png> | ||
133 | // You can see live samples of these on the Internet: | ||
134 | // <http://www.google.co.jp/search?q=font-family+MS+明朝|ゴシック> | ||
135 | // However, most of these fonts have ASCII equivalents: | ||
136 | // for example, 'MS Mincho', and it's considered | ||
137 | // professional to use ASCII font names instead of | ||
138 | // Unicode font names. Thanks Takeshi Terada for | ||
139 | // providing this information. | ||
140 | // The following characters, to my knowledge, have not been | ||
141 | // used to name font names. | ||
142 | // - Single quote. While theoretically you might find a | ||
143 | // font name that has a single quote in its name (serving | ||
144 | // as an apostrophe, e.g. Dave's Scribble), I haven't | ||
145 | // been able to find any actual examples of this. | ||
146 | // Internet Explorer's cssText translation (which I | ||
147 | // believe is invoked by innerHTML) normalizes any | ||
148 | // quoting to single quotes, and fails to escape single | ||
149 | // quotes. (Note that this is not IE's behavior for all | ||
150 | // CSS properties, just some sort of special casing for | ||
151 | // font-family). So a single quote *cannot* be used | ||
152 | // safely in the font-family context if there will be an | ||
153 | // innerHTML/cssText translation. Note that Firefox 3.x | ||
154 | // does this too. | ||
155 | // - Double quote. In IE, these get normalized to | ||
156 | // single-quotes, no matter what the encoding. (Fun | ||
157 | // fact, in IE8, the 'content' CSS property gained | ||
158 | // support, where they special cased to preserve encoded | ||
159 | // double quotes, but still translate unadorned double | ||
160 | // quotes into single quotes.) So, because their | ||
161 | // fixpoint behavior is identical to single quotes, they | ||
162 | // cannot be allowed either. Firefox 3.x displays | ||
163 | // single-quote style behavior. | ||
164 | // - Backslashes are reduced by one (so \\ -> \) every | ||
165 | // iteration, so they cannot be used safely. This shows | ||
166 | // up in IE7, IE8 and FF3 | ||
167 | // - Semicolons, commas and backticks are handled properly. | ||
168 | // - The rest of the ASCII punctuation is handled properly. | ||
169 | // We haven't checked what browsers do to unadorned | ||
170 | // versions, but this is not important as long as the | ||
171 | // browser doesn't /remove/ surrounding quotes (as IE does | ||
172 | // for HTML). | ||
173 | // | ||
174 | // With these results in hand, we conclude that there are | ||
175 | // various levels of safety: | ||
176 | // - Paranoid: alphanumeric, spaces and dashes(?) | ||
177 | // - International: Paranoid + non-ASCII Unicode | ||
178 | // - Edgy: Everything except quotes, backslashes | ||
179 | // - NoJS: Standards compliance, e.g. sod IE. Note that | ||
180 | // with some judicious character escaping (since certain | ||
181 | // types of escaping doesn't work) this is theoretically | ||
182 | // OK as long as innerHTML/cssText is not called. | ||
183 | // We believe that international is a reasonable default | ||
184 | // (that we will implement now), and once we do more | ||
185 | // extensive research, we may feel comfortable with dropping | ||
186 | // it down to edgy. | ||
187 | |||
188 | // Edgy: alphanumeric, spaces, dashes, underscores and Unicode. Use of | ||
189 | // str(c)spn assumes that the string was already well formed | ||
190 | // Unicode (which of course it is). | ||
191 | if (strspn($font, $this->mask) !== strlen($font)) { | ||
192 | continue; | ||
193 | } | ||
194 | |||
195 | // Historical: | ||
196 | // In the absence of innerHTML/cssText, these ugly | ||
197 | // transforms don't pose a security risk (as \\ and \" | ||
198 | // might--these escapes are not supported by most browsers). | ||
199 | // We could try to be clever and use single-quote wrapping | ||
200 | // when there is a double quote present, but I have choosen | ||
201 | // not to implement that. (NOTE: you can reduce the amount | ||
202 | // of escapes by one depending on what quoting style you use) | ||
203 | // $font = str_replace('\\', '\\5C ', $font); | ||
204 | // $font = str_replace('"', '\\22 ', $font); | ||
205 | // $font = str_replace("'", '\\27 ', $font); | ||
206 | |||
207 | // font possibly with spaces, requires quoting | ||
208 | $final .= "'$font', "; | ||
209 | } | ||
210 | $final = rtrim($final, ', '); | ||
211 | if ($final === '') { | ||
212 | return false; | ||
213 | } | ||
214 | return $final; | ||
215 | } | ||
216 | |||
217 | } | ||
218 | |||
219 | // vim: et sw=4 sts=4 | ||