PageRenderTime 92ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/e107_handlers/e_parse_class.php

https://github.com/CasperGemini/e107
PHP | 3465 lines | 2420 code | 436 blank | 609 comment | 274 complexity | 962cccc252f9cb2ff3168f1ac8fa9800 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /*
  3. * e107 website system
  4. *
  5. * Copyright (C) 2008-2011 e107 Inc (e107.org)
  6. * Released under the terms and conditions of the
  7. * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
  8. *
  9. * Text processing and parsing functions
  10. *
  11. * $URL$
  12. * $Id$
  13. *
  14. */
  15. /**
  16. * @package e107
  17. * @subpackage e107_handlers
  18. * @version $Id$
  19. *
  20. * Text processing and parsing functions.
  21. * Simple parse data model.
  22. */
  23. if (!defined('e107_INIT')) { exit(); }
  24. // Directory for the hard-coded utf-8 handling routines
  25. define('E_UTF8_PACK', e_HANDLER.'utf8/');
  26. define("E_NL", chr(2));
  27. class e_parse extends e_parser
  28. {
  29. /**
  30. * Determine how to handle utf-8.
  31. * 0 = 'do nothing'
  32. * 1 = 'use mb_string'
  33. * 2 = emulation
  34. *
  35. * @var integer
  36. */
  37. protected $utfAction;
  38. // Shortcode processor - see __get()
  39. //var $e_sc;
  40. // BBCode processor
  41. var $e_bb;
  42. // Profanity filter
  43. var $e_pf;
  44. // Emote filter
  45. var $e_emote;
  46. // 'Hooked' parsers (array)
  47. var $e_hook;
  48. var $search = array('&amp;#039;', '&#039;', '&#39;', '&quot;', 'onerror', '&gt;', '&amp;quot;', ' & ');
  49. var $replace = array("'", "'", "'", '"', 'one<i></i>rror', '>', '"', ' &amp; ');
  50. // Set to TRUE or FALSE once it has been calculated
  51. var $e_highlighting;
  52. // Highlight query
  53. var $e_query;
  54. public $thumbWidth = 100;
  55. public $thumbHeight = 0;
  56. public $thumbCrop = 0;
  57. // Set up the defaults
  58. var $e_optDefault = array(
  59. // default context: reflects legacy settings (many items enabled)
  60. 'context' => 'OLDDEFAULT',
  61. //
  62. 'fromadmin' => FALSE,
  63. // Enable emote display
  64. 'emotes' => TRUE,
  65. // Convert defines(constants) within text.
  66. 'defs' => FALSE,
  67. // replace all {e_XXX} constants with their e107 value - 'rel' or 'abs'
  68. 'constants' => FALSE,
  69. // Enable hooked parsers
  70. 'hook' => TRUE,
  71. // Allow scripts through (new for 0.8)
  72. 'scripts' => TRUE,
  73. // Make links clickable
  74. 'link_click' => TRUE,
  75. // Substitute on clickable links (only if link_click == TRUE)
  76. 'link_replace' => TRUE,
  77. // Parse shortcodes - TRUE enables parsing
  78. 'parse_sc' => FALSE,
  79. // remove HTML tags.
  80. 'no_tags' => FALSE,
  81. // Restore entity form of quotes and such to single characters - TRUE disables
  82. 'value' => FALSE,
  83. // Line break compression - TRUE removes newline characters
  84. 'nobreak' => FALSE,
  85. // Retain newlines - wraps to \n instead of <br /> if TRUE (for non-HTML email text etc)
  86. 'retain_nl' => FALSE
  87. );
  88. // Super modifiers override default option values
  89. var $e_SuperMods = array(
  90. //text is part of a title (e.g. news title)
  91. 'TITLE' =>
  92. array(
  93. 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'link_click' => FALSE, 'emotes'=>FALSE, 'defs'=>TRUE, 'parse_sc'=>TRUE
  94. ),
  95. 'TITLE_PLAIN' =>
  96. array(
  97. 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'link_click' => FALSE, 'emotes'=>FALSE, 'defs'=>TRUE, 'parse_sc'=>TRUE, 'no_tags' => TRUE
  98. ),
  99. //text is user-entered (i.e. untrusted) and part of a title (e.g. forum title)
  100. 'USER_TITLE' =>
  101. array(
  102. 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'link_click' => FALSE, 'scripts' => FALSE, 'emotes'=>FALSE, 'hook'=>FALSE
  103. ),
  104. // text is 'body' of email or similar - being sent 'off-site' so don't rely on server availability
  105. 'E_TITLE' =>
  106. array(
  107. 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'defs'=>TRUE, 'parse_sc'=>TRUE, 'emotes'=>FALSE, 'scripts' => FALSE, 'link_click' => FALSE
  108. ),
  109. // text is part of the summary of a longer item (e.g. content summary)
  110. 'SUMMARY' =>
  111. array(
  112. 'defs'=>TRUE, 'constants'=>'full', 'parse_sc'=>TRUE
  113. ),
  114. // text is the description of an item (e.g. download, link)
  115. 'DESCRIPTION' =>
  116. array(
  117. 'defs'=>TRUE, 'constants'=>'full', 'parse_sc'=>TRUE
  118. ),
  119. // text is 'body' or 'bulk' text (e.g. custom page body, content body)
  120. 'BODY' =>
  121. array(
  122. 'defs'=>TRUE, 'constants'=>'full', 'parse_sc'=>TRUE
  123. ),
  124. 'WYSIWYG' =>
  125. array(
  126. 'defs'=>FALSE, 'constants'=>'full', 'parse_sc'=>FALSE, 'wysiwyg'=>TRUE
  127. ),
  128. // text is user-entered (i.e. untrusted)'body' or 'bulk' text (e.g. custom page body, content body)
  129. 'USER_BODY' =>
  130. array(
  131. 'constants'=>'full', 'scripts' => FALSE, 'nostrip'=>FALSE
  132. ),
  133. // text is 'body' of email or similar - being sent 'off-site' so don't rely on server availability
  134. 'E_BODY' =>
  135. array(
  136. 'defs'=>TRUE, 'constants'=>'full', 'parse_sc'=>TRUE, 'emotes'=>FALSE, 'scripts' => FALSE, 'link_click' => FALSE
  137. ),
  138. // text is text-only 'body' of email or similar - being sent 'off-site' so don't rely on server availability
  139. 'E_BODY_PLAIN' =>
  140. array(
  141. 'defs'=>TRUE, 'constants'=>'full', 'parse_sc'=>TRUE, 'emotes'=>FALSE, 'scripts' => FALSE, 'link_click' => FALSE, 'retain_nl' => TRUE, 'no_tags' => TRUE
  142. ),
  143. // text is the 'content' of a link (A tag, etc)
  144. 'LINKTEXT' =>
  145. array(
  146. 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'link_click' => FALSE, 'emotes'=>FALSE, 'hook'=>FALSE, 'defs'=>TRUE, 'parse_sc'=>TRUE
  147. ),
  148. // text is used (for admin edit) without fancy conversions or html.
  149. 'RAWTEXT' =>
  150. array(
  151. 'nobreak'=>TRUE, 'retain_nl'=>TRUE, 'link_click' => FALSE, 'emotes'=>FALSE, 'hook'=>FALSE, 'no_tags'=>TRUE
  152. )
  153. );
  154. // Individual modifiers change the current context
  155. var $e_Modifiers = array(
  156. 'emotes_off' => array('emotes' => FALSE),
  157. 'emotes_on' => array('emotes' => TRUE),
  158. 'no_hook' => array('hook' => FALSE),
  159. 'do_hook' => array('hook' => TRUE),
  160. // New for 0.8
  161. 'scripts_off' => array('scripts' => FALSE),
  162. // New for 0.8
  163. 'scripts_on' => array('scripts' => TRUE),
  164. 'no_make_clickable' => array('link_click' => FALSE),
  165. 'make_clickable' => array('link_click' => TRUE),
  166. 'no_replace' => array('link_replace' => FALSE),
  167. // Replace text of clickable links (only if make_clickable option set)
  168. 'replace' => array('link_replace' => TRUE),
  169. // No path replacement
  170. 'consts_off' => array('constants' => FALSE),
  171. // Relative path replacement
  172. 'consts_rel' => array('constants' => 'rel'),
  173. // Absolute path replacement
  174. 'consts_abs' => array('constants' => 'abs'),
  175. // Full path replacement
  176. 'consts_full' => array('constants' => 'full'),
  177. // No shortcode parsing
  178. 'scparse_off' => array('parse_sc' => FALSE),
  179. 'scparse_on' => array('parse_sc' => TRUE),
  180. // Strip tags
  181. 'no_tags' => array('no_tags' => TRUE),
  182. // Leave tags
  183. 'do_tags' => array('no_tags' => FALSE),
  184. 'fromadmin' => array('fromadmin' => TRUE),
  185. 'notadmin' => array('fromadmin' => FALSE),
  186. // entity replacement
  187. 'er_off' => array('value' => FALSE),
  188. 'er_on' => array('value' => TRUE),
  189. // Decode constant if exists
  190. 'defs_off' => array('defs' => FALSE),
  191. 'defs_on' => array('defs' => TRUE),
  192. 'dobreak' => array('nobreak' => FALSE),
  193. 'nobreak' => array('nobreak' => TRUE),
  194. // Line break using \n
  195. 'lb_nl' => array('retain_nl' => TRUE),
  196. // Line break using <br />
  197. 'lb_br' => array('retain_nl' => FALSE),
  198. // Legacy option names below here - discontinue later
  199. 'retain_nl' => array('retain_nl' => TRUE),
  200. 'defs' => array('defs' => TRUE),
  201. 'parse_sc' => array('parse_sc' => TRUE),
  202. 'constants' => array('constants' => 'rel'),
  203. 'value' => array('value' => TRUE),
  204. 'wysiwyg' => array('wysiwyg'=>TRUE)
  205. );
  206. /**
  207. * Constructor - keep it public for backward compatibility
  208. still some new e_parse() in the core
  209. *
  210. * @return void
  211. */
  212. public function __construct()
  213. {
  214. // initialise the type of UTF-8 processing methods depending on PHP version and mb string extension
  215. $this->init();
  216. $this->initCharset();
  217. // Preprocess the supermods to be useful default arrays with all values
  218. foreach ($this->e_SuperMods as $key => $val)
  219. {
  220. // precalculate super defaults
  221. $this->e_SuperMods[$key] = array_merge($this->e_optDefault , $this->e_SuperMods[$key]);
  222. $this->e_SuperMods[$key]['context'] = $key;
  223. }
  224. }
  225. /**
  226. * Initialise the type of UTF-8 processing methods depending on PHP version and mb string extension.
  227. *
  228. * NOTE: can't be called until CHARSET is known
  229. but we all know that it is UTF-8 now
  230. *
  231. * @return void
  232. */
  233. private function initCharset()
  234. {
  235. // Start by working out what, if anything, we do about utf-8 handling.
  236. // 'Do nothing' is the simple option
  237. $this->utfAction = 0;
  238. // CHARSET is utf-8
  239. // if(strtolower(CHARSET) == 'utf-8')
  240. // {
  241. if(version_compare(PHP_VERSION, '6.0.0') < 1)
  242. {
  243. // Need to do something here
  244. if(extension_loaded('mbstring'))
  245. {
  246. // Check for function overloading
  247. $temp = ini_get('mbstring.func_overload');
  248. // Just check the string functions - will be non-zero if overloaded
  249. if(($temp & MB_OVERLOAD_STRING) == 0)
  250. {
  251. // Can use the mb_string routines
  252. $this->utfAction = 1;
  253. }
  254. // Set the default encoding, so we don't have to specify every time
  255. mb_internal_encoding('UTF-8');
  256. }
  257. else
  258. {
  259. // Must use emulation - will probably be slow!
  260. $this->utfAction = 2;
  261. require (E_UTF8_PACK.'utils/unicode.php');
  262. // Always load the core routines - bound to need some of them!
  263. require (E_UTF8_PACK.'native/core.php');
  264. }
  265. }
  266. // }
  267. }
  268. /**
  269. * Unicode (UTF-8) analogue of standard @link http://php.net/strlen strlen PHP function.
  270. * Returns the length of the given string.
  271. *
  272. * @param string $str The UTF-8 encoded string being measured for length.
  273. * @return integer The length (amount of UTF-8 characters) of the string on success, and 0 if the string is empty.
  274. */
  275. public function ustrlen($str)
  276. {
  277. switch($this->utfAction)
  278. {
  279. case 0:
  280. return strlen($str);
  281. case 1:
  282. return mb_strlen($str);
  283. }
  284. // Default case shouldn't happen often
  285. // Save a call - invoke the function directly
  286. return strlen(utf8_decode($str));
  287. }
  288. /**
  289. * Unicode (UTF-8) analogue of standard @link http://php.net/strtolower strtolower PHP function.
  290. * Make a string lowercase.
  291. *
  292. * @param string $str The UTF-8 encoded string to be lowercased.
  293. * @return string Specified string with all alphabetic characters converted to lowercase.
  294. */
  295. public function ustrtolower($str)
  296. {
  297. switch($this->utfAction)
  298. {
  299. case 0:
  300. return strtolower($str);
  301. case 1:
  302. return mb_strtolower($str);
  303. }
  304. // Default case shouldn't happen often
  305. return utf8_strtolower($str);
  306. }
  307. /**
  308. * Unicode (UTF-8) analogue of standard @link http://php.net/strtoupper strtoupper PHP function.
  309. * Make a string uppercase.
  310. *
  311. * @param string $str The UTF-8 encoded string to be uppercased.
  312. * @return string Specified string with all alphabetic characters converted to uppercase.
  313. */
  314. public function ustrtoupper($str)
  315. {
  316. switch($this->utfAction)
  317. {
  318. case 0:
  319. return strtoupper($str);
  320. case 1:
  321. return mb_strtoupper($str);
  322. }
  323. // Default case shouldn't happen often
  324. return utf8_strtoupper($str);
  325. }
  326. /**
  327. * Unicode (UTF-8) analogue of standard @link http://php.net/strpos strpos PHP function.
  328. * Find the position of the first occurrence of a case-sensitive UTF-8 encoded string.
  329. * Returns the numeric position (offset in amount of UTF-8 characters)
  330. * of the first occurrence of needle in the haystack string.
  331. *
  332. * @param string $haystack The UTF-8 encoded string being searched in.
  333. * @param integer $needle The UTF-8 encoded string being searched for.
  334. * @param integer $offset [optional] The optional offset parameter allows you to specify which character in haystack to start searching.
  335. * The position returned is still relative to the beginning of haystack.
  336. * @return integer|boolean Returns the position as an integer. If needle is not found, the function will return boolean FALSE.
  337. */
  338. public function ustrpos($haystack, $needle, $offset = 0)
  339. {
  340. switch($this->utfAction)
  341. {
  342. case 0:
  343. return strpos($haystack, $needle, $offset);
  344. case 1:
  345. return mb_strpos($haystack, $needle, $offset);
  346. }
  347. return utf8_strpos($haystack, $needle, $offset);
  348. }
  349. /**
  350. * Unicode (UTF-8) analogue of standard @link http://php.net/strrpos strrpos PHP function.
  351. * Find the position of the last occurrence of a case-sensitive UTF-8 encoded string.
  352. * Returns the numeric position (offset in amount of UTF-8 characters)
  353. * of the last occurrence of needle in the haystack string.
  354. *
  355. * @param string $haystack The UTF-8 encoded string being searched in.
  356. * @param integer $needle The UTF-8 encoded string being searched for.
  357. * @param integer $offset [optional] - The optional offset parameter allows you to specify which character in haystack to start searching.
  358. * The position returned is still relative to the beginning of haystack.
  359. * @return integer|boolean Returns the position as an integer. If needle is not found, the function will return boolean FALSE.
  360. */
  361. public function ustrrpos($haystack, $needle, $offset = 0)
  362. {
  363. switch($this->utfAction)
  364. {
  365. case 0:
  366. return strrpos($haystack, $needle, $offset);
  367. case 1:
  368. return mb_strrpos($haystack, $needle, $offset);
  369. }
  370. return utf8_strrpos($haystack, $needle, $offset);
  371. }
  372. /**
  373. * Unicode (UTF-8) analogue of standard @link http://php.net/stristr stristr PHP function.
  374. * Returns all of haystack starting from and including the first occurrence of needle to the end.
  375. *
  376. * @param string $haystack The UTF-8 encoded string to search in.
  377. * @param mixed $needle If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.
  378. * @param integer $length [optional] (PHP 5.3+) If TRUE, returns the part of the haystack before the first occurrence of the needle (excluding needle).
  379. * @return string Returns the matched substring. If needle is not found, returns FALSE.
  380. */
  381. public function ustristr($haystack, $needle, $before_needle = false)
  382. {
  383. switch($this->utfAction)
  384. {
  385. case 0:
  386. return stristr($haystack, $needle, $before_needle);
  387. case 1:
  388. return mb_substr($haystack, $needle, $before_needle);
  389. }
  390. // No utf8 pack backup
  391. return stristr($str, $start, $length);
  392. }
  393. /**
  394. * Unicode (UTF-8) analogue of standard @link http://php.net/substr substr PHP function.
  395. * Returns the portion of string specified by the start and length parameters.
  396. *
  397. * NOTE: May be subtle differences in return values dependent on which routine is used.
  398. * Native substr() routine can return FALSE. mb_substr() and utf8_substr() just return an empty string.
  399. *
  400. * @param string $str The UTF-8 encoded string.
  401. * @param integer $start Start of portion to be returned. Position is counted in amount of UTF-8 characters from the beginning of str.
  402. * First character's position is 0. Second character position is 1, and so on.
  403. * @param integer $length [optional] If length is given, the string returned will contain at most length characters beginning from start
  404. * (depending on the length of string). If length is omitted, the rest of string from start will be returned.
  405. * @return string The extracted UTF-8 encoded part of input string.
  406. */
  407. public function usubstr($str, $start, $length = NULL)
  408. {
  409. switch($this->utfAction)
  410. {
  411. case 0:
  412. return substr($str, $start, $length);
  413. case 1:
  414. if(is_null($length))
  415. {
  416. return mb_substr($str, $start);
  417. }
  418. else
  419. {
  420. return mb_substr($str, $start, $length);
  421. }
  422. }
  423. return utf8_substr($str, $start, $length);
  424. }
  425. /**
  426. * Converts the supplied text (presumed to be from user input) to a format suitable for storing in a database table.
  427. *
  428. * @param string $data
  429. * @param boolean $nostrip [optional] Assumes all data is GPC ($_GET, $_POST, $_COOKIE) unless indicate otherwise by setting this var to TRUE.
  430. * If magic quotes is enabled on the server and you do not tell toDB() that the data is non GPC then slashes will be stripped when they should not be.
  431. * @param boolean $no_encode [optional] This parameter should nearly always be FALSE. It is used by the save_prefs() function to preserve HTML content within prefs even when
  432. * the save_prefs() function has been called by a non admin user / user without html posting permissions.
  433. * @param boolean $mod [optional] The 'no_html' and 'no_php' modifiers blanket prevent HTML and PHP posting regardless of posting permissions. (used in logging)
  434. * The 'pReFs' value is for internal use only, when saving prefs, to prevent sanitisation of HTML.
  435. * @param boolean $original_author [optional]
  436. * @return string
  437. * @todo complete the documentation of this essential method
  438. */
  439. public function toDB($data, $nostrip = FALSE, $no_encode = FALSE, $mod = FALSE, $original_author = FALSE)
  440. {
  441. $core_pref = e107::getConfig();
  442. if (is_array($data))
  443. {
  444. foreach ($data as $key => $var)
  445. {
  446. //Fix - sanitize keys as well
  447. $ret[$this->toDB($key, $nostrip, $no_encode, $mod, $original_author)] = $this->toDB($var, $nostrip, $no_encode, $mod, $original_author);
  448. }
  449. return $ret;
  450. }
  451. if (MAGIC_QUOTES_GPC == TRUE && $nostrip == FALSE)
  452. {
  453. $data = stripslashes($data);
  454. }
  455. if ($mod != 'pReFs') //XXX We're not saving prefs.
  456. {
  457. $data = $this->preFilter($data); // used by bb_xxx.php toDB() functions. bb_code.php toDB() allows us to properly bypass HTML cleaning below.
  458. if (strip_tags($data) != $data) // html tags present.
  459. {
  460. // return $data;
  461. $data = $this->cleanHtml($data); // sanitize all html.
  462. $data = urldecode($data); // symptom of cleaning the HTML - urlencodes src attributes containing { and } .eg. {e_BASE}
  463. }
  464. if (!check_class($core_pref->get('post_html', e_UC_MAINADMIN)))
  465. {
  466. $data = strip_tags($data); // remove tags from cleaned html.
  467. $data = str_replace(array('[html]','[/html]'),'',$data);
  468. // $data = $this->dataFilter($data);
  469. }
  470. $data = html_entity_decode($data, ENT_QUOTES, 'utf-8'); // Prevent double-entities. Fix for [code] - see bb_code.php toDB();
  471. }
  472. if (check_class($core_pref->get('post_html'))) /*$core_pref->is('post_html') && */
  473. {
  474. $no_encode = TRUE;
  475. }
  476. if (is_numeric($original_author) && !check_class($core_pref->get('post_html'), '', $original_author))
  477. {
  478. $no_encode = FALSE;
  479. }
  480. if ($no_encode === TRUE && strpos($mod, 'no_html') === FALSE)
  481. {
  482. $search = array('$', '"', "'", '\\', '<?');
  483. $replace = array('&#036;', '&quot;', '&#039;', '&#092;', '&lt;?');
  484. $ret = str_replace($search, $replace, $data);
  485. }
  486. else
  487. {
  488. $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
  489. $data = str_replace('\\', '&#092;', $data);
  490. $ret = preg_replace("/&amp;#(\d*?);/", "&#\\1;", $data);
  491. }
  492. // XXX - php_bbcode has been deprecated.
  493. if ((strpos($mod, 'no_php') !== FALSE) || !check_class($core_pref->get('php_bbcode')))
  494. {
  495. $ret = preg_replace("#\[(php)#i", "&#91;\\1", $ret);
  496. }
  497. return $ret;
  498. }
  499. /**
  500. * Check for umatched 'dangerous' HTML tags
  501. * (these can destroy page layout where users are able to post HTML)
  502. * @DEPRECATED
  503. * @param string $data
  504. * @param string $tagList - if empty, uses default list of input tags. Otherwise a CSV list of tags to check (any type)
  505. *
  506. * @return boolean TRUE if an unopened closing tag found
  507. * FALSE if nothing found
  508. */
  509. function htmlAbuseFilter($data, $tagList = '')
  510. {
  511. if ($tagList == '')
  512. {
  513. $checkTags = array('textarea', 'input', 'td', 'tr', 'table');
  514. }
  515. else
  516. {
  517. $checkTags = explode(',', $tagList);
  518. }
  519. $tagArray = array_flip($checkTags);
  520. foreach ($tagArray as &$v) { $v = 0; }; // Data fields become zero; keys are tag names.
  521. $data = strtolower(preg_replace('#\[code\].*?\[\/code\]#i', '', $data)); // Ignore code blocks. All lower case simplifies the rest
  522. $matches = array();
  523. if (!preg_match_all('#<(\/|)([^<>]*?[^\/])>#', $data, $matches, PREG_SET_ORDER))
  524. {
  525. //echo "No tags found<br />";
  526. return TRUE; // No tags found; so all OK
  527. }
  528. //print_a($matches);
  529. foreach ($matches as $m)
  530. {
  531. // $m[0] is the complete tag; $m[1] is '/' or empty; $m[2] is the tag and any attributes
  532. list ($tag) = explode(' ', $m[2], 2);
  533. if (!isset($tagArray[$tag])) continue; // Not a tag of interest
  534. if ($m[1] == '/')
  535. { // Closing tag
  536. if ($tagArray[$tag] == 0)
  537. {
  538. //echo "Close before open: {$tag}<br />";
  539. return TRUE; // Closing tag before we've had an opening tag
  540. }
  541. $tagArray[$tag]--; // Obviously had at least one opening tag
  542. }
  543. else
  544. { // Opening tag
  545. $tagArray[$tag]++;
  546. }
  547. }
  548. //print_a($tagArray);
  549. foreach ($tagArray as $t)
  550. {
  551. if ($t > 0) return TRUE; // More opening tags than closing tags
  552. }
  553. return FALSE; // OK now
  554. }
  555. /**
  556. * @DEPRECATED XXX TODO Remove this horrible thing which adds junk to a db.
  557. * Checks a string for potentially dangerous HTML tags, including malformed tags
  558. *
  559. */
  560. public function dataFilter($data, $mode='bbcode')
  561. {
  562. $ans = '';
  563. $vetWords = array('<applet', '<body', '<embed', '<frame', '<script','%3Cscript',
  564. '<frameset', '<html', '<iframe', '<style', '<layer', '<link',
  565. '<ilayer', '<meta', '<object', '<plaintext', 'javascript:',
  566. 'vbscript:','data:text/html');
  567. $ret = preg_split('#(\[code.*?\[/code.*?])#mis', $data, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
  568. foreach ($ret as $s)
  569. {
  570. if (substr($s, 0, 5) != '[code')
  571. {
  572. $vl = array();
  573. $t = html_entity_decode(rawurldecode($s), ENT_QUOTES, CHARSET);
  574. $t = str_replace(array("\r", "\n", "\t", "\v", "\f", "\0"), '', $t);
  575. $t1 = strtolower($t);
  576. foreach ($vetWords as $vw)
  577. {
  578. if (strpos($t1, $vw) !== FALSE)
  579. {
  580. $vl[] = $vw; // Add to list of words found
  581. }
  582. if (substr($vw, 0, 1) == '<')
  583. {
  584. $vw = '</'.substr($vw, 1);
  585. if (strpos($t1, $vw) !== FALSE)
  586. {
  587. $vl[] = $vw; // Add to list of words found
  588. }
  589. }
  590. }
  591. // More checks here
  592. if (count($vl))
  593. { // Do something
  594. $s = preg_replace_callback('#('.implode('|', $vl).')#mis', array($this, 'modtag'), $t);
  595. }
  596. }
  597. $s = preg_replace('#(?:onmouse.+?|onclick|onfocus)\s*?\=#', '[sanitised]$0[/sanitised]', $s);
  598. $s = preg_replace_callback('#base64([,\(])(.+?)([\)\'\"])#mis', array($this, 'proc64'), $s);
  599. $ans .= $s;
  600. }
  601. if($mode == 'link' && count($vl))
  602. {
  603. return "#sanitized";
  604. }
  605. return $ans;
  606. }
  607. /**
  608. * Check base-64 encoded code
  609. */
  610. private function proc64($match)
  611. {
  612. $decode = base64_decode($match[2]);
  613. return 'base64'.$match[1].base64_encode($this->dataFilter($decode)).$match[3];
  614. }
  615. // XXX REmove ME.
  616. private function modTag($match)
  617. {
  618. $ans = '';
  619. if (isset($match[1]))
  620. {
  621. $chop = intval(strlen($match[1]) / 2);
  622. $ans = substr($match[1], 0, $chop).'##xss##'.substr($match[1], $chop);
  623. }
  624. else
  625. {
  626. $ans = '?????';
  627. }
  628. return '[sanitised]'.$ans.'[/sanitised]';
  629. }
  630. /**
  631. * Processes data as needed before its written to the DB.
  632. * Currently gives bbcodes the opportunity to do something
  633. *
  634. * @param $data string - data about to be written to DB
  635. * @return string - modified data
  636. */
  637. public function preFilter($data)
  638. {
  639. if (!is_object($this->e_bb))
  640. {
  641. require_once(e_HANDLER.'bbcode_handler.php');
  642. $this->e_bb = new e_bbcode;
  643. }
  644. $ret = $this->e_bb->parseBBCodes($data, USERID, 'default', 'PRE'); // $postID = logged in user here
  645. return $ret;
  646. }
  647. function toForm($text)
  648. {
  649. if(empty($text)) // fix - handle proper 0, Space etc values.
  650. {
  651. return $text;
  652. }
  653. $search = array('&#036;', '&quot;', '<', '>');
  654. $replace = array('$', '"', '&lt;', '&gt;');
  655. $text = str_replace($search, $replace, $text);
  656. if (e_WYSIWYG !== TRUE)
  657. {
  658. // fix for utf-8 issue with html_entity_decode(); ???
  659. $text = str_replace("&nbsp;", " ", $text);
  660. }
  661. return $text;
  662. }
  663. function post_toForm($text)
  664. {
  665. if(is_array($text))
  666. {
  667. foreach ($text as $key=>$value)
  668. {
  669. $text[$this->post_toForm($key)] = $this->post_toForm($value);
  670. }
  671. return $text;
  672. }
  673. if(MAGIC_QUOTES_GPC == TRUE)
  674. {
  675. $text = stripslashes($text);
  676. }
  677. return str_replace(array("'", '"', "<", ">"), array("&#039;", "&quot;", "&lt;", "&gt;"), $text);
  678. }
  679. function post_toHTML($text, $original_author = FALSE, $extra = '', $mod = FALSE)
  680. {
  681. $text = $this->toDB($text, FALSE, FALSE, $mod, $original_author);
  682. return $this->toHTML($text, TRUE, $extra);
  683. }
  684. function parseTemplate($text, $parseSCFiles = TRUE, $extraCodes = null, $eVars = null)
  685. {
  686. return e107::getScParser()->parseCodes($text, $parseSCFiles, $extraCodes, $eVars);
  687. }
  688. /**
  689. * Simple parser
  690. *
  691. * @param string $template
  692. * @param e_vars $vars
  693. * @param string $replaceUnset string to be used if replace variable is not set, false - don't replace
  694. * @return string parsed content
  695. */
  696. function simpleParse($template, e_vars $vars, $replaceUnset='')
  697. {
  698. $this->replaceVars = $vars;
  699. $this->replaceUnset = $replaceUnset;
  700. return preg_replace_callback("#\{([a-zA-Z0-9_]+)\}#", array($this, 'simpleReplace'), $template);
  701. }
  702. protected function simpleReplace($tmp)
  703. {
  704. $unset = ($this->replaceUnset !== false ? $this->replaceUnset : $tmp[0]);
  705. $key = $tmp[1];
  706. if(is_array($this->replaceVars))
  707. {
  708. return ($this->replaceVars[$key] !== null ? $this->replaceVars[$key]: $unset);
  709. }
  710. //
  711. return ($this->replaceVars->$tmp[1] !== null ? $this->replaceVars->$tmp[1] : $unset); // Doesn't work.
  712. }
  713. function htmlwrap($str, $width, $break = "\n", $nobreak = "a", $nobr = "pre", $utf = FALSE)
  714. {
  715. /*
  716. Pretty well complete rewrite to try and handle utf-8 properly.
  717. Breaks each utf-8 'word' every $width characters max. If possible, breaks after 'safe' characters.
  718. $break is the character inserted to flag the break.
  719. $nobreak is a list of tags within which word wrap is to be inactive
  720. */
  721. //TODO handle htmlwrap somehow
  722. //return $str;
  723. // Don't wrap if non-numeric width
  724. $width = intval($width);
  725. // And trap stupid wrap counts
  726. if ($width < 6)
  727. return $str;
  728. // Transform protected element lists into arrays
  729. $nobreak = explode(" ", strtolower($nobreak));
  730. // Variable setup
  731. $intag = FALSE;
  732. $innbk = array();
  733. $drain = "";
  734. // List of characters it is "safe" to insert line-breaks at
  735. // It is not necessary to add < and > as they are automatically implied
  736. $lbrks = "/?!%)-}]\\\"':;&";
  737. // Is $str a UTF8 string?
  738. if ($utf || strtolower(CHARSET) == 'utf-8')
  739. {
  740. // 0x1680, 0x180e, 0x2000-0x200a, 0x2028, 0x205f, 0x3000 are 'non-ASCII' Unicode UCS-4 codepoints - see http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
  741. // All convert to 3-byte utf-8 sequences:
  742. // 0x1680 0xe1 0x9a 0x80
  743. // 0x180e 0xe1 0xa0 0x8e
  744. // 0x2000 0xe2 0x80 0x80
  745. // -
  746. // 0x200a 0xe2 0x80 0x8a
  747. // 0x2028 0xe2 0x80 0xa8
  748. // 0x205f 0xe2 0x81 0x9f
  749. // 0x3000 0xe3 0x80 0x80
  750. $utf8 = 'u';
  751. $whiteSpace = '#([\x20|\x0c]|[\xe1][\x9a][\x80]|[\xe1][\xa0][\x8e]|[\xe2][\x80][\x80-\x8a,\xa8]|[\xe2][\x81][\x9f]|[\xe3][\x80][\x80]+)#';
  752. // Have to explicitly enumerate the whitespace chars, and use non-utf-8 mode, otherwise regex fails on badly formed utf-8
  753. }
  754. else
  755. {
  756. $utf8 = '';
  757. // For non-utf-8, can use a simple match string
  758. $whiteSpace = '#(\s+)#';
  759. }
  760. // Start of the serious stuff - split into HTML tags and text between
  761. $content = preg_split('#(<.*?'.'>)#mis', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
  762. foreach($content as $value)
  763. {
  764. if ($value[0] == "<")
  765. {
  766. // We are within an HTML tag
  767. // Create a lowercase copy of this tag's contents
  768. $lvalue = strtolower(substr($value, 1, -1));
  769. if ($lvalue)
  770. {
  771. // Tag of non-zero length
  772. // If the first character is not a / then this is an opening tag
  773. if ($lvalue[0] != "/")
  774. {
  775. // Collect the tag name
  776. preg_match("/^(\w*?)(\s|$)/", $lvalue, $t);
  777. // If this is a protected element, activate the associated protection flag
  778. if(in_array($t[1], $nobreak))
  779. array_unshift($innbk, $t[1]);
  780. }
  781. else
  782. {
  783. // Otherwise this is a closing tag
  784. // If this is a closing tag for a protected element, unset the flag
  785. if (in_array(substr($lvalue, 1), $nobreak))
  786. {
  787. reset($innbk);
  788. while (list($key, $tag) = each($innbk))
  789. {
  790. if (substr($lvalue, 1) == $tag)
  791. {
  792. unset($innbk[$key]);
  793. break;
  794. }
  795. }
  796. $innbk = array_values($innbk);
  797. }
  798. }
  799. }
  800. else
  801. {
  802. // Eliminate any empty tags altogether
  803. $value = '';
  804. }
  805. // Else if we're outside any tags, and with non-zero length string...
  806. }
  807. elseif ($value)
  808. {
  809. // If unprotected...
  810. if (!count($innbk))
  811. {
  812. // Use the ACK (006) ASCII symbol to replace all HTML entities temporarily
  813. $value = str_replace("\x06", "", $value);
  814. preg_match_all("/&([a-z\d]{2,7}|#\d{2,5});/i", $value, $ents);
  815. $value = preg_replace("/&([a-z\d]{2,7}|#\d{2,5});/i", "\x06", $value);
  816. // echo "Found block length ".strlen($value).': '.substr($value,20).'<br />';
  817. // Split at spaces - note that this will fail if presented with invalid utf-8 when doing the regex whitespace search
  818. // $split = preg_split('#(\s)#'.$utf8, $value, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
  819. $split = preg_split($whiteSpace, $value, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
  820. $value = '';
  821. foreach ($split as $sp)
  822. {
  823. // echo "Split length ".strlen($sp).': '.substr($sp,20).'<br />';
  824. $loopCount = 0;
  825. while (strlen($sp) > $width)
  826. {
  827. // Enough characters that we may need to do something.
  828. $pulled = '';
  829. if ($utf8)
  830. {
  831. // Pull out a piece of the maximum permissible length
  832. if (preg_match('#^((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$width.'})(.{0,1}).*#s',$sp,$matches) == 0)
  833. {
  834. // Make any problems obvious for now
  835. $value .= '[!<b>invalid utf-8: '.$sp.'<b>!]';
  836. $sp = '';
  837. }
  838. elseif (empty($matches[2]))
  839. {
  840. // utf-8 length is less than specified - treat as a special case
  841. $value .= $sp;
  842. $sp = '';
  843. }
  844. else
  845. {
  846. // Need to find somewhere to break the string
  847. for($i = strlen($matches[1]) - 1; $i >= 0; $i--)
  848. {
  849. if(strpos($lbrks, $matches[1][$i]) !== FALSE)
  850. break;
  851. }
  852. if($i < 0)
  853. {
  854. // No 'special' break character found - break at the word boundary
  855. $pulled = $matches[1];
  856. }
  857. else
  858. {
  859. $pulled = substr($sp, 0, $i + 1);
  860. }
  861. }
  862. $loopCount++;
  863. if ($loopCount > 20)
  864. {
  865. // Make any problems obvious for now
  866. $value .= '[!<b>loop count exceeded: '.$sp.'</b>!]';
  867. $sp = '';
  868. }
  869. }
  870. else
  871. {
  872. for ($i = min($width, strlen($sp)); $i > 0; $i--)
  873. {
  874. // No speed advantage to defining match character
  875. if (strpos($lbrks, $sp[$i-1]) !== FALSE)
  876. break;
  877. }
  878. if ($i == 0)
  879. {
  880. // No 'special' break boundary character found - break at the word boundary
  881. $pulled = substr($sp, 0, $width);
  882. }
  883. else
  884. {
  885. $pulled = substr($sp, 0, $i);
  886. }
  887. }
  888. if ($pulled)
  889. {
  890. $value .= $pulled.$break;
  891. // Shorten $sp by whatever we've processed (will work even for utf-8)
  892. $sp = substr($sp, strlen($pulled));
  893. }
  894. }
  895. // Add in any residue
  896. $value .= $sp;
  897. }
  898. // Put captured HTML entities back into the string
  899. foreach ($ents[0] as $ent)
  900. $value = preg_replace("/\x06/", $ent, $value, 1);
  901. }
  902. }
  903. // Send the modified segment down the drain
  904. $drain .= $value;
  905. }
  906. // Return contents of the drain
  907. return $drain;
  908. }
  909. /**
  910. * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
  911. * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
  912. *
  913. * Truncate a HTML string
  914. *
  915. * Cuts a string to the length of $length and adds the value of $ending if the text is longer than length.
  916. *
  917. * @param string $text String to truncate.
  918. * @param integer $length Length of returned string, including ellipsis.
  919. * @param string $ending It will be used as Ending and appended to the trimmed string.
  920. * @param boolean $exact If false, $text will not be cut mid-word
  921. * @return string Trimmed string.
  922. */
  923. function html_truncate($text, $length = 100, $ending = '...', $exact = true)
  924. {
  925. if($this->ustrlen(preg_replace('/<.*?>/', '', $text)) <= $length)
  926. {
  927. return $text;
  928. }
  929. $totalLength = 0;
  930. $openTags = array();
  931. $truncate = '';
  932. preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER);
  933. foreach($tags as $tag)
  934. {
  935. if(!$tag[2] || !preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/si', $tag[2]))
  936. {
  937. if(preg_match('/<[\w]+[^>]*>/s', $tag[0]))
  938. {
  939. array_unshift($openTags, $tag[2]);
  940. }
  941. else if(preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag))
  942. {
  943. $pos = array_search($closeTag[1], $openTags);
  944. if($pos !== false)
  945. {
  946. array_splice($openTags, $pos, 1);
  947. }
  948. }
  949. }
  950. $truncate .= $tag[1];
  951. $contentLength = $this->ustrlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3]));
  952. if($contentLength + $totalLength > $length)
  953. {
  954. $left = $length - $totalLength;
  955. $entitiesLength = 0;
  956. if(preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE))
  957. {
  958. foreach($entities[0] as $entity)
  959. {
  960. if($entity[1] + 1 - $entitiesLength <= $left)
  961. {
  962. $left--;
  963. $entitiesLength += $this->ustrlen($entity[0]);
  964. }
  965. else
  966. {
  967. break;
  968. }
  969. }
  970. }
  971. $truncate .= $this->usubstr($tag[3], 0, $left + $entitiesLength);
  972. break;
  973. }
  974. else
  975. {
  976. $truncate .= $tag[3];
  977. $totalLength += $contentLength;
  978. }
  979. if($totalLength >= $length)
  980. {
  981. break;
  982. }
  983. }
  984. if(!$exact)
  985. {
  986. $spacepos = $this->ustrrpos($truncate, ' ');
  987. if(isset($spacepos))
  988. {
  989. $bits = $this->usubstr($truncate, $spacepos);
  990. preg_match_all('/<\/([a-z]+)>/i', $bits, $droppedTags, PREG_SET_ORDER);
  991. if(!empty($droppedTags))
  992. {
  993. foreach($droppedTags as $closingTag)
  994. {
  995. if(!in_array($closingTag[1], $openTags))
  996. {
  997. array_unshift($openTags, $closingTag[1]);
  998. }
  999. }
  1000. }
  1001. $truncate = $this->usubstr($truncate, 0, $spacepos);
  1002. }
  1003. }
  1004. $truncate .= $ending;
  1005. foreach($openTags as $tag)
  1006. {
  1007. $truncate .= '</' . $tag . '>';
  1008. }
  1009. return $truncate;
  1010. }
  1011. /**
  1012. * Truncate a HTML string to a maximum length $len ­ append the string $more if it was truncated
  1013. *
  1014. * @param string $text String to process
  1015. * @param integer $len [optional] Length of characters to be truncated - default 200
  1016. * @param string $more [optional] String which will be added if truncation - default ' ... '
  1017. * @return string
  1018. */
  1019. public function html_truncate_old ($text, $len = 200, $more = ' ... ')
  1020. {
  1021. $pos = 0;
  1022. $curlen = 0;
  1023. $tmp_pos = 0;
  1024. $intag = FALSE;
  1025. while($curlen < $len && $curlen < strlen($text))
  1026. {
  1027. switch($text {$pos} )
  1028. {
  1029. case "<":
  1030. if($text {$pos + 1} == "/")
  1031. {
  1032. $closing_tag = TRUE;
  1033. }
  1034. $intag = TRUE;
  1035. $tmp_pos = $pos - 1;
  1036. $pos++;
  1037. break;
  1038. case ">":
  1039. if($text {$pos - 1} == "/")
  1040. {
  1041. $closing_tag = TRUE;
  1042. }
  1043. if($closing_tag == TRUE)
  1044. {
  1045. $tmp_pos = 0;
  1046. $closing_tag = FALSE;
  1047. }
  1048. $intag = FALSE;
  1049. $pos++;
  1050. break;
  1051. case "&":
  1052. if($text {$pos + 1} == "#")
  1053. {
  1054. $end = strpos(substr($text, $pos, 7), ";");
  1055. if($end !== FALSE)
  1056. {
  1057. $pos += ($end + 1);
  1058. if(!$intag)
  1059. {
  1060. $curlen++;
  1061. }
  1062. break;
  1063. }
  1064. }
  1065. else
  1066. {
  1067. $pos++;
  1068. if(!$intag)
  1069. {
  1070. $curlen++;
  1071. }
  1072. break;
  1073. }
  1074. default:
  1075. $pos++;
  1076. if(!$intag)
  1077. {
  1078. $curlen++;
  1079. }
  1080. break;
  1081. }
  1082. }
  1083. $ret = ($tmp_pos > 0 ? substr($text, 0, $tmp_pos+1) : substr($text, 0, $pos));
  1084. if($pos < strlen($text))
  1085. {
  1086. $ret = $ret.$more;
  1087. }
  1088. return $ret;
  1089. }
  1090. /**
  1091. * Truncate a string of text to a maximum length $len ­ append the string $more if it was truncated
  1092. * Uses current CHARSET ­ for utf-8, returns $len characters rather than $len bytes
  1093. *
  1094. * @param string $text ­ string to process
  1095. * @param integer $len ­ length of characters to be truncated
  1096. * @param string $more ­ string which will be added if truncation
  1097. * @return string
  1098. */
  1099. public function text_truncate($text, $len = 200, $more = ' ... ')
  1100. {
  1101. // Always valid
  1102. if($this->ustrlen($text) <= $len)
  1103. {
  1104. return $text;
  1105. }
  1106. $ret = $this->usubstr($text, 0, $len);
  1107. // search for possible broken html entities
  1108. // - if an & is in the last 8 chars, removing it and whatever follows shouldn't hurt
  1109. // it should work for any characters encoding
  1110. // FIXME - INVESTIGATE this one, switch to utf8 aware methods
  1111. $leftAmp = $this->ustrrpos($this->usubstr($ret, -8), '&');
  1112. if($leftAmp)
  1113. {
  1114. $ret = $this->usubstr($ret, 0, $this->ustrlen($ret) - 8 + $leftAmp);
  1115. }
  1116. return $ret.$more;
  1117. }
  1118. function textclean ($text, $wrap = 100)
  1119. {
  1120. $text = str_replace("\n\n\n", "\n\n", $text);
  1121. $text = $this->htmlwrap($text, $wrap);
  1122. $text = str_replace(array('<br /> ', ' <br />', ' <br /> '), '<br />', $text);
  1123. /* we can remove any linebreaks added by htmlwrap function as any \n's will be converted later anyway */
  1124. return $text;
  1125. }
  1126. // Test for text highlighting, and determine the text highlighting transformation
  1127. // Returns TRUE if highlighting is active for this page display
  1128. function checkHighlighting()
  1129. {
  1130. global $pref;
  1131. if (!defined('e_SELF'))
  1132. {
  1133. // Still in startup, so can't calculate highlighting
  1134. return FALSE;
  1135. }
  1136. if(!isset($this->e_highlighting))
  1137. {
  1138. $this->e_highlighting = FALSE;
  1139. $shr = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : "");
  1140. if($pref['search_highlight'] && (strpos(e_SELF, 'search.php') === FALSE) && ((strpos($shr, 'q=') !== FALSE) || (strpos($shr, 'p=') !== FALSE)))
  1141. {
  1142. $this->e_highlighting = TRUE;
  1143. if(!isset($this->e_query))
  1144. {
  1145. $query = preg_match('#(q|p)=(.*?)(&|$)#', $shr, $matches);
  1146. $this->e_query = str_replace(array('+', '*', '"', ' '), array('', '.*?', '', '\b|\b'), trim(urldecode($matches[2])));
  1147. }
  1148. }
  1149. }
  1150. return $this->e_highlighting;
  1151. }
  1152. /**
  1153. * Converts the text (presumably retrieved from the database) for HTML output.
  1154. *
  1155. * @param string $text
  1156. * @param boolean $parseBB [optional]
  1157. * @param string $modifiers [optional] TITLE|SUMMARY|DESCRIPTION|BODY|RAW|LINKTEXT etc.
  1158. * Comma-separated list, no spaces allowed
  1159. * first modifier must be a CONTEXT modifier, in UPPER CASE.
  1160. * subsequent modifiers are lower case - see $this->e_Modifiers for possible values
  1161. * @param mixed $postID [optional]
  1162. * @param boolean $wrap [optional]
  1163. * @return string
  1164. * @todo complete the documentation of this essential method
  1165. */
  1166. public function toHTML($text, $parseBB = FALSE, $modifiers = '', $postID = '', $wrap = FALSE)
  1167. {
  1168. if($text == '')
  1169. {
  1170. return $text;
  1171. }
  1172. $pref = e107::getPref();
  1173. global $fromadmin;
  1174. // Set default modifiers to start
  1175. $opts = $this->e_optDefault;
  1176. // Now process any modifiers that are specified
  1177. if ($modifiers)
  1178. {
  1179. $aMods = explode(',', $modifiers);
  1180. // If there's a supermodifier, it must be first, and in uppercase
  1181. $psm = trim($aMods[0]);
  1182. if (isset($this->e_SuperMods[$psm]))
  1183. {
  1184. // Supermodifier found - override default values where necessary
  1185. $opts = array_merge($opts,$this->e_SuperMods[$psm]);
  1186. $opts['context'] = $psm;
  1187. unset($aMods[0]);
  1188. }
  1189. // Now find any regular modifiers; use them to modify the context
  1190. // (there should only be one or two out of the list of possibles)
  1191. foreach ($aMods as $mod)
  1192. {
  1193. // Slight concession to varying coding styles - stripping spaces is a waste of CPU cycles!
  1194. $mod = trim($mod);
  1195. if (isset($this->e_Modifiers[$mod]))
  1196. {
  1197. // This is probably quicker than array_merge
  1198. // - especially as usually only one or two loops
  1199. foreach ($this->e_Modifiers[$mod] as $k => $v)
  1200. {
  1201. // Update our context-specific options
  1202. $opts[$k] = $v;
  1203. }
  1204. }
  1205. }
  1206. }
  1207. // Turn off a few things if not enabled in options
  1208. if(!varsettrue($pref['smiley_activate']))
  1209. {
  1210. $opts['emotes'] = FALSE;
  1211. }
  1212. if(!varsettrue($pref['make_clickable']))
  1213. {
  1214. $opts['link_click'] = FALSE;
  1215. }
  1216. if(!varsettrue($pref['link_replace']))
  1217. {
  1218. $opts['link_replace'] = FALSE;
  1219. }
  1220. $fromadmin = $opts['fromadmin'];
  1221. // Convert defines(constants) within text. eg. Lan_XXXX - must be the entire text string (i.e. not embedded)
  1222. // The check for '::' is a workaround for a bug in the Zend Optimiser 3.3.0 and PHP 5.2.4 combination
  1223. // - causes crashes if '::' in site name
  1224. if($opts['defs'] && (strlen($text) < 35) && ((strpos($text, '::') === FALSE) && defined(trim($text))))
  1225. {
  1226. return constant(trim($text));
  1227. }
  1228. if ($opts['no_tags'])
  1229. {
  1230. $text = strip_tags($text);
  1231. }
  1232. if (MAGIC_QUOTES_GPC == TRUE) // precaution for badly saved data.
  1233. {
  1234. $text = stripslashes($text);
  1235. }
  1236. // Make sure we have a valid count for word wrapping
  1237. if (!$wrap && $pref['main_wordwrap'])
  1238. {
  1239. $wrap = $pref['main_wordwrap'];
  1240. }
  1241. // $text = " ".$text;
  1242. // Now get on with the parsing
  1243. $ret_parser = '';
  1244. $last_bbcode = '';
  1245. // So we can change them on each loop
  1246. $saveOpts = $opts;
  1247. if ($parseBB == FALSE)
  1248. {
  1249. $content = array($text);
  1250. }
  1251. else
  1252. {
  1253. // Split each text block into bits which are either within one of the 'key' bbcodes, or outside them
  1254. // (Because we have to match end words, the 'extra' capturing subpattern gets added to output array. We strip it later)
  1255. $content = preg_split('#(\[(table|html|php|code|scode|hide).*?\[/(?:\\2)\])#mis', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
  1256. }
  1257. // Use $full_text variable so its available to special bbcodes if required
  1258. foreach ($content as $full_text)
  1259. {
  1260. $proc_funcs = TRUE;
  1261. $convertNL = TRUE;
  1262. // We may have 'captured' a bbcode word - strip it if so
  1263. if ($last_bbcode == $full_text)
  1264. {
  1265. $last_bbcode = '';
  1266. $proc_funcs = FALSE;
  1267. $full_text = '';
  1268. }
  1269. else
  1270. {
  1271. // Set the options for this pass
  1272. $opts = $saveOpts;
  1273. // Have to have a good test in case a 'non-key' bbcode starts the block
  1274. // - so pull out the bbcode parameters while we're there
  1275. if (($parseBB !== FALSE) && preg_match('#(^\[(table|html|php|code|scode|hide)(.*?)\])(.*?)(\[/\\2\]$)#is', $full_text, $matches ))
  1276. {
  1277. // It's one of the 'key' bbcodes
  1278. // Usually don't want 'normal' processing if its a 'special' bbcode
  1279. $proc_funcs = FALSE;
  1280. // $matches[0] - complete block from opening bracket of opening tag to closing bracket of closing tag
  1281. // $matches[1] - complete opening tag (inclusive of brackets)
  1282. // $matches[2] - bbcode word
  1283. // $matches[3] - parameter, including '='
  1284. // $matches[4] - bit between the tags (i.e. text to process)
  1285. // $matches[5] - closing tag
  1286. // In case we decide to load a file
  1287. $bbPath = e_CORE.'bbcodes/';
  1288. $bbFile = strtolower(str_replace('_', '', $matches[2]));
  1289. $bbcode = '';
  1290. $className = '';
  1291. $full_text = '';
  1292. $code_text = $matches[4];
  1293. $parm = $matches[3] ? substr($matches[3],1) : '';
  1294. $last_bbcode = $matches[2];
  1295. switch ($matches[2])
  1296. {
  1297. case 'php' :
  1298. // Probably run the output through the normal processing functions - but put here so the PHP code can disable if desired
  1299. $proc_funcs = TRUE;
  1300. // This is just the contents of the php.bb file pulled in - its short, so will be quicker
  1301. // $search = array("&quot;", "&#039;", "&#036;", '<br />', E_NL, "-&gt;", "&lt;br /&gt;");
  1302. // $replace = array('"', "'", "$", "\n", "\n", "->", "<br />");
  1303. // Shouldn't have any parameter on this bbcode
  1304. // Not sure whether checks are necessary now we've reorganised
  1305. // if (!$matches[3]) $bbcode = str_replace($search, $replace, $matches[4]);
  1306. // Because we're bypassing most of the initial parser processing, we should be able to just reverse the effects of toDB() and execute the code
  1307. // [SecretR] - avoid php code injections, missing php.bb will completely disable user posted php blocks
  1308. $bbcode = file_get_contents($bbPath.$bbFile.'.bb');
  1309. if (!$matches[3])
  1310. {
  1311. $code_text = html_entity_decode($matches[4], ENT_QUOTES, 'UTF-8');
  1312. }
  1313. break;
  1314. case 'html' : // This overrides and deprecates html.bb
  1315. $proc_funcs = TRUE;
  1316. $noBreak = TRUE;
  1317. // $code_text = str_replace("\r\n", " ", $code_text);
  1318. $code_text = html_entity_decode($code_text, ENT_QUOTES, CHARSET);
  1319. $html_start = "<!-- bbcode-html-start -->"; // markers for html-to-bbcode replacement.
  1320. $html_end = "<!-- bbcode-html-end -->";
  1321. $full_text = str_replace(array("[html]","[/html]"), "",$code_text); // quick fix.. security issue?
  1322. $full_text =$this->replaceConstants($full_text,'abs');
  1323. $full_text = $html_start.$full_text.$html_end;
  1324. $full_text = $this->parseBBTags($full_text); // strip <bbcode> tags.
  1325. $opts['nobreak'] = true;
  1326. break;
  1327. case 'table' : // strip <br /> from inside of <table>
  1328. $convertNL = FALSE;
  1329. // break;
  1330. case 'hide' :
  1331. $proc_funcs = TRUE;
  1332. default : // Most bbcodes will just execute their normal file
  1333. // @todo should we cache these bbcodes? require_once should make class-related codes quite efficient
  1334. if (file_exists($bbPath.'bb_'.$bbFile.'.php'))
  1335. { // Its a bbcode class file
  1336. require_once($bbPath.'bb_'.$bbFile.'.php');
  1337. $className = 'bb_'.$last_bbcode;
  1338. $this->bbList[$last_bbcode] = new $className();
  1339. }
  1340. elseif(file_exists($bbPath.$bbFile.'.bb'))
  1341. {
  1342. $bbcode = file_get_contents($bbPath.$bbFile.'.bb');
  1343. }
  1344. } // end - switch ($matches[2])
  1345. if ($className)
  1346. {
  1347. $tempCode = new $className();
  1348. $full_text = $tempCode->bbPreDisplay($matches[4], $parm);
  1349. }
  1350. elseif ($bbcode)
  1351. { // Execute the file
  1352. $full_text = eval($bbcode); // Require output of bbcode to be returned
  1353. // added to remove possibility of nested bbcode exploits ...
  1354. // (same as in bbcode_handler - is it right that it just operates on $bbcode_return and not on $bbcode_output? - QUERY XXX-02
  1355. }
  1356. if(strpos($full_text, '[') !== FALSE)
  1357. {
  1358. $exp_search = array('eval', 'expression');
  1359. $exp_replace = array('ev<b></b>al', 'expres<b></b>sion');
  1360. $bbcode_return = str_replace($exp_search, $exp_replace, $full_text);
  1361. }
  1362. }
  1363. }
  1364. // Do the 'normal' processing - in principle, as previously - but think about the order.
  1365. if ($proc_funcs && !empty($full_text)) // some more speed
  1366. {
  1367. // Split out and ignore any scripts and style blocks. With just two choices we can match the closing tag in the regex
  1368. $subcon = preg_split('#((?:<s)(?:cript[^>]+>.*?</script>|tyle[^>]+>.*?</style>))#mis', $full_text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
  1369. foreach ($subcon as $sub_blk)
  1370. {
  1371. if(substr($sub_blk, 0, 7) == '<script')
  1372. {
  1373. if($opts['scripts'])
  1374. {
  1375. // Strip scripts unless permitted
  1376. $ret_parser .= $sub_blk;
  1377. }
  1378. }
  1379. elseif(substr($sub_blk, 0, 6) == '<style')
  1380. {
  1381. // Its a style block - just pass it through unaltered - except, do we need the line break stuff? - QUERY XXX-01
  1382. if(DB_INF_SHOW)
  1383. {
  1384. echo "Processing stylesheet: {$sub_blk}<br />";
  1385. }
  1386. $ret_parser .= $sub_blk;
  1387. }
  1388. else
  1389. {
  1390. // Do 'normal' processing on a chunk
  1391. // Could put tag stripping in here
  1392. /*
  1393. // Line break compression - filter white space after HTML tags - among other things, ensures HTML tables display properly
  1394. // Hopefully now achieved by other means
  1395. if ($convertNL && !$opts['nobreak'])
  1396. {
  1397. $sub_blk = preg_replace("#>\s*[\r]*\n[\r]*#", ">", $sub_blk);
  1398. }
  1399. */
  1400. // Link substitution
  1401. // Convert URL's to clickable links, unless modifiers or prefs override
  1402. if ($opts['link_click'])
  1403. {
  1404. if ($opts['link_replace'])
  1405. {
  1406. $_ext = ($pref['links_new_window'] ? " rel=\"external\"" : "");
  1407. // $sub_blk = preg_replace("#(^|[\s])([\w]+?://(?:[\w-%]+?)(?:\.[\w-%]+?)+.*?)(?=$|[\s()[\]<]|\.\s|\.$|,\s|,$)#is", "\\1<a href=\"\\2\" {$_ext}>".$pref['link_text']."</a>", $sub_blk);
  1408. // $sub_blk = preg_replace("#(^|[\s])((?:www|ftp)(?:\.[\w-%]+?){2}.*?)(?=$|[\s()[\]<]|\.\s|\.$|,\s|,$)#is", "\\1<a href=\"http://\\2\" {$_ext}>".$pref['link_text']."</a>", $sub_blk);
  1409. $sub_blk = preg_replace("#(^|[\s])([\w]+?://(?:[\w-%]+?)(?:\.[\w-%]+?)+.*?)(?=$|[\s[\]<]|\.\s|\.$|,\s|,$)#is", "\\1<a href=\"\\2\" {$_ext}>".$pref['link_text']."</a>", $sub_blk);
  1410. $sub_blk = preg_replace("#(^|[\s])((?:www|ftp)(?:\.[\w-%]+?){2}.*?)(?=$|[\s[\]<]|\.\s|\.$|,\s|,$)#is", "\\1<a href=\"http://\\2\" {$_ext}>".$pref['link_text']."</a>", $sub_blk);
  1411. $email_text = ($pref['email_text']) ? $this->replaceConstants($pref['email_text']) : LAN_EMAIL_SUBS;
  1412. $sub_blk = preg_replace("#([\n ])([a-z0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)#i", "\\1<a rel='external' href='javascript:window.location=\"mai\"+\"lto:\"+\"\\2\"+\"@\"+\"\\3\";self.close();' onmouseover='window.status=\"mai\"+\"lto:\"+\"\\2\"+\"@\"+\"\\3\"; return true;' onmouseout='window.status=\"\";return true;'>".$email_text."</a>", $sub_blk);
  1413. }
  1414. else
  1415. {
  1416. $email_text = '$1$2©$3';
  1417. // $sub_blk = preg_replace("#(^|[\s])([\w]+?://(?:[\w-%]+?)(?:\.[\w-%]+?)+.*?)(?=$|[\s()[\]<]|\.\s|\.$|,\s|,$)#is", "\\1<a href=\"\\2\" rel=\"external\">\\2</a>", $sub_blk);
  1418. // $sub_blk = preg_replace("#(^|[\s])((?:www|ftp)(?:\.[\w-%]+?){2}.*?)(?=$|[\s()[\]<]|\.\s|\.$|,\s|,$)#is", "\\1<a href=\"http://\\2\" rel=\"external\">\\2</a>", $sub_blk);
  1419. $sub_blk = preg_replace("#(^|[\s])([\w]+?://(?:[\w-%]+?)(?:\.[\w-%]+?)+.*?)(?=$|[\s[\]<]|\.\s|\.$|,\s|,$)#is", "\\1<a href=\"\\2\" rel=\"external\">\\2</a>", $sub_blk);
  1420. $sub_blk = preg_replace("#(^|[\s])((?:www|ftp)(?:\.[\w-%]+?){2}.*?)(?=$|[\s[\]<]|\.\s|\.$|,\s|,$)#is", "\\1<a href=\"http://\\2\" rel=\"external\">\\2</a>", $sub_blk);
  1421. $sub_blk = preg_replace("#([\n ])([a-z0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)#i", "\\1<a rel='external' href='javascript:window.location=\"mai\"+\"lto:\"+\"\\2\"+\"@\"+\"\\3\";self.close();' onmouseover='window.status=\"mai\"+\"lto:\"+\"\\2\"+\"@\"+\"\\3\"; return true;' onmouseout='window.status=\"\";return true;'>".$email_text."</a>", $sub_blk);
  1422. }
  1423. }
  1424. // Convert emoticons to graphical icons, if enabled
  1425. if ($opts['emotes'])
  1426. {
  1427. if (!is_object($this->e_emote))
  1428. {
  1429. // require_once(e_HANDLER.'emote_filter.php');
  1430. $this->e_emote = new e_emoteFilter;
  1431. }
  1432. $sub_blk = $this->e_emote->filterEmotes($sub_blk);
  1433. }
  1434. // Reduce newlines in all forms to a single newline character (finds '\n', '\r\n', '\n\r')
  1435. if (!$opts['nobreak'])
  1436. {
  1437. if ($convertNL && substr($sub_blk,0,6) != '[html]') //XXX Quick Fix, find a cleaner way.
  1438. {
  1439. // We may need to convert to <br /> later
  1440. $sub_blk = preg_replace("#[\r]*\n[\r]*#", E_NL, $sub_blk);
  1441. }
  1442. else
  1443. {
  1444. // Not doing any more - its HTML so keep \n so HTML is formatted
  1445. $sub_blk = preg_replace("#[\r]*\n[\r]*#", "\n", $sub_blk);
  1446. }
  1447. }
  1448. // Entity conversion
  1449. // Restore entity form of quotes and such to single characters, except for text destined for tag attributes or JS.
  1450. if($opts['value'])
  1451. {
  1452. // output used for attribute values.
  1453. $sub_blk = str_replace($this->replace, $this->search, $sub_blk);
  1454. }
  1455. else
  1456. {
  1457. // output not used for attribute values.
  1458. $sub_blk = str_replace($this->search, $this->replace, $sub_blk);
  1459. }
  1460. // BBCode processing (other than the four already done, which shouldn't appear at all in the text)
  1461. if ($parseBB !== FALSE)
  1462. {
  1463. if (!is_object($this->e_bb))
  1464. {
  1465. require_once(e_HANDLER.'bbcode_handler.php');
  1466. $this->e_bb = new e_bbcode;
  1467. }
  1468. if ($parseBB === TRUE)
  1469. {
  1470. // 'Normal' or 'legacy' processing
  1471. if($modifiers == "WYSIWYG")
  1472. {
  1473. $sub_blk = $this->e_bb->parseBBCodes($sub_blk, $postID, 'wysiwyg');
  1474. }
  1475. else
  1476. {
  1477. $sub_blk = $this->e_bb->parseBBCodes($sub_blk, $postID);
  1478. }
  1479. }
  1480. elseif ($parseBB === 'STRIP')
  1481. {
  1482. // Need to strip all BBCodes
  1483. $sub_blk = $this->e_bb->parseBBCodes($sub_blk, $postID, 'default', TRUE);
  1484. }
  1485. else
  1486. {
  1487. // Need to strip just some BBCodes
  1488. $sub_blk = $this->e_bb->parseBBCodes($sub_blk, $postID, 'default', $parseBB);
  1489. }
  1490. }
  1491. // replace all {e_XXX} constants with their e107 value. modifier determines relative/absolute conversion
  1492. // (Moved to after bbcode processing by Cameron)
  1493. if ($opts['constants'])
  1494. {
  1495. $sub_blk = $this->replaceConstants($sub_blk, $opts['constants']); // Now decodes text values
  1496. }
  1497. // profanity filter
  1498. if ($pref['profanity_filter'])
  1499. {
  1500. if (!is_object($this->e_pf))
  1501. {
  1502. // require_once(e_HANDLER."profanity_filter.php");
  1503. $this->e_pf = new e_profanityFilter;
  1504. }
  1505. $sub_blk = $this->e_pf->filterProfanities($sub_blk);
  1506. }
  1507. // Shortcodes
  1508. // Optional short-code conversion
  1509. if ($opts['parse_sc'])
  1510. {
  1511. $sub_blk = $this->parseTemplate($sub_blk, TRUE);
  1512. }
  1513. //Run any hooked in parsers
  1514. if ($opts['hook'])
  1515. {
  1516. if ( varset($pref['tohtml_hook']))
  1517. {
  1518. //Process the older tohtml_hook pref (deprecated)
  1519. foreach(explode(",", $pref['tohtml_hook']) as $hook)
  1520. {
  1521. if (!is_object($this->e_hook[$hook]))
  1522. {
  1523. if(is_readable(e_PLUGIN.$hook."/".$hook.".php"))
  1524. {
  1525. require_once(e_PLUGIN.$hook."/".$hook.".php");
  1526. $hook_class = "e_".$hook;
  1527. $this->e_hook[$hook] = new $hook_class;
  1528. }
  1529. }
  1530. $sub_blk = $this->e_hook[$hook]->$hook($sub_blk,$opts['context']);
  1531. }
  1532. }
  1533. if(isset($pref['e_tohtml_list']) && is_array($pref['e_tohtml_list']))
  1534. {
  1535. foreach($pref['e_tohtml_list'] as $hook)
  1536. {
  1537. if (!is_object($this->e_hook[$hook]))
  1538. {
  1539. if(is_readable(e_PLUGIN.$hook."/e_tohtml.php"))
  1540. {
  1541. require_once(e_PLUGIN.$hook."/e_tohtml.php");
  1542. $hook_class = "e_tohtml_".$hook;
  1543. $this->e_hook[$hook] = new $hook_class;
  1544. }
  1545. }
  1546. $sub_blk = $this->e_hook[$hook]->to_html($sub_blk, $opts['context']);
  1547. }
  1548. }
  1549. }
  1550. // Word wrap
  1551. if ($wrap && !$opts['nobreak'])
  1552. {
  1553. $sub_blk = $this->textclean($sub_blk, $wrap);
  1554. }
  1555. // Search highlighting
  1556. if ($opts['emotes']) // Why??
  1557. {
  1558. if ($this->checkHighlighting())
  1559. {
  1560. $sub_blk = $this->e_highlight($sub_blk, $this->e_query);
  1561. }
  1562. }
  1563. if($convertNL == true)
  1564. {
  1565. // Default replaces all \n with <br /> for HTML display
  1566. $nl_replace = '<br />';
  1567. if ($opts['nobreak'])
  1568. {
  1569. $nl_replace = '';
  1570. }
  1571. elseif ($opts['retain_nl'])
  1572. {
  1573. $nl_replace = "\n";
  1574. }
  1575. $sub_blk = str_replace(E_NL, $nl_replace, $sub_blk);
  1576. }
  1577. $ret_parser .= $sub_blk;
  1578. } // End of 'normal' processing for a block of text
  1579. } // End of 'foreach() on each block of non-script text
  1580. } // End of 'normal' parsing (non-script text)
  1581. else
  1582. {
  1583. // Text block that needed no processing at all
  1584. $ret_parser .= $full_text;
  1585. }
  1586. }
  1587. // Quick Fix - Remove trailing <br /> on block-level elements (eg. div, pre, table, etc. )
  1588. $srch = array();
  1589. $repl = array();
  1590. foreach($this->blockTags as $val)
  1591. {
  1592. $srch[] = "</".$val."><br />";
  1593. $repl[] = "</".$val.">";
  1594. }
  1595. $ret_parser = str_replace($srch, $repl, $ret_parser);
  1596. return trim($ret_parser);
  1597. }
  1598. function toAttribute($text)
  1599. {
  1600. // URLs posted without HTML access may have an &amp; in them.
  1601. $text = str_replace('&amp;', '&', $text);
  1602. // Xhtml compliance.
  1603. $text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
  1604. if(!preg_match('/&#|\'|"|\(|\)|<|>/s', $text))
  1605. {
  1606. $text = $this->replaceConstants($text);
  1607. return $text;
  1608. }
  1609. else
  1610. {
  1611. return '';
  1612. }
  1613. }
  1614. /**
  1615. * Convert text blocks which are to be embedded within JS
  1616. *
  1617. * @param string|array $stringarray
  1618. * @return string
  1619. */
  1620. public function toJS($stringarray)
  1621. {
  1622. $search = array("\r\n", "\r", "<br />", "'");
  1623. $replace = array("\\n", "", "\\n", "\'");
  1624. $stringarray = str_replace($search, $replace, $stringarray);
  1625. $stringarray = strip_tags($stringarray);
  1626. $trans_tbl = get_html_translation_table(HTML_ENTITIES);
  1627. $trans_tbl = array_flip($trans_tbl);
  1628. return strtr($stringarray, $trans_tbl);
  1629. }
  1630. /**
  1631. * Convert Text for RSS/XML use.
  1632. *
  1633. * @param string $text
  1634. * @param boolean $tags [optional]
  1635. * @return string
  1636. */
  1637. function toRss($text, $tags = FALSE)
  1638. {
  1639. if($tags != TRUE)
  1640. {
  1641. $text = $this -> toHTML($text, TRUE);
  1642. $text = strip_tags($text);
  1643. }
  1644. $text = $this->toEmail($text);
  1645. $search = array("&amp;#039;", "&amp;#036;", "&#039;", "&#036;"," & ", e_BASE, "href='request.php");
  1646. $replace = array("'", '$', "'", '$',' &amp; ', SITEURL, "href='".SITEURL."request.php" );
  1647. $text = str_replace($search, $replace, $text);
  1648. if($tags == TRUE && ($text))
  1649. {
  1650. $text = "<![CDATA[".$text."]]>";
  1651. }
  1652. return $text;
  1653. }
  1654. //Convert specific characters back to original form, for use in storing code (or regex) values in the db.
  1655. function toText($text)
  1656. {
  1657. $search = array("&amp;#039;", "&amp;#036;", "&#039;", "&#036;", "&#092;", "&amp;#092;");
  1658. $replace = array("'", '$', "'", '$', "\\", "\\");
  1659. $text = str_replace($search, $replace, $text);
  1660. return $text;
  1661. }
  1662. /**
  1663. * Set the dimensions of a thumbNail (generated by thumbUrl)
  1664. */
  1665. public function setThumbSize($w=null,$h=null,$crop=null)
  1666. {
  1667. if($w)
  1668. {
  1669. $this->thumbWidth = intval($w);
  1670. }
  1671. if($h)
  1672. {
  1673. $this->thumbHeight = intval($h);
  1674. }
  1675. if($crop)
  1676. {
  1677. $this->thumbCrop = intval($crop);
  1678. }
  1679. }
  1680. /**
  1681. * Set or Get the value of the thumbNail Width.
  1682. * @param $width (optional)
  1683. */
  1684. public function thumbWidth($width=null)
  1685. {
  1686. if($val !== null)
  1687. {
  1688. $this->thumbWidth = intval($width);
  1689. }
  1690. return $this->thumbWidth;
  1691. }
  1692. /**
  1693. * Set or Get the value of the thumbNail height.
  1694. * @param $height (optional)
  1695. */
  1696. public function thumbHeight($height= null)
  1697. {
  1698. if($val !== null)
  1699. {
  1700. $this->thumbHeight = intval($height);
  1701. }
  1702. return $this->thumbHeight;
  1703. }
  1704. /**
  1705. * Generate an auto-sized Image URL.
  1706. * @param $url - path to image or leave blank for a placeholder.
  1707. * @param $options - width and height, but leaving this empty and using $this->thumbWidth() and $this->thumbHeight() is preferred. ie. {SETWIDTH: w=x&y=x}
  1708. * @param $raw ??
  1709. * @param $full
  1710. */
  1711. public function thumbUrl($url=null, $options = array(), $raw = false, $full = false)
  1712. {
  1713. if(substr($url,0,3)=="{e_") // Fix for broken links that use {e_MEDIA} etc.
  1714. {
  1715. //$url = $this->replaceConstants($url,'abs');
  1716. // always switch to 'nice' urls when SC is used
  1717. $url = str_replace($this->getUrlConstants('sc'), $this->getUrlConstants('raw'), $url);
  1718. }
  1719. if(!is_array($options))
  1720. {
  1721. parse_str($options, $options);
  1722. }
  1723. if(strstr($url,e_MEDIA) || strstr($url,e_SYSTEM)) // prevent disclosure of 'hashed' path.
  1724. {
  1725. $raw = true;
  1726. }
  1727. if($raw) $url = $this->createConstants($url, 'mix');
  1728. $baseurl = ($full ? SITEURL : e_HTTP).'thumb.php?';
  1729. $thurl = 'src='.urlencode($url).'&amp;';
  1730. if(vartrue($options['aw']) || vartrue($options['ah']) || $this->thumbCrop == 1)
  1731. {
  1732. if($this->thumbCrop == 1 && !vartrue($options['aw']) && !vartrue($options['ah'])) // Allow templates to determine dimensions. See {SETIMAGE}
  1733. {
  1734. $options['aw'] = $this->thumbWidth;
  1735. $options['ah'] = $this->thumbHeight;
  1736. }
  1737. $thurl .= 'aw='.((integer) vartrue($options['aw'], 0)).'&amp;ah='.((integer) vartrue($options['ah'], 0));
  1738. }
  1739. else
  1740. {
  1741. if(!vartrue($options['w']) && !vartrue($options['h'])) // Allow templates to determine dimensions. See {SETIMAGE}
  1742. {
  1743. $options['w'] = $this->thumbWidth;
  1744. $options['h'] = $this->thumbHeight;
  1745. }
  1746. $thurl .= 'w='.((integer) vartrue($options['w'], 0)).'&amp;h='.((integer) vartrue($options['h'], 0));
  1747. }
  1748. if(vartrue($options['x']))//base64 encode url
  1749. {
  1750. $thurl = 'id='.base64_encode($thurl);
  1751. }
  1752. // echo "<br /><br />".$thurl;
  1753. return $baseurl.$thurl;
  1754. }
  1755. /**
  1756. * Help for converting to more safe URLs
  1757. * e.g. {e_MEDIA_FILE}path/to/video.flv => e_MEDIA_FILE/path/to/video.flv
  1758. *
  1759. * @todo support for ALL URL shortcodes (replacement methods)
  1760. * @param string $type sc|raw|rev|all
  1761. * @return array
  1762. */
  1763. public function getUrlConstants($type = 'sc')
  1764. {
  1765. // sub-folders first!
  1766. static $array = array(
  1767. 'e_MEDIA_FILE/' => '{e_MEDIA_FILE}',
  1768. 'e_MEDIA_VIDEO/' => '{e_MEDIA_VIDEO}',
  1769. 'e_MEDIA_IMAGE/' => '{e_MEDIA_IMAGE}',
  1770. 'e_MEDIA_ICON/' => '{e_MEDIA_ICON}',
  1771. 'e_AVATAR/' => '{e_AVATAR}',
  1772. 'e_AVATAR_DEFAULT/' => '{e_AVATAR_DEFAULT}',
  1773. 'e_AVATAR_UPLOAD/' => '{e_AVATAR_UPLOAD}',
  1774. 'e_WEB_JS/' => '{e_WEB_JS}',
  1775. 'e_WEB_CSS/' => '{e_WEB_CSS}',
  1776. 'e_WEB_IMAGE/' => '{e_WEB_IMAGE}',
  1777. 'e_IMPORT/' => '{e_IMPORT}',
  1778. // 'e_WEB_PACK/' => '{e_WEB_PACK}',
  1779. 'e_BASE/' => '{e_BASE}',
  1780. 'e_ADMIN/' => '{e_ADMIN}',
  1781. 'e_IMAGE/' => '{e_IMAGE}',
  1782. 'e_THEME/' => '{e_THEME}',
  1783. 'e_PLUGIN/' => '{e_PLUGIN}',
  1784. 'e_HANDLER/' => '{e_HANDLER}', // BC
  1785. 'e_MEDIA/' => '{e_MEDIA}',
  1786. 'e_WEB/' => '{e_ADMIN}',
  1787. );
  1788. switch ($type)
  1789. {
  1790. case 'sc':
  1791. return array_values($array);
  1792. break;
  1793. case 'raw':
  1794. return array_keys($array);
  1795. break;
  1796. case 'rev':
  1797. return array_reverse($array, true);
  1798. break;
  1799. case 'all':
  1800. return $array;
  1801. break;
  1802. }
  1803. return array();
  1804. }
  1805. function getEmotes()
  1806. {
  1807. return $this->e_emote->emotes;
  1808. }
  1809. /**
  1810. * Replace e107 path constants
  1811. * Note: only an ADMIN user can convert {e_ADMIN}
  1812. * TODO - runtime cache of search/replace arrays (object property) when $mode !== ''
  1813. * @param string $text
  1814. * @param string $mode [optional] abs|full "full" = produce absolute URL path, e.g. http://sitename.com/e107_plugins/etc
  1815. * 'abs' = produce truncated URL path, e.g. e107plugins/etc
  1816. * "" (default) = URL's get relative path e.g. ../e107_plugins/etc
  1817. * @param mixed $all [optional] if TRUE, then when $mode is "full" or TRUE, USERID is also replaced...
  1818. * when $mode is "" (default), ALL other e107 constants are replaced
  1819. * @return string
  1820. */
  1821. public function replaceConstants($text, $mode = '', $all = FALSE)
  1822. {
  1823. if($mode != "")
  1824. {
  1825. $e107 = e107::getInstance();
  1826. $replace_relative = array(
  1827. $e107->getFolder('media_files'),
  1828. $e107->getFolder('media_video'),
  1829. $e107->getFolder('media_image'),
  1830. $e107->getFolder('media_icon'),
  1831. $e107->getFolder('avatars'),
  1832. $e107->getFolder('web_js'),
  1833. $e107->getFolder('web_css'),
  1834. $e107->getFolder('web_image'),
  1835. //$e107->getFolder('web_pack'),
  1836. e_IMAGE_ABS,
  1837. e_THEME_ABS,
  1838. $e107->getFolder('images'),
  1839. $e107->getFolder('plugins'),
  1840. $e107->getFolder('files'),
  1841. $e107->getFolder('themes'),
  1842. // $e107->getFolder('downloads'),
  1843. $e107->getFolder('handlers'),
  1844. $e107->getFolder('media'),
  1845. $e107->getFolder('web'),
  1846. $e107->site_theme ? $e107->getFolder('themes').$e107->site_theme.'/' : '',
  1847. defset('THEME_ABS'),
  1848. (ADMIN ? $e107->getFolder('admin') : ''),
  1849. '',
  1850. $e107->getFolder('core'),
  1851. $e107->getFolder('system'),
  1852. );
  1853. switch ($mode)
  1854. {
  1855. case 'abs':
  1856. $replace_absolute = array(
  1857. e_MEDIA_FILE_ABS,
  1858. e_MEDIA_VIDEO_ABS,
  1859. e_MEDIA_IMAGE_ABS,
  1860. e_MEDIA_ICON_ABS,
  1861. e_AVATAR_ABS,
  1862. e_JS_ABS,
  1863. e_CSS_ABS,
  1864. e_WEB_IMAGE_ABS,
  1865. // e_PACK_ABS,
  1866. e_IMAGE_ABS,
  1867. e_THEME_ABS,
  1868. e_IMAGE_ABS,
  1869. e_PLUGIN_ABS,
  1870. e_FILE_ABS,
  1871. e_THEME_ABS,
  1872. // e_DOWNLOAD_ABS, //impossible when download is done via php.
  1873. '', // handlers - no ABS path available
  1874. e_MEDIA_ABS,
  1875. e_WEB_ABS,
  1876. defset('THEME_ABS'),
  1877. defset('THEME_ABS'),
  1878. (ADMIN ? e_ADMIN_ABS : ''),
  1879. $e107->server_path,
  1880. '', // no e_CORE absolute path
  1881. '', // no e_SYSTEM absolute path
  1882. );
  1883. break;
  1884. case 'full':
  1885. $replace_absolute = array(
  1886. SITEURLBASE.e_MEDIA_FILE_ABS,
  1887. SITEURLBASE.e_MEDIA_VIDEO_ABS,
  1888. SITEURLBASE.e_MEDIA_IMAGE_ABS,
  1889. SITEURLBASE.e_MEDIA_ICON_ABS,
  1890. SITEURLBASE.e_AVATAR_ABS,
  1891. SITEURLBASE.e_JS_ABS,
  1892. SITEURLBASE.e_CSS_ABS,
  1893. SITEURLBASE.e_WEB_IMAGE_ABS,
  1894. // SITEURLBASE.e_PACK_ABS,
  1895. SITEURLBASE.e_IMAGE_ABS,
  1896. SITEURLBASE.e_THEME_ABS,
  1897. SITEURLBASE.e_IMAGE_ABS,
  1898. SITEURLBASE.e_PLUGIN_ABS,
  1899. SITEURLBASE.e_FILE_ABS, // deprecated
  1900. SITEURLBASE.e_THEME_ABS,
  1901. //SITEURL.$e107->getFolder('downloads'),
  1902. '', // handlers - no ABS path available
  1903. SITEURLBASE.e_MEDIA_ABS,
  1904. SITEURLBASE.e_WEB_ABS,
  1905. defset('THEME_ABS') ? SITEURLBASE.THEME_ABS : '',
  1906. defset('THEME_ABS') ? SITEURLBASE.THEME_ABS : '',
  1907. (ADMIN ? SITEURLBASE.e_ADMIN_ABS : ''),
  1908. SITEURL,
  1909. '', // no e_CORE absolute path
  1910. '', // no e_SYSTEM absolute path
  1911. );
  1912. break;
  1913. }
  1914. // sub-folders first!
  1915. $search = array(
  1916. '{e_MEDIA_FILE}',
  1917. '{e_MEDIA_VIDEO}',
  1918. '{e_MEDIA_IMAGE}',
  1919. '{e_MEDIA_ICON}',
  1920. '{e_AVATAR}',
  1921. '{e_WEB_JS}',
  1922. '{e_WEB_CSS}',
  1923. '{e_WEB_IMAGE}',
  1924. // '{e_WEB_PACK}',
  1925. "{e_IMAGE_ABS}",
  1926. "{e_THEME_ABS}",
  1927. "{e_IMAGE}",
  1928. "{e_PLUGIN}",
  1929. "{e_FILE}",
  1930. "{e_THEME}",
  1931. //,"{e_DOWNLOAD}"
  1932. "{e_HANDLER}",
  1933. "{e_MEDIA}",
  1934. "{e_WEB}",
  1935. "{THEME}",
  1936. "{THEME_ABS}",
  1937. "{e_ADMIN}",
  1938. "{e_BASE}",
  1939. "{e_CORE}",
  1940. "{e_SYSTEM}",
  1941. );
  1942. /*if (ADMIN)
  1943. {
  1944. $replace_relative[] = $e107->getFolder('admin');
  1945. $replace_absolute[] = SITEURL.$e107->getFolder('admin');
  1946. $search[] = "{e_ADMIN}";
  1947. }*/
  1948. if ($all)
  1949. {
  1950. if (USER)
  1951. { // Can only replace with valid number for logged in users
  1952. $replace_relative[] = USERID;
  1953. $replace_absolute[] = USERID;
  1954. }
  1955. else
  1956. {
  1957. $replace_relative[] = '';
  1958. $replace_absolute[] = '';
  1959. }
  1960. $search[] = "{USERID}";
  1961. }
  1962. // current THEME
  1963. /*if(!defined('THEME'))
  1964. {
  1965. //if not already parsed by doReplace
  1966. $text = str_replace(array('{THEME}', '{THEME_ABS}'), '', $text);
  1967. }
  1968. else
  1969. {
  1970. $replace_relative[] = THEME;
  1971. $replace_absolute[] = THEME_ABS;
  1972. $search[] = "{THEME}";
  1973. $replace_relative[] = THEME;
  1974. $replace_absolute[] = THEME_ABS;
  1975. $search[] = "{THEME_ABS}";
  1976. }*/
  1977. $replace = ((string)$mode == "full" || (string)$mode=='abs' ) ? $replace_absolute : $replace_relative;
  1978. return str_replace($search,$replace,$text);
  1979. }
  1980. // $pattern = ($all ? "#\{([A-Za-z_0-9]*)\}#s" : "#\{(e_[A-Z]*)\}#s");
  1981. $pattern = ($all ? '#\{([A-Za-z_0-9]*)\}#s' : '#\{(e_[A-Z]*(?:_IMAGE|_VIDEO|_FILE|_CONTENT|_ICON|_AVATAR|_JS|_CSS|_PACK|_DB|_ABS){0,1})\}#s');
  1982. $text = preg_replace_callback($pattern, array($this, 'doReplace'), $text);
  1983. if(!defined('THEME'))
  1984. {
  1985. //if not already parsed by doReplace
  1986. $text = str_replace(array('{THEME}', '{THEME_ABS}'), '', $text);
  1987. }
  1988. else
  1989. {
  1990. $srch = array('{THEME}', '{THEME_ABS}');
  1991. $repl = array(THEME, THEME_ABS);
  1992. $text = str_replace($srch, $repl, $text);
  1993. }
  1994. return $text;
  1995. }
  1996. function doReplace($matches)
  1997. {
  1998. if(defined($matches[1]) && (deftrue('ADMIN') || strpos($matches[1], 'ADMIN') === FALSE))
  1999. {
  2000. return constant($matches[1]);
  2001. }
  2002. return $matches[1];
  2003. }
  2004. /**
  2005. * Create and substitute e107 constants in passed URL
  2006. *
  2007. * @param string $url
  2008. * @param integer $mode 0-folders, 1-relative ('rel'), 2-absolute ('abs'), 3-full ('full') (with domain), 4-absolute & relative ('mix') (combination of 1,2,3)
  2009. * @return string
  2010. */
  2011. public function createConstants($url, $mode = 0)
  2012. {
  2013. //FIXME - create constants for absolute paths and site URL's
  2014. if (!is_numeric($mode))
  2015. {
  2016. switch ($mode)
  2017. {
  2018. case 'rel' : $mode = 1; break;
  2019. case 'abs' : $mode = 2; break;
  2020. case 'full' : $mode = 3; break;
  2021. case 'mix' : $mode = 4; break;
  2022. case 'nice': $mode = 5; break;
  2023. }
  2024. }
  2025. $e107 = e107::getInstance();
  2026. switch($mode)
  2027. {
  2028. case 0: // folder name only.
  2029. $tmp = array(
  2030. '{e_MEDIA_FILE}' => $e107->getFolder('media_files'),
  2031. '{e_MEDIA_VIDEO}' => $e107->getFolder('media_videos'),
  2032. '{e_MEDIA_IMAGE}' => $e107->getFolder('media_images'),
  2033. '{e_MEDIA_ICON}' => $e107->getFolder('media_icons'),
  2034. '{e_AVATAR}' => $e107->getFolder('avatars'),
  2035. '{e_WEB_JS}' => $e107->getFolder('web_js'),
  2036. '{e_WEB_CSS}' => $e107->getFolder('web_css'),
  2037. '{e_WEB_IMAGE}' => $e107->getFolder('web_images'),
  2038. // '{e_WEB_PACK}' => $e107->getFolder('web_packs'),
  2039. '{e_IMAGE}' => $e107->getFolder('images'),
  2040. '{e_PLUGIN}' => $e107->getFolder('plugins'),
  2041. '{e_FILE}' => $e107->getFolder('files'),
  2042. '{e_THEME}' => $e107->getFolder('themes'),
  2043. '{e_DOWNLOAD}' => $e107->getFolder('downloads'),
  2044. '{e_ADMIN}' => $e107->getFolder('admin'),
  2045. '{e_HANDLER}' => $e107->getFolder('handlers'),
  2046. '{e_MEDIA}' => $e107->getFolder('media'),
  2047. '{e_WEB}' => $e107->getFolder('web'),
  2048. '{e_UPLOAD}' => $e107->getFolder('uploads'),
  2049. );
  2050. break;
  2051. case 1: // relative path only
  2052. $tmp = array(
  2053. '{e_MEDIA_FILE}' => e_MEDIA_FILE,
  2054. '{e_MEDIA_VIDEO}' => e_MEDIA_VIDEO,
  2055. '{e_MEDIA_IMAGE}' => e_MEDIA_IMAGE,
  2056. '{e_MEDIA_ICON}' => e_MEDIA_ICON,
  2057. '{e_AVATAR}' => e_AVATAR,
  2058. '{e_IMPORT}' => e_IMPORT,
  2059. '{e_WEB_JS}' => e_WEB_JS,
  2060. '{e_WEB_CSS}' => e_WEB_CSS,
  2061. '{e_WEB_IMAGE}' => e_WEB_IMAGE,
  2062. // '{e_WEB_PACK}' => e_WEB_PACK,
  2063. '{e_IMAGE}' => e_IMAGE,
  2064. '{e_PLUGIN}' => e_PLUGIN,
  2065. '{e_FILE}' => e_FILE,
  2066. '{e_THEME}' => e_THEME,
  2067. '{e_DOWNLOAD}' => e_DOWNLOAD,
  2068. '{e_ADMIN}' => e_ADMIN,
  2069. '{e_HANDLER}' => e_HANDLER,
  2070. '{e_MEDIA}' => e_MEDIA,
  2071. '{e_WEB}' => e_WEB,
  2072. '{e_UPLOAD}' => e_UPLOAD,
  2073. );
  2074. break;
  2075. case 2: // absolute path only
  2076. $tmp = array(
  2077. '{e_MEDIA_FILE}' => e_MEDIA_FILE_ABS,
  2078. '{e_MEDIA_VIDEO}' => e_MEDIA_VIDEO_ABS,
  2079. '{e_MEDIA_IMAGE}' => e_MEDIA_IMAGE_ABS,
  2080. '{e_MEDIA_ICON}' => e_MEDIA_ICON_ABS,
  2081. '{e_AVATAR}' => e_AVATAR_ABS,
  2082. '{e_WEB_JS}' => e_JS_ABS,
  2083. '{e_WEB_CSS}' => e_CSS_ABS,
  2084. '{e_WEB_IMAGE}' => e_WEB_IMAGE_ABS,
  2085. // '{e_WEB_PACK}' => e_PACK_ABS,
  2086. '{e_IMAGE}' => e_IMAGE_ABS,
  2087. '{e_PLUGIN}' => e_PLUGIN_ABS,
  2088. '{e_FILE}' => e_FILE_ABS, // deprecated
  2089. '{e_THEME}' => e_THEME_ABS,
  2090. '{e_DOWNLOAD}' => e_HTTP.'request.php?',// FIXME - we need solution!
  2091. '{e_ADMIN}' => e_ADMIN_ABS,
  2092. //'{e_HANDLER}' => e_HANDLER_ABS, - no ABS path available
  2093. '{e_MEDIA}' => e_MEDIA_ABS,
  2094. '{e_WEB}' => e_WEB_ABS,
  2095. '{e_BASE}' => e_HTTP,
  2096. );
  2097. break;
  2098. case 3: // full path (e.g http://domain.com/e107_images/)
  2099. $tmp = array(
  2100. '{e_MEDIA_FILE}' => SITEURLBASE.e_MEDIA_FILE_ABS,
  2101. '{e_MEDIA_VIDEO}' => SITEURLBASE.e_MEDIA_VIDEO_ABS,
  2102. '{e_MEDIA_IMAGE}' => SITEURLBASE.e_MEDIA_IMAGE_ABS,
  2103. '{e_MEDIA_ICON}' => SITEURLBASE.e_MEDIA_ICON_ABS,
  2104. '{e_AVATAR}' => SITEURLBASE.e_AVATAR_ABS,
  2105. '{e_WEB_JS}' => SITEURLBASE.e_JS_ABS,
  2106. '{e_WEB_CSS}' => SITEURLBASE.e_CSS_ABS,
  2107. '{e_WEB_IMAGE}' => SITEURLBASE.e_WEB_IMAGE_ABS,
  2108. // '{e_WEB_PACK}' => SITEURLBASE.e_PACK_ABS,
  2109. '{e_IMAGE}' => SITEURLBASE.e_IMAGE_ABS,
  2110. '{e_PLUGIN}' => SITEURLBASE.e_PLUGIN_ABS,
  2111. '{e_FILE}' => SITEURLBASE.e_FILE_ABS, // deprecated
  2112. '{e_THEME}' => SITEURLBASE.e_THEME_ABS,
  2113. '{e_DOWNLOAD}' => SITEURLBASE.e_HTTP.'request.php?',// FIXME - we need solution!
  2114. '{e_ADMIN}' => SITEURLBASE.e_ADMIN_ABS,
  2115. //'{e_HANDLER}' => e_HANDLER_ABS, - no ABS path available
  2116. '{e_MEDIA}' => SITEURLBASE.e_MEDIA_ABS,
  2117. '{e_WEB}' => SITEURLBASE.e_WEB_ABS,
  2118. '{e_BASE}' => SITEURL,
  2119. );
  2120. break;
  2121. case 4: // absolute & relative paths
  2122. $url = $this->createConstants($url, 3);
  2123. $url = $this->createConstants($url, 2);
  2124. $url = $this->createConstants($url, 1);
  2125. return $url;
  2126. break;
  2127. case 5: // nice urls - e.g. e_MEDIA_VIDEO/mystream.flv
  2128. $url = $this->createConstants($url, 4);
  2129. return str_replace($this->getUrlConstants('sc'), $this->getUrlConstants('raw'), $url);
  2130. break;
  2131. default:
  2132. $tmp = array();
  2133. break;
  2134. }
  2135. foreach($tmp as $key=>$val)
  2136. {
  2137. $len = strlen($val);
  2138. if(substr($url, 0, $len) == $val)
  2139. {
  2140. // replace the first instance only
  2141. return substr_replace($url, $key, 0, $len);
  2142. }
  2143. }
  2144. return $url;
  2145. }
  2146. //FIXME - $match not used?
  2147. function e_highlight($text, $match)
  2148. {
  2149. $tags = array();
  2150. preg_match_all('#<[^>]+>#', $text, $tags);
  2151. $text = preg_replace('#<[^>]+>#', '<|>', $text);
  2152. $text = preg_replace('#(\b".$match."\b)#i', '<span class="searchhighlight">\\1</span>', $text);
  2153. foreach ($tags[0] as $tag)
  2154. {
  2155. $text = preg_replace('#<\|>#', $tag, $text, 1);
  2156. }
  2157. return $text;
  2158. }
  2159. /**
  2160. * Convert Text to a suitable format for use in emails. eg. relative links will be replaced with full links etc.
  2161. * @param string $text
  2162. * @param boolean $posted - if the text has been posted. (uses stripslashes etc)
  2163. * @param string $mods - flags for text transformation.
  2164. */
  2165. public function toEmail($text, $posted = "", $mods = "parse_sc, no_make_clickable")
  2166. {
  2167. if ($posted === TRUE)
  2168. {
  2169. if (MAGIC_QUOTES_GPC)
  2170. {
  2171. $text = stripslashes($text);
  2172. }
  2173. $text = preg_replace('#\[(php)#i', '&#91;\\1', $text);
  2174. }
  2175. $text = (strtolower($mods) != "rawtext") ? $this->replaceConstants($text, "full") : $text;
  2176. $text = $this->toHTML($text, TRUE, $mods);
  2177. return $text;
  2178. }
  2179. /**
  2180. * Given an email address, returns a link including js-based obfuscation
  2181. */
  2182. function emailObfuscate($email, $words = '', $subject = '')
  2183. {
  2184. if(strpos($email, '@') === FALSE)
  2185. {
  2186. return '';
  2187. }
  2188. if ($subject)
  2189. {
  2190. $subject = '?subject='.$subject;
  2191. }
  2192. list($name, $address) = explode('@', $email, 2);
  2193. $reassembled = '"'.$name.'"+"@"+"'.$address.'"';
  2194. return "<a rel='external' href='javascript:window.location=\"mai\"+\"lto:\"+".$reassembled.$subject.";self.close();' onmouseover='window.status=\"mai\"+\"lto:\"+".$reassembled."; return true;' onmouseout='window.status=\"\";return true;'>".$words.'</a>';
  2195. }
  2196. public function __get($name)
  2197. {
  2198. switch($name)
  2199. {
  2200. case 'e_sc':
  2201. $ret = e107::getScParser();
  2202. break;
  2203. default:
  2204. trigger_error('$e107->$'.$name.' not defined', E_USER_WARNING);
  2205. return NULL;
  2206. break;
  2207. }
  2208. $this->$name = $ret;
  2209. return $ret;
  2210. }
  2211. }
  2212. /**
  2213. * New v2 Parser
  2214. * Start Fresh and Build on it over time to become eventual replacement to e_parse.
  2215. * Cameron's DOM-based parser.
  2216. */
  2217. class e_parser
  2218. {
  2219. /**
  2220. * @var DOMDocument
  2221. */
  2222. public $domObj = null;
  2223. protected $removedList = array();
  2224. protected $nodesToDelete = array();
  2225. protected $nodesToConvert = array();
  2226. protected $pathList = array();
  2227. protected $allowedAttributes = array(
  2228. 'default' => array('id', 'style', 'class'),
  2229. 'img' => array('id', 'src', 'style', 'class', 'alt', 'title', 'width', 'height'),
  2230. 'a' => array('id', 'href', 'style', 'class', 'title', 'target'),
  2231. 'script' => array('type', 'src', 'language'),
  2232. 'iframe' => array('id', 'src', 'frameborder', 'class', 'width', 'height', 'style')
  2233. );
  2234. protected $badAttrValues = array('javascript[\s]*?:','alert\(','vbscript[\s]*?:','data:text\/html', 'mhtml[\s]*?:', 'data:[\s]*?image');
  2235. protected $replaceAttrValues = array(
  2236. 'default' => array()
  2237. );
  2238. protected $allowedTags = array('html', 'body','div','a','img','table','tr', 'td', 'th', 'tbody', 'thead', 'colgroup', 'b',
  2239. 'i', 'pre','code', 'strong', 'u', 'em','ul','li','img','h1','h2','h3','h4','h5','h6','p',
  2240. 'div','pre','section','article', 'blockquote','hgroup','aside','figure','span', 'video', 'br',
  2241. 'small', 'caption', 'noscript'
  2242. );
  2243. protected $scriptTags = array('script','applet','iframe'); //allowed when $pref['post_script'] is enabled.
  2244. protected $blockTags = array('pre','div','h1','h2','h3','h4','h5','h6','blockquote'); // element includes its own line-break.
  2245. public function __construct()
  2246. {
  2247. $this->init();
  2248. /*
  2249. $meths = get_class_methods('DomDocument');
  2250. sort($meths);
  2251. print_a($meths);
  2252. */
  2253. }
  2254. /**
  2255. * Used by e_parse to start
  2256. */
  2257. function init()
  2258. {
  2259. $this->domObj = new DOMDocument();
  2260. }
  2261. /**
  2262. * Set Allowed Tags.
  2263. * @param $array
  2264. */
  2265. public function setAllowedTags($array=array())
  2266. {
  2267. $this->allowedTags = $array;
  2268. }
  2269. /**
  2270. * Set Allowed Attributes.
  2271. * @param $array
  2272. */
  2273. public function setAllowedAttributes($array=array())
  2274. {
  2275. $this->allowedAttributes = $array;
  2276. }
  2277. /**
  2278. * Set Script Tags.
  2279. * @param $array
  2280. */
  2281. public function setScriptTags($array=array())
  2282. {
  2283. $this->scriptTags = $array;
  2284. }
  2285. /**
  2286. * Add leading zeros to a number. eg. 3 might become 000003
  2287. * @param $num integer
  2288. * @param $numDigits - total number of digits
  2289. * @return number with leading zeros.
  2290. */
  2291. public function leadingZeros($num,$numDigits)
  2292. {
  2293. return sprintf("%0".$numDigits."d",$num);
  2294. }
  2295. /**
  2296. * Generic variable translator for LAN definitions.
  2297. * @param $lan - string LAN
  2298. * @param $vals - either a single value, which will replace '[x]' or an array with key=>value pairs.
  2299. * @example $tp->lanVars("My name is [x] and I own a [y]", array('x'=>"John", 'y'=>"Cat"));
  2300. */
  2301. function lanVars($lan, $vals, $bold=false)
  2302. {
  2303. $array = (!is_array($vals)) ? array('x'=>$vals) : $vals;
  2304. foreach($array as $k=>$v)
  2305. {
  2306. $search[] = "[".$k."]";
  2307. $replace[] = ($bold===true) ? "<strong>".$v."</strong>" : $v;
  2308. }
  2309. return str_replace($search, $replace, $lan);
  2310. }
  2311. /**
  2312. * Return an Array of all specific tags found in an HTML document and their attributes.
  2313. * @param $html - raw html code
  2314. * @param $taglist - comma separated list of tags to search or '*' for all.
  2315. * @param $header - if the $html includes the html head or body tags - it should be set to true.
  2316. */
  2317. public function getTags($html, $taglist='*', $header = false)
  2318. {
  2319. if($header == false)
  2320. {
  2321. $html = "<html><body>".$html."</body></html>";
  2322. }
  2323. $doc = $this->domObj;
  2324. $doc->preserveWhiteSpace = true;
  2325. libxml_use_internal_errors(true);
  2326. $doc->loadHTML($html);
  2327. $tg = explode(",", $taglist);
  2328. $ret = array();
  2329. foreach($tg as $find)
  2330. {
  2331. $tmp = $doc->getElementsByTagName($find);
  2332. foreach($tmp as $k=>$node)
  2333. {
  2334. $tag = $node->nodeName;
  2335. $inner = $node->C14N();
  2336. $inner = str_replace("&#xD;","",$inner);
  2337. foreach ($node->attributes as $attr)
  2338. {
  2339. $name = $attr->nodeName;
  2340. $value = $attr->nodeValue;
  2341. $ret[$tag][$k][$name] = $value;
  2342. }
  2343. $ret[$tag][$k]['@value'] = $inner;
  2344. }
  2345. }
  2346. if($header == false)
  2347. {
  2348. unset($ret['html'],$ret['body']);
  2349. }
  2350. return $ret;
  2351. }
  2352. /**
  2353. * Parse xxxxx.glyph file to bootstrap glyph format.
  2354. * @param string $text
  2355. * @param array of $parms
  2356. */
  2357. public function toGlyph($text, $space=" ")
  2358. {
  2359. if(!deftrue('BOOTSTRAP') || empty($text))
  2360. {
  2361. return false;
  2362. }
  2363. if(is_array($space))
  2364. {
  2365. $parm = $space;
  2366. $space = varset($parm['space'],'');
  2367. }
  2368. else
  2369. {
  2370. $parm = array();
  2371. }
  2372. if(substr($text,0,2) == 'e-') // e107 admin icon.
  2373. {
  2374. $size = (substr($text,-3) == '-32') ? 'S32' : 'S16';
  2375. return "<i class='".$size." ".$text."'></i>";
  2376. }
  2377. // Get Glyph names.
  2378. $bs3 = e107::getMedia()->getGlyphs('bs3','');
  2379. $fa4 = e107::getMedia()->getGlyphs('fa4','');
  2380. list($cls) = explode('.glyph',$text,2);
  2381. // list($type, $tmp2) = explode("-",$text,2);
  2382. // return $cls;
  2383. $removePrefix = array('glyphicon-','icon-','fa-');
  2384. $id = str_replace($removePrefix, "", $cls);
  2385. // return print_r($fa4,true);
  2386. if(deftrue('FONTAWESOME') && in_array($id ,$fa4)) // Contains FontAwesome 3 set also.
  2387. {
  2388. $prefix = 'fa fa-';
  2389. $size = (vartrue($parm['size'])) ? ' fa-'.$parm['size'] : '';
  2390. $tag = 'i';
  2391. }
  2392. elseif(deftrue("BOOTSTRAP"))
  2393. {
  2394. if(BOOTSTRAP === 3 && in_array($id ,$bs3))
  2395. {
  2396. $prefix = 'glyphicon glyphicon-';
  2397. $tag = 'span';
  2398. }
  2399. else
  2400. {
  2401. $prefix = 'icon-';
  2402. $tag = 'i';
  2403. }
  2404. $size = '';
  2405. }
  2406. $text = "<".$tag." class='".$prefix.$id.$size."'></".$tag.">" ;
  2407. $text .= ($space !== false) ? $space : "";
  2408. return $text;
  2409. //$text = preg_replace('/\[(i_[\w]*)\]/',"<i class='$1'></i>", $text);
  2410. // return $text;
  2411. }
  2412. /**
  2413. * Display an icon.
  2414. * @param string $icon
  2415. * @example $tp->toIcon("{e_IMAGES}icons/something.png");
  2416. */
  2417. public function toIcon($icon='',$parm = array())
  2418. {
  2419. if(!vartrue($icon))
  2420. {
  2421. return;
  2422. }
  2423. $ext = pathinfo($icon, PATHINFO_EXTENSION);
  2424. if(!$ext || $ext == 'glyph') // Bootstrap or Font-Awesome.
  2425. {
  2426. return $this->toGlyph($icon,$parm);
  2427. }
  2428. if(strpos($icon,'e_MEDIA')!==FALSE)
  2429. {
  2430. $path = $this->thumbUrl($icon);
  2431. }
  2432. elseif($icon[0] == '{')
  2433. {
  2434. $path = $this->replaceConstants($icon,'full');
  2435. }
  2436. elseif(vartrue($parm['legacy']))
  2437. {
  2438. $legacyPath = $parm['legacy'].$icon;
  2439. $filePath = $this->replaceConstants($legacyPath,'rel');
  2440. if(is_readable($filePath))
  2441. {
  2442. $path = $this->replaceConstants($legacyPath,'full');
  2443. }
  2444. else
  2445. {
  2446. $log = e107::getAdminLog();
  2447. $log->addDebug('Broken Icon Path: '.$legacyPath, false)->save('IMALAN_00');
  2448. }
  2449. }
  2450. else
  2451. {
  2452. $path = $icon;
  2453. }
  2454. return "<img class='icon' src='".$path."' alt='".basename($path)."' />";
  2455. }
  2456. /**
  2457. * Check if a file is an video or not.
  2458. * @param $file string
  2459. * @return boolean
  2460. */
  2461. function isVideo($file)
  2462. {
  2463. $ext = pathinfo($file,PATHINFO_EXTENSION);
  2464. return ($ext == 'youtube') ? true : false;
  2465. }
  2466. /**
  2467. * Display a Video file.
  2468. * @param string $file - format: id.type eg. x123dkax.youtube
  2469. * @param boolean $thumbnail - set to 'tag' to return an image thumbnail and 'src' to return the src url or 'video' for a small video thumbnail.
  2470. */
  2471. function toVideo($file, $parm=array())
  2472. {
  2473. list($id,$type) = explode(".",$file,2);
  2474. $thumb = vartrue($parm['thumb']);
  2475. $pref = e107::getPref();
  2476. $ytpref = array();
  2477. foreach($pref as $k=>$v) // Find all Youtube Prefs.
  2478. {
  2479. if(substr($k,0,8) == 'youtube_')
  2480. {
  2481. $key = substr($k,8);
  2482. $ytpref[$key] = $v;
  2483. }
  2484. }
  2485. $ytqry = http_build_query($ytpref);
  2486. if($type == 'youtube')
  2487. {
  2488. $video = '<iframe width="560" height="315" src="//www.youtube.com/embed/'.$id.'?'.$ytqry.'" style="border:0px" allowfullscreen></iframe>';
  2489. $thumbSrc = "https://i1.ytimg.com/vi/".$id."/0.jpg";
  2490. if($thumb == 'tag')
  2491. {
  2492. return "<img class='img-responsive' src='".$thumbSrc."' alt='Youtube Video' style='width:".vartrue($parm['w'],'80')."px'/>";
  2493. }
  2494. if($thumb == 'src')
  2495. {
  2496. return $thumbSrc;
  2497. }
  2498. if($thumb == 'video')
  2499. {
  2500. return '<div class="video-responsive video-thumbnail thumbnail">'.$video.'</div>';
  2501. }
  2502. return '<div class="video-responsive '.vartrue($parm['class']).'">'.$video.'</div>';
  2503. }
  2504. if($type == 'mp4') //TODO FIXME
  2505. {
  2506. return '
  2507. <div class="video-responsive">
  2508. <video width="320" height="240" controls>
  2509. <source src="movie.mp4" type="video/mp4">
  2510. Your browser does not support the video tag.
  2511. </video>
  2512. </div>';
  2513. }
  2514. return false;
  2515. }
  2516. /**
  2517. * Display a Date in the browser.
  2518. * Includes support for 'livestamp' (http://mattbradley.github.io/livestampjs/)
  2519. * @param integer $datestamp - unix timestamp
  2520. * @param string $format - short | long | relative
  2521. * @return HTML with converted date.
  2522. */
  2523. public function toDate($datestamp = null, $format='short')
  2524. {
  2525. if(!is_numeric($datestamp)){ return; }
  2526. return '<span data-livestamp="'.$datestamp.'">'.e107::getDate()->convert($datestamp, $format).'</span>';
  2527. }
  2528. /**
  2529. * Parse new <bbcode> tags into bbcode output.
  2530. * @param $retainTags : when you want to replace html and retain the <bbcode> tags wrapping it.
  2531. * @return html
  2532. */
  2533. function parseBBTags($text,$retainTags = false)
  2534. {
  2535. $bbcodes = $this->getTags($text, 'bbcode');
  2536. foreach($bbcodes as $v)
  2537. {
  2538. foreach($v as $val)
  2539. {
  2540. $tag = urldecode($val['alt']);
  2541. $repl = ($retainTags == true) ? '$1'.$tag.'$2' : $tag;
  2542. $text = preg_replace('/(<bbcode[^>]*>).*(<\/bbcode>)/s',$repl, $text); //FIXME - handle multiple instances of bbcodes.
  2543. }
  2544. }
  2545. return $text;
  2546. }
  2547. /**
  2548. * Perform and render XSS Test Comparison
  2549. */
  2550. public function test()
  2551. {
  2552. // $tp = e107::getParser();
  2553. $sql = e107::getDb();
  2554. $tp = e107::getParser();
  2555. $html = $this->getXss();
  2556. echo "<h2>Unprocessed XSS</h2>";
  2557. // echo $html; // Remove Comment for a real mess!
  2558. print_a($html);
  2559. echo "<h2>Standard v2 Parser</h2>";
  2560. echo "<h3>\$tp->dataFilter()</h3>";
  2561. // echo $tp->dataFilter($html); // Remove Comment for a real mess!
  2562. $sql->db_Mark_Time('------ Start Parser Test -------');
  2563. print_a($tp->dataFilter($html));
  2564. $sql->db_Mark_Time('tp->dataFilter');
  2565. echo "<h3>\$tp->toHtml()</h3>";
  2566. // echo $tp->dataFilter($html); // Remove Comment for a real mess!
  2567. print_a($tp->toHTML($html));
  2568. $sql->db_Mark_Time('tp->toHtml');
  2569. echo "<h3>\$tp->toDB()</h3>";
  2570. // echo $tp->dataFilter($html); // Remove Comment for a real mess!
  2571. print_a($tp->toDB($html));
  2572. $sql->db_Mark_Time('tp->toDB');
  2573. echo "<h2>New Parser</h2>";
  2574. echo "<h3>Processed</h3>";
  2575. $cleaned = $this->cleanHtml($html);
  2576. print_a($cleaned);
  2577. $sql->db_Mark_Time('new Parser');
  2578. // $sql->db_Mark_Time('------ End Parser Test -------');
  2579. echo "<h3>Processed &amp; Rendered</h3>";
  2580. echo $cleaned;
  2581. echo "<h2>New Parser - Data</h2>";
  2582. echo "<h3>Converted Paths</h3>";
  2583. print_a($this->pathList);
  2584. echo "<h3>Removed Tags and Attributes</h3>";
  2585. print_a($this->removedList);
  2586. // print_a($p);
  2587. }
  2588. /**
  2589. * Process and clean HTML from user input.
  2590. * TODO Html5 tag support.
  2591. * @param string $html raw HTML
  2592. * @param boolean $checkPref
  2593. * @return string
  2594. */
  2595. public function cleanHtml($html='', $checkPref = true)
  2596. {
  2597. if(empty($html)){ return; }
  2598. // $html = mb_convert_encoding($html, 'UTF-8');
  2599. if(preg_match("/<body/i",$html)!==true) // HTML Fragment
  2600. {
  2601. $html = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html><html><head><meta charset="utf-8"></head><body>'.$html.'</body></html>';
  2602. }
  2603. else // Full HTML page.
  2604. {
  2605. // $this->allowedTags[] = 'head';
  2606. // $this->allowedTags[] = 'body';
  2607. // $this->allowedTags[] = 'title';
  2608. //$this->allowedTags[] = 'meta';
  2609. }
  2610. if(!is_object($this->domObj))
  2611. {
  2612. $this->init();
  2613. }
  2614. if($checkPref)
  2615. {
  2616. $post_scripts = e107::getConfig()->get('post_script', e_UC_MAINADMIN); // Pref to Allow <script> tags
  2617. if(check_class($post_scripts))
  2618. {
  2619. $this->allowedTags = array_merge($this->allowedTags,$this->scriptTags);
  2620. }
  2621. }
  2622. // Set it up for processing.
  2623. $doc = $this->domObj;
  2624. @$doc->loadHTML($html);
  2625. $doc->encoding = 'UTF-8'; //FIXME
  2626. // $doc->resolveExternals = true;
  2627. // $tmp = $doc->getElementsByTagName('*');
  2628. $this->nodesToConvert = array(); // required.
  2629. $this->nodesToDelete = array(); // required.
  2630. $this->removedList = array();
  2631. $tmp = $doc->getElementsByTagName('*');
  2632. /** @var DOMElement $node */
  2633. foreach($tmp as $node)
  2634. {
  2635. $path = $node->getNodePath();
  2636. // echo "<br />Path = ".$path;
  2637. // $tag = strval(basename($path));
  2638. $tag = preg_replace('/([a-z0-9\[\]\/]*)?\/([\w]*)(\[(\d)*\])?$/i', "$2", $path);
  2639. if(!in_array($tag, $this->allowedTags))
  2640. {
  2641. if(strpos($path,'/code/') !== false || strpos($path,'/pre/') !== false) // treat as html.
  2642. {
  2643. $this->pathList[] = $path;
  2644. $this->nodesToConvert[] = $node->parentNode; // $node;
  2645. continue;
  2646. }
  2647. $this->removedList['tags'][] = $tag;
  2648. $this->nodesToDelete[] = $node;
  2649. continue;
  2650. }
  2651. foreach ($node->attributes as $attr)
  2652. {
  2653. $name = $attr->nodeName;
  2654. $value = $attr->nodeValue;
  2655. $allow = varset($this->allowedAttributes[$tag], $this->allowedAttributes['default']);
  2656. $removeAttributes = array();
  2657. if(!in_array($name, $allow))
  2658. {
  2659. $removeAttributes[] = $name;
  2660. //$node->removeAttribute($name);
  2661. $this->removedList['attributes'][] = $name. " from <".$tag.">";
  2662. continue;
  2663. }
  2664. if($this->invalidAttributeValue($value)) // Check value against blacklisted values.
  2665. {
  2666. //$node->removeAttribute($name);
  2667. $node->setAttribute($name, '#---sanitized---#');
  2668. $this->removedList['sanitized'][] = $tag.'['.$name.']';
  2669. }
  2670. else
  2671. {
  2672. $_value = $this->secureAttributeValue($name, $value);
  2673. $node->setAttribute($name, $_value);
  2674. if($_value !== $value)
  2675. {
  2676. $this->removedList['sanitized'][] = $tag.'['.$name.'] converted "'.$value.'" -> "'.$_value.'"';
  2677. }
  2678. }
  2679. }
  2680. // required - removing attributes in a loop breaks the loop
  2681. foreach ($removeAttributes as $name)
  2682. {
  2683. $node->removeAttribute($name);
  2684. }
  2685. }
  2686. // Remove some stuff.
  2687. foreach($this->nodesToDelete as $node)
  2688. {
  2689. $node->parentNode->removeChild($node);
  2690. }
  2691. // Convert <code> and <pre> Tags to Htmlentities.
  2692. foreach($this->nodesToConvert as $node)
  2693. {
  2694. $value = $node->C14N();
  2695. $value = str_replace("&#xD;","",$value);
  2696. if($node->nodeName == 'pre')
  2697. {
  2698. $value = substr($value,5);
  2699. $end = strrpos($value,"</pre>");
  2700. $value = substr($value,0,$end);
  2701. }
  2702. if($node->nodeName == 'code')
  2703. {
  2704. $value = substr($value,6);
  2705. $end = strrpos($value,"</code>");
  2706. $value = substr($value,0,$end);
  2707. }
  2708. $value = htmlentities(htmlentities($value)); // Needed
  2709. $node->nodeValue = $value;
  2710. }
  2711. $cleaned = $doc->saveHTML();
  2712. $cleaned = str_replace(array('<body>','</body>','<html>','</html>','<!DOCTYPE html>','<meta charset="UTF-8">','<?xml version="1.0" encoding="utf-8"?>'),'',$cleaned); // filter out tags.
  2713. $cleaned = html_entity_decode($cleaned, ENT_QUOTES, 'UTF-8');
  2714. return trim($cleaned);
  2715. }
  2716. public function secureAttributeValue($attribute, $value)
  2717. {
  2718. $search = isset($this->replaceAttrValues[$attribute]) ? $this->replaceAttrValues[$attribute] : $this->replaceAttrValues['default'];
  2719. if(!empty($search))
  2720. {
  2721. $value = str_replace($search, '', $value);
  2722. }
  2723. return $value;
  2724. }
  2725. /**
  2726. * Check for Invalid Attribute Values
  2727. * @param $value string
  2728. * @return true/false
  2729. */
  2730. function invalidAttributeValue($value)
  2731. {
  2732. foreach($this->badAttrValues as $v) // global list because a bad value is bad regardless of the attribute it's in. ;-)
  2733. {
  2734. if(preg_match('/'.$v.'/i',$value)==true)
  2735. {
  2736. $this->removedList['blacklist'][] = "Match found for '{$v}' in '{$value}'";
  2737. return true;
  2738. }
  2739. }
  2740. return false;
  2741. }
  2742. /**
  2743. * XSS HTML code to test against
  2744. */
  2745. private function getXss()
  2746. {
  2747. $html = <<<EOF
  2748. Internationalization Test:
  2749. ภาษาไทย <br />
  2750. 日本語 <br />
  2751. 简体中文 <br />
  2752. <a href='somewhere.html' src='invalidatrribute' >Test</a>
  2753. A GOOD LINK: <a href='http://mylink.php'>Some Link</a>
  2754. <a href='javascript: something' src='invalidatrribute' >Test regex</a>
  2755. <img href='invalidattribute' src='myimage.jpg' />
  2756. <frameset onload=alert(1) data-something=where>
  2757. <table background="javascript:alert(1)"><tr><td><a href="something.php" onclick="alert(1)">Hi there</a></td></tr></table>
  2758. <div>
  2759. <!--<img src="--><img src=x onerror=alert(1)//">
  2760. <comment><img src="</comment><img src=x onerror=alert(1)//">
  2761. <ul>
  2762. <li style=list-style:url() onerror=alert(1)></li> <div style=content:url(data:image/svg+xml,%3Csvg/%3E);visibility:hidden onload=alert(1)></div>
  2763. </ul>
  2764. </div>
  2765. </frameset>
  2766. <head><base href="javascript://"/></head><body><a href="/. /,alert(1)//#">XXX</a></body>
  2767. <SCRIPT FOR=document EVENT=onreadystatechange>alert(1)</SCRIPT>
  2768. <OBJECT CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83"><PARAM NAME="DataURL" VALUE="javascript:alert(1)"></OBJECT>
  2769. <b <script>alert(1)//</script>0</script></b>
  2770. <div id="div1"><input value="``onmouseover=alert(1)"></div> <div id="div2"></div><
  2771. script>document.getElementById("div2").innerHTML = document.getElementById("div1").innerHTML;</script>
  2772. Some example text<br />
  2773. <b>This is bold</b><br />
  2774. <i>This is italic</i><br />
  2775. <small>Some small text</small>
  2776. <pre>This is pre-formatted
  2777. <script>alert('something')</script>
  2778. <b>Bold Stuff</b>
  2779. <pre>something</pre>
  2780. <code>code</code>
  2781. <b>BOLD</b>
  2782. function myfunction()
  2783. {
  2784. }
  2785. </pre>
  2786. <code>
  2787. function myfunction()
  2788. {
  2789. }
  2790. <script>alert('something')</script>
  2791. </code>
  2792. <svg><![CDATA[><image xlink:href="]]><img src=xx:x onerror=alert(2)//"></svg>
  2793. <style><img src="</style><img src=x onerror=alert(1)//">
  2794. <x '="foo"><x foo='><img src=x onerror=alert(1)//'> <!-- IE 6-9 --> <! '="foo"><x foo='><img src=x onerror=alert(2)//'> <? '="foo"><x foo='><img src=x onerror=alert(3)//'>
  2795. <embed src="javascript:alert(1)"></embed> // O10.10↓, OM10.0↓, GC6↓, FF <img src="javascript:alert(2)"> <image src="javascript:alert(2)"> // IE6, O10.10↓, OM10.0↓ <script src="javascript:alert(3)"></script> // IE6, O11.01↓, OM10.1↓
  2796. <div style=width:1px;filter:glow onfilterchange=alert(1)>x</div>
  2797. <object allowscriptaccess="always" data="test.swf"></object>
  2798. [A] <? foo="><script>alert(1)</script>"> <! foo="><script>alert(1)</script>"> </ foo="><script>alert(1)</script>"> [B] <? foo="><x foo='?><script>alert(1)</script>'>"> [C] <! foo="[[[x]]"><x foo="]foo><script>alert(1)</script>"> [D] <% foo><x foo="%><script>alert(1)</script>">
  2799. <iframe src=mhtml:http://html5sec.org/test.html!xss.html></iframe> <iframe src=mhtml:http://html5sec.org/test.gif!xss.html></iframe>
  2800. <html> <body> <b>some content without two new line \n\n</b> Content-Type: multipart/related; boundary="******"<b>some content without two new line</b> --****** Content-Location: xss.html Content-Transfer-Encoding: base64 PGlmcmFtZSBuYW1lPWxvIHN0eWxlPWRpc3BsYXk6bm9uZT48L2lmcmFtZT4NCjxzY3JpcHQ+DQp1 cmw9bG9jYXRpb24uaHJlZjtkb2N1bWVudC5nZXRFbGVtZW50c0J5TmFtZSgnbG8nKVswXS5zcmM9 dXJsLnN1YnN0cmluZyg2LHVybC5pbmRleE9mKCcvJywxNSkpO3NldFRpbWVvdXQoImFsZXJ0KGZy YW1lc1snbG8nXS5kb2N1bWVudC5jb29raWUpIiwyMDAwKTsNCjwvc2NyaXB0PiAgICAg --******-- </body> </html>
  2801. <!-- IE 5-9 --> <div id=d><x xmlns="><iframe onload=alert(1)"></div> <script>d.innerHTML+='';</script> <!-- IE 10 in IE5-9 Standards mode --> <div id=d><x xmlns='"><iframe onload=alert(2)//'></div> <script>d.innerHTML+='';</script>
  2802. <img[a][b]src=x[d]onerror[c]=[e]"alert(1)">
  2803. <a href="[a]java[b]script[c]:alert(1)">XXX</a>
  2804. <img src="x` `<script>alert(1)</script>"` `>
  2805. <img src onerror /" '"= alt=alert(1)//">
  2806. <title onpropertychange=alert(1)></title><title title=></title>
  2807. <!-- IE 5-8 standards mode --> <a href=http://foo.bar/#x=`y></a><img alt="`><img src=xx:x onerror=alert(1)></a>"> <!-- IE 5-9 standards mode --> <!a foo=x=`y><img alt="`><img src=xx:x onerror=alert(2)//"> <?a foo=x=`y><img alt="`><img src=xx:x onerror=alert(3)//">
  2808. <!--[if]><script>alert(1)</script --> <!--[if<img src=x onerror=alert(2)//]> -->
  2809. <script> Blabla </script>
  2810. <script src="/\example.com\foo.js"></script> // Safari 5.0, Chrome 9, 10 <script src="\\example.com\foo.js"></script> // Safari 5.0
  2811. <object id="x" classid="clsid:CB927D12-4FF7-4a9e-A169-56E4B8A75598"></object> <object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" onqt_error="alert(1)" style="behavior:url(#x);"><param name=postdomevents /></object>
  2812. <!-- `<img/src=xx:xx onerror=alert(1)//--!>
  2813. <xmp> <% </xmp> <img alt='%></xmp><img src=xx:x onerror=alert(1)//'> <script> x='<%' </script> %>/ alert(2) </script> XXX <style> *['<!--']{} </style> -->{} *{color:red}</style>
  2814. <a style="-o-link:'javascript:alert(1)';-o-link-source:current">X</a>
  2815. <style>p[foo=bar{}*{-o-link:'javascript:alert(1)'}{}*{-o-link-source:current}*{background:red}]{background:green};</style>
  2816. <div style="font-family:'foo[a];color:red;';">XXX</div>
  2817. <form id="test"></form><button form="test" formaction="javascript:alert(1)">X</button>
  2818. <input onfocus=write(1) autofocus>
  2819. <video poster=javascript:alert(1)//></video>
  2820. <video>somemovei.mp4</video>
  2821. <body onscroll=alert(1)><br><br><br><br><br><br>...<br><br><br><br><input autofocus>
  2822. <article id="something">Some text goes here</article>
  2823. EOF;
  2824. return $html;
  2825. }
  2826. }
  2827. class e_emotefilter {
  2828. var $search;
  2829. var $replace;
  2830. var $emotes;
  2831. function e_emotefilter() /* constructor */
  2832. {
  2833. $pref = e107::getPref();
  2834. if(!$pref['emotepack'])
  2835. {
  2836. $pref['emotepack'] = "default";
  2837. save_prefs();
  2838. }
  2839. $this->emotes = e107::getConfig("emote")->getPref();
  2840. if(!vartrue($this->emotes))
  2841. {
  2842. return;
  2843. }
  2844. foreach($this->emotes as $key => $value)
  2845. {
  2846. $value = trim($value);
  2847. if ($value)
  2848. { // Only 'activate' emote if there's a substitution string set
  2849. $key = preg_replace("#!(\w{3,}?)$#si", ".\\1", $key);
  2850. // Next two probably to sort out legacy issues - may not be required any more
  2851. $key = preg_replace("#_(\w{3})$#", ".\\1", $key);
  2852. $key = str_replace("!", "_", $key);
  2853. $filename = e_IMAGE."emotes/" . $pref['emotepack'] . "/" . $key;
  2854. $fileloc = SITEURLBASE.e_IMAGE_ABS."emotes/" . $pref['emotepack'] . "/" . $key;
  2855. if(file_exists($filename))
  2856. {
  2857. if(strstr($value, " "))
  2858. {
  2859. $tmp = explode(" ", $value);
  2860. foreach($tmp as $code)
  2861. {
  2862. $this->search[] = " ".$code;
  2863. $this->search[] = "\n".$code;
  2864. //TODO CSS class?
  2865. $this->replace[] = " <img src='".$fileloc."' alt='' style='vertical-align:middle; border:0' /> ";
  2866. $this->replace[] = "\n <img src='".$fileloc."' alt='' style='vertical-align:middle; border:0' /> ";
  2867. }
  2868. unset($tmp);
  2869. }
  2870. else
  2871. {
  2872. if($value)
  2873. {
  2874. $this->search[] = " ".$value;
  2875. $this->search[] = "\n".$value;
  2876. //TODO CSS class?
  2877. $this->replace[] = " <img src='".$filename."' alt='' style='vertical-align:middle; border:0' /> ";
  2878. $this->replace[] = "\n <img src='".$filename."' alt='' style='vertical-align:middle; border:0' /> ";
  2879. }
  2880. }
  2881. }
  2882. }
  2883. else
  2884. {
  2885. unset($this->emotes[$key]);
  2886. }
  2887. }
  2888. }
  2889. function filterEmotes($text)
  2890. {
  2891. $text = str_replace($this->search, $this->replace, $text);
  2892. return $text;
  2893. }
  2894. function filterEmotesRev($text)
  2895. {
  2896. $text = str_replace($this->replace, $this->search, $text);
  2897. return $text;
  2898. }
  2899. }
  2900. class e_profanityFilter
  2901. {
  2902. var $profanityList;
  2903. function e_profanityFilter()
  2904. {
  2905. global $pref;
  2906. $words = explode(",", $pref['profanity_words']);
  2907. $word_array = array();
  2908. foreach($words as $word)
  2909. {
  2910. $word = trim($word);
  2911. if($word != "")
  2912. {
  2913. $word_array[] = $word;
  2914. if (strpos($word, '&#036;') !== FALSE)
  2915. {
  2916. $word_array[] = str_replace('&#036;', '\$', $word); // Special case - '$' may be 'in clear' or as entity
  2917. }
  2918. }
  2919. }
  2920. if(count($word_array))
  2921. {
  2922. $this->profanityList = str_replace('#','\#',implode("\b|\b", $word_array)); // We can get entities in the string - confuse the regex delimiters
  2923. }
  2924. unset($words);
  2925. return TRUE;
  2926. }
  2927. function filterProfanities($text)
  2928. {
  2929. global $pref;
  2930. if (!$this->profanityList)
  2931. {
  2932. return $text;
  2933. }
  2934. if ($pref['profanity_replace'])
  2935. {
  2936. return preg_replace("#\b".$this->profanityList."\b#is", $pref['profanity_replace'], $text);
  2937. }
  2938. else
  2939. {
  2940. return preg_replace_callback("#\b".$this->profanityList."\b#is", array($this, 'replaceProfanities'), $text);
  2941. }
  2942. }
  2943. function replaceProfanities($matches)
  2944. {
  2945. /*!
  2946. @function replaceProfanities callback
  2947. @abstract replaces vowels in profanity words with stars
  2948. @param text string - text string to be filtered
  2949. @result filtered text
  2950. */
  2951. return preg_replace("#a|e|i|o|u#i", "*" , $matches[0]);
  2952. }
  2953. }