Wordpress 3.0.4
[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 ( strtolower($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(strtolower($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(strtolower($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(strtolower($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         $string2 = preg_split( '/:|&#0*58;|&#x0*3a;/i', $string, 2 );
1021         if ( isset($string2[1]) && ! preg_match('%/\?%', $string2[0]) )
1022                 $string = wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols ) . trim( $string2[1] );
1023
1024         return $string;
1025 }
1026
1027 /**
1028  * Callback for wp_kses_bad_protocol_once() regular expression.
1029  *
1030  * This function processes URL protocols, checks to see if they're in the
1031  * white-list or not, and returns different data depending on the answer.
1032  *
1033  * @access private
1034  * @since 1.0.0
1035  *
1036  * @param string $string URI scheme to check against the whitelist
1037  * @param string $allowed_protocols Allowed protocols
1038  * @return string Sanitized content
1039  */
1040 function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) {
1041         $string2 = wp_kses_decode_entities($string);
1042         $string2 = preg_replace('/\s/', '', $string2);
1043         $string2 = wp_kses_no_null($string2);
1044         $string2 = strtolower($string2);
1045
1046         $allowed = false;
1047         foreach ( (array) $allowed_protocols as $one_protocol )
1048                 if ( strtolower($one_protocol) == $string2 ) {
1049                         $allowed = true;
1050                         break;
1051                 }
1052
1053         if ($allowed)
1054                 return "$string2:";
1055         else
1056                 return '';
1057 }
1058
1059 /**
1060  * Converts and fixes HTML entities.
1061  *
1062  * This function normalizes HTML entities. It will convert "AT&T" to the correct
1063  * "AT&amp;T", "&#00058;" to "&#58;", "&#XYZZY;" to "&amp;#XYZZY;" and so on.
1064  *
1065  * @since 1.0.0
1066  *
1067  * @param string $string Content to normalize entities
1068  * @return string Content with normalized entities
1069  */
1070 function wp_kses_normalize_entities($string) {
1071         # Disarm all entities by converting & to &amp;
1072
1073         $string = str_replace('&', '&amp;', $string);
1074
1075         # Change back the allowed entities in our entity whitelist
1076
1077         $string = preg_replace_callback('/&amp;([A-Za-z]{2,8});/', 'wp_kses_named_entities', $string);
1078         $string = preg_replace_callback('/&amp;#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string);
1079         $string = preg_replace_callback('/&amp;#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string);
1080
1081         return $string;
1082 }
1083
1084 /**
1085  * Callback for wp_kses_normalize_entities() regular expression.
1086  *
1087  * This function only accepts valid named entity references, which are finite,
1088  * case-sensitive, and highly scrutinized by HTML and XML validators.
1089  *
1090  * @since 3.0.0
1091  *
1092  * @param array $matches preg_replace_callback() matches array
1093  * @return string Correctly encoded entity
1094  */
1095 function wp_kses_named_entities($matches) {
1096         global $allowedentitynames;
1097
1098         if ( empty($matches[1]) )
1099                 return '';
1100
1101         $i = $matches[1];
1102         return ( ( ! in_array($i, $allowedentitynames) ) ? "&amp;$i;" : "&$i;" );
1103 }
1104
1105 /**
1106  * Callback for wp_kses_normalize_entities() regular expression.
1107  *
1108  * This function helps wp_kses_normalize_entities() to only accept 16 bit values
1109  * and nothing more for &#number; entities.
1110  *
1111  * @access private
1112  * @since 1.0.0
1113  *
1114  * @param array $matches preg_replace_callback() matches array
1115  * @return string Correctly encoded entity
1116  */
1117 function wp_kses_normalize_entities2($matches) {
1118         if ( empty($matches[1]) )
1119                 return '';
1120
1121         $i = $matches[1];
1122         if (valid_unicode($i)) {
1123                 $i = str_pad(ltrim($i,'0'), 3, '0', STR_PAD_LEFT);
1124                 $i = "&#$i;";
1125         } else {
1126                 $i = "&amp;#$i;";
1127         }
1128
1129         return $i;
1130 }
1131
1132 /**
1133  * Callback for wp_kses_normalize_entities() for regular expression.
1134  *
1135  * This function helps wp_kses_normalize_entities() to only accept valid Unicode
1136  * numeric entities in hex form.
1137  *
1138  * @access private
1139  *
1140  * @param array $matches preg_replace_callback() matches array
1141  * @return string Correctly encoded entity
1142  */
1143 function wp_kses_normalize_entities3($matches) {
1144         if ( empty($matches[1]) )
1145                 return '';
1146
1147         $hexchars = $matches[1];
1148         return ( ( ! valid_unicode(hexdec($hexchars)) ) ? "&amp;#x$hexchars;" : '&#x'.ltrim($hexchars,'0').';' );
1149 }
1150
1151 /**
1152  * Helper function to determine if a Unicode value is valid.
1153  *
1154  * @param int $i Unicode value
1155  * @return bool true if the value was a valid Unicode number
1156  */
1157 function valid_unicode($i) {
1158         return ( $i == 0x9 || $i == 0xa || $i == 0xd ||
1159                         ($i >= 0x20 && $i <= 0xd7ff) ||
1160                         ($i >= 0xe000 && $i <= 0xfffd) ||
1161                         ($i >= 0x10000 && $i <= 0x10ffff) );
1162 }
1163
1164 /**
1165  * Convert all entities to their character counterparts.
1166  *
1167  * This function decodes numeric HTML entities (&#65; and &#x41;). It doesn't do
1168  * anything with other entities like &auml;, but we don't need them in the URL
1169  * protocol whitelisting system anyway.
1170  *
1171  * @since 1.0.0
1172  *
1173  * @param string $string Content to change entities
1174  * @return string Content after decoded entities
1175  */
1176 function wp_kses_decode_entities($string) {
1177         $string = preg_replace_callback('/&#([0-9]+);/', '_wp_kses_decode_entities_chr', $string);
1178         $string = preg_replace_callback('/&#[Xx]([0-9A-Fa-f]+);/', '_wp_kses_decode_entities_chr_hexdec', $string);
1179
1180         return $string;
1181 }
1182
1183 /**
1184  * Regex callback for wp_kses_decode_entities()
1185  *
1186  * @param array $match preg match
1187  * @return string
1188  */
1189 function _wp_kses_decode_entities_chr( $match ) {
1190         return chr( $match[1] );
1191 }
1192
1193 /**
1194  * Regex callback for wp_kses_decode_entities()
1195  *
1196  * @param array $match preg match
1197  * @return string
1198  */
1199 function _wp_kses_decode_entities_chr_hexdec( $match ) {
1200         return chr( hexdec( $match[1] ) );
1201 }
1202
1203 /**
1204  * Sanitize content with allowed HTML Kses rules.
1205  *
1206  * @since 1.0.0
1207  * @uses $allowedtags
1208  *
1209  * @param string $data Content to filter, expected to be escaped with slashes
1210  * @return string Filtered content
1211  */
1212 function wp_filter_kses($data) {
1213         global $allowedtags;
1214         return addslashes( wp_kses(stripslashes( $data ), $allowedtags) );
1215 }
1216
1217 /**
1218  * Sanitize content with allowed HTML Kses rules.
1219  *
1220  * @since 2.9.0
1221  * @uses $allowedtags
1222  *
1223  * @param string $data Content to filter, expected to not be escaped
1224  * @return string Filtered content
1225  */
1226 function wp_kses_data($data) {
1227         global $allowedtags;
1228         return wp_kses( $data , $allowedtags );
1229 }
1230
1231 /**
1232  * Sanitize content for allowed HTML tags for post content.
1233  *
1234  * Post content refers to the page contents of the 'post' type and not $_POST
1235  * data from forms.
1236  *
1237  * @since 2.0.0
1238  * @uses $allowedposttags
1239  *
1240  * @param string $data Post content to filter, expected to be escaped with slashes
1241  * @return string Filtered post content with allowed HTML tags and attributes intact.
1242  */
1243 function wp_filter_post_kses($data) {
1244         global $allowedposttags;
1245         return addslashes ( wp_kses(stripslashes( $data ), $allowedposttags) );
1246 }
1247
1248 /**
1249  * Sanitize content for allowed HTML tags for post content.
1250  *
1251  * Post content refers to the page contents of the 'post' type and not $_POST
1252  * data from forms.
1253  *
1254  * @since 2.9.0
1255  * @uses $allowedposttags
1256  *
1257  * @param string $data Post content to filter
1258  * @return string Filtered post content with allowed HTML tags and attributes intact.
1259  */
1260 function wp_kses_post($data) {
1261         global $allowedposttags;
1262         return wp_kses( $data , $allowedposttags );
1263 }
1264
1265 /**
1266  * Strips all of the HTML in the content.
1267  *
1268  * @since 2.1.0
1269  *
1270  * @param string $data Content to strip all HTML from
1271  * @return string Filtered content without any HTML
1272  */
1273 function wp_filter_nohtml_kses($data) {
1274         return addslashes ( wp_kses(stripslashes( $data ), array()) );
1275 }
1276
1277 /**
1278  * Adds all Kses input form content filters.
1279  *
1280  * All hooks have default priority. The wp_filter_kses() function is added to
1281  * the 'pre_comment_content' and 'title_save_pre' hooks.
1282  *
1283  * The wp_filter_post_kses() function is added to the 'content_save_pre',
1284  * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks.
1285  *
1286  * @since 2.0.0
1287  * @uses add_filter() See description for what functions are added to what hooks.
1288  */
1289 function kses_init_filters() {
1290         // Normal filtering.
1291         add_filter('pre_comment_content', 'wp_filter_kses');
1292         add_filter('title_save_pre', 'wp_filter_kses');
1293
1294         // Post filtering
1295         add_filter('content_save_pre', 'wp_filter_post_kses');
1296         add_filter('excerpt_save_pre', 'wp_filter_post_kses');
1297         add_filter('content_filtered_save_pre', 'wp_filter_post_kses');
1298 }
1299
1300 /**
1301  * Removes all Kses input form content filters.
1302  *
1303  * A quick procedural method to removing all of the filters that kses uses for
1304  * content in WordPress Loop.
1305  *
1306  * Does not remove the kses_init() function from 'init' hook (priority is
1307  * default). Also does not remove kses_init() function from 'set_current_user'
1308  * hook (priority is also default).
1309  *
1310  * @since 2.0.6
1311  */
1312 function kses_remove_filters() {
1313         // Normal filtering.
1314         remove_filter('pre_comment_content', 'wp_filter_kses');
1315         remove_filter('title_save_pre', 'wp_filter_kses');
1316
1317         // Post filtering
1318         remove_filter('content_save_pre', 'wp_filter_post_kses');
1319         remove_filter('excerpt_save_pre', 'wp_filter_post_kses');
1320         remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
1321 }
1322
1323 /**
1324  * Sets up most of the Kses filters for input form content.
1325  *
1326  * If you remove the kses_init() function from 'init' hook and
1327  * 'set_current_user' (priority is default), then none of the Kses filter hooks
1328  * will be added.
1329  *
1330  * First removes all of the Kses filters in case the current user does not need
1331  * to have Kses filter the content. If the user does not have unfiltered html
1332  * capability, then Kses filters are added.
1333  *
1334  * @uses kses_remove_filters() Removes the Kses filters
1335  * @uses kses_init_filters() Adds the Kses filters back if the user
1336  *              does not have unfiltered HTML capability.
1337  * @since 2.0.0
1338  */
1339 function kses_init() {
1340         kses_remove_filters();
1341
1342         if (current_user_can('unfiltered_html') == false)
1343                 kses_init_filters();
1344 }
1345
1346 add_action('init', 'kses_init');
1347 add_action('set_current_user', 'kses_init');
1348
1349 /**
1350  * Inline CSS filter
1351  *
1352  * @since 2.8.1
1353  */
1354 function safecss_filter_attr( $css, $deprecated = '' ) {
1355         if ( !empty( $deprecated ) )
1356                 _deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented
1357
1358         $css = wp_kses_no_null($css);
1359         $css = str_replace(array("\n","\r","\t"), '', $css);
1360
1361         if ( preg_match( '%[\\(&=}]|/\*%', $css ) ) // remove any inline css containing \ ( & } = or comments
1362                 return '';
1363
1364         $css_array = explode( ';', trim( $css ) );
1365         $allowed_attr = apply_filters( 'safe_style_css', array( 'text-align', 'margin', 'color', 'float',
1366         'border', 'background', 'background-color', 'border-bottom', 'border-bottom-color',
1367         'border-bottom-style', 'border-bottom-width', 'border-collapse', 'border-color', 'border-left',
1368         'border-left-color', 'border-left-style', 'border-left-width', 'border-right', 'border-right-color',
1369         'border-right-style', 'border-right-width', 'border-spacing', 'border-style', 'border-top',
1370         'border-top-color', 'border-top-style', 'border-top-width', 'border-width', 'caption-side',
1371         'clear', 'cursor', 'direction', 'font', 'font-family', 'font-size', 'font-style',
1372         'font-variant', 'font-weight', 'height', 'letter-spacing', 'line-height', 'margin-bottom',
1373         'margin-left', 'margin-right', 'margin-top', 'overflow', 'padding', 'padding-bottom',
1374         'padding-left', 'padding-right', 'padding-top', 'text-decoration', 'text-indent', 'vertical-align',
1375         'width' ) );
1376
1377         if ( empty($allowed_attr) )
1378                 return $css;
1379
1380         $css = '';
1381         foreach ( $css_array as $css_item ) {
1382                 if ( $css_item == '' )
1383                         continue;
1384                 $css_item = trim( $css_item );
1385                 $found = false;
1386                 if ( strpos( $css_item, ':' ) === false ) {
1387                         $found = true;
1388                 } else {
1389                         $parts = split( ':', $css_item );
1390                         if ( in_array( trim( $parts[0] ), $allowed_attr ) )
1391                                 $found = true;
1392                 }
1393                 if ( $found ) {
1394                         if( $css != '' )
1395                                 $css .= ';';
1396                         $css .= $css_item;
1397                 }
1398         }
1399
1400         return $css;
1401 }