PageRenderTime 70ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/Upload/inc/class_parser.php

https://gitlab.com/mybbpl/ppm-1.8
PHP | 1693 lines | 1241 code | 158 blank | 294 comment | 111 complexity | 35ef7e2c8e30e2a797f97e895c851f02 MD5 | raw file
  1. <?php
  2. /**
  3. * MyBB 1.8
  4. * Copyright 2014 MyBB Group, All Rights Reserved
  5. *
  6. * Website: http://www.mybb.com
  7. * License: http://www.mybb.com/about/license
  8. *
  9. */
  10. /*
  11. options = array(
  12. allow_html
  13. allow_smilies
  14. allow_mycode
  15. nl2br
  16. filter_badwords
  17. me_username
  18. shorten_urls
  19. highlight
  20. filter_cdata
  21. )
  22. */
  23. class postParser
  24. {
  25. /**
  26. * Internal cache of MyCode.
  27. *
  28. * @access public
  29. * @var mixed
  30. */
  31. public $mycode_cache = 0;
  32. /**
  33. * Internal cache of smilies
  34. *
  35. * @access public
  36. * @var mixed
  37. */
  38. public $smilies_cache = 0;
  39. /**
  40. * Internal cache of badwords filters
  41. *
  42. * @access public
  43. * @var mixed
  44. */
  45. public $badwords_cache = 0;
  46. /**
  47. * Base URL for smilies
  48. *
  49. * @access public
  50. * @var string
  51. */
  52. public $base_url;
  53. /**
  54. * Parsed Highlights cache
  55. *
  56. * @access public
  57. * @var array
  58. */
  59. public $highlight_cache = array();
  60. /**
  61. * Options for this parsed message
  62. *
  63. * @access public
  64. * @var array
  65. */
  66. public $options;
  67. /**
  68. * Internal cache for nested lists
  69. *
  70. * @access public
  71. * @var array
  72. */
  73. public $list_elements;
  74. /**
  75. * Internal counter for nested lists
  76. *
  77. * @access public
  78. * @var int
  79. */
  80. public $list_count;
  81. /**
  82. * Parses a message with the specified options.
  83. *
  84. * @param string $message The message to be parsed.
  85. * @param array $options Array of yes/no options - allow_html,filter_badwords,allow_mycode,allow_smilies,nl2br,me_username,filter_cdata.
  86. * @return string The parsed message.
  87. */
  88. function parse_message($message, $options=array())
  89. {
  90. global $plugins, $mybb;
  91. // Set base URL for parsing smilies
  92. $this->base_url = $mybb->settings['bburl'];
  93. if($this->base_url != "")
  94. {
  95. if(my_substr($this->base_url, my_strlen($this->base_url) -1) != "/")
  96. {
  97. $this->base_url = $this->base_url."/";
  98. }
  99. }
  100. // Set the options
  101. $this->options = $options;
  102. $message = $plugins->run_hooks("parse_message_start", $message);
  103. // Get rid of carriage returns for they are the workings of the devil
  104. $message = str_replace("\r", "", $message);
  105. // Filter bad words if requested.
  106. if(!empty($this->options['filter_badwords']))
  107. {
  108. $message = $this->parse_badwords($message);
  109. }
  110. // Filter CDATA tags if requested (syndication.php).
  111. if(!empty($this->options['filter_cdata']))
  112. {
  113. $message = $this->parse_cdata($message);
  114. }
  115. // If MyCode needs to be replaced, first filter out [code] and [php] tags.
  116. if(!empty($this->options['allow_mycode']) && $mybb->settings['allowcodemycode'] == 1)
  117. {
  118. // This code is reserved and could break codes
  119. $message = str_replace("<mybb-code>\n", "<mybb_code>\n", $message);
  120. preg_match_all("#\[(code|php)\](.*?)\[/\\1\](\r\n?|\n?)#si", $message, $code_matches, PREG_SET_ORDER);
  121. $message = preg_replace("#\[(code|php)\](.*?)\[/\\1\](\r\n?|\n?)#si", "<mybb-code>\n", $message);
  122. }
  123. if(empty($this->options['allow_html']))
  124. {
  125. $message = $this->parse_html($message);
  126. $message = str_replace("&lt;mybb-code&gt;\n", "<mybb-code>\n", $message);
  127. }
  128. else
  129. {
  130. // Replace base, meta,script and style tags in our post - these are > dangerous <
  131. $message = preg_replace('#<(/?)(base|meta|script|style)([^>]*)>#i', '&lt;$1$2$3&gt;', $message);
  132. $message = $this->fix_javascript($message);
  133. $find = array("<br />\n", "<br>\n");
  134. $replace = array("\n", "\n");
  135. $message = str_replace($find, $replace, $message);
  136. }
  137. // Replace "me" code and slaps if we have a username
  138. if(!empty($this->options['me_username']) && $mybb->settings['allowmemycode'] == 1)
  139. {
  140. global $lang;
  141. $message = preg_replace('#(>|^|\r|\n)/me ([^\r\n<]*)#i', "\\1<span style=\"color: red;\">* {$this->options['me_username']} \\2</span>", $message);
  142. $message = preg_replace('#(>|^|\r|\n)/slap ([^\r\n<]*)#i', "\\1<span style=\"color: red;\">* {$this->options['me_username']} {$lang->slaps} \\2 {$lang->with_trout}</span>", $message);
  143. }
  144. // If we can, parse smilies
  145. if(!empty($this->options['allow_smilies']))
  146. {
  147. $message = $this->parse_smilies($message, $this->options['allow_html']);
  148. }
  149. // Replace MyCode if requested.
  150. if(!empty($this->options['allow_mycode']))
  151. {
  152. $message = $this->parse_mycode($message);
  153. }
  154. // Parse Highlights
  155. if(!empty($this->options['highlight']))
  156. {
  157. $message = $this->highlight_message($message, $this->options['highlight']);
  158. }
  159. // Run plugin hooks
  160. $message = $plugins->run_hooks("parse_message", $message);
  161. if(!empty($this->options['allow_mycode']))
  162. {
  163. // Now that we're done, if we split up any code tags, parse them and glue it all back together
  164. if(count($code_matches) > 0)
  165. {
  166. foreach($code_matches as $text)
  167. {
  168. if(my_strtolower($text[1]) == "code")
  169. {
  170. // Fix up HTML inside the code tags so it is clean
  171. $text[2] = $this->parse_html($text[2]);
  172. $code = $this->mycode_parse_code($text[2]);
  173. }
  174. elseif(my_strtolower($text[1]) == "php")
  175. {
  176. $code = $this->mycode_parse_php($text[2]);
  177. }
  178. $message = preg_replace("#\<mybb-code>\n?#", $code, $message, 1);
  179. }
  180. }
  181. }
  182. if(!isset($this->options['nl2br']) || $this->options['nl2br'] != 0)
  183. {
  184. $message = nl2br($message);
  185. // Fix up new lines and block level elements
  186. $message = preg_replace("#(</?(?:html|head|body|div|p|form|table|thead|tbody|tfoot|tr|td|th|ul|ol|li|div|p|blockquote|cite|hr)[^>]*>)\s*<br />#i", "$1", $message);
  187. $message = preg_replace("#(&nbsp;)+(</?(?:html|head|body|div|p|form|table|thead|tbody|tfoot|tr|td|th|ul|ol|li|div|p|blockquote|cite|hr)[^>]*>)#i", "$2", $message);
  188. }
  189. $message = $plugins->run_hooks("parse_message_end", $message);
  190. return $message;
  191. }
  192. /**
  193. * Converts HTML in a message to their specific entities whilst allowing unicode characters.
  194. *
  195. * @param string $message The message to be parsed.
  196. * @return string The formatted message.
  197. */
  198. function parse_html($message)
  199. {
  200. $message = preg_replace("#&(?!\#[0-9]+;)#si", "&amp;", $message); // fix & but allow unicode
  201. $message = str_replace("<","&lt;",$message);
  202. $message = str_replace(">","&gt;",$message);
  203. return $message;
  204. }
  205. /**
  206. * Generates a cache of MyCode, both standard and custom.
  207. *
  208. * @access private
  209. */
  210. function cache_mycode()
  211. {
  212. global $cache, $lang, $mybb;
  213. $this->mycode_cache = array();
  214. $standard_mycode = $callback_mycode = $nestable_mycode = array();
  215. $standard_count = $callback_count = $nestable_count = 0;
  216. if($mybb->settings['allowbasicmycode'] == 1)
  217. {
  218. $standard_mycode['b']['regex'] = "#\[b\](.*?)\[/b\]#si";
  219. $standard_mycode['b']['replacement'] = "<span style=\"font-weight: bold;\">$1</span>";
  220. $standard_mycode['u']['regex'] = "#\[u\](.*?)\[/u\]#si";
  221. $standard_mycode['u']['replacement'] = "<span style=\"text-decoration: underline;\">$1</span>";
  222. $standard_mycode['i']['regex'] = "#\[i\](.*?)\[/i\]#si";
  223. $standard_mycode['i']['replacement'] = "<span style=\"font-style: italic;\">$1</span>";
  224. $standard_mycode['s']['regex'] = "#\[s\](.*?)\[/s\]#si";
  225. $standard_mycode['s']['replacement'] = "<del>$1</del>";
  226. $standard_mycode['hr']['regex'] = "#\[hr\]#si";
  227. $standard_mycode['hr']['replacement'] = "<hr />";
  228. ++$standard_count;
  229. }
  230. if($mybb->settings['allowsymbolmycode'] == 1)
  231. {
  232. $standard_mycode['copy']['regex'] = "#\(c\)#i";
  233. $standard_mycode['copy']['replacement'] = "&copy;";
  234. $standard_mycode['tm']['regex'] = "#\(tm\)#i";
  235. $standard_mycode['tm']['replacement'] = "&#153;";
  236. $standard_mycode['reg']['regex'] = "#\(r\)#i";
  237. $standard_mycode['reg']['replacement'] = "&reg;";
  238. ++$standard_count;
  239. }
  240. if($mybb->settings['allowlinkmycode'] == 1)
  241. {
  242. $callback_mycode['url_simple']['regex'] = "#\[url\]([a-z]+?://)([^\r\n\"<]+?)\[/url\]#si";
  243. $callback_mycode['url_simple']['replacement'] = array($this, 'mycode_parse_url_callback1');
  244. $callback_mycode['url_simple2']['regex'] = "#\[url\]([^\r\n\"<]+?)\[/url\]#i";
  245. $callback_mycode['url_simple2']['replacement'] = array($this, 'mycode_parse_url_callback2');
  246. $callback_mycode['url_complex']['regex'] = "#\[url=([a-z]+?://)([^\r\n\"<]+?)\](.+?)\[/url\]#si";
  247. $callback_mycode['url_complex']['replacement'] = array($this, 'mycode_parse_url_callback1');
  248. $callback_mycode['url_complex2']['regex'] = "#\[url=([^\r\n\"<]+?)\](.+?)\[/url\]#si";
  249. $callback_mycode['url_complex2']['replacement'] = array($this, 'mycode_parse_url_callback2');
  250. ++$callback_count;
  251. }
  252. if($mybb->settings['allowemailmycode'] == 1)
  253. {
  254. $callback_mycode['email_simple']['regex'] = "#\[email\](.*?)\[/email\]#i";
  255. $callback_mycode['email_simple']['replacement'] = array($this, 'mycode_parse_email_callback');
  256. $callback_mycode['email_complex']['regex'] = "#\[email=(.*?)\](.*?)\[/email\]#i";
  257. $callback_mycode['email_complex']['replacement'] = array($this, 'mycode_parse_email_callback');
  258. ++$callback_count;
  259. }
  260. if($mybb->settings['allowcolormycode'] == 1)
  261. {
  262. $nestable_mycode['color']['regex'] = "#\[color=([a-zA-Z]*|\#?[\da-fA-F]{3}|\#?[\da-fA-F]{6})](.*?)\[/color\]#si";
  263. $nestable_mycode['color']['replacement'] = "<span style=\"color: $1;\">$2</span>";
  264. ++$nestable_count;
  265. }
  266. if($mybb->settings['allowsizemycode'] == 1)
  267. {
  268. $nestable_mycode['size']['regex'] = "#\[size=(xx-small|x-small|small|medium|large|x-large|xx-large)\](.*?)\[/size\]#si";
  269. $nestable_mycode['size']['replacement'] = "<span style=\"font-size: $1;\">$2</span>";
  270. $callback_mycode['size_int']['regex'] = "#\[size=([0-9\+\-]+?)\](.*?)\[/size\]#si";
  271. $callback_mycode['size_int']['replacement'] = array($this, 'mycode_handle_size_callback');
  272. ++$nestable_count;
  273. ++$callback_count;
  274. }
  275. if($mybb->settings['allowfontmycode'] == 1)
  276. {
  277. $nestable_mycode['font']['regex'] = "#\[font=([a-z0-9 ,\-_'\"]+)\](.*?)\[/font\]#si";
  278. $nestable_mycode['font']['replacement'] = "<span style=\"font-family: $1;\">$2</span>";
  279. ++$nestable_count;
  280. }
  281. if($mybb->settings['allowalignmycode'] == 1)
  282. {
  283. $nestable_mycode['align']['regex'] = "#\[align=(left|center|right|justify)\](.*?)\[/align\]#si";
  284. $nestable_mycode['align']['replacement'] = "<div style=\"text-align: $1;\">$2</div>";
  285. ++$nestable_count;
  286. }
  287. $custom_mycode = $cache->read("mycode");
  288. // If there is custom MyCode, load it.
  289. if(is_array($custom_mycode))
  290. {
  291. foreach($custom_mycode as $key => $mycode)
  292. {
  293. $mycode['regex'] = str_replace("\x0", "", $mycode['regex']);
  294. $custom_mycode[$key]['regex'] = "#".$mycode['regex']."#si";
  295. ++$standard_count;
  296. }
  297. $mycode = array_merge($standard_mycode, $custom_mycode);
  298. }
  299. else
  300. {
  301. $mycode = $standard_mycode;
  302. }
  303. // Assign the MyCode to the cache.
  304. foreach($mycode as $code)
  305. {
  306. $this->mycode_cache['standard']['find'][] = $code['regex'];
  307. $this->mycode_cache['standard']['replacement'][] = $code['replacement'];
  308. }
  309. // Assign the nestable MyCode to the cache.
  310. foreach($nestable_mycode as $code)
  311. {
  312. $this->mycode_cache['nestable'][] = array('find' => $code['regex'], 'replacement' => $code['replacement']);
  313. }
  314. // Assign the nestable MyCode to the cache.
  315. foreach($callback_mycode as $code)
  316. {
  317. $this->mycode_cache['callback'][] = array('find' => $code['regex'], 'replacement' => $code['replacement']);
  318. }
  319. $this->mycode_cache['standard_count'] = $standard_count;
  320. $this->mycode_cache['callback_count'] = $callback_count;
  321. $this->mycode_cache['nestable_count'] = $nestable_count;
  322. }
  323. /**
  324. * Parses MyCode tags in a specific message with the specified options.
  325. *
  326. * @param string $message The message to be parsed.
  327. * @param array $options Array of options in yes/no format. Options are allow_imgcode.
  328. * @return string The parsed message.
  329. */
  330. function parse_mycode($message, $options=array())
  331. {
  332. global $lang, $mybb;
  333. if(empty($this->options))
  334. {
  335. $this->options = $options;
  336. }
  337. // Cache the MyCode globally if needed.
  338. if($this->mycode_cache == 0)
  339. {
  340. $this->cache_mycode();
  341. }
  342. // Parse quotes first
  343. $message = $this->mycode_parse_quotes($message);
  344. $message = $this->mycode_auto_url($message);
  345. $message = str_replace('$', '&#36;', $message);
  346. // Replace the rest
  347. if($this->mycode_cache['standard_count'] > 0)
  348. {
  349. $message = preg_replace($this->mycode_cache['standard']['find'], $this->mycode_cache['standard']['replacement'], $message);
  350. }
  351. if($this->mycode_cache['callback_count'] > 0)
  352. {
  353. foreach($this->mycode_cache['callback'] as $replace)
  354. {
  355. $message = preg_replace_callback($replace['find'], $replace['replacement'], $message);
  356. }
  357. }
  358. // Replace the nestable mycode's
  359. if($this->mycode_cache['nestable_count'] > 0)
  360. {
  361. foreach($this->mycode_cache['nestable'] as $mycode)
  362. {
  363. while(preg_match($mycode['find'], $message))
  364. {
  365. $message = preg_replace($mycode['find'], $mycode['replacement'], $message);
  366. }
  367. }
  368. }
  369. // Reset list cache
  370. if($mybb->settings['allowlistmycode'] == 1)
  371. {
  372. $this->list_elements = array();
  373. $this->list_count = 0;
  374. // Find all lists
  375. $message = preg_replace_callback("#(\[list(=(a|A|i|I|1))?\]|\[/list\])#si", array($this, 'mycode_prepare_list'), $message);
  376. // Replace all lists
  377. for($i = $this->list_count; $i > 0; $i--)
  378. {
  379. // Ignores missing end tags
  380. $message = preg_replace_callback("#\s?\[list(=(a|A|i|I|1))?&{$i}\](.*?)(\[/list&{$i}\]|$)(\r\n?|\n?)#si", array($this, 'mycode_parse_list_callback'), $message, 1);
  381. }
  382. }
  383. // Convert images when allowed.
  384. if(!empty($this->options['allow_imgcode']))
  385. {
  386. $message = preg_replace_callback("#\[img\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_callback1'), $message);
  387. $message = preg_replace_callback("#\[img=([0-9]{1,3})x([0-9]{1,3})\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_callback2'), $message);
  388. $message = preg_replace_callback("#\[img align=([a-z]+)\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_callback3'), $message);
  389. $message = preg_replace_callback("#\[img=([0-9]{1,3})x([0-9]{1,3}) align=([a-z]+)\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_callback4'), $message);
  390. }
  391. else
  392. {
  393. $message = preg_replace_callback("#\[img\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_disabled_callback1'), $message);
  394. $message = preg_replace_callback("#\[img=([0-9]{1,3})x([0-9]{1,3})\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_disabled_callback2'), $message);
  395. $message = preg_replace_callback("#\[img align=([a-z]+)\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_disabled_callback3'), $message);
  396. $message = preg_replace_callback("#\[img=([0-9]{1,3})x([0-9]{1,3}) align=([a-z]+)\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is", array($this, 'mycode_parse_img_disabled_callback4'), $message);
  397. }
  398. // Convert videos when allow.
  399. if(!empty($this->options['allow_videocode']))
  400. {
  401. $message = preg_replace_callback("#\[video=(.*?)\](.*?)\[/video\]#i", array($this, 'mycode_parse_video_callback'), $message);
  402. }
  403. else
  404. {
  405. $message = preg_replace_callback("#\[video=(.*?)\](.*?)\[/video\]#i", array($this, 'mycode_parse_video_disabled_callback'), $message);
  406. }
  407. return $message;
  408. }
  409. /**
  410. * Generates a cache of smilies
  411. *
  412. * @access private
  413. */
  414. function cache_smilies()
  415. {
  416. global $cache, $mybb, $theme, $templates;
  417. $this->smilies_cache = array();
  418. $smilies = $cache->read("smilies");
  419. if(is_array($smilies))
  420. {
  421. $extra_class = $onclick = '';
  422. foreach($smilies as $sid => $smilie)
  423. {
  424. $smilie['find'] = explode("\n", $smilie['find']);
  425. $smilie['image'] = str_replace("{theme}", $theme['imgdir'], $smilie['image']);
  426. $smilie['image'] = htmlspecialchars_uni($mybb->get_asset_url($smilie['image']));
  427. $smilie['name'] = htmlspecialchars_uni($smilie['name']);
  428. foreach($smilie['find'] as $s)
  429. {
  430. $s = $this->parse_html($s);
  431. eval("\$smilie_template = \"".$templates->get("smilie", 1, 0)."\";");
  432. $this->smilies_cache[$s] = $smilie_template;
  433. // workaround for smilies starting with ;
  434. if($s[0] == ";")
  435. {
  436. $this->smilies_cache += array(
  437. "&amp$s" => "&amp$s",
  438. "&lt$s" => "&lt$s",
  439. "&gt$s" => "&gt$s",
  440. );
  441. }
  442. }
  443. }
  444. }
  445. }
  446. /**
  447. * Parses smilie code in the specified message.
  448. *
  449. * @param string $message $message The message being parsed.
  450. * @param int $allow_html not used
  451. * @return string The parsed message.
  452. */
  453. function parse_smilies($message, $allow_html=0)
  454. {
  455. if($this->smilies_cache == 0)
  456. {
  457. $this->cache_smilies();
  458. }
  459. // No smilies?
  460. if(!count($this->smilies_cache))
  461. {
  462. return $message;
  463. }
  464. // First we take out any of the tags we don't want parsed between (url= etc)
  465. preg_match_all("#\[(url(=[^\]]*)?\]|quote=([^\]]*)?\])|(http|ftp)(s|)://[^\s]*#i", $message, $bad_matches, PREG_PATTERN_ORDER);
  466. if(count($bad_matches[0]) > 0)
  467. {
  468. $message = preg_replace("#\[(url(=[^\]]*)?\]|quote=([^\]]*)?\])|(http|ftp)(s|)://[^\s]*#si", "<mybb-bad-sm>", $message);
  469. }
  470. $message = strtr($message, $this->smilies_cache);
  471. // If we matched any tags previously, swap them back in
  472. if(count($bad_matches[0]) > 0)
  473. {
  474. $message = explode("<mybb-bad-sm>", $message);
  475. $i = 0;
  476. foreach($bad_matches[0] as $match)
  477. {
  478. $message[$i] .= $match;
  479. $i++;
  480. }
  481. $message = implode("", $message);
  482. }
  483. return $message;
  484. }
  485. /**
  486. * Generates a cache of badwords filters.
  487. *
  488. * @access private
  489. */
  490. function cache_badwords()
  491. {
  492. global $cache;
  493. $this->badwords_cache = array();
  494. $this->badwords_cache = $cache->read("badwords");
  495. }
  496. /**
  497. * Parses a list of filtered/badwords in the specified message.
  498. *
  499. * @param string $message The message to be parsed.
  500. * @param array $options Array of parser options in yes/no format.
  501. * @return string The parsed message.
  502. */
  503. function parse_badwords($message, $options=array())
  504. {
  505. if(empty($this->options))
  506. {
  507. $this->options = $options;
  508. }
  509. if($this->badwords_cache == 0)
  510. {
  511. $this->cache_badwords();
  512. }
  513. if(is_array($this->badwords_cache))
  514. {
  515. reset($this->badwords_cache);
  516. foreach($this->badwords_cache as $bid => $badword)
  517. {
  518. if(!$badword['replacement'])
  519. {
  520. $badword['replacement'] = "*****";
  521. }
  522. // Take into account the position offset for our last replacement.
  523. $index = substr_count($badword['badword'], '*')+2;
  524. $badword['badword'] = str_replace('\*', '([a-zA-Z0-9_]{1})', preg_quote($badword['badword'], "#"));
  525. // Ensure we run the replacement enough times but not recursively (i.e. not while(preg_match..))
  526. $count = preg_match_all("#(^|\W)".$badword['badword']."(\W|$)#i", $message, $matches);
  527. for($i=0; $i < $count; ++$i)
  528. {
  529. $message = preg_replace("#(^|\W)".$badword['badword']."(\W|$)#i", "\\1".$badword['replacement'].'\\'.$index, $message);
  530. }
  531. }
  532. }
  533. if(!empty($this->options['strip_tags']))
  534. {
  535. $message = strip_tags($message);
  536. }
  537. return $message;
  538. }
  539. /**
  540. * Resolves nested CDATA tags in the specified message.
  541. *
  542. * @param string $message The message to be parsed.
  543. * @return string The parsed message.
  544. */
  545. function parse_cdata($message)
  546. {
  547. $message = str_replace(']]>', ']]]]><![CDATA[>', $message);
  548. return $message;
  549. }
  550. /**
  551. * Attempts to move any javascript references in the specified message.
  552. *
  553. * @param string The message to be parsed.
  554. * @return string The parsed message.
  555. */
  556. function fix_javascript($message)
  557. {
  558. $js_array = array(
  559. "#(&\#(0*)106;?|&\#(0*)74;?|&\#x(0*)4a;?|&\#x(0*)6a;?|j)((&\#(0*)97;?|&\#(0*)65;?|a)(&\#(0*)118;?|&\#(0*)86;?|v)(&\#(0*)97;?|&\#(0*)65;?|a)(\s)?(&\#(0*)115;?|&\#(0*)83;?|s)(&\#(0*)99;?|&\#(0*)67;?|c)(&\#(0*)114;?|&\#(0*)82;?|r)(&\#(0*)105;?|&\#(0*)73;?|i)(&\#112;?|&\#(0*)80;?|p)(&\#(0*)116;?|&\#(0*)84;?|t)(&\#(0*)58;?|\:))#i",
  560. "#([\s\"']on)([a-z]+\s*=)#i",
  561. );
  562. // Add invisible white space
  563. $message = preg_replace($js_array, "$1\xE2\x80\x8C$2$6", $message);
  564. return $message;
  565. }
  566. /**
  567. * Handles fontsize.
  568. *
  569. * @param int $size The original size.
  570. * @param string $text The text within a size tag.
  571. * @return string The parsed text.
  572. */
  573. function mycode_handle_size($size, $text)
  574. {
  575. $size = (int)$size+10;
  576. if($size > 50)
  577. {
  578. $size = 50;
  579. }
  580. $text = "<span style=\"font-size: {$size}pt;\">".str_replace("\'", "'", $text)."</span>";
  581. return $text;
  582. }
  583. /**
  584. * Handles fontsize.
  585. *
  586. * @param array $matches Matches.
  587. * @return string The parsed text.
  588. */
  589. function mycode_handle_size_callback($matches)
  590. {
  591. return $this->mycode_handle_size($matches[1], $matches[2]);
  592. }
  593. /**
  594. * Parses quote MyCode.
  595. *
  596. * @param string $message The message to be parsed
  597. * @param boolean $text_only Are we formatting as text?
  598. * @return string The parsed message.
  599. */
  600. function mycode_parse_quotes($message, $text_only=false)
  601. {
  602. global $lang, $templates, $theme, $mybb;
  603. // Assign pattern and replace values.
  604. $pattern = "#\[quote\](.*?)\[\/quote\](\r\n?|\n?)#si";
  605. $pattern_callback = "#\[quote=([\"']|&quot;|)(.*?)(?:\\1)(.*?)(?:[\"']|&quot;)?\](.*?)\[/quote\](\r\n?|\n?)#si";
  606. if($text_only == false)
  607. {
  608. $replace = "<blockquote><cite>$lang->quote</cite>$1</blockquote>\n";
  609. $replace_callback = array($this, 'mycode_parse_post_quotes_callback1');
  610. }
  611. else
  612. {
  613. $replace = "\n{$lang->quote}\n--\n$1\n--\n";
  614. $replace_callback = array($this, 'mycode_parse_post_quotes_callback2');
  615. }
  616. do
  617. {
  618. // preg_replace has erased the message? Restore it...
  619. $previous_message = $message;
  620. $message = preg_replace($pattern, $replace, $message, -1, $count);
  621. $message = preg_replace_callback($pattern_callback, $replace_callback, $message, -1, $count_callback);
  622. if(!$message)
  623. {
  624. $message = $previous_message;
  625. break;
  626. }
  627. } while($count || $count_callback);
  628. if($text_only == false)
  629. {
  630. $find = array(
  631. "#(\r\n*|\n*)<\/cite>(\r\n*|\n*)#",
  632. "#(\r\n*|\n*)<\/blockquote>#"
  633. );
  634. $replace = array(
  635. "</cite><br />",
  636. "</blockquote>"
  637. );
  638. $message = preg_replace($find, $replace, $message);
  639. }
  640. return $message;
  641. }
  642. /**
  643. * Parses quotes with post id and/or dateline.
  644. *
  645. * @param string $message The message to be parsed
  646. * @param string $username The username to be parsed
  647. * @param boolean $text_only Are we formatting as text?
  648. * @return string The parsed message.
  649. */
  650. function mycode_parse_post_quotes($message, $username, $text_only=false)
  651. {
  652. global $lang, $templates, $theme, $mybb;
  653. $linkback = $date = "";
  654. $message = trim($message);
  655. $message = preg_replace("#(^<br(\s?)(\/?)>|<br(\s?)(\/?)>$)#i", "", $message);
  656. if(!$message)
  657. {
  658. return '';
  659. }
  660. $username .= "'";
  661. $delete_quote = true;
  662. preg_match("#pid=(?:&quot;|\"|')?([0-9]+)[\"']?(?:&quot;|\"|')?#i", $username, $match);
  663. if((int)$match[1])
  664. {
  665. $pid = (int)$match[1];
  666. $url = $mybb->settings['bburl']."/".get_post_link($pid)."#pid$pid";
  667. if(defined("IN_ARCHIVE"))
  668. {
  669. $linkback = " <a href=\"{$url}\">[ -> ]</a>";
  670. }
  671. else
  672. {
  673. eval("\$linkback = \" ".$templates->get("postbit_gotopost", 1, 0)."\";");
  674. }
  675. $username = preg_replace("#(?:&quot;|\"|')? pid=(?:&quot;|\"|')?[0-9]+[\"']?(?:&quot;|\"|')?#i", '', $username);
  676. $delete_quote = false;
  677. }
  678. unset($match);
  679. preg_match("#dateline=(?:&quot;|\"|')?([0-9]+)(?:&quot;|\"|')?#i", $username, $match);
  680. if((int)$match[1])
  681. {
  682. if($match[1] < TIME_NOW)
  683. {
  684. $postdate = my_date('relative', (int)$match[1]);
  685. $date = " ({$postdate})";
  686. }
  687. $username = preg_replace("#(?:&quot;|\"|')? dateline=(?:&quot;|\"|')?[0-9]+(?:&quot;|\"|')?#i", '', $username);
  688. $delete_quote = false;
  689. }
  690. if($delete_quote)
  691. {
  692. $username = my_substr($username, 0, my_strlen($username)-1);
  693. }
  694. if(!empty($this->options['allow_html']))
  695. {
  696. $username = htmlspecialchars_uni($username);
  697. }
  698. if($text_only)
  699. {
  700. return "\n{$username} {$lang->wrote}{$date}\n--\n{$message}\n--\n";
  701. }
  702. else
  703. {
  704. $span = "";
  705. if(!$delete_quote)
  706. {
  707. $span = "<span>{$date}</span>";
  708. }
  709. return "<blockquote><cite>{$span}{$username} {$lang->wrote}{$linkback}</cite>{$message}</blockquote>\n";
  710. }
  711. }
  712. /**
  713. * Parses quotes with post id and/or dateline.
  714. *
  715. * @param array $matches Matches.
  716. * @return string The parsed message.
  717. */
  718. function mycode_parse_post_quotes_callback1($matches)
  719. {
  720. return $this->mycode_parse_post_quotes($matches[4],$matches[2].$matches[3]);
  721. }
  722. /**
  723. * Parses quotes with post id and/or dateline.
  724. *
  725. * @param array $matches Matches.
  726. * @return string The parsed message.
  727. */
  728. function mycode_parse_post_quotes_callback2($matches)
  729. {
  730. return $this->mycode_parse_post_quotes($matches[4],$matches[2].$matches[3], true);
  731. }
  732. /**
  733. * Parses code MyCode.
  734. *
  735. * @param string $code The message to be parsed
  736. * @param boolean $text_only Are we formatting as text?
  737. * @return string The parsed message.
  738. */
  739. function mycode_parse_code($code, $text_only=false)
  740. {
  741. global $lang;
  742. if($text_only == true)
  743. {
  744. return "\n{$lang->code}\n--\n{$code}\n--\n";
  745. }
  746. // Clean the string before parsing.
  747. $code = preg_replace('#^(\t*)(\n|\r|\0|\x0B| )*#', '\\1', $code);
  748. $code = rtrim($code);
  749. $original = preg_replace('#^\t*#', '', $code);
  750. if(empty($original))
  751. {
  752. return;
  753. }
  754. $code = str_replace('$', '&#36;', $code);
  755. $code = preg_replace('#\$([0-9])#', '\\\$\\1', $code);
  756. $code = str_replace('\\', '&#92;', $code);
  757. $code = str_replace("\t", '&nbsp;&nbsp;&nbsp;&nbsp;', $code);
  758. $code = str_replace(" ", '&nbsp;&nbsp;', $code);
  759. return "<div class=\"codeblock\">\n<div class=\"title\">".$lang->code."\n</div><div class=\"body\" dir=\"ltr\"><code>".$code."</code></div></div>\n";
  760. }
  761. /**
  762. * Parses code MyCode.
  763. *
  764. * @param array $matches Matches.
  765. * @return string The parsed message.
  766. */
  767. function mycode_parse_code_callback($matches)
  768. {
  769. return $this->mycode_parse_code($matches[1], true);
  770. }
  771. /**
  772. * Parses PHP code MyCode.
  773. *
  774. * @param string $str The message to be parsed
  775. * @param boolean $bare_return Whether or not it should return it as pre-wrapped in a div or not.
  776. * @param boolean $text_only Are we formatting as text?
  777. * @return string The parsed message.
  778. */
  779. function mycode_parse_php($str, $bare_return = false, $text_only = false)
  780. {
  781. global $lang;
  782. if($text_only == true)
  783. {
  784. return "\n{$lang->php_code}\n--\n$str\n--\n";
  785. }
  786. // Clean the string before parsing except tab spaces.
  787. $str = preg_replace('#^(\t*)(\n|\r|\0|\x0B| )*#', '\\1', $str);
  788. $str = rtrim($str);
  789. $original = preg_replace('#^\t*#', '', $str);
  790. if(empty($original))
  791. {
  792. return;
  793. }
  794. // See if open and close tags are provided.
  795. $added_open_tag = false;
  796. if(!preg_match("#^\s*<\?#si", $str))
  797. {
  798. $added_open_tag = true;
  799. $str = "<?php \n".$str;
  800. }
  801. $added_end_tag = false;
  802. if(!preg_match("#\?>\s*$#si", $str))
  803. {
  804. $added_end_tag = true;
  805. $str = $str." \n?>";
  806. }
  807. $code = @highlight_string($str, true);
  808. // Do the actual replacing.
  809. $code = preg_replace('#<code>\s*<span style="color: \#000000">\s*#i', "<code>", $code);
  810. $code = preg_replace("#</span>\s*</code>#", "</code>", $code);
  811. $code = preg_replace("#</span>(\r\n?|\n?)</code>#", "</span></code>", $code);
  812. $code = str_replace("\\", '&#092;', $code);
  813. $code = str_replace('$', '&#36;', $code);
  814. $code = preg_replace("#&amp;\#([0-9]+);#si", "&#$1;", $code);
  815. if($added_open_tag)
  816. {
  817. $code = preg_replace("#<code><span style=\"color: \#([A-Z0-9]{6})\">&lt;\?php( |&nbsp;)(<br />?)#", "<code><span style=\"color: #$1\">", $code);
  818. }
  819. if($added_end_tag)
  820. {
  821. $code = str_replace("?&gt;</span></code>", "</span></code>", $code);
  822. // Wait a minute. It fails highlighting? Stupid highlighter.
  823. $code = str_replace("?&gt;</code>", "</code>", $code);
  824. }
  825. $code = preg_replace("#<span style=\"color: \#([A-Z0-9]{6})\"></span>#", "", $code);
  826. $code = str_replace("<code>", "<div dir=\"ltr\"><code>", $code);
  827. $code = str_replace("</code>", "</code></div>", $code);
  828. $code = preg_replace("# *$#", "", $code);
  829. if($bare_return)
  830. {
  831. return $code;
  832. }
  833. // Send back the code all nice and pretty
  834. return "<div class=\"codeblock phpcodeblock\"><div class=\"title\">$lang->php_code\n</div><div class=\"body\">".$code."</div></div>\n";
  835. }
  836. /**
  837. * Parses PHP code MyCode.
  838. *
  839. * @param array $matches Matches.
  840. * @return string The parsed message.
  841. */
  842. function mycode_parse_php_callback($matches)
  843. {
  844. return $this->mycode_parse_php($matches[1], false, true);
  845. }
  846. /**
  847. * Parses URL MyCode.
  848. *
  849. * @param string $url The URL to link to.
  850. * @param string $name The name of the link.
  851. * @return string The built-up link.
  852. */
  853. function mycode_parse_url($url, $name="")
  854. {
  855. if(!preg_match("#^[a-z0-9]+://#i", $url))
  856. {
  857. $url = "http://".$url;
  858. }
  859. if(!empty($this->options['allow_html']))
  860. {
  861. $url = $this->parse_html($url);
  862. }
  863. if(!$name)
  864. {
  865. $name = $url;
  866. }
  867. if($name == $url && (!isset($this->options['shorten_urls']) || !empty($this->options['shorten_urls'])))
  868. {
  869. $name = htmlspecialchars_decode($name);
  870. if(my_strlen($name) > 55)
  871. {
  872. $name = my_substr($name , 0, 40).'...'.my_substr($name , -10);
  873. }
  874. $name = htmlspecialchars_uni($name);
  875. }
  876. $nofollow = '';
  877. if(!empty($this->options['nofollow_on']))
  878. {
  879. $nofollow = " rel=\"nofollow\"";
  880. }
  881. // Fix some entities in URLs
  882. $entities = array('$' => '%24', '&#36;' => '%24', '^' => '%5E', '`' => '%60', '[' => '%5B', ']' => '%5D', '{' => '%7B', '}' => '%7D', '"' => '%22', '<' => '%3C', '>' => '%3E', ' ' => '%20');
  883. $url = str_replace(array_keys($entities), array_values($entities), $url);
  884. $name = preg_replace("#&amp;\#([0-9]+);#si", "&#$1;", $name); // Fix & but allow unicode
  885. $link = "<a href=\"$url\" target=\"_blank\"{$nofollow}>$name</a>";
  886. return $link;
  887. }
  888. /**
  889. * Parses URL MyCode.
  890. *
  891. * @param array $matches Matches.
  892. * @return string The built-up link.
  893. */
  894. function mycode_parse_url_callback1($matches)
  895. {
  896. if(!isset($matches[3]))
  897. {
  898. $matches[3] = '';
  899. }
  900. return $this->mycode_parse_url($matches[1].$matches[2], $matches[3]);
  901. }
  902. /**
  903. * Parses URL MyCode.
  904. *
  905. * @param array $matches Matches.
  906. * @return string The built-up link.
  907. */
  908. function mycode_parse_url_callback2($matches)
  909. {
  910. if(!isset($matches[2]))
  911. {
  912. $matches[2] = '';
  913. }
  914. return $this->mycode_parse_url($matches[1], $matches[2]);
  915. }
  916. /**
  917. * Parses IMG MyCode.
  918. *
  919. * @param string $url The URL to the image
  920. * @param array $dimensions Optional array of dimensions
  921. * @param string $align
  922. * @return string
  923. */
  924. function mycode_parse_img($url, $dimensions=array(), $align='')
  925. {
  926. global $lang;
  927. $url = trim($url);
  928. $url = str_replace("\n", "", $url);
  929. $url = str_replace("\r", "", $url);
  930. if(!empty($this->options['allow_html']))
  931. {
  932. $url = $this->parse_html($url);
  933. }
  934. $css_align = '';
  935. if($align == "right")
  936. {
  937. $css_align = " style=\"float: right;\"";
  938. }
  939. else if($align == "left")
  940. {
  941. $css_align = " style=\"float: left;\"";
  942. }
  943. $alt = basename($url);
  944. $alt = htmlspecialchars_decode($alt);
  945. if(my_strlen($alt) > 55)
  946. {
  947. $alt = my_substr($alt, 0, 40).'...'.my_substr($alt, -10);
  948. }
  949. $alt = htmlspecialchars_uni($alt);
  950. $alt = $lang->sprintf($lang->posted_image, $alt);
  951. if(isset($dimensions[0]) && $dimensions[0] > 0 && isset($dimensions[1]) && $dimensions[1] > 0)
  952. {
  953. return "<img src=\"{$url}\" width=\"{$dimensions[0]}\" height=\"{$dimensions[1]}\" border=\"0\" alt=\"{$alt}\"{$css_align} />";
  954. }
  955. else
  956. {
  957. return "<img src=\"{$url}\" border=\"0\" alt=\"{$alt}\"{$css_align} />";
  958. }
  959. }
  960. /**
  961. * Parses IMG MyCode.
  962. *
  963. * @param array $matches Matches.
  964. * @return string Image code.
  965. */
  966. function mycode_parse_img_callback1($matches)
  967. {
  968. return $this->mycode_parse_img($matches[2]);
  969. }
  970. /**
  971. * Parses IMG MyCode.
  972. *
  973. * @param array $matches Matches.
  974. * @return string Image code.
  975. */
  976. function mycode_parse_img_callback2($matches)
  977. {
  978. return $this->mycode_parse_img($matches[4], array($matches[1], $matches[2]));
  979. }
  980. /**
  981. * Parses IMG MyCode.
  982. *
  983. * @param array $matches Matches.
  984. * @return string Image code.
  985. */
  986. function mycode_parse_img_callback3($matches)
  987. {
  988. return $this->mycode_parse_img($matches[3], array(), $matches[1]);
  989. }
  990. /**
  991. * Parses IMG MyCode.
  992. *
  993. * @param array $matches Matches.
  994. * @return string Image code.
  995. */
  996. function mycode_parse_img_callback4($matches)
  997. {
  998. return $this->mycode_parse_img($matches[5], array($matches[1], $matches[2]), $matches[3]);
  999. }
  1000. /**
  1001. * Parses IMG MyCode disabled.
  1002. *
  1003. * @param string $url The URL to the image
  1004. * @return string
  1005. */
  1006. function mycode_parse_img_disabled($url)
  1007. {
  1008. global $lang;
  1009. $url = trim($url);
  1010. $url = str_replace("\n", "", $url);
  1011. $url = str_replace("\r", "", $url);
  1012. $url = str_replace("\'", "'", $url);
  1013. $image = $lang->sprintf($lang->posted_image, $this->mycode_parse_url($url));
  1014. return $image;
  1015. }
  1016. /**
  1017. * Parses IMG MyCode disabled.
  1018. *
  1019. * @param array $matches Matches.
  1020. * @return string Image code.
  1021. */
  1022. function mycode_parse_img_disabled_callback1($matches)
  1023. {
  1024. return $this->mycode_parse_img_disabled($matches[2]);
  1025. }
  1026. /**
  1027. * Parses IMG MyCode disabled.
  1028. *
  1029. * @param array $matches Matches.
  1030. * @return string Image code.
  1031. */
  1032. function mycode_parse_img_disabled_callback2($matches)
  1033. {
  1034. return $this->mycode_parse_img_disabled($matches[4]);
  1035. }
  1036. /**
  1037. * Parses IMG MyCode disabled.
  1038. *
  1039. * @param array $matches Matches.
  1040. * @return string Image code.
  1041. */
  1042. function mycode_parse_img_disabled_callback3($matches)
  1043. {
  1044. return $this->mycode_parse_img_disabled($matches[3]);
  1045. }
  1046. /**
  1047. * Parses IMG MyCode disabled.
  1048. *
  1049. * @param array $matches Matches.
  1050. * @return string Image code.
  1051. */
  1052. function mycode_parse_img_disabled_callback4($matches)
  1053. {
  1054. return $this->mycode_parse_img_disabled($matches[5]);
  1055. }
  1056. /**
  1057. * Parses email MyCode.
  1058. *
  1059. * @param string $email The email address to link to.
  1060. * @param string $name The name for the link.
  1061. * @return string The built-up email link.
  1062. */
  1063. function mycode_parse_email($email, $name="")
  1064. {
  1065. if(!$name)
  1066. {
  1067. $name = $email;
  1068. }
  1069. if(preg_match("/^([a-zA-Z0-9-_\+\.]+?)@[a-zA-Z0-9-]+\.[a-zA-Z0-9\.-]+$/si", $email))
  1070. {
  1071. return "<a href=\"mailto:$email\">".$name."</a>";
  1072. }
  1073. elseif(preg_match("/^([a-zA-Z0-9-_\+\.]+?)@[a-zA-Z0-9-]+\.[a-zA-Z0-9\.-]+\?(.*?)$/si", $email))
  1074. {
  1075. return "<a href=\"mailto:".htmlspecialchars_uni($email)."\">".$name."</a>";
  1076. }
  1077. else
  1078. {
  1079. return $email;
  1080. }
  1081. }
  1082. /**
  1083. * Parses email MyCode.
  1084. *
  1085. * @param array $matches Matches
  1086. * @return string The built-up email link.
  1087. */
  1088. function mycode_parse_email_callback($matches)
  1089. {
  1090. if(!isset($matches[2]))
  1091. {
  1092. $matches[2] = '';
  1093. }
  1094. return $this->mycode_parse_email($matches[1], $matches[2]);
  1095. }
  1096. /**
  1097. * Parses video MyCode.
  1098. *
  1099. * @param string $video The video provider.
  1100. * @param string $url The video to link to.
  1101. * @return string The built-up video code.
  1102. */
  1103. function mycode_parse_video($video, $url)
  1104. {
  1105. global $templates;
  1106. if(empty($video) || empty($url))
  1107. {
  1108. return "[video={$video}]{$url}[/video]";
  1109. }
  1110. $parsed_url = @parse_url(urldecode($url));
  1111. if($parsed_url == false)
  1112. {
  1113. return "[video={$video}]{$url}[/video]";
  1114. }
  1115. $fragments = array();
  1116. if($parsed_url['fragment'])
  1117. {
  1118. $fragments = explode("&", $parsed_url['fragment']);
  1119. }
  1120. $queries = explode("&", $parsed_url['query']);
  1121. $input = array();
  1122. foreach($queries as $query)
  1123. {
  1124. list($key, $value) = explode("=", $query);
  1125. $key = str_replace("amp;", "", $key);
  1126. $input[$key] = $value;
  1127. }
  1128. $path = explode('/', $parsed_url['path']);
  1129. switch($video)
  1130. {
  1131. case "dailymotion":
  1132. list($id) = explode('_', $path[2], 2); // http://www.dailymotion.com/video/fds123_title-goes-here
  1133. break;
  1134. case "metacafe":
  1135. $id = $path[2]; // http://www.metacafe.com/watch/fds123/title_goes_here/
  1136. $title = htmlspecialchars_uni($path[3]);
  1137. break;
  1138. case "myspacetv":
  1139. $id = $path[4]; // http://www.myspace.com/video/fds/fds/123
  1140. break;
  1141. case "facebook":
  1142. if(isset($input['v']))
  1143. {
  1144. $id = $input['v']; // http://www.facebook.com/video/video.php?v=123
  1145. }
  1146. elseif(substr($path[3], 0, 3) == 'vb.')
  1147. {
  1148. $id = $path[4]; // https://www.facebook.com/fds/videos/vb.123/123/
  1149. }
  1150. else
  1151. {
  1152. $id = $path[3]; // https://www.facebook.com/fds/videos/123/
  1153. }
  1154. break;
  1155. case "veoh":
  1156. $id = $path[2]; // http://www.veoh.com/watch/123
  1157. break;
  1158. case "liveleak":
  1159. $id = $input['i']; // http://www.liveleak.com/view?i=123
  1160. break;
  1161. case "yahoo":
  1162. if(isset($path[2]))
  1163. {
  1164. $id = $path[2]; // http://xy.screen.yahoo.com/fds/fds-123.html
  1165. }
  1166. else
  1167. {
  1168. $id = $path[1]; // http://xy.screen.yahoo.com/fds-123.html
  1169. }
  1170. // Support for localized portals
  1171. $domain = explode('.', $parsed_url['host']);
  1172. if($domain[0] != 'screen' && preg_match('#^([a-z-]+)$#', $domain[0]))
  1173. {
  1174. $local = "{$domain[0]}.";
  1175. }
  1176. else
  1177. {
  1178. $local = '';
  1179. }
  1180. break;
  1181. case "vimeo":
  1182. if(isset($path[3]))
  1183. {
  1184. $id = $path[3]; // http://vimeo.com/fds/fds/fds123
  1185. }
  1186. else
  1187. {
  1188. $id = $path[1]; // http://vimeo.com/fds123
  1189. }
  1190. break;
  1191. case "youtube":
  1192. if($fragments[0])
  1193. {
  1194. $id = str_replace('!v=', '', $fragments[0]); // http://www.youtube.com/watch#!v=fds123
  1195. }
  1196. elseif($input['v'])
  1197. {
  1198. $id = $input['v']; // http://www.youtube.com/watch?v=fds123
  1199. }
  1200. else
  1201. {
  1202. $id = $path[1]; // http://www.youtu.be/fds123
  1203. }
  1204. break;
  1205. default:
  1206. return "[video={$video}]{$url}[/video]";
  1207. }
  1208. if(empty($id))
  1209. {
  1210. return "[video={$video}]{$url}[/video]";
  1211. }
  1212. $id = htmlspecialchars_uni($id);
  1213. eval("\$video_code = \"".$templates->get("video_{$video}_embed")."\";");
  1214. return $video_code;
  1215. }
  1216. /**
  1217. * Parses video MyCode.
  1218. *
  1219. * @param array $matches Matches.
  1220. * @return string The built-up video code.
  1221. */
  1222. function mycode_parse_video_callback($matches)
  1223. {
  1224. return $this->mycode_parse_video($matches[1], $matches[2]);
  1225. }
  1226. /**
  1227. * Parses video MyCode disabled.
  1228. *
  1229. * @param string $url The URL to the video
  1230. * @return string
  1231. */
  1232. function mycode_parse_video_disabled($url)
  1233. {
  1234. global $lang;
  1235. $url = trim($url);
  1236. $url = str_replace("\n", "", $url);
  1237. $url = str_replace("\r", "", $url);
  1238. $url = str_replace("\'", "'", $url);
  1239. $video = $lang->sprintf($lang->posted_video, $this->mycode_parse_url($url));
  1240. return $video;
  1241. }
  1242. /**
  1243. * Parses video MyCode disabled.
  1244. *
  1245. * @param array $matches Matches.
  1246. * @return string The built-up video code.
  1247. */
  1248. function mycode_parse_video_disabled_callback($matches)
  1249. {
  1250. return $this->mycode_parse_video_disabled($matches[2]);
  1251. }
  1252. /**
  1253. * Parses URLs automatically.
  1254. *
  1255. * @param string $message The message to be parsed
  1256. * @return string The parsed message.
  1257. */
  1258. function mycode_auto_url($message)
  1259. {
  1260. $message = " ".$message;
  1261. // Links should end with slashes, numbers, characters and braces but not with dots, commas or question marks
  1262. $message = preg_replace_callback("#([\>\s\(\)])(http|https|ftp|news|irc|ircs|irc6){1}://([^\/\"\s\<\[\.]+\.([^\/\"\s\<\[\.]+\.)*[\w]+(:[0-9]+)?(/([^\"\s<\[]|\[\])*)?([\w\/\)]))#iu", array($this, 'mycode_auto_url_callback'), $message);
  1263. $message = preg_replace_callback("#([\>\s\(\)])(www|ftp)\.(([^\/\"\s\<\[\.]+\.)*[\w]+(:[0-9]+)?(/([^\"\s<\[]|\[\])*)?([\w\/\)]))#iu", array($this, 'mycode_auto_url_callback'), $message);
  1264. $message = my_substr($message, 1);
  1265. return $message;
  1266. }
  1267. /**
  1268. * Parses URLs automatically.
  1269. *
  1270. * @param array $matches Matches
  1271. * @return string The parsed message.
  1272. */
  1273. function mycode_auto_url_callback($matches)
  1274. {
  1275. $external = '';
  1276. // Allow links like http://en.wikipedia.org/wiki/PHP_(disambiguation) but detect mismatching braces
  1277. while(my_substr($matches[3], -1) == ')')
  1278. {
  1279. if(substr_count($matches[3], ')') > substr_count($matches[3], '('))
  1280. {
  1281. $matches[3] = my_substr($matches[3], 0, -1);
  1282. $external = ')'.$external;
  1283. }
  1284. else
  1285. {
  1286. break;
  1287. }
  1288. // Example: ([...] http://en.wikipedia.org/Example_(disambiguation).)
  1289. $last_char = my_substr($matches[3], -1);
  1290. while($last_char == '.' || $last_char == ',' || $last_char == '?' || $last_char == '!')
  1291. {
  1292. $matches[3] = my_substr($matches[3], 0, -1);
  1293. $external = $last_char.$external;
  1294. $last_char = my_substr($matches[3], -1);
  1295. }
  1296. }
  1297. if($matches[2] == 'www' || $matches[2] == 'ftp')
  1298. {
  1299. return "{$matches[1]}[url]{$matches[2]}.{$matches[3]}[/url]{$external}";
  1300. }
  1301. else
  1302. {
  1303. return "{$matches[1]}[url]{$matches[2]}://{$matches[3]}[/url]{$external}";
  1304. }
  1305. }
  1306. /**
  1307. * Parses list MyCode.
  1308. *
  1309. * @param string $message The message to be parsed
  1310. * @param string $type The list type
  1311. * @return string The parsed message.
  1312. */
  1313. function mycode_parse_list($message, $type="")
  1314. {
  1315. // No list elements? That's invalid HTML
  1316. if(strpos($message, '[*]') === false)
  1317. {
  1318. $message = "[*]{$message}";
  1319. }
  1320. $message = preg_replace("#[^\S\n\r]*\[\*\]\s*#", "</li>\n<li>", $message);
  1321. $message .= "</li>";
  1322. if($type)
  1323. {
  1324. $list = "\n<ol type=\"$type\">$message</ol>\n";
  1325. }
  1326. else
  1327. {
  1328. $list = "<ul>$message</ul>\n";
  1329. }
  1330. $list = preg_replace("#<(ol type=\"$type\"|ul)>\s*</li>#", "<$1>", $list);
  1331. return $list;
  1332. }
  1333. /**
  1334. * Parses list MyCode.
  1335. *
  1336. * @param array $matches Matches
  1337. * @return string The parsed message.
  1338. */
  1339. function mycode_parse_list_callback($matches)
  1340. {
  1341. return $this->mycode_parse_list($matches[3], $matches[2]);
  1342. }
  1343. /**
  1344. * Prepares list MyCode by finding the matching list tags.
  1345. *
  1346. * @param array $matches Matches
  1347. * @return string Temporary replacements.
  1348. */
  1349. function mycode_prepare_list($matches)
  1350. {
  1351. // Append number to identify matching list tags
  1352. if(strcasecmp($matches[1], '[/list]') == 0)
  1353. {
  1354. $count = array_pop($this->list_elements);
  1355. if($count !== NULL)
  1356. {
  1357. return "[/list&{$count}]";
  1358. }
  1359. else
  1360. {
  1361. // No open list tag...
  1362. return $matches[0];
  1363. }
  1364. }
  1365. else
  1366. {
  1367. ++$this->list_count;
  1368. $this->list_elements[] = $this->list_count;
  1369. if(!empty($matches[2]))
  1370. {
  1371. return "[list{$matches[2]}&{$this->list_count}]";
  1372. }
  1373. else
  1374. {
  1375. return "[list&{$this->list_count}]";
  1376. }
  1377. }
  1378. }
  1379. /**
  1380. * Strips smilies from a string
  1381. *
  1382. * @param string $message The message for smilies to be stripped from
  1383. * @return string The message with smilies stripped
  1384. */
  1385. function strip_smilies($message)
  1386. {
  1387. if($this->smilies_cache == 0)
  1388. {
  1389. $this->cache_smilies();
  1390. }
  1391. if(is_array($this->smilies_cache))
  1392. {
  1393. $message = str_replace($this->smilies_cache, array_keys($this->smilies_cache), $message);
  1394. }
  1395. return $message;
  1396. }
  1397. /**
  1398. * Highlights a string
  1399. *
  1400. * @param string $message The message to be highligted
  1401. * @param string $highlight The highlight keywords
  1402. * @return string The message with highlight bbcodes
  1403. */
  1404. function highlight_message($message, $highlight)
  1405. {
  1406. if(empty($this->highlight_cache))
  1407. {
  1408. $this->highlight_cache = build_highlight_array($highlight);
  1409. }
  1410. if(is_array($this->highlight_cache) && !empty($this->highlight_cache))
  1411. {
  1412. $message = preg_replace(array_keys($this->highlight_cache), $this->highlight_cache, $message);
  1413. }
  1414. return $message;
  1415. }
  1416. /**
  1417. * Parses message to plain text equivalents of MyCode.
  1418. *
  1419. * @param string $message The message to be parsed
  1420. * @param array $options
  1421. * @return string The parsed message.
  1422. */
  1423. function text_parse_message($message, $options=array())
  1424. {
  1425. global $plugins;
  1426. if(empty($this->options))
  1427. {
  1428. $this->options = $options;
  1429. }
  1430. // Filter bad words if requested.
  1431. if(!empty($this->options['filter_badwords']))
  1432. {
  1433. $message = $this->parse_badwords($message);
  1434. }
  1435. // Parse quotes first
  1436. $message = $this->mycode_parse_quotes($message, true);
  1437. $message = preg_replace_callback("#\[php\](.*?)\[/php\](\r\n?|\n?)#is", array($this, 'mycode_parse_php_callback'), $message);
  1438. $message = preg_replace_callback("#\[code\](.*?)\[/code\](\r\n?|\n?)#is", array($this, 'mycode_parse_code_callback'), $message);
  1439. $find = array(
  1440. "#\[(b|u|i|s|url|email|color|img)\](.*?)\[/\\1\]#is",
  1441. "#\[img=([0-9]{1,3})x([0-9]{1,3})\](\r\n?|\n?)(https?://([^<>\"']+?))\[/img\]#is",
  1442. "#\[url=([a-z]+?://)([^\r\n\"<]+?)\](.+?)\[/url\]#si",
  1443. "#\[url=([^\r\n\"<&\(\)]+?)\](.+?)\[/url\]#si",
  1444. );
  1445. $replace = array(
  1446. "$2",
  1447. "$4",
  1448. "$3 ($1$2)",
  1449. "$2 ($1)",
  1450. );
  1451. $message = preg_replace($find, $replace, $message);
  1452. // Replace "me" code and slaps if we have a username
  1453. if(!empty($this->options['me_username']))
  1454. {
  1455. global $lang;
  1456. $message = preg_replace('#(>|^|\r|\n)/me ([^\r\n<]*)#i', "\\1* {$this->options['me_username']} \\2", $message);
  1457. $message = preg_replace('#(>|^|\r|\n)/slap ([^\r\n<]*)#i', "\\1* {$this->options['me_username']} {$lang->slaps} \\2 {$lang->with_trout}", $message);
  1458. }
  1459. // Reset list cache
  1460. $this->list_elements = array();
  1461. $this->list_count = 0;
  1462. // Find all lists
  1463. $message = preg_replace_callback("#(\[list(=(a|A|i|I|1))?\]|\[/list\])#si", array($this, 'mycode_prepare_list'), $message);
  1464. // Replace all lists
  1465. for($i = $this->list_count; $i > 0; $i--)
  1466. {
  1467. // Ignores missing end tags
  1468. $message = preg_replace_callback("#\s?\[list(=(a|A|i|I|1))?&{$i}\](.*?)(\[/list&{$i}\]|$)(\r\n?|\n?)#si", array($this, 'mycode_parse_list_callback'), $message, 1);
  1469. }
  1470. // Run plugin hooks
  1471. $message = $plugins->run_hooks("text_parse_message", $message);
  1472. return $message;
  1473. }
  1474. }