/wp-includes/kses.php

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