PageRenderTime 46ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/include/parser.php

https://github.com/gencer/punbb
PHP | 999 lines | 707 code | 181 blank | 111 comment | 241 complexity | f1df8b977b84f17b4df69e865a8855d0 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * Loads various functions used to parse posts.
  4. *
  5. * @copyright (C) 2008-2012 PunBB, partially based on code (C) 2008-2009 FluxBB.org
  6. * @license http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  7. * @package PunBB
  8. */
  9. // Make sure no one attempts to run this script "directly"
  10. if (!defined('FORUM'))
  11. exit;
  12. // Load the IDNA class for international url handling
  13. if (defined('FORUM_SUPPORT_PCRE_UNICODE') && defined('FORUM_ENABLE_IDNA'))
  14. {
  15. require FORUM_ROOT.'include/idna/idna_convert.class.php';
  16. }
  17. // Here you can add additional smilies if you like (please note that you must escape singlequote and backslash)
  18. $smilies = array(':)' => 'smile.png', '=)' => 'smile.png', ':|' => 'neutral.png', '=|' => 'neutral.png', ':(' => 'sad.png', '=(' => 'sad.png', ':D' => 'big_smile.png', '=D' => 'big_smile.png', ':o' => 'yikes.png', ':O' => 'yikes.png', ';)' => 'wink.png', ':/' => 'hmm.png', ':P' => 'tongue.png', ':p' => 'tongue.png', ':lol:' => 'lol.png', ':mad:' => 'mad.png', ':rolleyes:' => 'roll.png', ':cool:' => 'cool.png');
  19. ($hook = get_hook('ps_start')) ? eval($hook) : null;
  20. //
  21. // Make sure all BBCodes are lower case and do a little cleanup
  22. //
  23. function preparse_bbcode($text, &$errors, $is_signature = false)
  24. {
  25. global $forum_config;
  26. $return = ($hook = get_hook('ps_preparse_bbcode_start')) ? eval($hook) : null;
  27. if ($return != null)
  28. return $return;
  29. if ($is_signature)
  30. {
  31. global $lang_profile;
  32. if (preg_match('#\[quote(=(&quot;|"|\'|)(.*)\\1)?\]|\[/quote\]|\[code\]|\[/code\]|\[list(=([1a\*]))?\]|\[/list\]#i', $text))
  33. $errors[] = $lang_profile['Signature quote/code/list'];
  34. }
  35. if ($forum_config['p_sig_bbcode'] == '1' && $is_signature || $forum_config['p_message_bbcode'] == '1' && !$is_signature)
  36. {
  37. // If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
  38. if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
  39. {
  40. list($inside, $outside) = split_text($text, '[code]', '[/code]', $errors);
  41. $text = implode("\x1", $outside);
  42. }
  43. // Tidy up lists
  44. $pattern = array('%\[list(?:=([1a*]))?+\]((?:(?>.*?(?=\[list(?:=[1a*])?+\]|\[/list\]))|(?R))*)\[/list\]%ise');
  45. $replace = array('preparse_list_tag(\'$2\', \'$1\', $errors)');
  46. $text = preg_replace($pattern, $replace, $text);
  47. $text = str_replace('*'."\0".']', '*]', $text);
  48. if ($forum_config['o_make_links'] == '1')
  49. {
  50. $text = do_clickable($text, defined('FORUM_SUPPORT_PCRE_UNICODE'));
  51. }
  52. // If we split up the message before we have to concatenate it together again (code tags)
  53. if (isset($inside))
  54. {
  55. $outside = explode("\x1", $text);
  56. $text = '';
  57. $num_tokens = count($outside);
  58. for ($i = 0; $i < $num_tokens; ++$i)
  59. {
  60. $text .= $outside[$i];
  61. if (isset($inside[$i]))
  62. $text .= '[code]'.$inside[$i].'[/code]';
  63. }
  64. }
  65. $temp_text = false;
  66. if (empty($errors))
  67. $temp_text = preparse_tags($text, $errors, $is_signature);
  68. if ($temp_text !== false)
  69. $text = $temp_text;
  70. // Remove empty tags
  71. while ($new_text = preg_replace('/\[(b|u|i|h|colou?r|quote|code|img|url|email|list)(?:\=[^\]]*)?\]\[\/\1\]/', '', $text))
  72. {
  73. if ($new_text != $text)
  74. $text = $new_text;
  75. else
  76. break;
  77. }
  78. }
  79. $return = ($hook = get_hook('ps_preparse_bbcode_end')) ? eval($hook) : null;
  80. if ($return != null)
  81. return $return;
  82. return forum_trim($text);
  83. }
  84. //
  85. // Check the structure of bbcode tags and fix simple mistakes where possible
  86. //
  87. function preparse_tags($text, &$errors, $is_signature = false)
  88. {
  89. global $lang_common, $forum_config;
  90. // Start off by making some arrays of bbcode tags and what we need to do with each one
  91. // List of all the tags
  92. $tags = array('quote', 'code', 'b', 'i', 'u', 'color', 'colour', 'url', 'email', 'img', 'list', '*', 'h');
  93. // List of tags that we need to check are open (You could not put b,i,u in here then illegal nesting like [b][i][/b][/i] would be allowed)
  94. $tags_opened = $tags;
  95. // and tags we need to check are closed (the same as above, added it just in case)
  96. $tags_closed = $tags;
  97. // Tags we can nest and the depth they can be nested to (only quotes )
  98. $tags_nested = array('quote' => $forum_config['o_quote_depth'], 'list' => 5, '*' => 5);
  99. // Tags to ignore the contents of completely (just code)
  100. $tags_ignore = array('code');
  101. // Block tags, block tags can only go within another block tag, they cannot be in a normal tag
  102. $tags_block = array('quote', 'code', 'list', 'h', '*');
  103. // Inline tags, we do not allow new lines in these
  104. $tags_inline = array('b', 'i', 'u', 'color', 'colour', 'h');
  105. // Tags we trim interior space
  106. $tags_trim = array('img');
  107. // Tags we remove quotes from the argument
  108. $tags_quotes = array('url', 'email', 'img');
  109. // Tags we limit bbcode in
  110. $tags_limit_bbcode = array(
  111. '*' => array('b', 'i', 'u', 'color', 'colour', 'url', 'email', 'list', 'img'),
  112. 'list' => array('*'),
  113. 'url' => array('b', 'i', 'u', 'color', 'colour', 'img'),
  114. 'email' => array('b', 'i', 'u', 'color', 'colour', 'img'),
  115. 'img' => array()
  116. );
  117. // Tags we can automatically fix bad nesting
  118. $tags_fix = array('quote', 'b', 'i', 'u', 'color', 'colour', 'url', 'email', 'h');
  119. $return = ($hook = get_hook('ps_preparse_tags_start')) ? eval($hook) : null;
  120. if ($return != null)
  121. return $return;
  122. $split_text = preg_split("/(\[[\*a-zA-Z0-9-\/]*?(?:=.*?)?\])/", $text, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
  123. $open_tags = array('post');
  124. $open_args = array('');
  125. $opened_tag = 0;
  126. $new_text = '';
  127. $current_ignore = '';
  128. $current_nest = '';
  129. $current_depth = array();
  130. $limit_bbcode = $tags;
  131. foreach ($split_text as $current)
  132. {
  133. if ($current == '')
  134. continue;
  135. // Are we dealing with a tag?
  136. if (substr($current, 0, 1) != '[' || substr($current, -1, 1) != ']')
  137. {
  138. // Its not a bbcode tag so we put it on the end and continue
  139. // If we are nested too deeply don't add to the end
  140. if ($current_nest)
  141. continue;
  142. $current = str_replace("\r\n", "\n", $current);
  143. $current = str_replace("\r", "\n", $current);
  144. if (in_array($open_tags[$opened_tag], $tags_inline) && strpos($current, "\n") !== false)
  145. {
  146. // Deal with new lines
  147. $split_current = preg_split("/(\n\n+)/", $current, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
  148. $current = '';
  149. if (!forum_trim($split_current[0], "\n")) // the first part is a linebreak so we need to handle any open tags first
  150. array_unshift($split_current, '');
  151. for ($i = 1; $i < count($split_current); $i += 2)
  152. {
  153. $temp_opened = array();
  154. $temp_opened_arg = array();
  155. $temp = $split_current[$i - 1];
  156. while (!empty($open_tags))
  157. {
  158. $temp_tag = array_pop($open_tags);
  159. $temp_arg = array_pop($open_args);
  160. if (in_array($temp_tag , $tags_inline))
  161. {
  162. array_push($temp_opened, $temp_tag);
  163. array_push($temp_opened_arg, $temp_arg);
  164. $temp .= '[/'.$temp_tag.']';
  165. }
  166. else
  167. {
  168. array_push($open_tags, $temp_tag);
  169. array_push($open_args, $temp_arg);
  170. break;
  171. }
  172. }
  173. $current .= $temp.$split_current[$i];
  174. $temp = '';
  175. while (!empty($temp_opened))
  176. {
  177. $temp_tag = array_pop($temp_opened);
  178. $temp_arg = array_pop($temp_opened_arg);
  179. if (empty($temp_arg))
  180. $temp .= '['.$temp_tag.']';
  181. else
  182. $temp .= '['.$temp_tag.'='.$temp_arg.']';
  183. array_push($open_tags, $temp_tag);
  184. array_push($open_args, $temp_arg);
  185. }
  186. $current .= $temp;
  187. }
  188. if (array_key_exists($i - 1, $split_current))
  189. $current .= $split_current[$i - 1];
  190. }
  191. if (in_array($open_tags[$opened_tag], $tags_trim))
  192. $new_text .= forum_trim($current);
  193. else
  194. $new_text .= $current;
  195. continue;
  196. }
  197. // Get the name of the tag
  198. $current_arg = '';
  199. if (strpos($current, '/') === 1)
  200. {
  201. $current_tag = substr($current, 2, -1);
  202. }
  203. else if (strpos($current, '=') === false)
  204. {
  205. $current_tag = substr($current, 1, -1);
  206. }
  207. else
  208. {
  209. $current_tag = substr($current, 1, strpos($current, '=')-1);
  210. $current_arg = substr($current, strpos($current, '=')+1, -1);
  211. }
  212. $current_tag = strtolower($current_tag);
  213. // Is the tag defined?
  214. if (!in_array($current_tag, $tags))
  215. {
  216. // Its not a bbcode tag so we put it on the end and continue
  217. if (!$current_nest)
  218. $new_text .= $current;
  219. continue;
  220. }
  221. // We definitely have a bbcode tag.
  222. // Make the tag string lower case
  223. if ($equalpos = strpos($current,'='))
  224. {
  225. // We have an argument for the tag which we don't want to make lowercase
  226. if (strlen(substr($current, $equalpos)) == 2)
  227. {
  228. // Empty tag argument
  229. $errors[] = sprintf($lang_common['BBCode error 6'], $current_tag);
  230. return false;
  231. }
  232. $current = strtolower(substr($current, 0, $equalpos)).substr($current, $equalpos);
  233. }
  234. else
  235. $current = strtolower($current);
  236. //This is if we are currently in a tag which escapes other bbcode such as code
  237. if ($current_ignore)
  238. {
  239. if ('[/'.$current_ignore.']' == $current)
  240. {
  241. // We've finished the ignored section
  242. $current = '[/'.$current_tag.']';
  243. $current_ignore = '';
  244. }
  245. $new_text .= $current;
  246. continue;
  247. }
  248. if ($current_nest)
  249. {
  250. // We are currently too deeply nested so lets see if we are closing the tag or not.
  251. if ($current_tag != $current_nest)
  252. continue;
  253. if (substr($current, 1, 1) == '/')
  254. $current_depth[$current_nest]--;
  255. else
  256. $current_depth[$current_nest]++;
  257. if ($current_depth[$current_nest] <= $tags_nested[$current_nest])
  258. $current_nest = '';
  259. continue;
  260. }
  261. // Check the current tag is allowed here
  262. if (!in_array($current_tag, $limit_bbcode) && $current_tag != $open_tags[$opened_tag])
  263. {
  264. $errors[] = sprintf($lang_common['BBCode error 3'], $current_tag, $open_tags[$opened_tag]);
  265. return false;
  266. }
  267. if (substr($current, 1, 1) == '/')
  268. {
  269. //This is if we are closing a tag
  270. if ($opened_tag == 0 || !in_array($current_tag, $open_tags))
  271. {
  272. //We tried to close a tag which is not open
  273. if (in_array($current_tag, $tags_opened))
  274. {
  275. $errors[] = sprintf($lang_common['BBCode error 1'], $current_tag);
  276. return false;
  277. }
  278. }
  279. else
  280. {
  281. // Check nesting
  282. while (true)
  283. {
  284. // Nesting is ok
  285. if ($open_tags[$opened_tag] == $current_tag)
  286. {
  287. array_pop($open_tags);
  288. array_pop($open_args);
  289. $opened_tag--;
  290. break;
  291. }
  292. // Nesting isn't ok, try to fix it
  293. if (in_array($open_tags[$opened_tag], $tags_closed) && in_array($current_tag, $tags_closed))
  294. {
  295. if (in_array($current_tag, $open_tags))
  296. {
  297. $temp_opened = array();
  298. $temp_opened_arg = array();
  299. $temp = '';
  300. while (!empty($open_tags))
  301. {
  302. $temp_tag = array_pop($open_tags);
  303. $temp_arg = array_pop($open_args);
  304. if (!in_array($temp_tag, $tags_fix))
  305. {
  306. // We couldn't fix nesting
  307. $errors[] = sprintf($lang_common['BBCode error 5'], array_pop($temp_opened));
  308. return false;
  309. }
  310. array_push($temp_opened, $temp_tag);
  311. array_push($temp_opened_arg, $temp_arg);
  312. if ($temp_tag == $current_tag)
  313. break;
  314. else
  315. $temp .= '[/'.$temp_tag.']';
  316. }
  317. $current = $temp.$current;
  318. $temp = '';
  319. array_pop($temp_opened);
  320. array_pop($temp_opened_arg);
  321. while (!empty($temp_opened))
  322. {
  323. $temp_tag = array_pop($temp_opened);
  324. $temp_arg = array_pop($temp_opened_arg);
  325. if (empty($temp_arg))
  326. $temp .= '['.$temp_tag.']';
  327. else
  328. $temp .= '['.$temp_tag.'='.$temp_arg.']';
  329. array_push($open_tags, $temp_tag);
  330. array_push($open_args, $temp_arg);
  331. }
  332. $current .= $temp;
  333. $opened_tag--;
  334. break;
  335. }
  336. else
  337. {
  338. // We couldn't fix nesting
  339. $errors[] = sprintf($lang_common['BBCode error 1'], $current_tag);
  340. return false;
  341. }
  342. }
  343. else if (in_array($open_tags[$opened_tag], $tags_closed))
  344. break;
  345. else
  346. {
  347. array_pop($open_tags);
  348. array_pop($open_args);
  349. $opened_tag--;
  350. }
  351. }
  352. }
  353. if (in_array($current_tag, array_keys($tags_nested)))
  354. {
  355. if (isset($current_depth[$current_tag]))
  356. $current_depth[$current_tag]--;
  357. }
  358. if (in_array($open_tags[$opened_tag], array_keys($tags_limit_bbcode)))
  359. $limit_bbcode = $tags_limit_bbcode[$open_tags[$opened_tag]];
  360. else
  361. $limit_bbcode = $tags;
  362. $new_text .= $current;
  363. continue;
  364. }
  365. else
  366. {
  367. // We are opening a tag
  368. if (in_array($current_tag, array_keys($tags_limit_bbcode)))
  369. $limit_bbcode = $tags_limit_bbcode[$current_tag];
  370. else
  371. $limit_bbcode = $tags;
  372. if (in_array($current_tag, $tags_block) && !in_array($open_tags[$opened_tag], $tags_block) && $opened_tag != 0)
  373. {
  374. // We tried to open a block tag within a non-block tag
  375. $errors[] = sprintf($lang_common['BBCode error 3'], $current_tag, $open_tags[$opened_tag]);
  376. return false;
  377. }
  378. if (in_array($current_tag, $tags_ignore))
  379. {
  380. // Its an ignore tag so we don't need to worry about whats inside it,
  381. $current_ignore = $current_tag;
  382. $new_text .= $current;
  383. continue;
  384. }
  385. // Deal with nested tags
  386. if (in_array($current_tag, $open_tags) && !in_array($current_tag, array_keys($tags_nested)))
  387. {
  388. // We nested a tag we shouldn't
  389. $errors[] = sprintf($lang_common['BBCode error 4'], $current_tag);
  390. return false;
  391. }
  392. else if (in_array($current_tag, array_keys($tags_nested)))
  393. {
  394. // We are allowed to nest this tag
  395. if (isset($current_depth[$current_tag]))
  396. $current_depth[$current_tag]++;
  397. else
  398. $current_depth[$current_tag] = 1;
  399. // See if we are nested too deep
  400. if ($current_depth[$current_tag] > $tags_nested[$current_tag])
  401. {
  402. $current_nest = $current_tag;
  403. continue;
  404. }
  405. }
  406. // Remove quotes from arguments for certain tags
  407. if (strpos($current, '=') !== false && in_array($current_tag, $tags_quotes))
  408. {
  409. $current = preg_replace('#\['.$current_tag.'=("|\'|)(.*?)\\1\]\s*#i', '['.$current_tag.'=$2]', $current);
  410. }
  411. if (in_array($current_tag, array_keys($tags_limit_bbcode)))
  412. $limit_bbcode = $tags_limit_bbcode[$current_tag];
  413. $open_tags[] = $current_tag;
  414. $open_args[] = $current_arg;
  415. $opened_tag++;
  416. $new_text .= $current;
  417. continue;
  418. }
  419. }
  420. // Check we closed all the tags we needed to
  421. foreach ($tags_closed as $check)
  422. {
  423. if (in_array($check, $open_tags))
  424. {
  425. // We left an important tag open
  426. $errors[] = sprintf($lang_common['BBCode error 5'], $check);
  427. return false;
  428. }
  429. }
  430. if ($current_ignore)
  431. {
  432. // We left an ignore tag open
  433. $errors[] = sprintf($lang_common['BBCode error 5'], $current_ignore);
  434. return false;
  435. }
  436. $return = ($hook = get_hook('ps_preparse_tags_end')) ? eval($hook) : null;
  437. if ($return != null)
  438. return $return;
  439. return $new_text;
  440. }
  441. //
  442. // Preparse the contents of [list] bbcode
  443. //
  444. function preparse_list_tag($content, $type = '*', &$errors)
  445. {
  446. global $lang_common;
  447. if (strlen($type) != 1)
  448. $type = '*';
  449. if (strpos($content,'[list') !== false)
  450. {
  451. $pattern = array('%\[list(?:=([1a*]))?+\]((?:(?>.*?(?=\[list(?:=[1a*])?+\]|\[/list\]))|(?R))*)\[/list\]%ise');
  452. $replace = array('preparse_list_tag(\'$2\', \'$1\', $errors)');
  453. $content = preg_replace($pattern, $replace, $content);
  454. }
  455. $items = explode('[*]', str_replace('\"', '"', $content));
  456. $content = '';
  457. foreach ($items as $item)
  458. {
  459. if (forum_trim($item) != '')
  460. $content .= '[*'."\0".']'.str_replace('[/*]', '', forum_trim($item)).'[/*'."\0".']'."\n";
  461. }
  462. return '[list='.$type.']'."\n".$content.'[/list]';
  463. }
  464. //
  465. // Split text into chunks ($inside contains all text inside $start and $end, and $outside contains all text outside)
  466. //
  467. function split_text($text, $start, $end, &$errors, $retab = true)
  468. {
  469. global $forum_config, $lang_common;
  470. $tokens = explode($start, $text);
  471. $outside[] = $tokens[0];
  472. $num_tokens = count($tokens);
  473. for ($i = 1; $i < $num_tokens; ++$i)
  474. {
  475. $temp = explode($end, $tokens[$i]);
  476. if (count($temp) != 2)
  477. {
  478. $errors[] = $lang_common['BBCode code problem'];
  479. return array(null, array($text));
  480. }
  481. $inside[] = $temp[0];
  482. $outside[] = $temp[1];
  483. }
  484. if ($forum_config['o_indent_num_spaces'] != 8 && $retab)
  485. {
  486. $spaces = str_repeat(' ', $forum_config['o_indent_num_spaces']);
  487. $inside = str_replace("\t", $spaces, $inside);
  488. }
  489. return array($inside, $outside);
  490. }
  491. //
  492. // Truncate URL if longer than 55 characters (add http:// or ftp:// if missing)
  493. //
  494. function handle_url_tag($url, $link = '', $bbcode = false)
  495. {
  496. $return = ($hook = get_hook('ps_handle_url_tag_start')) ? eval($hook) : null;
  497. if ($return != null)
  498. return $return;
  499. $full_url = str_replace(array(' ', '\'', '`', '"'), array('%20', '', '', ''), $url);
  500. if (strpos($url, 'www.') === 0) // If it starts with www, we add http://
  501. $full_url = 'http://'.$full_url;
  502. else if (strpos($url, 'ftp.') === 0) // Else if it starts with ftp, we add ftp://
  503. $full_url = 'ftp://'.$full_url;
  504. else if (!preg_match('#^([a-z0-9]{3,6})://#', $url)) // Else if it doesn't start with abcdef://, we add http://
  505. $full_url = 'http://'.$full_url;
  506. if (defined('FORUM_SUPPORT_PCRE_UNICODE') && defined('FORUM_ENABLE_IDNA'))
  507. {
  508. static $idn;
  509. static $cached_encoded_urls = null;
  510. if (is_null($cached_encoded_urls))
  511. $cached_encoded_urls = array();
  512. // Check in cache
  513. $cache_key = md5($full_url);
  514. if (isset($cached_encoded_urls[$cache_key]))
  515. $full_url = $cached_encoded_urls[$cache_key];
  516. else
  517. {
  518. if (!isset($idn))
  519. {
  520. $idn = new idna_convert();
  521. $idn->set_parameter('encoding', 'utf8');
  522. $idn->set_parameter('strict', false);
  523. }
  524. $full_url = $idn->encode($full_url);
  525. $cached_encoded_urls[$cache_key] = $full_url;
  526. }
  527. }
  528. // Ok, not very pretty :-)
  529. if (!$bbcode)
  530. {
  531. if (defined('FORUM_SUPPORT_PCRE_UNICODE') && defined('FORUM_ENABLE_IDNA'))
  532. {
  533. $link_name = ($link == '' || $link == $url) ? $url : $link;
  534. if (preg_match('!^(https?|ftp|news){1}'.preg_quote('://xn--', '!').'!', $link_name))
  535. {
  536. $link = $idn->decode($link_name);
  537. }
  538. }
  539. $link = ($link == '' || $link == $url) ? ((utf8_strlen($url) > 55) ? utf8_substr($url, 0 , 39).' … '.utf8_substr($url, -10) : $url) : stripslashes($link);
  540. }
  541. $return = ($hook = get_hook('ps_handle_url_tag_end')) ? eval($hook) : null;
  542. if ($return != null)
  543. return $return;
  544. if ($bbcode)
  545. {
  546. if (defined('FORUM_SUPPORT_PCRE_UNICODE') && defined('FORUM_ENABLE_IDNA'))
  547. {
  548. if (preg_match('!^(https?|ftp|news){1}'.preg_quote('://xn--', '!').'!', $link))
  549. {
  550. $link = $idn->decode($link);
  551. }
  552. }
  553. if ($full_url == $link)
  554. return '[url]'.$link.'[/url]';
  555. else
  556. return '[url='.$full_url.']'.$link.'[/url]';
  557. }
  558. else
  559. return '<a href="'.$full_url.'">'.$link.'</a>';
  560. }
  561. //
  562. // Callback for handle_url_tag
  563. //
  564. function callback_handle_url_nobb($reg)
  565. {
  566. return handle_url_tag($reg[1], (isset($reg[2]) ? $reg[2] : ''), false);
  567. }
  568. //
  569. // Callback for handle_url_tag
  570. //
  571. function callback_handle_url_bb($reg)
  572. {
  573. return handle_url_tag($reg[1], (isset($reg[2]) ? $reg[2] : ''), true);
  574. }
  575. //
  576. // Turns an URL from the [img] tag into an <img> tag or a <a href...> tag
  577. //
  578. function handle_img_tag($url, $is_signature = false, $alt = null)
  579. {
  580. global $lang_common, $forum_user;
  581. $return = ($hook = get_hook('ps_handle_img_tag_start')) ? eval($hook) : null;
  582. if ($return != null)
  583. return $return;
  584. if ($alt == null)
  585. $alt = $url;
  586. $img_tag = '<a href="'.$url.'">&lt;'.$lang_common['Image link'].'&gt;</a>';
  587. if ($is_signature && $forum_user['show_img_sig'] != '0')
  588. $img_tag = '<img class="sigimage" src="'.$url.'" alt="'.forum_htmlencode($alt).'" />';
  589. else if (!$is_signature && $forum_user['show_img'] != '0')
  590. $img_tag = '<span class="postimg"><img src="'.$url.'" alt="'.forum_htmlencode($alt).'" /></span>';
  591. $return = ($hook = get_hook('ps_handle_img_tag_end')) ? eval($hook) : null;
  592. if ($return != null)
  593. return $return;
  594. return $img_tag;
  595. }
  596. //
  597. // Parse the contents of [list] bbcode
  598. //
  599. function handle_list_tag($content, $type = '*')
  600. {
  601. if (strlen($type) != 1)
  602. $type = '*';
  603. if (strpos($content,'[list') !== false)
  604. {
  605. $pattern = array('%\[list(?:=([1a*]))?+\]((?:(?>.*?(?=\[list(?:=[1a*])?+\]|\[/list\]))|(?R))*)\[/list\]%ise');
  606. $replace = array('handle_list_tag(\'$2\', \'$1\')');
  607. $content = preg_replace($pattern, $replace, $content);
  608. }
  609. $content = preg_replace('#\s*\[\*\](.*?)\[/\*\]\s*#s', '<li><p>$1</p></li>', forum_trim($content));
  610. if ($type == '*')
  611. $content = '<ul>'.$content.'</ul>';
  612. else
  613. if ($type == 'a')
  614. $content = '<ol class="alpha">'.$content.'</ol>';
  615. else
  616. $content = '<ol class="decimal">'.$content.'</ol>';
  617. return '</p>'.$content.'<p>';
  618. }
  619. //
  620. // Convert BBCodes to their HTML equivalent
  621. //
  622. function do_bbcode($text, $is_signature = false)
  623. {
  624. global $lang_common, $forum_user, $forum_config;
  625. $return = ($hook = get_hook('ps_do_bbcode_start')) ? eval($hook) : null;
  626. if ($return != null)
  627. return $return;
  628. if (strpos($text, '[quote') !== false)
  629. {
  630. $text = preg_replace('#\[quote=(&\#039;|&quot;|"|\'|)(.*?)\\1\]#e', '"</p><div class=\"quotebox\"><cite>".str_replace(array(\'[\', \'\\"\'), array(\'&#91;\', \'"\'), \'$2\')." ".$lang_common[\'wrote\'].":</cite><blockquote><p>"', $text);
  631. $text = preg_replace('#\[quote\]\s*#', '</p><div class="quotebox"><blockquote><p>', $text);
  632. $text = preg_replace('#\s*\[\/quote\]#S', '</p></blockquote></div><p>', $text);
  633. }
  634. if (!$is_signature)
  635. {
  636. $pattern[] = '%\[list(?:=([1a*]))?+\]((?:(?>.*?(?=\[list(?:=[1a*])?+\]|\[/list\]))|(?R))*)\[/list\]%ise';
  637. $replace[] = 'handle_list_tag(\'$2\', \'$1\')';
  638. }
  639. $pattern[] = '#\[b\](.*?)\[/b\]#ms';
  640. $pattern[] = '#\[i\](.*?)\[/i\]#ms';
  641. $pattern[] = '#\[u\](.*?)\[/u\]#ms';
  642. $pattern[] = '#\[colou?r=([a-zA-Z]{3,20}|\#[0-9a-fA-F]{6}|\#[0-9a-fA-F]{3})](.*?)\[/colou?r\]#ms';
  643. $pattern[] = '#\[h\](.*?)\[/h\]#ms';
  644. $replace[] = '<strong>$1</strong>';
  645. $replace[] = '<em>$1</em>';
  646. $replace[] = '<span class="bbu">$1</span>';
  647. $replace[] = '<span style="color: $1">$2</span>';
  648. $replace[] = '</p><h5>$1</h5><p>';
  649. if (($is_signature && $forum_config['p_sig_img_tag'] == '1') || (!$is_signature && $forum_config['p_message_img_tag'] == '1'))
  650. {
  651. $pattern[] = '#\[img\]((ht|f)tps?://)([^\s<"]*?)\[/img\]#e';
  652. $pattern[] = '#\[img=([^\[]*?)\]((ht|f)tps?://)([^\s<"]*?)\[/img\]#e';
  653. if ($is_signature)
  654. {
  655. $replace[] = 'handle_img_tag(\'$1$3\', true)';
  656. $replace[] = 'handle_img_tag(\'$2$4\', true, \'$1\')';
  657. }
  658. else
  659. {
  660. $replace[] = 'handle_img_tag(\'$1$3\', false)';
  661. $replace[] = 'handle_img_tag(\'$2$4\', false, \'$1\')';
  662. }
  663. }
  664. $text = preg_replace_callback('#\[url\]([^\[]*?)\[/url\]#', 'callback_handle_url_nobb', $text);
  665. $text = preg_replace_callback('#\[url=([^\[]+?)\](.*?)\[/url\]#', 'callback_handle_url_nobb', $text);
  666. $pattern[] = '#\[email\]([^\[]*?)\[/email\]#';
  667. $pattern[] = '#\[email=([^\[]+?)\](.*?)\[/email\]#';
  668. $replace[] = '<a href="mailto:$1">$1</a>';
  669. $replace[] = '<a href="mailto:$1">$2</a>';
  670. $return = ($hook = get_hook('ps_do_bbcode_replace')) ? eval($hook) : null;
  671. if ($return != null)
  672. return $return;
  673. // This thing takes a while! :)
  674. $text = preg_replace($pattern, $replace, $text);
  675. $return = ($hook = get_hook('ps_do_bbcode_end')) ? eval($hook) : null;
  676. if ($return != null)
  677. return $return;
  678. return $text;
  679. }
  680. //
  681. // Make hyperlinks clickable
  682. //
  683. function do_clickable($text, $unicode = FALSE)
  684. {
  685. $text = ' '.$text;
  686. if ($unicode)
  687. {
  688. $text = preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(https?|ftp|news){1}://([\p{Nd}\p{L}\-]+\.([\p{Nd}\p{L}\-]+\.)*[\p{Nd}\p{L}\-]+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-]?)?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#ieu', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5://$6\', \'$5://$6\', true).stripslashes(\'$4$10$11$12\')', $text);
  689. $text = preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(www|ftp)\.(([\p{Nd}\p{L}\-]+\.)*[\p{Nd}\p{L}\-]+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-])?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#ieu', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5.$6\', \'$5.$6\', true).stripslashes(\'$4$10$11$12\')', $text);
  690. }
  691. else
  692. {
  693. $text = preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(https?|ftp|news){1}://([\w\-]+\.([\w\-]+\.)*[\w]+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-]?)?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#ie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5://$6\', \'$5://$6\', true).stripslashes(\'$4$10$11$12\')', $text);
  694. $text = preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(www|ftp)\.(([\w\-]+\.)*[\w]+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-])?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#ie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5.$6\', \'$5.$6\', true).stripslashes(\'$4$10$11$12\')', $text);
  695. }
  696. return substr($text, 1);
  697. }
  698. //
  699. // Convert a series of smilies to images
  700. //
  701. function do_smilies($text)
  702. {
  703. global $forum_config, $base_url, $smilies;
  704. $return = ($hook = get_hook('ps_do_smilies_start')) ? eval($hook) : null;
  705. if ($return != null)
  706. return $return;
  707. $text = ' '.$text.' ';
  708. foreach ($smilies as $smiley_text => $smiley_img)
  709. {
  710. if (strpos($text, $smiley_text) !== false)
  711. $text = preg_replace("#(?<=[>\s])".preg_quote($smiley_text, '#')."(?=\W)#m", '<img src="'.$base_url.'/img/smilies/'.$smiley_img.'" width="15" height="15" alt="'.substr($smiley_img, 0, strrpos($smiley_img, '.')).'" />', $text);
  712. }
  713. $return = ($hook = get_hook('ps_do_smilies_end')) ? eval($hook) : null;
  714. return substr($text, 1, -1);
  715. }
  716. //
  717. // Parse message text
  718. //
  719. function parse_message($text, $hide_smilies)
  720. {
  721. global $forum_config, $lang_common, $forum_user;
  722. $return = ($hook = get_hook('ps_parse_message_start')) ? eval($hook) : null;
  723. if ($return != null)
  724. return $return;
  725. if ($forum_config['o_censoring'] == '1')
  726. $text = censor_words($text);
  727. $return = ($hook = get_hook('ps_parse_message_post_censor')) ? eval($hook) : null;
  728. if ($return != null)
  729. return $return;
  730. // Convert applicable characters to HTML entities
  731. $text = forum_htmlencode($text);
  732. $return = ($hook = get_hook('ps_parse_message_pre_split')) ? eval($hook) : null;
  733. if ($return != null)
  734. return $return;
  735. // If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
  736. if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
  737. {
  738. list($inside, $outside) = split_text($text, '[code]', '[/code]', $errors);
  739. $text = implode("\x1", $outside);
  740. }
  741. $return = ($hook = get_hook('ps_parse_message_post_split')) ? eval($hook) : null;
  742. if ($return != null)
  743. return $return;
  744. if ($forum_config['p_message_bbcode'] == '1' && strpos($text, '[') !== false && strpos($text, ']') !== false)
  745. $text = do_bbcode($text);
  746. if ($forum_config['o_smilies'] == '1' && $forum_user['show_smilies'] == '1' && $hide_smilies == '0')
  747. $text = do_smilies($text);
  748. $return = ($hook = get_hook('ps_parse_message_bbcode')) ? eval($hook) : null;
  749. if ($return != null)
  750. return $return;
  751. // Deal with newlines, tabs and multiple spaces
  752. $pattern = array("\n", "\t", ' ', ' ');
  753. $replace = array('<br />', '&nbsp; &nbsp; ', '&nbsp; ', ' &nbsp;');
  754. $text = str_replace($pattern, $replace, $text);
  755. $return = ($hook = get_hook('ps_parse_message_pre_merge')) ? eval($hook) : null;
  756. if ($return != null)
  757. return $return;
  758. // If we split up the message before we have to concatenate it together again (code tags)
  759. if (isset($inside))
  760. {
  761. $outside = explode("\x1", $text);
  762. $text = '';
  763. $num_tokens = count($outside);
  764. for ($i = 0; $i < $num_tokens; ++$i)
  765. {
  766. $text .= $outside[$i];
  767. if (isset($inside[$i]))
  768. $text .= '</p><div class="codebox"><pre><code>'.forum_trim($inside[$i], "\n\r").'</code></pre></div><p>';
  769. }
  770. }
  771. $return = ($hook = get_hook('ps_parse_message_post_merge')) ? eval($hook) : null;
  772. if ($return != null)
  773. return $return;
  774. // Add paragraph tag around post, but make sure there are no empty paragraphs
  775. $text = preg_replace('#<br />\s*?<br />((\s*<br />)*)#i', "</p>$1<p>", $text);
  776. $text = str_replace('<p><br />', '<p>', $text);
  777. $text = str_replace('<p></p>', '', '<p>'.$text.'</p>');
  778. $return = ($hook = get_hook('ps_parse_message_end')) ? eval($hook) : null;
  779. if ($return != null)
  780. return $return;
  781. return $text;
  782. }
  783. //
  784. // Parse signature text
  785. //
  786. function parse_signature($text)
  787. {
  788. global $forum_config, $lang_common, $forum_user;
  789. $return = ($hook = get_hook('ps_parse_signature_start')) ? eval($hook) : null;
  790. if ($return != null)
  791. return $return;
  792. if ($forum_config['o_censoring'] == '1')
  793. $text = censor_words($text);
  794. $return = ($hook = get_hook('ps_parse_signature_post_censor')) ? eval($hook) : null;
  795. if ($return != null)
  796. return $return;
  797. // Convert applicable characters to HTML entities
  798. $text = forum_htmlencode($text);
  799. $return = ($hook = get_hook('ps_parse_signature_pre_bbcode')) ? eval($hook) : null;
  800. if ($return != null)
  801. return $return;
  802. if ($forum_config['p_sig_bbcode'] == '1' && strpos($text, '[') !== false && strpos($text, ']') !== false)
  803. $text = do_bbcode($text, true);
  804. if ($forum_config['o_smilies_sig'] == '1' && $forum_user['show_smilies'] == '1')
  805. $text = do_smilies($text);
  806. $return = ($hook = get_hook('ps_parse_signature_post_bbcode')) ? eval($hook) : null;
  807. if ($return != null)
  808. return $return;
  809. // Deal with newlines, tabs and multiple spaces
  810. $pattern = array("\n", "\t", ' ', ' ');
  811. $replace = array('<br />', '&nbsp; &nbsp; ', '&nbsp; ', ' &nbsp;');
  812. $text = str_replace($pattern, $replace, $text);
  813. $return = ($hook = get_hook('ps_parse_signature_end')) ? eval($hook) : null;
  814. if ($return != null)
  815. return $return;
  816. return $text;
  817. }
  818. define('FORUM_PARSER_LOADED', 1);