2 namespace wcf\system\bbcode
;
3 use wcf\data\smiley\Smiley
;
4 use wcf\data\smiley\SmileyCache
;
5 use wcf\system\event\EventHandler
;
6 use wcf\system\SingletonFactory
;
7 use wcf\util\StringUtil
;
10 * Parses urls and smilies in simple messages.
13 * @copyright 2001-2016 WoltLab GmbH
14 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
15 * @package WoltLabSuite\Core\System\Bbcode
17 class SimpleMessageParser
extends SingletonFactory
{
19 * forbidden characters
22 protected static $illegalChars = '[^\x0-\x2C\x2E\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+';
28 protected $smilies = [];
34 protected $cachedURLs = [];
40 protected $cachedEmails = [];
43 * currently parsed message
51 protected function init() {
54 if (MODULE_SMILEY
== 1) {
56 $smilies = SmileyCache
::getInstance()->getSmilies();
57 $categories = SmileyCache
::getInstance()->getCategories();
58 foreach ($smilies as $categoryID => $categorySmilies) {
59 if ($categories[$categoryID ?
: null]->isDisabled
) continue;
61 /** @var Smiley $smiley */
62 foreach ($categorySmilies as $smiley) {
63 foreach ($smiley->smileyCodes
as $smileyCode) {
64 $this->smilies
[$smileyCode] = $smiley->getHtml();
68 krsort($this->smilies
);
73 * Parses the given message and returns the parsed message.
75 * @param string $message
76 * @param boolean $parseURLs
77 * @param boolean $parseSmilies
80 public function parse($message, $parseURLs = true, $parseSmilies = true) {
81 $this->message
= $message;
82 $this->cachedURLs
= $this->cachedEmails
= [];
85 EventHandler
::getInstance()->fireAction($this, 'beforeParsing');
89 $this->message
= $this->parseURLs($this->message
);
93 $this->message
= StringUtil
::encodeHTML($this->message
);
95 // converts newlines to <br>'s
96 $this->message
= nl2br($this->message
, false);
100 $this->message
= $this->insertCachedURLs($this->message
);
105 $this->message
= $this->parseSmilies($this->message
);
108 // replace bad html tags (script etc.)
109 $badSearch = ['/(javascript):/i', '/(about):/i', '/(vbscript):/i'];
110 $badReplace = ['$1<b></b>:', '$1<b></b>:', '$1<b></b>:'];
111 $this->message
= preg_replace($badSearch, $badReplace, $this->message
);
114 EventHandler
::getInstance()->fireAction($this, 'afterParsing');
116 return $this->message
;
122 * @param string $text
123 * @return string text
125 public function parseURLs($text) {
127 $urlPattern = '~(?<!\B|"|\'|=|/|\]|,|\?)
129 (?:ftp|https?)://'.static::$illegalChars.'(?:\.'.static::$illegalChars.')*
131 www\.(?:'.static::$illegalChars.'\.)+
132 (?:[a-z]{2,63}(?=\b)) # tld
139 [^!.,?;"\'<>()\[\]{}\s]*
141 [!.,?;(){}]+ [^!.,?;"\'<>()\[\]{}\s]+
145 $emailPattern = '~(?<!\B|"|\'|=|/|\]|,|:)
149 (?:'.static::$illegalChars.'\.)+ # hostname
155 $text = preg_replace_callback($urlPattern, [$this, 'cacheURLsCallback'], $text);
158 if (mb_strpos($text, '@') !== false) {
159 $text = preg_replace_callback($emailPattern, [$this, 'cacheEmailsCallback'], $text);
166 * Returns the hash for an matched URL in the message.
168 * @param array $matches
171 protected function cacheURLsCallback($matches) {
172 $hash = '@@'.StringUtil
::getHash(uniqid(microtime()).$matches[0]).'@@';
173 $this->cachedURLs
[$hash] = $matches[0];
179 * Returns the hash for an matched e-mail in the message.
181 * @param array $matches
184 protected function cacheEmailsCallback($matches) {
185 $hash = '@@'.StringUtil
::getHash(uniqid(microtime()).$matches[0]).'@@';
186 $this->cachedEmails
[$hash] = $matches[0];
192 * Reinserts cached URLs and e-mails.
194 * @param string $text
197 protected function insertCachedURLs($text) {
198 foreach ($this->cachedURLs
as $hash => $url) {
199 // add protocol if necessary
200 if (!preg_match("/[a-z]:\/\//si", $url)) {
201 $url = 'http://'.$url;
204 $text = str_replace($hash, StringUtil
::getAnchorTag($url), $text);
207 foreach ($this->cachedEmails
as $hash => $email) {
208 $email = StringUtil
::encodeHTML($email);
210 $text = str_replace($hash, '<a href="mailto:'.$email.'">'.$email.'</a>', $text);
217 * Parses smiley codes.
219 * @param string $text
220 * @return string text
222 public function parseSmilies($text) {
223 foreach ($this->smilies
as $code => $html) {
224 //$text = preg_replace('~(?<!&\w{2}|&\w{3}|&\w{4}|&\w{5}|&\w{6}|&#\d{2}|&#\d{3}|&#\d{4}|&#\d{5})'.preg_quote(StringUtil::encodeHTML($code), '~').'(?![^<]*>)~', $html, $text);
225 $text = preg_replace('~(?<=^|\s)'.preg_quote(StringUtil
::encodeHTML($code), '~').'(?=$|\s|<br />|<br>)~', $html, $text);