3 namespace wcf\system\bbcode
;
5 use wcf\data\smiley\Smiley
;
6 use wcf\data\smiley\SmileyCache
;
7 use wcf\system\event\EventHandler
;
8 use wcf\system\SingletonFactory
;
9 use wcf\util\StringUtil
;
12 * Parses urls and smilies in simple messages.
15 * @copyright 2001-2019 WoltLab GmbH
16 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
18 class SimpleMessageParser
extends SingletonFactory
21 * forbidden characters
24 protected static $illegalChars = '[^\x0-\x2C\x2E\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+';
30 protected $smilies = [];
36 protected $cachedURLs = [];
42 protected $cachedEmails = [];
45 * currently parsed message
53 protected function init()
57 if (MODULE_SMILEY
== 1) {
59 $smilies = SmileyCache
::getInstance()->getSmilies();
60 $categories = SmileyCache
::getInstance()->getCategories();
61 foreach ($smilies as $categoryID => $categorySmilies) {
62 if ($categories[$categoryID ?
: null]->isDisabled
) {
66 /** @var Smiley $smiley */
67 foreach ($categorySmilies as $smiley) {
68 foreach ($smiley->smileyCodes
as $smileyCode) {
69 $this->smilies
[$smileyCode] = $smiley->getHtml();
73 \
krsort($this->smilies
);
78 * Parses the given message and returns the parsed message.
80 * @param string $message
81 * @param bool $parseURLs
82 * @param bool $parseSmilies
85 public function parse($message, $parseURLs = true, $parseSmilies = true)
87 $this->message
= $message;
88 $this->cachedURLs
= $this->cachedEmails
= [];
91 EventHandler
::getInstance()->fireAction($this, 'beforeParsing');
95 $this->message
= $this->parseURLs($this->message
);
99 $this->message
= StringUtil
::encodeHTML($this->message
);
101 // converts newlines to <br>'s
102 $this->message
= \nl
2br
($this->message
, false);
106 $this->message
= $this->insertCachedURLs($this->message
);
111 $this->message
= $this->parseSmilies($this->message
);
114 // replace bad html tags (script etc.)
115 $badSearch = ['/(javascript):/i', '/(about):/i', '/(vbscript):/i'];
116 $badReplace = ['$1<b></b>:', '$1<b></b>:', '$1<b></b>:'];
117 $this->message
= \
preg_replace($badSearch, $badReplace, $this->message
);
120 EventHandler
::getInstance()->fireAction($this, 'afterParsing');
122 return $this->message
;
128 * @param string $text
129 * @return string text
131 public function parseURLs($text)
134 $urlPattern = '~(?<!\B|"|\'|=|/|\]|,|\?)
136 (?:ftp|https?)://' . static::$illegalChars . '(?:\.' . static::$illegalChars . ')*
138 www\.(?:' . static::$illegalChars . '\.)+
139 (?:[a-z]{2,63}(?=\b)) # tld
146 [^!.,?;"\'<>()\[\]{}\s]*
148 [!.,?;(){}]+ [^!.,?;"\'<>()\[\]{}\s]+
152 $emailPattern = '~(?<!\B|"|\'|=|/|\]|,|:)
156 (?:' . static::$illegalChars . '\.)+ # hostname
162 $text = \
preg_replace_callback($urlPattern, [$this, 'cacheURLsCallback'], $text);
165 if (\
str_contains($text, '@')) {
166 $text = \
preg_replace_callback($emailPattern, [$this, 'cacheEmailsCallback'], $text);
173 * Returns the hash for an matched URL in the message.
175 * @param array $matches
178 protected function cacheURLsCallback($matches)
180 $hash = '@@' . StringUtil
::getRandomID() . '@@';
181 $this->cachedURLs
[$hash] = $matches[0];
187 * Returns the hash for an matched e-mail in the message.
189 * @param array $matches
192 protected function cacheEmailsCallback($matches)
194 $hash = '@@' . StringUtil
::getRandomID() . '@@';
195 $this->cachedEmails
[$hash] = $matches[0];
201 * Reinserts cached URLs and e-mails.
203 * @param string $text
206 protected function insertCachedURLs($text)
208 foreach ($this->cachedURLs
as $hash => $url) {
209 // add protocol if necessary
210 if (!\
preg_match("/[a-z]:\\/\\//si", $url)) {
211 $url = 'http://' . $url;
214 $text = \
str_replace($hash, StringUtil
::getAnchorTag($url, '', true, true), $text);
217 foreach ($this->cachedEmails
as $hash => $email) {
218 $email = StringUtil
::encodeHTML($email);
220 $text = \
str_replace($hash, '<a href="mailto:' . $email . '">' . $email . '</a>', $text);
227 * Parses smiley codes.
229 * @param string $text
230 * @return string text
232 public function parseSmilies($text)
235 foreach ($this->smilies
as $code => $html) {
236 $text = \
preg_replace_callback('~(?<=^|\s)' . \
preg_quote(
237 StringUtil
::encodeHTML($code),
239 ) . '(?=$|\s|<br />|<br>)~', static function () use ($code, $html, &$smileyCount) {
240 if ($smileyCount === 50) {