]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/kses.php
Wordpress 3.0
[autoinstalls/wordpress.git] / wp-includes / kses.php
1 <?php
2 /**
3  * HTML/XHTML filter that only allows some elements and attributes
4  *
5  * Added wp_ prefix to avoid conflicts with existing kses users
6  *
7  * @version 0.2.2
8  * @copyright (C) 2002, 2003, 2005
9  * @author Ulf Harnhammar <metaur@users.sourceforge.net>
10  *
11  * @package External
12  * @subpackage KSES
13  *
14  * @internal
15  * *** CONTACT INFORMATION ***
16  * E-mail:      metaur at users dot sourceforge dot net
17  * Web page:    http://sourceforge.net/projects/kses
18  * Paper mail:  Ulf Harnhammar
19  *              Ymergatan 17 C
20  *              753 25  Uppsala
21  *              SWEDEN
22  *
23  * [kses strips evil scripts!]
24  */
25
26 /**
27  * You can override this in a plugin.
28  *
29  * @since 1.2.0
30  */
31 if ( ! defined( 'CUSTOM_TAGS' ) )
32         define( 'CUSTOM_TAGS', false );
33
34 if ( ! CUSTOM_TAGS ) {
35         /**
36          * Kses global for default allowable HTML tags.
37          *
38          * Can be override by using CUSTOM_TAGS constant.
39          *
40          * @global array $allowedposttags
41          * @since 2.0.0
42          */
43         $allowedposttags = array(
44                 'address' => array(),
45                 'a' => array(
46                         'class' => array (),
47                         'href' => array (),
48                         'id' => array (),
49                         'title' => array (),
50                         'rel' => array (),
51                         'rev' => array (),
52                         'name' => array (),
53                         'target' => array()),
54                 'abbr' => array(
55                         'class' => array (),
56                         'title' => array ()),
57                 'acronym' => array(
58                         'title' => array ()),
59                 'article' => array(
60                         'align' => array (),
61                         'class' => array (),
62                         'dir' => array (),
63                         'lang' => array(),
64                         'style' => array (),
65                         'xml:lang' => array(),
66                 ),
67                 'aside' => array(
68                         'align' => array (),
69                         'class' => array (),
70                         'dir' => array (),
71                         'lang' => array(),
72                         'style' => array (),
73                         'xml:lang' => array(),
74                 ),
75                 'b' => array(),
76                 'big' => array(),
77                 'blockquote' => array(
78                         'id' => array (),
79                         'cite' => array (),
80                         'class' => array(),
81                         'lang' => array(),
82                         'xml:lang' => array()),
83                 'br' => array (
84                         'class' => array ()),
85                 'button' => array(
86                         'disabled' => array (),
87                         'name' => array (),
88                         'type' => array (),
89                         'value' => array ()),
90                 'caption' => array(
91                         'align' => array (),
92                         'class' => array ()),
93                 'cite' => array (
94                         'class' => array(),
95                         'dir' => array(),
96                         'lang' => array(),
97                         'title' => array ()),
98                 'code' => array (
99                         'style' => array()),
100                 'col' => array(
101                         'align' => array (),
102                         'char' => array (),
103                         'charoff' => array (),
104                         'span' => array (),
105                         'dir' => array(),
106                         'style' => array (),
107                         'valign' => array (),
108                         'width' => array ()),
109                 'del' => array(
110                         'datetime' => array ()),
111                 'dd' => array(),
112                 'details' => array(
113                         'align' => array (),
114                         'class' => array (),
115                         'dir' => array (),
116                         'lang' => array(),
117                         'open' => array (),
118                         'style' => array (),
119                         'xml:lang' => array(),
120                 ),
121                 'div' => array(
122                         'align' => array (),
123                         'class' => array (),
124                         'dir' => array (),
125                         'lang' => array(),
126                         'style' => array (),
127                         'xml:lang' => array()),
128                 'dl' => array(),
129                 'dt' => array(),
130                 'em' => array(),
131                 'fieldset' => array(),
132                 'figure' => array(
133                         'align' => array (),
134                         'class' => array (),
135                         'dir' => array (),
136                         'lang' => array(),
137                         'style' => array (),
138                         'xml:lang' => array(),
139                 ),
140                 'figcaption' => array(
141                         'align' => array (),
142                         'class' => array (),
143                         'dir' => array (),
144                         'lang' => array(),
145                         'style' => array (),
146                         'xml:lang' => array(),
147                 ),
148                 'font' => array(
149                         'color' => array (),
150                         'face' => array (),
151                         'size' => array ()),
152                 'footer' => array(
153                         'align' => array (),
154                         'class' => array (),
155                         'dir' => array (),
156                         'lang' => array(),
157                         'style' => array (),
158                         'xml:lang' => array(),
159                 ),
160                 'form' => array(
161                         'action' => array (),
162                         'accept' => array (),
163                         'accept-charset' => array (),
164                         'enctype' => array (),
165                         'method' => array (),
166                         'name' => array (),
167                         'target' => array ()),
168                 'h1' => array(
169                         'align' => array (),
170                         'class' => array (),
171                         'id'    => array (),
172                         'style' => array ()),
173                 'h2' => array (
174                         'align' => array (),
175                         'class' => array (),
176                         'id'    => array (),
177                         'style' => array ()),
178                 'h3' => array (
179                         'align' => array (),
180                         'class' => array (),
181                         'id'    => array (),
182                         'style' => array ()),
183                 'h4' => array (
184                         'align' => array (),
185                         'class' => array (),
186                         'id'    => array (),
187                         'style' => array ()),
188                 'h5' => array (
189                         'align' => array (),
190                         'class' => array (),
191                         'id'    => array (),
192                         'style' => array ()),
193                 'h6' => array (
194                         'align' => array (),
195                         'class' => array (),
196                         'id'    => array (),
197                         'style' => array ()),
198                 'header' => array(
199                         'align' => array (),
200                         'class' => array (),
201                         'dir' => array (),
202                         'lang' => array(),
203                         'style' => array (),
204                         'xml:lang' => array(),
205                 ),
206                 'hgroup' => array(
207                         'align' => array (),
208                         'class' => array (),
209                         'dir' => array (),
210                         'lang' => array(),
211                         'style' => array (),
212                         'xml:lang' => array(),
213                 ),
214                 'hr' => array (
215                         'align' => array (),
216                         'class' => array (),
217                         'noshade' => array (),
218                         'size' => array (),
219                         'width' => array ()),
220                 'i' => array(),
221                 'img' => array(
222                         'alt' => array (),
223                         'align' => array (),
224                         'border' => array (),
225                         'class' => array (),
226                         'height' => array (),
227                         'hspace' => array (),
228                         'longdesc' => array (),
229                         'vspace' => array (),
230                         'src' => array (),
231                         'style' => array (),
232                         'width' => array ()),
233                 'ins' => array(
234                         'datetime' => array (),
235                         'cite' => array ()),
236                 'kbd' => array(),
237                 'label' => array(
238                         'for' => array ()),
239                 'legend' => array(
240                         'align' => array ()),
241                 'li' => array (
242                         'align' => array (),
243                         'class' => array ()),
244                 'menu' => array (
245                         'class' => array (),
246                         'style' => array (),
247                         'type' => array ()),
248                 'nav' => array(
249                         'align' => array (),
250                         'class' => array (),
251                         'dir' => array (),
252                         'lang' => array(),
253                         'style' => array (),
254                         'xml:lang' => array(),
255                 ),
256                 'p' => array(
257                         'class' => array (),
258                         'align' => array (),
259                         'dir' => array(),
260                         'lang' => array(),
261                         'style' => array (),
262                         'xml:lang' => array()),
263                 'pre' => array(
264                         'style' => array(),
265                         'width' => array ()),
266                 'q' => array(
267                         'cite' => array ()),
268                 's' => array(),
269                 'span' => array (
270                         'class' => array (),
271                         'dir' => array (),
272                         'align' => array (),
273                         'lang' => array (),
274                         'style' => array (),
275                         'title' => array (),
276                         'xml:lang' => array()),
277                 'section' => array(
278                         'align' => array (),
279                         'class' => array (),
280                         'dir' => array (),
281                         'lang' => array(),
282                         'style' => array (),
283                         'xml:lang' => array(),
284                 ),
285                 'strike' => array(),
286                 'strong' => array(),
287                 'sub' => array(),
288                 'summary' => array(
289                         'align' => array (),
290                         'class' => array (),
291                         'dir' => array (),
292                         'lang' => array(),
293                         'style' => array (),
294                         'xml:lang' => array(),
295                 ),
296                 'sup' => array(),
297                 'table' => array(
298                         'align' => array (),
299                         'bgcolor' => array (),
300                         'border' => array (),
301                         'cellpadding' => array (),
302                         'cellspacing' => array (),
303                         'class' => array (),
304                         'dir' => array(),
305                         'id' => array(),
306                         'rules' => array (),
307                         'style' => array (),
308                         'summary' => array (),
309                         'width' => array ()),
310                 'tbody' => array(
311                         'align' => array (),
312                         'char' => array (),
313                         'charoff' => array (),
314                         'valign' => array ()),
315                 'td' => array(
316                         'abbr' => array (),
317                         'align' => array (),
318                         'axis' => array (),
319                         'bgcolor' => array (),
320                         'char' => array (),
321                         'charoff' => array (),
322                         'class' => array (),
323                         'colspan' => array (),
324                         'dir' => array(),
325                         'headers' => array (),
326                         'height' => array (),
327                         'nowrap' => array (),
328                         'rowspan' => array (),
329                         'scope' => array (),
330                         'style' => array (),
331                         'valign' => array (),
332                         'width' => array ()),
333                 'textarea' => array(
334                         'cols' => array (),
335                         'rows' => array (),
336                         'disabled' => array (),
337                         'name' => array (),
338                         'readonly' => array ()),
339                 'tfoot' => array(
340                         'align' => array (),
341                         'char' => array (),
342                         'class' => array (),
343                         'charoff' => array (),
344                         'valign' => array ()),
345                 'th' => array(
346                         'abbr' => array (),
347                         'align' => array (),
348                         'axis' => array (),
349                         'bgcolor' => array (),
350                         'char' => array (),
351                         'charoff' => array (),
352                         'class' => array (),
353                         'colspan' => array (),
354                         'headers' => array (),
355                         'height' => array (),
356                         'nowrap' => array (),
357                         'rowspan' => array (),
358                         'scope' => array (),
359                         'valign' => array (),
360                         'width' => array ()),
361                 'thead' => array(
362                         'align' => array (),
363                         'char' => array (),
364                         'charoff' => array (),
365                         'class' => array (),
366                         'valign' => array ()),
367                 'title' => array(),
368                 'tr' => array(
369                         'align' => array (),
370                         'bgcolor' => array (),
371                         'char' => array (),
372                         'charoff' => array (),
373                         'class' => array (),
374                         'style' => array (),
375                         'valign' => array ()),
376                 'tt' => array(),
377                 'u' => array(),
378                 'ul' => array (
379                         'class' => array (),
380                         'style' => array (),
381                         'type' => array ()),
382                 'ol' => array (
383                         'class' => array (),
384                         'start' => array (),
385                         'style' => array (),
386                         'type' => array ()),
387                 'var' => array ());
388
389         /**
390          * Kses allowed HTML elements.
391          *
392          * @global array $allowedtags
393          * @since 1.0.0
394          */
395         $allowedtags = array(
396                 'a' => array(
397                         'href' => array (),
398                         'title' => array ()),
399                 'abbr' => array(
400                         'title' => array ()),
401                 'acronym' => array(
402                         'title' => array ()),
403                 'b' => array(),
404                 'blockquote' => array(
405                         'cite' => array ()),
406                 //      'br' => array(),
407                 'cite' => array (),
408                 'code' => array(),
409                 'del' => array(
410                         'datetime' => array ()),
411                 //      'dd' => array(),
412                 //      'dl' => array(),
413                 //      'dt' => array(),
414                 'em' => array (), 'i' => array (),
415                 //      'ins' => array('datetime' => array(), 'cite' => array()),
416                 //      'li' => array(),
417                 //      'ol' => array(),
418                 //      'p' => array(),
419                 'q' => array(
420                         'cite' => array ()),
421                 'strike' => array(),
422                 'strong' => array(),
423                 //      'sub' => array(),
424                 //      'sup' => array(),
425                 //      'u' => array(),
426                 //      'ul' => array(),
427         );
428
429         $allowedentitynames = array(
430                 'nbsp',    'iexcl',  'cent',    'pound',  'curren', 'yen',
431                 'brvbar',  'sect',   'uml',     'copy',   'ordf',   'laquo',
432                 'not',     'shy',    'reg',     'macr',   'deg',    'plusmn',
433                 'acute',   'micro',  'para',    'middot', 'cedil',  'ordm',
434                 'raquo',   'iquest', 'Agrave',  'Aacute', 'Acirc',  'Atilde',
435                 'Auml',    'Aring',  'AElig',   'Ccedil', 'Egrave', 'Eacute',
436                 'Ecirc',   'Euml',   'Igrave',  'Iacute', 'Icirc',  'Iuml',
437                 'ETH',     'Ntilde', 'Ograve',  'Oacute', 'Ocirc',  'Otilde',
438                 'Ouml',    'times',  'Oslash',  'Ugrave', 'Uacute', 'Ucirc',
439                 'Uuml',    'Yacute', 'THORN',   'szlig',  'agrave', 'aacute',
440                 'acirc',   'atilde', 'auml',    'aring',  'aelig',  'ccedil',
441                 'egrave',  'eacute', 'ecirc',   'euml',   'igrave', 'iacute',
442                 'icirc',   'iuml',   'eth',     'ntilde', 'ograve', 'oacute',
443                 'ocirc',   'otilde', 'ouml',    'divide', 'oslash', 'ugrave',
444                 'uacute',  'ucirc',  'uuml',    'yacute', 'thorn',  'yuml',
445                 'quot',    'amp',    'lt',      'gt',     'apos',   'OElig',
446                 'oelig',   'Scaron', 'scaron',  'Yuml',   'circ',   'tilde',
447                 'ensp',    'emsp',   'thinsp',  'zwnj',   'zwj',    'lrm',
448                 'rlm',     'ndash',  'mdash',   'lsquo',  'rsquo',  'sbquo',
449                 'ldquo',   'rdquo',  'bdquo',   'dagger', 'Dagger', 'permil',
450                 'lsaquo',  'rsaquo', 'euro',    'fnof',   'Alpha',  'Beta',
451                 'Gamma',   'Delta',  'Epsilon', 'Zeta',   'Eta',    'Theta',
452                 'Iota',    'Kappa',  'Lambda',  'Mu',     'Nu',     'Xi',
453                 'Omicron', 'Pi',     'Rho',     'Sigma',  'Tau',    'Upsilon',
454                 'Phi',     'Chi',    'Psi',     'Omega',  'alpha',  'beta',
455                 'gamma',   'delta',  'epsilon', 'zeta',   'eta',    'theta',
456                 'iota',    'kappa',  'lambda',  'mu',     'nu',     'xi',
457                 'omicron', 'pi',     'rho',     'sigmaf', 'sigma',  'tau',
458                 'upsilon', 'phi',    'chi',     'psi',    'omega',  'thetasym',
459                 'upsih',   'piv',    'bull',    'hellip', 'prime',  'Prime',
460                 'oline',   'frasl',  'weierp',  'image',  'real',   'trade',
461                 'alefsym', 'larr',   'uarr',    'rarr',   'darr',   'harr',
462                 'crarr',   'lArr',   'uArr',    'rArr',   'dArr',   'hArr',
463                 'forall',  'part',   'exist',   'empty',  'nabla',  'isin',
464                 'notin',   'ni',     'prod',    'sum',    'minus',  'lowast',
465                 'radic',   'prop',   'infin',   'ang',    'and',    'or',
466                 'cap',     'cup',    'int',     'sim',    'cong',   'asymp',
467                 'ne',      'equiv',  'le',      'ge',     'sub',    'sup',
468                 'nsub',    'sube',   'supe',    'oplus',  'otimes', 'perp',
469                 'sdot',    'lceil',  'rceil',   'lfloor', 'rfloor', 'lang',
470                 'rang',    'loz',    'spades',  'clubs',  'hearts', 'diams',
471         );
472 }
473
474 /**
475  * Filters content and keeps only allowable HTML elements.
476  *
477  * This function makes sure that only the allowed HTML element names, attribute
478  * names and attribute values plus only sane HTML entities will occur in
479  * $string. You have to remove any slashes from PHP's magic quotes before you
480  * call this function.
481  *
482  * The default allowed protocols are 'http', 'https', 'ftp', 'mailto', 'news',
483  * 'irc', 'gopher', 'nntp', 'feed', 'telnet, 'mms', 'rtsp' and 'svn'. This
484  * covers all common link protocols, except for 'javascript' which should not
485  * be allowed for untrusted users.
486  *
487  * @since 1.0.0
488  *
489  * @param string $string Content to filter through kses
490  * @param array $allowed_html List of allowed HTML elements
491  * @param array $allowed_protocols Optional. Allowed protocol in links.
492  * @return string Filtered content with only allowed HTML elements
493  */
494 function wp_kses($string, $allowed_html, $allowed_protocols = array ()) {
495         $allowed_protocols = wp_parse_args( $allowed_protocols, apply_filters('kses_allowed_protocols', array ('http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn') ));
496         $string = wp_kses_no_null($string);
497         $string = wp_kses_js_entities($string);
498         $string = wp_kses_normalize_entities($string);
499         $allowed_html_fixed = wp_kses_array_lc($allowed_html);
500         $string = wp_kses_hook($string, $allowed_html_fixed, $allowed_protocols); // WP changed the order of these funcs and added args to wp_kses_hook
501         return wp_kses_split($string, $allowed_html_fixed, $allowed_protocols);
502 }
503
504 /**
505  * You add any kses hooks here.
506  *
507  * There is currently only one kses WordPress hook and it is called here. All
508  * parameters are passed to the hooks and expected to recieve a string.
509  *
510  * @since 1.0.0
511  *
512  * @param string $string Content to filter through kses
513  * @param array $allowed_html List of allowed HTML elements
514  * @param array $allowed_protocols Allowed protocol in links
515  * @return string Filtered content through 'pre_kses' hook
516  */
517 function wp_kses_hook($string, $allowed_html, $allowed_protocols) {
518         $string = apply_filters('pre_kses', $string, $allowed_html, $allowed_protocols);
519         return $string;
520 }
521
522 /**
523  * This function returns kses' version number.
524  *
525  * @since 1.0.0
526  *
527  * @return string KSES Version Number
528  */
529 function wp_kses_version() {
530         return '0.2.2';
531 }
532
533 /**
534  * Searches for HTML tags, no matter how malformed.
535  *
536  * It also matches stray ">" characters.
537  *
538  * @since 1.0.0
539  *
540  * @param string $string Content to filter
541  * @param array $allowed_html Allowed HTML elements
542  * @param array $allowed_protocols Allowed protocols to keep
543  * @return string Content with fixed HTML tags
544  */
545 function wp_kses_split($string, $allowed_html, $allowed_protocols) {
546         global $pass_allowed_html, $pass_allowed_protocols;
547         $pass_allowed_html = $allowed_html;
548         $pass_allowed_protocols = $allowed_protocols;
549         return preg_replace_callback('%((<!--.*?(-->|$))|(<[^>]*(>|$)|>))%',
550                 create_function('$match', 'global $pass_allowed_html, $pass_allowed_protocols; return wp_kses_split2($match[1], $pass_allowed_html, $pass_allowed_protocols);'), $string);
551 }
552
553 /**
554  * Callback for wp_kses_split for fixing malformed HTML tags.
555  *
556  * This function does a lot of work. It rejects some very malformed things like
557  * <:::>. It returns an empty string, if the element isn't allowed (look ma, no
558  * strip_tags()!). Otherwise it splits the tag into an element and an attribute
559  * list.
560  *
561  * After the tag is split into an element and an attribute list, it is run
562  * through another filter which will remove illegal attributes and once that is
563  * completed, will be returned.
564  *
565  * @access private
566  * @since 1.0.0
567  * @uses wp_kses_attr()
568  *
569  * @param string $string Content to filter
570  * @param array $allowed_html Allowed HTML elements
571  * @param array $allowed_protocols Allowed protocols to keep
572  * @return string Fixed HTML element
573  */
574 function wp_kses_split2($string, $allowed_html, $allowed_protocols) {
575         $string = wp_kses_stripslashes($string);
576
577         if (substr($string, 0, 1) != '<')
578                 return '&gt;';
579         # It matched a ">" character
580
581         if (preg_match('%^<!--(.*?)(-->)?$%', $string, $matches)) {
582                 $string = str_replace(array('<!--', '-->'), '', $matches[1]);
583                 while ( $string != $newstring = wp_kses($string, $allowed_html, $allowed_protocols) )
584                         $string = $newstring;
585                 if ( $string == '' )
586                         return '';
587                 // prevent multiple dashes in comments
588                 $string = preg_replace('/--+/', '-', $string);
589                 // prevent three dashes closing a comment
590                 $string = preg_replace('/-$/', '', $string);
591                 return "<!--{$string}-->";
592         }
593         # Allow HTML comments
594
595         if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches))
596                 return '';
597         # It's seriously malformed
598
599         $slash = trim($matches[1]);
600         $elem = $matches[2];
601         $attrlist = $matches[3];
602
603         if (!@isset($allowed_html[strtolower($elem)]))
604                 return '';
605         # They are using a not allowed HTML element
606
607         if ($slash != '')
608                 return "<$slash$elem>";
609         # No attributes are allowed for closing elements
610
611         return wp_kses_attr("$slash$elem", $attrlist, $allowed_html, $allowed_protocols);
612 }
613
614 /**
615  * Removes all attributes, if none are allowed for this element.
616  *
617  * If some are allowed it calls wp_kses_hair() to split them further, and then
618  * it builds up new HTML code from the data that kses_hair() returns. It also
619  * removes "<" and ">" characters, if there are any left. One more thing it does
620  * is to check if the tag has a closing XHTML slash, and if it does, it puts one
621  * in the returned code as well.
622  *
623  * @since 1.0.0
624  *
625  * @param string $element HTML element/tag
626  * @param string $attr HTML attributes from HTML element to closing HTML element tag
627  * @param array $allowed_html Allowed HTML elements
628  * @param array $allowed_protocols Allowed protocols to keep
629  * @return string Sanitized HTML element
630  */
631 function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
632         # Is there a closing XHTML slash at the end of the attributes?
633
634         $xhtml_slash = '';
635         if (preg_match('%\s*/\s*$%', $attr))
636                 $xhtml_slash = ' /';
637
638         # Are any attributes allowed at all for this element?
639
640         if (@ count($allowed_html[strtolower($element)]) == 0)
641                 return "<$element$xhtml_slash>";
642
643         # Split it
644
645         $attrarr = wp_kses_hair($attr, $allowed_protocols);
646
647         # Go through $attrarr, and save the allowed attributes for this element
648         # in $attr2
649
650         $attr2 = '';
651
652         foreach ($attrarr as $arreach) {
653                 if (!@ isset ($allowed_html[strtolower($element)][strtolower($arreach['name'])]))
654                         continue; # the attribute is not allowed
655
656                 $current = $allowed_html[strtolower($element)][strtolower($arreach['name'])];
657                 if ($current == '')
658                         continue; # the attribute is not allowed
659
660                 if (!is_array($current))
661                         $attr2 .= ' '.$arreach['whole'];
662                 # there are no checks
663
664                 else {
665                         # there are some checks
666                         $ok = true;
667                         foreach ($current as $currkey => $currval)
668                                 if (!wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval)) {
669                                         $ok = false;
670                                         break;
671                                 }
672
673                         if ( $arreach['name'] == 'style' ) {
674                                 $orig_value = $arreach['value'];
675
676                                 $value = safecss_filter_attr($orig_value);
677
678                                 if ( empty($value) )
679                                         continue;
680
681                                 $arreach['value'] = $value;
682
683                                 $arreach['whole'] = str_replace($orig_value, $value, $arreach['whole']);
684                         }
685
686                         if ($ok)
687                                 $attr2 .= ' '.$arreach['whole']; # it passed them
688                 } # if !is_array($current)
689         } # foreach
690
691         # Remove any "<" or ">" characters
692
693         $attr2 = preg_replace('/[<>]/', '', $attr2);
694
695         return "<$element$attr2$xhtml_slash>";
696 }
697
698 /**
699  * Builds an attribute list from string containing attributes.
700  *
701  * This function does a lot of work. It parses an attribute list into an array
702  * with attribute data, and tries to do the right thing even if it gets weird
703  * input. It will add quotes around attribute values that don't have any quotes
704  * or apostrophes around them, to make it easier to produce HTML code that will
705  * conform to W3C's HTML specification. It will also remove bad URL protocols
706  * from attribute values.  It also reduces duplicate attributes by using the
707  * attribute defined first (foo='bar' foo='baz' will result in foo='bar').
708  *
709  * @since 1.0.0
710  *
711  * @param string $attr Attribute list from HTML element to closing HTML element tag
712  * @param array $allowed_protocols Allowed protocols to keep
713  * @return array List of attributes after parsing
714  */
715 function wp_kses_hair($attr, $allowed_protocols) {
716         $attrarr = array ();
717         $mode = 0;
718         $attrname = '';
719         $uris = array('xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
720
721         # Loop through the whole attribute list
722
723         while (strlen($attr) != 0) {
724                 $working = 0; # Was the last operation successful?
725
726                 switch ($mode) {
727                         case 0 : # attribute name, href for instance
728
729                                 if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
730                                         $attrname = $match[1];
731                                         $working = $mode = 1;
732                                         $attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
733                                 }
734
735                                 break;
736
737                         case 1 : # equals sign or valueless ("selected")
738
739                                 if (preg_match('/^\s*=\s*/', $attr)) # equals sign
740                                         {
741                                         $working = 1;
742                                         $mode = 2;
743                                         $attr = preg_replace('/^\s*=\s*/', '', $attr);
744                                         break;
745                                 }
746
747                                 if (preg_match('/^\s+/', $attr)) # valueless
748                                         {
749                                         $working = 1;
750                                         $mode = 0;
751                                         if(FALSE === array_key_exists($attrname, $attrarr)) {
752                                                 $attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
753                                         }
754                                         $attr = preg_replace('/^\s+/', '', $attr);
755                                 }
756
757                                 break;
758
759                         case 2 : # attribute value, a URL after href= for instance
760
761                                 if (preg_match('%^"([^"]*)"(\s+|/?$)%', $attr, $match))
762                                         # "value"
763                                         {
764                                         $thisval = $match[1];
765                                         if ( in_array($attrname, $uris) )
766                                                 $thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
767
768                                         if(FALSE === array_key_exists($attrname, $attrarr)) {
769                                                 $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
770                                         }
771                                         $working = 1;
772                                         $mode = 0;
773                                         $attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
774                                         break;
775                                 }
776
777                                 if (preg_match("%^'([^']*)'(\s+|/?$)%", $attr, $match))
778                                         # 'value'
779                                         {
780                                         $thisval = $match[1];
781                                         if ( in_array($attrname, $uris) )
782                                                 $thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
783
784                                         if(FALSE === array_key_exists($attrname, $attrarr)) {
785                                                 $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname='$thisval'", 'vless' => 'n');
786                                         }
787                                         $working = 1;
788                                         $mode = 0;
789                                         $attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
790                                         break;
791                                 }
792
793                                 if (preg_match("%^([^\s\"']+)(\s+|/?$)%", $attr, $match))
794                                         # value
795                                         {
796                                         $thisval = $match[1];
797                                         if ( in_array($attrname, $uris) )
798                                                 $thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
799
800                                         if(FALSE === array_key_exists($attrname, $attrarr)) {
801                                                 $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
802                                         }
803                                         # We add quotes to conform to W3C's HTML spec.
804                                         $working = 1;
805                                         $mode = 0;
806                                         $attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
807                                 }
808
809                                 break;
810                 } # switch
811
812                 if ($working == 0) # not well formed, remove and try again
813                 {
814                         $attr = wp_kses_html_error($attr);
815                         $mode = 0;
816                 }
817         } # while
818
819         if ($mode == 1 && FALSE === array_key_exists($attrname, $attrarr))
820                 # special case, for when the attribute list ends with a valueless
821                 # attribute like "selected"
822                 $attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
823
824         return $attrarr;
825 }
826
827 /**
828  * Performs different checks for attribute values.
829  *
830  * The currently implemented checks are "maxlen", "minlen", "maxval", "minval"
831  * and "valueless" with even more checks to come soon.
832  *
833  * @since 1.0.0
834  *
835  * @param string $value Attribute value
836  * @param string $vless Whether the value is valueless. Use 'y' or 'n'
837  * @param string $checkname What $checkvalue is checking for.
838  * @param mixed $checkvalue What constraint the value should pass
839  * @return bool Whether check passes
840  */
841 function wp_kses_check_attr_val($value, $vless, $checkname, $checkvalue) {
842         $ok = true;
843
844         switch (strtolower($checkname)) {
845                 case 'maxlen' :
846                         # The maxlen check makes sure that the attribute value has a length not
847                         # greater than the given value. This can be used to avoid Buffer Overflows
848                         # in WWW clients and various Internet servers.
849
850                         if (strlen($value) > $checkvalue)
851                                 $ok = false;
852                         break;
853
854                 case 'minlen' :
855                         # The minlen check makes sure that the attribute value has a length not
856                         # smaller than the given value.
857
858                         if (strlen($value) < $checkvalue)
859                                 $ok = false;
860                         break;
861
862                 case 'maxval' :
863                         # The maxval check does two things: it checks that the attribute value is
864                         # an integer from 0 and up, without an excessive amount of zeroes or
865                         # whitespace (to avoid Buffer Overflows). It also checks that the attribute
866                         # value is not greater than the given value.
867                         # This check can be used to avoid Denial of Service attacks.
868
869                         if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
870                                 $ok = false;
871                         if ($value > $checkvalue)
872                                 $ok = false;
873                         break;
874
875                 case 'minval' :
876                         # The minval check checks that the attribute value is a positive integer,
877                         # and that it is not smaller than the given value.
878
879                         if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
880                                 $ok = false;
881                         if ($value < $checkvalue)
882                                 $ok = false;
883                         break;
884
885                 case 'valueless' :
886                         # The valueless check checks if the attribute has a value
887                         # (like <a href="blah">) or not (<option selected>). If the given value
888                         # is a "y" or a "Y", the attribute must not have a value.
889                         # If the given value is an "n" or an "N", the attribute must have one.
890
891                         if (strtolower($checkvalue) != $vless)
892                                 $ok = false;
893                         break;
894         } # switch
895
896         return $ok;
897 }
898
899 /**
900  * Sanitize string from bad protocols.
901  *
902  * This function removes all non-allowed protocols from the beginning of
903  * $string. It ignores whitespace and the case of the letters, and it does
904  * understand HTML entities. It does its work in a while loop, so it won't be
905  * fooled by a string like "javascript:javascript:alert(57)".
906  *
907  * @since 1.0.0
908  *
909  * @param string $string Content to filter bad protocols from
910  * @param array $allowed_protocols Allowed protocols to keep
911  * @return string Filtered content
912  */
913 function wp_kses_bad_protocol($string, $allowed_protocols) {
914         $string = wp_kses_no_null($string);
915         $string2 = $string.'a';
916
917         while ($string != $string2) {
918                 $string2 = $string;
919                 $string = wp_kses_bad_protocol_once($string, $allowed_protocols);
920         } # while
921
922         return $string;
923 }
924
925 /**
926  * Removes any NULL characters in $string.
927  *
928  * @since 1.0.0
929  *
930  * @param string $string
931  * @return string
932  */
933 function wp_kses_no_null($string) {
934         $string = preg_replace('/\0+/', '', $string);
935         $string = preg_replace('/(\\\\0)+/', '', $string);
936
937         return $string;
938 }
939
940 /**
941  * Strips slashes from in front of quotes.
942  *
943  * This function changes the character sequence  \"  to just  ". It leaves all
944  * other slashes alone. It's really weird, but the quoting from
945  * preg_replace(//e) seems to require this.
946  *
947  * @since 1.0.0
948  *
949  * @param string $string String to strip slashes
950  * @return string Fixed strings with quoted slashes
951  */
952 function wp_kses_stripslashes($string) {
953         return preg_replace('%\\\\"%', '"', $string);
954 }
955
956 /**
957  * Goes through an array and changes the keys to all lower case.
958  *
959  * @since 1.0.0
960  *
961  * @param array $inarray Unfiltered array
962  * @return array Fixed array with all lowercase keys
963  */
964 function wp_kses_array_lc($inarray) {
965         $outarray = array ();
966
967         foreach ( (array) $inarray as $inkey => $inval) {
968                 $outkey = strtolower($inkey);
969                 $outarray[$outkey] = array ();
970
971                 foreach ( (array) $inval as $inkey2 => $inval2) {
972                         $outkey2 = strtolower($inkey2);
973                         $outarray[$outkey][$outkey2] = $inval2;
974                 } # foreach $inval
975         } # foreach $inarray
976
977         return $outarray;
978 }
979
980 /**
981  * Removes the HTML JavaScript entities found in early versions of Netscape 4.
982  *
983  * @since 1.0.0
984  *
985  * @param string $string
986  * @return string
987  */
988 function wp_kses_js_entities($string) {
989         return preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
990 }
991
992 /**
993  * Handles parsing errors in wp_kses_hair().
994  *
995  * The general plan is to remove everything to and including some whitespace,
996  * but it deals with quotes and apostrophes as well.
997  *
998  * @since 1.0.0
999  *
1000  * @param string $string
1001  * @return string
1002  */
1003 function wp_kses_html_error($string) {
1004         return preg_replace('/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $string);
1005 }
1006
1007 /**
1008  * Sanitizes content from bad protocols and other characters.
1009  *
1010  * This function searches for URL protocols at the beginning of $string, while
1011  * handling whitespace and HTML entities.
1012  *
1013  * @since 1.0.0
1014  *
1015  * @param string $string Content to check for bad protocols
1016  * @param string $allowed_protocols Allowed protocols
1017  * @return string Sanitized content
1018  */
1019 function wp_kses_bad_protocol_once($string, $allowed_protocols) {
1020         global $_kses_allowed_protocols;
1021         $_kses_allowed_protocols = $allowed_protocols;
1022
1023         $string2 = preg_split('/:|&#58;|&#x3a;/i', $string, 2);
1024         if ( isset($string2[1]) && !preg_match('%/\?%', $string2[0]) )
1025                 $string = wp_kses_bad_protocol_once2($string2[0]) . trim($string2[1]);
1026         else
1027                 $string = preg_replace_callback('/^((&[^;]*;|[\sA-Za-z0-9])*)'.'(:|&#58;|&#[Xx]3[Aa];)\s*/', 'wp_kses_bad_protocol_once2', $string);
1028
1029         return $string;
1030 }
1031
1032 /**
1033  * Callback for wp_kses_bad_protocol_once() regular expression.
1034  *
1035  * This function processes URL protocols, checks to see if they're in the
1036  * white-list or not, and returns different data depending on the answer.
1037  *
1038  * @access private
1039  * @since 1.0.0
1040  *
1041  * @param mixed $matches string or preg_replace_callback() matches array to check for bad protocols
1042  * @return string Sanitized content
1043  */
1044 function wp_kses_bad_protocol_once2($matches) {
1045         global $_kses_allowed_protocols;
1046
1047         if ( is_array($matches) ) {
1048                 if ( empty($matches[1]) )
1049                         return '';
1050
1051                 $string = $matches[1];
1052         } else {
1053                 $string = $matches;
1054         }
1055
1056         $string2 = wp_kses_decode_entities($string);
1057         $string2 = preg_replace('/\s/', '', $string2);
1058         $string2 = wp_kses_no_null($string2);
1059         $string2 = strtolower($string2);
1060
1061         $allowed = false;
1062         foreach ( (array) $_kses_allowed_protocols as $one_protocol)
1063                 if (strtolower($one_protocol) == $string2) {
1064                         $allowed = true;
1065                         break;
1066                 }
1067
1068         if ($allowed)
1069                 return "$string2:";
1070         else
1071                 return '';
1072 }
1073
1074 /**
1075  * Converts and fixes HTML entities.
1076  *
1077  * This function normalizes HTML entities. It will convert "AT&T" to the correct
1078  * "AT&amp;T", "&#00058;" to "&#58;", "&#XYZZY;" to "&amp;#XYZZY;" and so on.
1079  *
1080  * @since 1.0.0
1081  *
1082  * @param string $string Content to normalize entities
1083  * @return string Content with normalized entities
1084  */
1085 function wp_kses_normalize_entities($string) {
1086         # Disarm all entities by converting & to &amp;
1087
1088         $string = str_replace('&', '&amp;', $string);
1089
1090         # Change back the allowed entities in our entity whitelist
1091
1092         $string = preg_replace_callback('/&amp;([A-Za-z]{2,8});/', 'wp_kses_named_entities', $string);
1093         $string = preg_replace_callback('/&amp;#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string);
1094         $string = preg_replace_callback('/&amp;#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string);
1095
1096         return $string;
1097 }
1098
1099 /**
1100  * Callback for wp_kses_normalize_entities() regular expression.
1101  *
1102  * This function only accepts valid named entity references, which are finite,
1103  * case-sensitive, and highly scrutinized by HTML and XML validators.
1104  *
1105  * @since 3.0.0
1106  *
1107  * @param array $matches preg_replace_callback() matches array
1108  * @return string Correctly encoded entity
1109  */
1110 function wp_kses_named_entities($matches) {
1111         global $allowedentitynames;
1112
1113         if ( empty($matches[1]) )
1114                 return '';
1115
1116         $i = $matches[1];
1117         return ( ( ! in_array($i, $allowedentitynames) ) ? "&amp;$i;" : "&$i;" );
1118 }
1119
1120 /**
1121  * Callback for wp_kses_normalize_entities() regular expression.
1122  *
1123  * This function helps wp_kses_normalize_entities() to only accept 16 bit values
1124  * and nothing more for &#number; entities.
1125  *
1126  * @access private
1127  * @since 1.0.0
1128  *
1129  * @param array $matches preg_replace_callback() matches array
1130  * @return string Correctly encoded entity
1131  */
1132 function wp_kses_normalize_entities2($matches) {
1133         if ( empty($matches[1]) )
1134                 return '';
1135
1136         $i = $matches[1];
1137         if (valid_unicode($i)) {
1138                 $i = str_pad(ltrim($i,'0'), 3, '0', STR_PAD_LEFT);
1139                 $i = "&#$i;";
1140         } else {
1141                 $i = "&amp;#$i;";
1142         }
1143
1144         return $i;
1145 }
1146
1147 /**
1148  * Callback for wp_kses_normalize_entities() for regular expression.
1149  *
1150  * This function helps wp_kses_normalize_entities() to only accept valid Unicode
1151  * numeric entities in hex form.
1152  *
1153  * @access private
1154  *
1155  * @param array $matches preg_replace_callback() matches array
1156  * @return string Correctly encoded entity
1157  */
1158 function wp_kses_normalize_entities3($matches) {
1159         if ( empty($matches[1]) )
1160                 return '';
1161
1162         $hexchars = $matches[1];
1163         return ( ( ! valid_unicode(hexdec($hexchars)) ) ? "&amp;#x$hexchars;" : '&#x'.ltrim($hexchars,'0').';' );
1164 }
1165
1166 /**
1167  * Helper function to determine if a Unicode value is valid.
1168  *
1169  * @param int $i Unicode value
1170  * @return bool true if the value was a valid Unicode number
1171  */
1172 function valid_unicode($i) {
1173         return ( $i == 0x9 || $i == 0xa || $i == 0xd ||
1174                         ($i >= 0x20 && $i <= 0xd7ff) ||
1175                         ($i >= 0xe000 && $i <= 0xfffd) ||
1176                         ($i >= 0x10000 && $i <= 0x10ffff) );
1177 }
1178
1179 /**
1180  * Convert all entities to their character counterparts.
1181  *
1182  * This function decodes numeric HTML entities (&#65; and &#x41;). It doesn't do
1183  * anything with other entities like &auml;, but we don't need them in the URL
1184  * protocol whitelisting system anyway.
1185  *
1186  * @since 1.0.0
1187  *
1188  * @param string $string Content to change entities
1189  * @return string Content after decoded entities
1190  */
1191 function wp_kses_decode_entities($string) {
1192         $string = preg_replace_callback('/&#([0-9]+);/', '_wp_kses_decode_entities_chr', $string);
1193         $string = preg_replace_callback('/&#[Xx]([0-9A-Fa-f]+);/', '_wp_kses_decode_entities_chr_hexdec', $string);
1194
1195         return $string;
1196 }
1197
1198 /**
1199  * Regex callback for wp_kses_decode_entities()
1200  *
1201  * @param array $match preg match
1202  * @return string
1203  */
1204 function _wp_kses_decode_entities_chr( $match ) {
1205         return chr( $match[1] );
1206 }
1207
1208 /**
1209  * Regex callback for wp_kses_decode_entities()
1210  *
1211  * @param array $match preg match
1212  * @return string
1213  */
1214 function _wp_kses_decode_entities_chr_hexdec( $match ) {
1215         return chr( hexdec( $match[1] ) );
1216 }
1217
1218 /**
1219  * Sanitize content with allowed HTML Kses rules.
1220  *
1221  * @since 1.0.0
1222  * @uses $allowedtags
1223  *
1224  * @param string $data Content to filter, expected to be escaped with slashes
1225  * @return string Filtered content
1226  */
1227 function wp_filter_kses($data) {
1228         global $allowedtags;
1229         return addslashes( wp_kses(stripslashes( $data ), $allowedtags) );
1230 }
1231
1232 /**
1233  * Sanitize content with allowed HTML Kses rules.
1234  *
1235  * @since 2.9.0
1236  * @uses $allowedtags
1237  *
1238  * @param string $data Content to filter, expected to not be escaped
1239  * @return string Filtered content
1240  */
1241 function wp_kses_data($data) {
1242         global $allowedtags;
1243         return wp_kses( $data , $allowedtags );
1244 }
1245
1246 /**
1247  * Sanitize content for allowed HTML tags for post content.
1248  *
1249  * Post content refers to the page contents of the 'post' type and not $_POST
1250  * data from forms.
1251  *
1252  * @since 2.0.0
1253  * @uses $allowedposttags
1254  *
1255  * @param string $data Post content to filter, expected to be escaped with slashes
1256  * @return string Filtered post content with allowed HTML tags and attributes intact.
1257  */
1258 function wp_filter_post_kses($data) {
1259         global $allowedposttags;
1260         return addslashes ( wp_kses(stripslashes( $data ), $allowedposttags) );
1261 }
1262
1263 /**
1264  * Sanitize content for allowed HTML tags for post content.
1265  *
1266  * Post content refers to the page contents of the 'post' type and not $_POST
1267  * data from forms.
1268  *
1269  * @since 2.9.0
1270  * @uses $allowedposttags
1271  *
1272  * @param string $data Post content to filter
1273  * @return string Filtered post content with allowed HTML tags and attributes intact.
1274  */
1275 function wp_kses_post($data) {
1276         global $allowedposttags;
1277         return wp_kses( $data , $allowedposttags );
1278 }
1279
1280 /**
1281  * Strips all of the HTML in the content.
1282  *
1283  * @since 2.1.0
1284  *
1285  * @param string $data Content to strip all HTML from
1286  * @return string Filtered content without any HTML
1287  */
1288 function wp_filter_nohtml_kses($data) {
1289         return addslashes ( wp_kses(stripslashes( $data ), array()) );
1290 }
1291
1292 /**
1293  * Adds all Kses input form content filters.
1294  *
1295  * All hooks have default priority. The wp_filter_kses() function is added to
1296  * the 'pre_comment_content' and 'title_save_pre' hooks.
1297  *
1298  * The wp_filter_post_kses() function is added to the 'content_save_pre',
1299  * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks.
1300  *
1301  * @since 2.0.0
1302  * @uses add_filter() See description for what functions are added to what hooks.
1303  */
1304 function kses_init_filters() {
1305         // Normal filtering.
1306         add_filter('pre_comment_content', 'wp_filter_kses');
1307         add_filter('title_save_pre', 'wp_filter_kses');
1308
1309         // Post filtering
1310         add_filter('content_save_pre', 'wp_filter_post_kses');
1311         add_filter('excerpt_save_pre', 'wp_filter_post_kses');
1312         add_filter('content_filtered_save_pre', 'wp_filter_post_kses');
1313 }
1314
1315 /**
1316  * Removes all Kses input form content filters.
1317  *
1318  * A quick procedural method to removing all of the filters that kses uses for
1319  * content in WordPress Loop.
1320  *
1321  * Does not remove the kses_init() function from 'init' hook (priority is
1322  * default). Also does not remove kses_init() function from 'set_current_user'
1323  * hook (priority is also default).
1324  *
1325  * @since 2.0.6
1326  */
1327 function kses_remove_filters() {
1328         // Normal filtering.
1329         remove_filter('pre_comment_content', 'wp_filter_kses');
1330         remove_filter('title_save_pre', 'wp_filter_kses');
1331
1332         // Post filtering
1333         remove_filter('content_save_pre', 'wp_filter_post_kses');
1334         remove_filter('excerpt_save_pre', 'wp_filter_post_kses');
1335         remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
1336 }
1337
1338 /**
1339  * Sets up most of the Kses filters for input form content.
1340  *
1341  * If you remove the kses_init() function from 'init' hook and
1342  * 'set_current_user' (priority is default), then none of the Kses filter hooks
1343  * will be added.
1344  *
1345  * First removes all of the Kses filters in case the current user does not need
1346  * to have Kses filter the content. If the user does not have unfiltered html
1347  * capability, then Kses filters are added.
1348  *
1349  * @uses kses_remove_filters() Removes the Kses filters
1350  * @uses kses_init_filters() Adds the Kses filters back if the user
1351  *              does not have unfiltered HTML capability.
1352  * @since 2.0.0
1353  */
1354 function kses_init() {
1355         kses_remove_filters();
1356
1357         if (current_user_can('unfiltered_html') == false)
1358                 kses_init_filters();
1359 }
1360
1361 add_action('init', 'kses_init');
1362 add_action('set_current_user', 'kses_init');
1363
1364 /**
1365  * Inline CSS filter
1366  *
1367  * @since 2.8.1
1368  */
1369 function safecss_filter_attr( $css, $deprecated = '' ) {
1370         if ( !empty( $deprecated ) )
1371                 _deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented
1372
1373         $css = wp_kses_no_null($css);
1374         $css = str_replace(array("\n","\r","\t"), '', $css);
1375
1376         if ( preg_match( '%[\\(&]|/\*%', $css ) ) // remove any inline css containing \ ( & or comments
1377                 return '';
1378
1379         $css_array = split( ';', trim( $css ) );
1380         $allowed_attr = apply_filters( 'safe_style_css', array( 'text-align', 'margin', 'color', 'float',
1381         'border', 'background', 'background-color', 'border-bottom', 'border-bottom-color',
1382         'border-bottom-style', 'border-bottom-width', 'border-collapse', 'border-color', 'border-left',
1383         'border-left-color', 'border-left-style', 'border-left-width', 'border-right', 'border-right-color',
1384         'border-right-style', 'border-right-width', 'border-spacing', 'border-style', 'border-top',
1385         'border-top-color', 'border-top-style', 'border-top-width', 'border-width', 'caption-side',
1386         'clear', 'cursor', 'direction', 'font', 'font-family', 'font-size', 'font-style',
1387         'font-variant', 'font-weight', 'height', 'letter-spacing', 'line-height', 'margin-bottom',
1388         'margin-left', 'margin-right', 'margin-top', 'overflow', 'padding', 'padding-bottom',
1389         'padding-left', 'padding-right', 'padding-top', 'text-decoration', 'text-indent', 'vertical-align',
1390         'width' ) );
1391
1392         if ( empty($allowed_attr) )
1393                 return $css;
1394
1395         $css = '';
1396         foreach ( $css_array as $css_item ) {
1397                 if ( $css_item == '' )
1398                         continue;
1399                 $css_item = trim( $css_item );
1400                 $found = false;
1401                 if ( strpos( $css_item, ':' ) === false ) {
1402                         $found = true;
1403                 } else {
1404                         $parts = split( ':', $css_item );
1405                         if ( in_array( trim( $parts[0] ), $allowed_attr ) )
1406                                 $found = true;
1407                 }
1408                 if ( $found ) {
1409                         if( $css != '' )
1410                                 $css .= ';';
1411                         $css .= $css_item;
1412                 }
1413         }
1414
1415         return $css;
1416 }