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 com.woltlab.wcf
16 * @subpackage system.bbcode
17 * @category Community Framework
19 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() {
56 if (MODULE_SMILEY
== 1) {
58 $smilies = SmileyCache
::getInstance()->getSmilies();
59 $categories = SmileyCache
::getInstance()->getCategories();
60 foreach ($smilies as $categoryID => $categorySmilies) {
61 if ($categories[$categoryID ?
: null]->isDisabled
) continue;
63 /** @var Smiley $smiley */
64 foreach ($categorySmilies as $smiley) {
65 foreach ($smiley->smileyCodes
as $smileyCode) {
66 $this->smilies
[$smileyCode] = '<img src="'.$smiley->getURL().'" alt="'.StringUtil
::encodeHTML($smiley->smileyCode
).'" />';
70 krsort($this->smilies
);
75 * Parses the given message and returns the parsed message.
77 * @param string $message
78 * @param boolean $parseURLs
79 * @param boolean $parseSmilies
82 public function parse($message, $parseURLs = true, $parseSmilies = true) {
83 $this->message
= $message;
84 $this->cachedURLs
= $this->cachedEmails
= [];
87 EventHandler
::getInstance()->fireAction($this, 'beforeParsing');
91 $this->message
= $this->parseURLs($this->message
);
95 $this->message
= StringUtil
::encodeHTML($this->message
);
97 // converts newlines to <br />'s
98 $this->message
= nl2br($this->message
);
102 $this->message
= $this->insertCachedURLs($this->message
);
107 $this->message
= $this->parseSmilies($this->message
);
110 // replace bad html tags (script etc.)
111 $badSearch = ['/(javascript):/i', '/(about):/i', '/(vbscript):/i'];
112 $badReplace = ['$1<b></b>:', '$1<b></b>:', '$1<b></b>:'];
113 $this->message
= preg_replace($badSearch, $badReplace, $this->message
);
116 EventHandler
::getInstance()->fireAction($this, 'afterParsing');
118 return $this->message
;
124 * @param string $text
125 * @return string text
127 public function parseURLs($text) {
129 $urlPattern = '~(?<!\B|"|\'|=|/|\]|,|\?)
131 (?:ftp|https?)://'.static::$illegalChars.'(?:\.'.static::$illegalChars.')*
133 www\.(?:'.static::$illegalChars.'\.)+
134 (?:[a-z]{2,63}(?=\b)) # tld
141 [^!.,?;"\'<>()\[\]{}\s]*
143 [!.,?;(){}]+ [^!.,?;"\'<>()\[\]{}\s]+
147 $emailPattern = '~(?<!\B|"|\'|=|/|\]|,|:)
151 (?:'.static::$illegalChars.'\.)+ # hostname
157 $text = preg_replace_callback($urlPattern, [$this, 'cacheURLsCallback'], $text);
160 if (mb_strpos($text, '@') !== false) {
161 $text = preg_replace_callback($emailPattern, [$this, 'cacheEmailsCallback'], $text);
168 * Returns the hash for an matched URL in the message.
170 * @param array $matches
173 protected function cacheURLsCallback($matches) {
174 $hash = '@@'.StringUtil
::getHash(uniqid(microtime()).$matches[0]).'@@';
175 $this->cachedURLs
[$hash] = $matches[0];
181 * Returns the hash for an matched e-mail in the message.
183 * @param array $matches
186 protected function cacheEmailsCallback($matches) {
187 $hash = '@@'.StringUtil
::getHash(uniqid(microtime()).$matches[0]).'@@';
188 $this->cachedEmails
[$hash] = $matches[0];
194 * Reinserts cached URLs and e-mails.
196 * @param string $text
199 protected function insertCachedURLs($text) {
200 foreach ($this->cachedURLs
as $hash => $url) {
201 // add protocol if necessary
202 if (!preg_match("/[a-z]:\/\//si", $url)) {
203 $url = 'http://'.$url;
206 $text = str_replace($hash, StringUtil
::getAnchorTag($url), $text);
209 foreach ($this->cachedEmails
as $hash => $email) {
210 $email = StringUtil
::encodeHTML($email);
212 $text = str_replace($hash, '<a href="mailto:'.$email.'">'.$email.'</a>', $text);
219 * Parses smiley codes.
221 * @param string $text
222 * @return string text
224 public function parseSmilies($text) {
225 foreach ($this->smilies
as $code => $html) {
226 //$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);
227 $text = preg_replace('~(?<=^|\s)'.preg_quote(StringUtil
::encodeHTML($code), '~').'(?=$|\s|<br />)~', $html, $text);