aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector
diff options
context:
space:
mode:
Diffstat (limited to 'inc/3rdparty/htmlpurifier/HTMLPurifier/Injector')
-rw-r--r--inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/AutoParagraph.php356
-rw-r--r--inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/DisplayLinkURI.php40
-rw-r--r--inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/Linkify.php59
-rw-r--r--inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/PurifierLinkify.php71
-rw-r--r--inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/RemoveEmpty.php101
-rw-r--r--inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php84
-rw-r--r--inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/SafeObject.php121
7 files changed, 832 insertions, 0 deletions
diff --git a/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/AutoParagraph.php b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/AutoParagraph.php
new file mode 100644
index 00000000..d3ec44f1
--- /dev/null
+++ b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/AutoParagraph.php
@@ -0,0 +1,356 @@
1<?php
2
3/**
4 * Injector that auto paragraphs text in the root node based on
5 * double-spacing.
6 * @todo Ensure all states are unit tested, including variations as well.
7 * @todo Make a graph of the flow control for this Injector.
8 */
9class HTMLPurifier_Injector_AutoParagraph extends HTMLPurifier_Injector
10{
11 /**
12 * @type string
13 */
14 public $name = 'AutoParagraph';
15
16 /**
17 * @type array
18 */
19 public $needed = array('p');
20
21 /**
22 * @return HTMLPurifier_Token_Start
23 */
24 private function _pStart()
25 {
26 $par = new HTMLPurifier_Token_Start('p');
27 $par->armor['MakeWellFormed_TagClosedError'] = true;
28 return $par;
29 }
30
31 /**
32 * @param HTMLPurifier_Token_Text $token
33 */
34 public function handleText(&$token)
35 {
36 $text = $token->data;
37 // Does the current parent allow <p> tags?
38 if ($this->allowsElement('p')) {
39 if (empty($this->currentNesting) || strpos($text, "\n\n") !== false) {
40 // Note that we have differing behavior when dealing with text
41 // in the anonymous root node, or a node inside the document.
42 // If the text as a double-newline, the treatment is the same;
43 // if it doesn't, see the next if-block if you're in the document.
44
45 $i = $nesting = null;
46 if (!$this->forwardUntilEndToken($i, $current, $nesting) && $token->is_whitespace) {
47 // State 1.1: ... ^ (whitespace, then document end)
48 // ----
49 // This is a degenerate case
50 } else {
51 if (!$token->is_whitespace || $this->_isInline($current)) {
52 // State 1.2: PAR1
53 // ----
54
55 // State 1.3: PAR1\n\nPAR2
56 // ------------
57
58 // State 1.4: <div>PAR1\n\nPAR2 (see State 2)
59 // ------------
60 $token = array($this->_pStart());
61 $this->_splitText($text, $token);
62 } else {
63 // State 1.5: \n<hr />
64 // --
65 }
66 }
67 } else {
68 // State 2: <div>PAR1... (similar to 1.4)
69 // ----
70
71 // We're in an element that allows paragraph tags, but we're not
72 // sure if we're going to need them.
73 if ($this->_pLookAhead()) {
74 // State 2.1: <div>PAR1<b>PAR1\n\nPAR2
75 // ----
76 // Note: This will always be the first child, since any
77 // previous inline element would have triggered this very
78 // same routine, and found the double newline. One possible
79 // exception would be a comment.
80 $token = array($this->_pStart(), $token);
81 } else {
82 // State 2.2.1: <div>PAR1<div>
83 // ----
84
85 // State 2.2.2: <div>PAR1<b>PAR1</b></div>
86 // ----
87 }
88 }
89 // Is the current parent a <p> tag?
90 } elseif (!empty($this->currentNesting) &&
91 $this->currentNesting[count($this->currentNesting) - 1]->name == 'p') {
92 // State 3.1: ...<p>PAR1
93 // ----
94
95 // State 3.2: ...<p>PAR1\n\nPAR2
96 // ------------
97 $token = array();
98 $this->_splitText($text, $token);
99 // Abort!
100 } else {
101 // State 4.1: ...<b>PAR1
102 // ----
103
104 // State 4.2: ...<b>PAR1\n\nPAR2
105 // ------------
106 }
107 }
108
109 /**
110 * @param HTMLPurifier_Token $token
111 */
112 public function handleElement(&$token)
113 {
114 // We don't have to check if we're already in a <p> tag for block
115 // tokens, because the tag would have been autoclosed by MakeWellFormed.
116 if ($this->allowsElement('p')) {
117 if (!empty($this->currentNesting)) {
118 if ($this->_isInline($token)) {
119 // State 1: <div>...<b>
120 // ---
121 // Check if this token is adjacent to the parent token
122 // (seek backwards until token isn't whitespace)
123 $i = null;
124 $this->backward($i, $prev);
125
126 if (!$prev instanceof HTMLPurifier_Token_Start) {
127 // Token wasn't adjacent
128 if ($prev instanceof HTMLPurifier_Token_Text &&
129 substr($prev->data, -2) === "\n\n"
130 ) {
131 // State 1.1.4: <div><p>PAR1</p>\n\n<b>
132 // ---
133 // Quite frankly, this should be handled by splitText
134 $token = array($this->_pStart(), $token);
135 } else {
136 // State 1.1.1: <div><p>PAR1</p><b>
137 // ---
138 // State 1.1.2: <div><br /><b>
139 // ---
140 // State 1.1.3: <div>PAR<b>
141 // ---
142 }
143 } else {
144 // State 1.2.1: <div><b>
145 // ---
146 // Lookahead to see if <p> is needed.
147 if ($this->_pLookAhead()) {
148 // State 1.3.1: <div><b>PAR1\n\nPAR2
149 // ---
150 $token = array($this->_pStart(), $token);
151 } else {
152 // State 1.3.2: <div><b>PAR1</b></div>
153 // ---
154
155 // State 1.3.3: <div><b>PAR1</b><div></div>\n\n</div>
156 // ---
157 }
158 }
159 } else {
160 // State 2.3: ...<div>
161 // -----
162 }
163 } else {
164 if ($this->_isInline($token)) {
165 // State 3.1: <b>
166 // ---
167 // This is where the {p} tag is inserted, not reflected in
168 // inputTokens yet, however.
169 $token = array($this->_pStart(), $token);
170 } else {
171 // State 3.2: <div>
172 // -----
173 }
174
175 $i = null;
176 if ($this->backward($i, $prev)) {
177 if (!$prev instanceof HTMLPurifier_Token_Text) {
178 // State 3.1.1: ...</p>{p}<b>
179 // ---
180 // State 3.2.1: ...</p><div>
181 // -----
182 if (!is_array($token)) {
183 $token = array($token);
184 }
185 array_unshift($token, new HTMLPurifier_Token_Text("\n\n"));
186 } else {
187 // State 3.1.2: ...</p>\n\n{p}<b>
188 // ---
189 // State 3.2.2: ...</p>\n\n<div>
190 // -----
191 // Note: PAR<ELEM> cannot occur because PAR would have been
192 // wrapped in <p> tags.
193 }
194 }
195 }
196 } else {
197 // State 2.2: <ul><li>
198 // ----
199 // State 2.4: <p><b>
200 // ---
201 }
202 }
203
204 /**
205 * Splits up a text in paragraph tokens and appends them
206 * to the result stream that will replace the original
207 * @param string $data String text data that will be processed
208 * into paragraphs
209 * @param HTMLPurifier_Token[] $result Reference to array of tokens that the
210 * tags will be appended onto
211 */
212 private function _splitText($data, &$result)
213 {
214 $raw_paragraphs = explode("\n\n", $data);
215 $paragraphs = array(); // without empty paragraphs
216 $needs_start = false;
217 $needs_end = false;
218
219 $c = count($raw_paragraphs);
220 if ($c == 1) {
221 // There were no double-newlines, abort quickly. In theory this
222 // should never happen.
223 $result[] = new HTMLPurifier_Token_Text($data);
224 return;
225 }
226 for ($i = 0; $i < $c; $i++) {
227 $par = $raw_paragraphs[$i];
228 if (trim($par) !== '') {
229 $paragraphs[] = $par;
230 } else {
231 if ($i == 0) {
232 // Double newline at the front
233 if (empty($result)) {
234 // The empty result indicates that the AutoParagraph
235 // injector did not add any start paragraph tokens.
236 // This means that we have been in a paragraph for
237 // a while, and the newline means we should start a new one.
238 $result[] = new HTMLPurifier_Token_End('p');
239 $result[] = new HTMLPurifier_Token_Text("\n\n");
240 // However, the start token should only be added if
241 // there is more processing to be done (i.e. there are
242 // real paragraphs in here). If there are none, the
243 // next start paragraph tag will be handled by the
244 // next call to the injector
245 $needs_start = true;
246 } else {
247 // We just started a new paragraph!
248 // Reinstate a double-newline for presentation's sake, since
249 // it was in the source code.
250 array_unshift($result, new HTMLPurifier_Token_Text("\n\n"));
251 }
252 } elseif ($i + 1 == $c) {
253 // Double newline at the end
254 // There should be a trailing </p> when we're finally done.
255 $needs_end = true;
256 }
257 }
258 }
259
260 // Check if this was just a giant blob of whitespace. Move this earlier,
261 // perhaps?
262 if (empty($paragraphs)) {
263 return;
264 }
265
266 // Add the start tag indicated by \n\n at the beginning of $data
267 if ($needs_start) {
268 $result[] = $this->_pStart();
269 }
270
271 // Append the paragraphs onto the result
272 foreach ($paragraphs as $par) {
273 $result[] = new HTMLPurifier_Token_Text($par);
274 $result[] = new HTMLPurifier_Token_End('p');
275 $result[] = new HTMLPurifier_Token_Text("\n\n");
276 $result[] = $this->_pStart();
277 }
278
279 // Remove trailing start token; Injector will handle this later if
280 // it was indeed needed. This prevents from needing to do a lookahead,
281 // at the cost of a lookbehind later.
282 array_pop($result);
283
284 // If there is no need for an end tag, remove all of it and let
285 // MakeWellFormed close it later.
286 if (!$needs_end) {
287 array_pop($result); // removes \n\n
288 array_pop($result); // removes </p>
289 }
290 }
291
292 /**
293 * Returns true if passed token is inline (and, ergo, allowed in
294 * paragraph tags)
295 * @param HTMLPurifier_Token $token
296 * @return bool
297 */
298 private function _isInline($token)
299 {
300 return isset($this->htmlDefinition->info['p']->child->elements[$token->name]);
301 }
302
303 /**
304 * Looks ahead in the token list and determines whether or not we need
305 * to insert a <p> tag.
306 * @return bool
307 */
308 private function _pLookAhead()
309 {
310 if ($this->currentToken instanceof HTMLPurifier_Token_Start) {
311 $nesting = 1;
312 } else {
313 $nesting = 0;
314 }
315 $ok = false;
316 $i = null;
317 while ($this->forwardUntilEndToken($i, $current, $nesting)) {
318 $result = $this->_checkNeedsP($current);
319 if ($result !== null) {
320 $ok = $result;
321 break;
322 }
323 }
324 return $ok;
325 }
326
327 /**
328 * Determines if a particular token requires an earlier inline token
329 * to get a paragraph. This should be used with _forwardUntilEndToken
330 * @param HTMLPurifier_Token $current
331 * @return bool
332 */
333 private function _checkNeedsP($current)
334 {
335 if ($current instanceof HTMLPurifier_Token_Start) {
336 if (!$this->_isInline($current)) {
337 // <div>PAR1<div>
338 // ----
339 // Terminate early, since we hit a block element
340 return false;
341 }
342 } elseif ($current instanceof HTMLPurifier_Token_Text) {
343 if (strpos($current->data, "\n\n") !== false) {
344 // <div>PAR1<b>PAR1\n\nPAR2
345 // ----
346 return true;
347 } else {
348 // <div>PAR1<b>PAR1...
349 // ----
350 }
351 }
352 return null;
353 }
354}
355
356// vim: et sw=4 sts=4
diff --git a/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/DisplayLinkURI.php b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/DisplayLinkURI.php
new file mode 100644
index 00000000..9f904482
--- /dev/null
+++ b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/DisplayLinkURI.php
@@ -0,0 +1,40 @@
1<?php
2
3/**
4 * Injector that displays the URL of an anchor instead of linking to it, in addition to showing the text of the link.
5 */
6class HTMLPurifier_Injector_DisplayLinkURI extends HTMLPurifier_Injector
7{
8 /**
9 * @type string
10 */
11 public $name = 'DisplayLinkURI';
12
13 /**
14 * @type array
15 */
16 public $needed = array('a');
17
18 /**
19 * @param $token
20 */
21 public function handleElement(&$token)
22 {
23 }
24
25 /**
26 * @param HTMLPurifier_Token $token
27 */
28 public function handleEnd(&$token)
29 {
30 if (isset($token->start->attr['href'])) {
31 $url = $token->start->attr['href'];
32 unset($token->start->attr['href']);
33 $token = array($token, new HTMLPurifier_Token_Text(" ($url)"));
34 } else {
35 // nothing to display
36 }
37 }
38}
39
40// vim: et sw=4 sts=4
diff --git a/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/Linkify.php b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/Linkify.php
new file mode 100644
index 00000000..5eed3c12
--- /dev/null
+++ b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/Linkify.php
@@ -0,0 +1,59 @@
1<?php
2
3/**
4 * Injector that converts http, https and ftp text URLs to actual links.
5 */
6class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector
7{
8 /**
9 * @type string
10 */
11 public $name = 'Linkify';
12
13 /**
14 * @type array
15 */
16 public $needed = array('a' => array('href'));
17
18 /**
19 * @param HTMLPurifier_Token $token
20 */
21 public function handleText(&$token)
22 {
23 if (!$this->allowsElement('a')) {
24 return;
25 }
26
27 if (strpos($token->data, '://') === false) {
28 // our really quick heuristic failed, abort
29 // this may not work so well if we want to match things like
30 // "google.com", but then again, most people don't
31 return;
32 }
33
34 // there is/are URL(s). Let's split the string:
35 // Note: this regex is extremely permissive
36 $bits = preg_split('#((?:https?|ftp)://[^\s\'",<>()]+)#Su', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
37
38
39 $token = array();
40
41 // $i = index
42 // $c = count
43 // $l = is link
44 for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
45 if (!$l) {
46 if ($bits[$i] === '') {
47 continue;
48 }
49 $token[] = new HTMLPurifier_Token_Text($bits[$i]);
50 } else {
51 $token[] = new HTMLPurifier_Token_Start('a', array('href' => $bits[$i]));
52 $token[] = new HTMLPurifier_Token_Text($bits[$i]);
53 $token[] = new HTMLPurifier_Token_End('a');
54 }
55 }
56 }
57}
58
59// vim: et sw=4 sts=4
diff --git a/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/PurifierLinkify.php b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/PurifierLinkify.php
new file mode 100644
index 00000000..d7dd7d97
--- /dev/null
+++ b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/PurifierLinkify.php
@@ -0,0 +1,71 @@
1<?php
2
3/**
4 * Injector that converts configuration directive syntax %Namespace.Directive
5 * to links
6 */
7class HTMLPurifier_Injector_PurifierLinkify extends HTMLPurifier_Injector
8{
9 /**
10 * @type string
11 */
12 public $name = 'PurifierLinkify';
13
14 /**
15 * @type string
16 */
17 public $docURL;
18
19 /**
20 * @type array
21 */
22 public $needed = array('a' => array('href'));
23
24 /**
25 * @param HTMLPurifier_Config $config
26 * @param HTMLPurifier_Context $context
27 * @return string
28 */
29 public function prepare($config, $context)
30 {
31 $this->docURL = $config->get('AutoFormat.PurifierLinkify.DocURL');
32 return parent::prepare($config, $context);
33 }
34
35 /**
36 * @param HTMLPurifier_Token $token
37 */
38 public function handleText(&$token)
39 {
40 if (!$this->allowsElement('a')) {
41 return;
42 }
43 if (strpos($token->data, '%') === false) {
44 return;
45 }
46
47 $bits = preg_split('#%([a-z0-9]+\.[a-z0-9]+)#Si', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
48 $token = array();
49
50 // $i = index
51 // $c = count
52 // $l = is link
53 for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
54 if (!$l) {
55 if ($bits[$i] === '') {
56 continue;
57 }
58 $token[] = new HTMLPurifier_Token_Text($bits[$i]);
59 } else {
60 $token[] = new HTMLPurifier_Token_Start(
61 'a',
62 array('href' => str_replace('%s', $bits[$i], $this->docURL))
63 );
64 $token[] = new HTMLPurifier_Token_Text('%' . $bits[$i]);
65 $token[] = new HTMLPurifier_Token_End('a');
66 }
67 }
68 }
69}
70
71// vim: et sw=4 sts=4
diff --git a/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/RemoveEmpty.php b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/RemoveEmpty.php
new file mode 100644
index 00000000..55669d1c
--- /dev/null
+++ b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/RemoveEmpty.php
@@ -0,0 +1,101 @@
1<?php
2
3class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
4{
5 /**
6 * @type HTMLPurifier_Context
7 */
8 private $context;
9
10 /**
11 * @type HTMLPurifier_Config
12 */
13 private $config;
14
15 /**
16 * @type HTMLPurifier_AttrValidator
17 */
18 private $attrValidator;
19
20 /**
21 * @type bool
22 */
23 private $removeNbsp;
24
25 /**
26 * @type bool
27 */
28 private $removeNbspExceptions;
29
30 /**
31 * @type array
32 * TODO: make me configurable
33 */
34 private $_exclude = array('colgroup' => 1, 'th' => 1, 'td' => 1, 'iframe' => 1);
35
36 /**
37 * @param HTMLPurifier_Config $config
38 * @param HTMLPurifier_Context $context
39 * @return void
40 */
41 public function prepare($config, $context)
42 {
43 parent::prepare($config, $context);
44 $this->config = $config;
45 $this->context = $context;
46 $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
47 $this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
48 $this->attrValidator = new HTMLPurifier_AttrValidator();
49 }
50
51 /**
52 * @param HTMLPurifier_Token $token
53 */
54 public function handleElement(&$token)
55 {
56 if (!$token instanceof HTMLPurifier_Token_Start) {
57 return;
58 }
59 $next = false;
60 $deleted = 1; // the current tag
61 for ($i = count($this->inputZipper->back) - 1; $i >= 0; $i--, $deleted++) {
62 $next = $this->inputZipper->back[$i];
63 if ($next instanceof HTMLPurifier_Token_Text) {
64 if ($next->is_whitespace) {
65 continue;
66 }
67 if ($this->removeNbsp && !isset($this->removeNbspExceptions[$token->name])) {
68 $plain = str_replace("\xC2\xA0", "", $next->data);
69 $isWsOrNbsp = $plain === '' || ctype_space($plain);
70 if ($isWsOrNbsp) {
71 continue;
72 }
73 }
74 }
75 break;
76 }
77 if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {
78 if (isset($this->_exclude[$token->name])) {
79 return;
80 }
81 $this->attrValidator->validateToken($token, $this->config, $this->context);
82 $token->armor['ValidateAttributes'] = true;
83 if (isset($token->attr['id']) || isset($token->attr['name'])) {
84 return;
85 }
86 $token = $deleted + 1;
87 for ($b = 0, $c = count($this->inputZipper->front); $b < $c; $b++) {
88 $prev = $this->inputZipper->front[$b];
89 if ($prev instanceof HTMLPurifier_Token_Text && $prev->is_whitespace) {
90 continue;
91 }
92 break;
93 }
94 // This is safe because we removed the token that triggered this.
95 $this->rewindOffset($b+$deleted);
96 return;
97 }
98 }
99}
100
101// vim: et sw=4 sts=4
diff --git a/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php
new file mode 100644
index 00000000..270b7f82
--- /dev/null
+++ b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php
@@ -0,0 +1,84 @@
1<?php
2
3/**
4 * Injector that removes spans with no attributes
5 */
6class HTMLPurifier_Injector_RemoveSpansWithoutAttributes extends HTMLPurifier_Injector
7{
8 /**
9 * @type string
10 */
11 public $name = 'RemoveSpansWithoutAttributes';
12
13 /**
14 * @type array
15 */
16 public $needed = array('span');
17
18 /**
19 * @type HTMLPurifier_AttrValidator
20 */
21 private $attrValidator;
22
23 /**
24 * Used by AttrValidator.
25 * @type HTMLPurifier_Config
26 */
27 private $config;
28
29 /**
30 * @type HTMLPurifier_Context
31 */
32 private $context;
33
34 public function prepare($config, $context)
35 {
36 $this->attrValidator = new HTMLPurifier_AttrValidator();
37 $this->config = $config;
38 $this->context = $context;
39 return parent::prepare($config, $context);
40 }
41
42 /**
43 * @param HTMLPurifier_Token $token
44 */
45 public function handleElement(&$token)
46 {
47 if ($token->name !== 'span' || !$token instanceof HTMLPurifier_Token_Start) {
48 return;
49 }
50
51 // We need to validate the attributes now since this doesn't normally
52 // happen until after MakeWellFormed. If all the attributes are removed
53 // the span needs to be removed too.
54 $this->attrValidator->validateToken($token, $this->config, $this->context);
55 $token->armor['ValidateAttributes'] = true;
56
57 if (!empty($token->attr)) {
58 return;
59 }
60
61 $nesting = 0;
62 while ($this->forwardUntilEndToken($i, $current, $nesting)) {
63 }
64
65 if ($current instanceof HTMLPurifier_Token_End && $current->name === 'span') {
66 // Mark closing span tag for deletion
67 $current->markForDeletion = true;
68 // Delete open span tag
69 $token = false;
70 }
71 }
72
73 /**
74 * @param HTMLPurifier_Token $token
75 */
76 public function handleEnd(&$token)
77 {
78 if ($token->markForDeletion) {
79 $token = false;
80 }
81 }
82}
83
84// vim: et sw=4 sts=4
diff --git a/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/SafeObject.php b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/SafeObject.php
new file mode 100644
index 00000000..8450948c
--- /dev/null
+++ b/inc/3rdparty/htmlpurifier/HTMLPurifier/Injector/SafeObject.php
@@ -0,0 +1,121 @@
1<?php
2
3/**
4 * Adds important param elements to inside of object in order to make
5 * things safe.
6 */
7class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
8{
9 /**
10 * @type string
11 */
12 public $name = 'SafeObject';
13
14 /**
15 * @type array
16 */
17 public $needed = array('object', 'param');
18
19 /**
20 * @type array
21 */
22 protected $objectStack = array();
23
24 /**
25 * @type array
26 */
27 protected $paramStack = array();
28
29 /**
30 * Keep this synchronized with AttrTransform/SafeParam.php.
31 * @type array
32 */
33 protected $addParam = array(
34 'allowScriptAccess' => 'never',
35 'allowNetworking' => 'internal',
36 );
37
38 /**
39 * @type array
40 */
41 protected $allowedParam = array(
42 'wmode' => true,
43 'movie' => true,
44 'flashvars' => true,
45 'src' => true,
46 'allowFullScreen' => true, // if omitted, assume to be 'false'
47 );
48
49 /**
50 * @param HTMLPurifier_Config $config
51 * @param HTMLPurifier_Context $context
52 * @return void
53 */
54 public function prepare($config, $context)
55 {
56 parent::prepare($config, $context);
57 }
58
59 /**
60 * @param HTMLPurifier_Token $token
61 */
62 public function handleElement(&$token)
63 {
64 if ($token->name == 'object') {
65 $this->objectStack[] = $token;
66 $this->paramStack[] = array();
67 $new = array($token);
68 foreach ($this->addParam as $name => $value) {
69 $new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value));
70 }
71 $token = $new;
72 } elseif ($token->name == 'param') {
73 $nest = count($this->currentNesting) - 1;
74 if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') {
75 $i = count($this->objectStack) - 1;
76 if (!isset($token->attr['name'])) {
77 $token = false;
78 return;
79 }
80 $n = $token->attr['name'];
81 // We need this fix because YouTube doesn't supply a data
82 // attribute, which we need if a type is specified. This is
83 // *very* Flash specific.
84 if (!isset($this->objectStack[$i]->attr['data']) &&
85 ($token->attr['name'] == 'movie' || $token->attr['name'] == 'src')
86 ) {
87 $this->objectStack[$i]->attr['data'] = $token->attr['value'];
88 }
89 // Check if the parameter is the correct value but has not
90 // already been added
91 if (!isset($this->paramStack[$i][$n]) &&
92 isset($this->addParam[$n]) &&
93 $token->attr['name'] === $this->addParam[$n]) {
94 // keep token, and add to param stack
95 $this->paramStack[$i][$n] = true;
96 } elseif (isset($this->allowedParam[$n])) {
97 // keep token, don't do anything to it
98 // (could possibly check for duplicates here)
99 } else {
100 $token = false;
101 }
102 } else {
103 // not directly inside an object, DENY!
104 $token = false;
105 }
106 }
107 }
108
109 public function handleEnd(&$token)
110 {
111 // This is the WRONG way of handling the object and param stacks;
112 // we should be inserting them directly on the relevant object tokens
113 // so that the global stack handling handles it.
114 if ($token->name == 'object') {
115 array_pop($this->objectStack);
116 array_pop($this->paramStack);
117 }
118 }
119}
120
121// vim: et sw=4 sts=4