diff options
Diffstat (limited to 'phpBB/includes/message_parser.php')
| -rw-r--r-- | phpBB/includes/message_parser.php | 518 | 
1 files changed, 374 insertions, 144 deletions
| diff --git a/phpBB/includes/message_parser.php b/phpBB/includes/message_parser.php index ad6743b3a3..97e0dd9f0d 100644 --- a/phpBB/includes/message_parser.php +++ b/phpBB/includes/message_parser.php @@ -1,9 +1,13 @@  <?php  /**  * -* @package phpBB3 -* @copyright (c) 2005 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file.  *  */ @@ -17,13 +21,25 @@ if (!defined('IN_PHPBB'))  if (!class_exists('bbcode'))  { +	// The following lines are for extensions which include message_parser.php +	// while $phpbb_root_path and $phpEx are out of the script scope +	// which may lead to the 'Undefined variable' and 'failed to open stream' errors +	if (!isset($phpbb_root_path)) +	{ +		global $phpbb_root_path; +	} + +	if (!isset($phpEx)) +	{ +		global $phpEx; +	} +  	include($phpbb_root_path . 'includes/bbcode.' . $phpEx);  }  /**  * BBCODE FIRSTPASS  * BBCODE first pass class (functions for parsing messages for db storage) -* @package phpBB3  */  class bbcode_firstpass extends bbcode  { @@ -67,7 +83,14 @@ class bbcode_firstpass extends bbcode  					// it should not demand recompilation  					if (preg_match($regexp, $this->message))  					{ -						$this->message = preg_replace($regexp, $replacement, $this->message); +						if (is_callable($replacement)) +						{ +							$this->message = preg_replace_callback($regexp, $replacement, $this->message); +						} +						else +						{ +							$this->message = preg_replace($regexp, $replacement, $this->message); +						}  						$bitfield->set($bbcode_data['bbcode_id']);  					}  				} @@ -104,28 +127,85 @@ class bbcode_firstpass extends bbcode  	function bbcode_init($allow_custom_bbcode = true)  	{  		global $phpbb_dispatcher; -		 +  		static $rowset; +		$bbcode_class = $this; +  		// This array holds all bbcode data. BBCodes will be processed in this  		// order, so it is important to keep [code] in first position and  		// [quote] in second position.  		// To parse multiline URL we enable dotall option setting only for URL text  		// but not for link itself, thus [url][/url] is not affected. +		// +		// To perform custom validation in extension, use $this->validate_bbcode_by_extension() +		// method which accepts variable number of parameters  		$this->bbcodes = array( -			'code'			=> array('bbcode_id' => 8,	'regexp' => array('#\[code(?:=([a-z]+))?\](.+\[/code\])#uise' => "\$this->bbcode_code('\$1', '\$2')")), -			'quote'			=> array('bbcode_id' => 0,	'regexp' => array('#\[quote(?:="(.*?)")?\](.+)\[/quote\]#uise' => "\$this->bbcode_quote('\$0')")), -			'attachment'	=> array('bbcode_id' => 12,	'regexp' => array('#\[attachment=([0-9]+)\](.*?)\[/attachment\]#uise' => "\$this->bbcode_attachment('\$1', '\$2')")), -			'b'				=> array('bbcode_id' => 1,	'regexp' => array('#\[b\](.*?)\[/b\]#uise' => "\$this->bbcode_strong('\$1')")), -			'i'				=> array('bbcode_id' => 2,	'regexp' => array('#\[i\](.*?)\[/i\]#uise' => "\$this->bbcode_italic('\$1')")), -			'url'			=> array('bbcode_id' => 3,	'regexp' => array('#\[url(=(.*))?\](?(1)((?s).*(?-s))|(.*))\[/url\]#uiUe' => "\$this->validate_url('\$2', ('\$3') ? '\$3' : '\$4')")), -			'img'			=> array('bbcode_id' => 4,	'regexp' => array('#\[img\](.*)\[/img\]#uiUe' => "\$this->bbcode_img('\$1')")), -			'size'			=> array('bbcode_id' => 5,	'regexp' => array('#\[size=([\-\+]?\d+)\](.*?)\[/size\]#uise' => "\$this->bbcode_size('\$1', '\$2')")), -			'color'			=> array('bbcode_id' => 6,	'regexp' => array('!\[color=(#[0-9a-f]{3}|#[0-9a-f]{6}|[a-z\-]+)\](.*?)\[/color\]!uise' => "\$this->bbcode_color('\$1', '\$2')")), -			'u'				=> array('bbcode_id' => 7,	'regexp' => array('#\[u\](.*?)\[/u\]#uise' => "\$this->bbcode_underline('\$1')")), -			'list'			=> array('bbcode_id' => 9,	'regexp' => array('#\[list(?:=(?:[a-z0-9]|disc|circle|square))?].*\[/list]#uise' => "\$this->bbcode_parse_list('\$0')")), -			'email'			=> array('bbcode_id' => 10,	'regexp' => array('#\[email=?(.*?)?\](.*?)\[/email\]#uise' => "\$this->validate_email('\$1', '\$2')")), -			'flash'			=> array('bbcode_id' => 11,	'regexp' => array('#\[flash=([0-9]+),([0-9]+)\](.*?)\[/flash\]#uie' => "\$this->bbcode_flash('\$1', '\$2', '\$3')")) +			'code'			=> array('bbcode_id' => 8,	'regexp' => array('#\[code(?:=([a-z]+))?\](.+\[/code\])#uis' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_code($match[1], $match[2]); +				} +			)), +			'quote'			=> array('bbcode_id' => 0,	'regexp' => array('#\[quote(?:="(.*?)")?\](.+)\[/quote\]#uis' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_quote($match[0]); +				} +			)), +			'attachment'	=> array('bbcode_id' => 12,	'regexp' => array('#\[attachment=([0-9]+)\](.*?)\[/attachment\]#uis' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_attachment($match[1], $match[2]); +				} +			)), +			'b'				=> array('bbcode_id' => 1,	'regexp' => array('#\[b\](.*?)\[/b\]#uis' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_strong($match[1]); +				} +			)), +			'i'				=> array('bbcode_id' => 2,	'regexp' => array('#\[i\](.*?)\[/i\]#uis' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_italic($match[1]); +				} +			)), +			'url'			=> array('bbcode_id' => 3,	'regexp' => array('#\[url(=(.*))?\](?(1)((?s).*(?-s))|(.*))\[/url\]#uiU' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->validate_url($match[2], ($match[3]) ? $match[3] : $match[4]); +				} +			)), +			'img'			=> array('bbcode_id' => 4,	'regexp' => array('#\[img\](.*)\[/img\]#uiU' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_img($match[1]); +				} +			)), +			'size'			=> array('bbcode_id' => 5,	'regexp' => array('#\[size=([\-\+]?\d+)\](.*?)\[/size\]#uis' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_size($match[1], $match[2]); +				} +			)), +			'color'			=> array('bbcode_id' => 6,	'regexp' => array('!\[color=(#[0-9a-f]{3}|#[0-9a-f]{6}|[a-z\-]+)\](.*?)\[/color\]!uis' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_color($match[1], $match[2]); +				} +			)), +			'u'				=> array('bbcode_id' => 7,	'regexp' => array('#\[u\](.*?)\[/u\]#uis' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_underline($match[1]); +				} +			)), +			'list'			=> array('bbcode_id' => 9,	'regexp' => array('#\[list(?:=(?:[a-z0-9]|disc|circle|square))?].*\[/list]#uis' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_parse_list($match[0]); +				} +			)), +			'email'			=> array('bbcode_id' => 10,	'regexp' => array('#\[email=?(.*?)?\](.*?)\[/email\]#uis' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->validate_email($match[1], $match[2]); +				} +			)), +			'flash'			=> array('bbcode_id' => 11,	'regexp' => array('#\[flash=([0-9]+),([0-9]+)\](.*?)\[/flash\]#ui' => function ($match) use($bbcode_class) +				{ +					return $bbcode_class->bbcode_flash($match[1], $match[2], $match[3]); +				} +			))  		);  		// Zero the parsed items array @@ -310,7 +390,7 @@ class bbcode_firstpass extends bbcode  		$in = str_replace(' ', '%20', $in);  		// Checking urls -		if (!preg_match('#^' . get_preg_expression('url') . '$#i', $in) && !preg_match('#^' . get_preg_expression('www_url') . '$#i', $in)) +		if (!preg_match('#^' . get_preg_expression('url') . '$#iu', $in) && !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $in))  		{  			return '[img]' . $in . '[/img]';  		} @@ -323,22 +403,23 @@ class bbcode_firstpass extends bbcode  		if ($config['max_' . $this->mode . '_img_height'] || $config['max_' . $this->mode . '_img_width'])  		{ -			$stats = @getimagesize(htmlspecialchars_decode($in)); +			$imagesize = new \FastImageSize\FastImageSize(); +			$size_info = $imagesize->getImageSize(htmlspecialchars_decode($in)); -			if ($stats === false) +			if ($size_info === false)  			{  				$error = true;  				$this->warn_msg[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];  			}  			else  			{ -				if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $stats[1]) +				if ($config['max_' . $this->mode . '_img_height'] && $config['max_' . $this->mode . '_img_height'] < $size_info['height'])  				{  					$error = true;  					$this->warn_msg[] = $user->lang('MAX_IMG_HEIGHT_EXCEEDED', (int) $config['max_' . $this->mode . '_img_height']);  				} -				if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $stats[0]) +				if ($config['max_' . $this->mode . '_img_width'] && $config['max_' . $this->mode . '_img_width'] < $size_info['width'])  				{  					$error = true;  					$this->warn_msg[] = $user->lang('MAX_IMG_WIDTH_EXCEEDED', (int) $config['max_' . $this->mode . '_img_width']); @@ -378,8 +459,8 @@ class bbcode_firstpass extends bbcode  		$in = str_replace(' ', '%20', $in);  		// Make sure $in is a URL. -		if (!preg_match('#^' . get_preg_expression('url') . '$#i', $in) && -			!preg_match('#^' . get_preg_expression('www_url') . '$#i', $in)) +		if (!preg_match('#^' . get_preg_expression('url') . '$#iu', $in) && +			!preg_match('#^' . get_preg_expression('www_url') . '$#iu', $in))  		{  			return '[flash=' . $width . ',' . $height . ']' . $in . '[/flash]';  		} @@ -717,8 +798,6 @@ class bbcode_firstpass extends bbcode  	*/  	function bbcode_quote($in)  	{ -		global $config, $user; -  		$in = str_replace("\r\n", "\n", str_replace('\"', '"', trim($in)));  		if (!$in) @@ -727,7 +806,9 @@ class bbcode_firstpass extends bbcode  		}  		// To let the parser not catch tokens within quote_username quotes we encode them before we start this... -		$in = preg_replace('#quote="(.*?)"\]#ie', "'quote="' . str_replace(array('[', ']', '\\\"'), array('[', ']', '\"'), '\$1') . '"]'", $in); +		$in = preg_replace_callback('#quote="(.*?)"\]#i', function ($match) { +			return 'quote="' . str_replace(array('[', ']', '\\\"'), array('[', ']', '\"'), $match[1]) . '"]'; +		}, $in);  		$tok = ']';  		$out = '['; @@ -772,20 +853,6 @@ class bbcode_firstpass extends bbcode  				else if (preg_match('#^quote(?:="(.*?)")?$#is', $buffer, $m) && substr($out, -1, 1) == '[')  				{  					$this->parsed_items['quote']++; - -					// the buffer holds a valid opening tag -					if ($config['max_quote_depth'] && sizeof($close_tags) >= $config['max_quote_depth']) -					{ -						// there are too many nested quotes -						$error_ary['quote_depth'] = $user->lang('QUOTE_DEPTH_EXCEEDED', (int) $config['max_quote_depth']); - -						$out .= $buffer . $tok; -						$tok = '[]'; -						$buffer = ''; - -						continue; -					} -  					array_push($close_tags, '/quote:' . $this->bbcode_uid);  					if (isset($m[1]) && $m[1]) @@ -940,8 +1007,6 @@ class bbcode_firstpass extends bbcode  	*/  	function validate_url($var1, $var2)  	{ -		global $config; -  		$var1 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var1)));  		$var2 = str_replace("\r\n", "\n", str_replace('\"', '"', trim($var2))); @@ -962,9 +1027,9 @@ class bbcode_firstpass extends bbcode  		$url = str_replace(' ', '%20', $url);  		// Checking urls -		if (preg_match('#^' . get_preg_expression('url') . '$#i', $url) || -			preg_match('#^' . get_preg_expression('www_url') . '$#i', $url) || -			preg_match('#^' . preg_quote(generate_board_url(), '#') . get_preg_expression('relative_url') . '$#i', $url)) +		if (preg_match('#^' . get_preg_expression('url') . '$#iu', $url) || +			preg_match('#^' . get_preg_expression('www_url') . '$#iu', $url) || +			preg_match('#^' . preg_quote(generate_board_url(), '#') . get_preg_expression('relative_url') . '$#iu', $url))  		{  			$valid = true;  		} @@ -1049,7 +1114,6 @@ class bbcode_firstpass extends bbcode  /**  * Main message parser for posting, pm, etc. takes raw message  * and parses it for attachments, bbcode and smilies -* @package phpBB3  */  class parse_message extends bbcode_firstpass  { @@ -1087,7 +1151,7 @@ class parse_message extends bbcode_firstpass  	*/  	function parse($allow_bbcode, $allow_magic_url, $allow_smilies, $allow_img_bbcode = true, $allow_flash_bbcode = true, $allow_quote_bbcode = true, $allow_url_bbcode = true, $update_this_message = true, $mode = 'post')  	{ -		global $config, $db, $user; +		global $config, $user, $phpbb_dispatcher, $phpbb_container;  		$this->mode = $mode; @@ -1116,19 +1180,13 @@ class parse_message extends bbcode_firstpass  			$this->decode_message();  		} -		// Do some general 'cleanup' first before processing message, -		// e.g. remove excessive newlines(?), smilies(?) -		$match = array('#(script|about|applet|activex|chrome):#i'); -		$replace = array("\\1:"); -		$this->message = preg_replace($match, $replace, trim($this->message)); -  		// Store message length...  		$message_length = ($mode == 'post') ? utf8_strlen($this->message) : utf8_strlen(preg_replace('#\[\/?[a-z\*\+\-]+(=[\S]+)?\]#ius', ' ', $this->message));  		// Maximum message length check. 0 disables this check completely.  		if ((int) $config['max_' . $mode . '_chars'] > 0 && $message_length > (int) $config['max_' . $mode . '_chars'])  		{ -			$this->warn_msg[] = $user->lang('TOO_MANY_CHARS_' . strtoupper($mode), $message_length, (int) $config['max_' . $mode . '_chars']); +			$this->warn_msg[] = $user->lang('CHARS_' . strtoupper($mode) . '_CONTAINS', $message_length) . '<br />' . $user->lang('TOO_MANY_CHARS_LIMIT', (int) $config['max_' . $mode . '_chars']);  			return (!$update_this_message) ? $return_message : $this->warn_msg;  		} @@ -1137,51 +1195,91 @@ class parse_message extends bbcode_firstpass  		{  			if (!$message_length || $message_length < (int) $config['min_post_chars'])  			{ -				$this->warn_msg[] = (!$message_length) ? $user->lang['TOO_FEW_CHARS'] : $user->lang('TOO_FEW_CHARS_LIMIT', $message_length, (int) $config['min_post_chars']); +				$this->warn_msg[] = (!$message_length) ? $user->lang['TOO_FEW_CHARS'] : ($user->lang('CHARS_POST_CONTAINS', $message_length) . '<br />' . $user->lang('TOO_FEW_CHARS_LIMIT', (int) $config['min_post_chars']));  				return (!$update_this_message) ? $return_message : $this->warn_msg;  			}  		} -		// Prepare BBcode (just prepares some tags for better parsing) -		if ($allow_bbcode && strpos($this->message, '[') !== false) +		/** +		* This event can be used for additional message checks/cleanup before parsing +		* +		* @event core.message_parser_check_message +		* @var bool		allow_bbcode			Do we allow BBCodes +		* @var bool		allow_magic_url			Do we allow magic urls +		* @var bool		allow_smilies			Do we allow smilies +		* @var bool		allow_img_bbcode		Do we allow image BBCode +		* @var bool		allow_flash_bbcode		Do we allow flash BBCode +		* @var bool		allow_quote_bbcode		Do we allow quote BBCode +		* @var bool		allow_url_bbcode		Do we allow url BBCode +		* @var bool		update_this_message		Do we alter the parsed message +		* @var string	mode					Posting mode +		* @var string	message					The message text to parse +		* @var string	bbcode_bitfield			The bbcode_bitfield before parsing +		* @var string	bbcode_uid				The bbcode_uid before parsing +		* @var bool		return					Do we return after the event is triggered if $warn_msg is not empty +		* @var array	warn_msg				Array of the warning messages +		* @since 3.1.2-RC1 +		* @change 3.1.3-RC1 Added vars $bbcode_bitfield and $bbcode_uid +		*/ +		$message = $this->message; +		$warn_msg = $this->warn_msg; +		$return = false; +		$bbcode_bitfield = $this->bbcode_bitfield; +		$bbcode_uid = $this->bbcode_uid; +		$vars = array( +			'allow_bbcode', +			'allow_magic_url', +			'allow_smilies', +			'allow_img_bbcode', +			'allow_flash_bbcode', +			'allow_quote_bbcode', +			'allow_url_bbcode', +			'update_this_message', +			'mode', +			'message', +			'bbcode_bitfield', +			'bbcode_uid', +			'return', +			'warn_msg', +		); +		extract($phpbb_dispatcher->trigger_event('core.message_parser_check_message', compact($vars))); +		$this->message = $message; +		$this->warn_msg = $warn_msg; +		$this->bbcode_bitfield = $bbcode_bitfield; +		$this->bbcode_uid = $bbcode_uid; +		if ($return && !empty($this->warn_msg))  		{ -			$this->bbcode_init(); -			$disallow = array('img', 'flash', 'quote', 'url'); -			foreach ($disallow as $bool) -			{ -				if (!${'allow_' . $bool . '_bbcode'}) -				{ -					$this->bbcodes[$bool]['disabled'] = true; -				} -			} - -			$this->prepare_bbcodes(); +			return (!$update_this_message) ? $return_message : $this->warn_msg;  		} -		// Parse smilies -		if ($allow_smilies) -		{ -			$this->smilies($config['max_' . $mode . '_smilies']); -		} +		// Get the parser +		$parser = $phpbb_container->get('text_formatter.parser'); -		$num_urls = 0; +		// Set the parser's options +		($allow_bbcode)       ? $parser->enable_bbcodes()       : $parser->disable_bbcodes(); +		($allow_magic_url)    ? $parser->enable_magic_url()     : $parser->disable_magic_url(); +		($allow_smilies)      ? $parser->enable_smilies()       : $parser->disable_smilies(); +		($allow_img_bbcode)   ? $parser->enable_bbcode('img')   : $parser->disable_bbcode('img'); +		($allow_flash_bbcode) ? $parser->enable_bbcode('flash') : $parser->disable_bbcode('flash'); +		($allow_quote_bbcode) ? $parser->enable_bbcode('quote') : $parser->disable_bbcode('quote'); +		($allow_url_bbcode)   ? $parser->enable_bbcode('url')   : $parser->disable_bbcode('url'); -		// Parse BBCode -		if ($allow_bbcode && strpos($this->message, '[') !== false) -		{ -			$this->parse_bbcode(); -			$num_urls += $this->parsed_items['url']; -		} +		// Set some config values +		$parser->set_vars(array( +			'max_font_size'  => $config['max_' . $this->mode . '_font_size'], +			'max_img_height' => $config['max_' . $this->mode . '_img_height'], +			'max_img_width'  => $config['max_' . $this->mode . '_img_width'], +			'max_smilies'    => $config['max_' . $this->mode . '_smilies'], +			'max_urls'       => $config['max_' . $this->mode . '_urls'] +		)); -		// Parse URL's -		if ($allow_magic_url) -		{ -			$this->magic_url(generate_board_url()); +		// Parse this message +		$this->message = $parser->parse(htmlspecialchars_decode($this->message, ENT_QUOTES)); -			if ($config['max_' . $mode . '_urls']) -			{ -				$num_urls += preg_match_all('#\<!-- ([lmwe]) --\>.*?\<!-- \1 --\>#', $this->message, $matches); -			} +		// Remove quotes that are nested too deep +		if ($config['max_quote_depth'] > 0) +		{ +			$this->remove_nested_quotes($config['max_quote_depth']);  		}  		// Check for "empty" message. We do not check here for maximum length, because bbcode, smilies, etc. can add to the length. @@ -1192,10 +1290,27 @@ class parse_message extends bbcode_firstpass  			return (!$update_this_message) ? $return_message : $this->warn_msg;  		} -		// Check number of links -		if ($config['max_' . $mode . '_urls'] && $num_urls > $config['max_' . $mode . '_urls']) +		// Remove quotes that are nested too deep +		if ($config['max_quote_depth'] > 0)  		{ -			$this->warn_msg[] = sprintf($user->lang['TOO_MANY_URLS'], $config['max_' . $mode . '_urls']); +			$this->message = $phpbb_container->get('text_formatter.utils')->remove_bbcode( +				$this->message, +				'quote', +				$config['max_quote_depth'] +			); +		} + +		// Check for errors +		$errors = $parser->get_errors(); +		if ($errors) +		{ +			foreach ($errors as $i => $args) +			{ +				// Translate each error with $user->lang() +				$errors[$i] = call_user_func_array(array($user, 'lang'), $args); +			} +			$this->warn_msg = array_merge($this->warn_msg, $errors); +  			return (!$update_this_message) ? $return_message : $this->warn_msg;  		} @@ -1215,7 +1330,7 @@ class parse_message extends bbcode_firstpass  	*/  	function format_display($allow_bbcode, $allow_magic_url, $allow_smilies, $update_this_message = true)  	{ -		global $phpbb_dispatcher; +		global $phpbb_container, $phpbb_dispatcher;  		// If false, then the parsed message get returned but internal message not processed.  		if (!$update_this_message) @@ -1224,26 +1339,48 @@ class parse_message extends bbcode_firstpass  			$return_message = &$this->message;  		} -		if ($this->message_status == 'plain') +		$text = $this->message; +		$uid = $this->bbcode_uid; + +		/** +		* Event to modify the text before it is parsed +		* +		* @event core.modify_format_display_text_before +		* @var string	text				The message text to parse +		* @var string	uid					The bbcode uid +		* @var bool		allow_bbcode		Do we allow bbcodes +		* @var bool		allow_magic_url		Do we allow magic urls +		* @var bool		allow_smilies		Do we allow smilies +		* @var bool		update_this_message	Do we update the internal message +		*									with the parsed result +		* @since 3.1.6-RC1 +		*/ +		$vars = array('text', 'uid', 'allow_bbcode', 'allow_magic_url', 'allow_smilies', 'update_this_message'); +		extract($phpbb_dispatcher->trigger_event('core.modify_format_display_text_before', compact($vars))); + +		$this->message = $text; +		$this->bbcode_uid = $uid; +		unset($text, $uid); + +		// NOTE: message_status is unreliable for detecting unparsed text because some callers +		//       change $this->message without resetting $this->message_status to 'plain' so we +		//       inspect the message instead +		//if ($this->message_status == 'plain') +		if (!preg_match('/^<[rt][ >]/', $this->message))  		{  			// Force updating message - of course.  			$this->parse($allow_bbcode, $allow_magic_url, $allow_smilies, $this->allow_img_bbcode, $this->allow_flash_bbcode, $this->allow_quote_bbcode, $this->allow_url_bbcode, true);  		} -		// Replace naughty words such as farty pants -		$this->message = censor_text($this->message); - -		// Parse BBcode -		if ($allow_bbcode) +		// There's a bug when previewing a topic with no poll, because the empty title of the poll +		// gets parsed but $this->message still ends up empty. This fixes it, until a proper fix is +		// devised +		if ($this->message === '')  		{ -			$this->bbcode_cache_init(); - -			// We are giving those parameters to be able to use the bbcode class on its own -			$this->bbcode_second_pass($this->message, $this->bbcode_uid); +			$this->message = $phpbb_container->get('text_formatter.parser')->parse($this->message);  		} -		$this->message = bbcode_nl2br($this->message); -		$this->message = smiley_text($this->message, !$allow_smilies); +		$this->message = $phpbb_container->get('text_formatter.renderer')->render($this->message);  		$text = $this->message;  		$uid = $this->bbcode_uid; @@ -1331,7 +1468,7 @@ class parse_message extends bbcode_firstpass  			// NOTE: obtain_* function? chaching the table contents?  			// For now setting the ttl to 10 minutes -			switch ($db->sql_layer) +			switch ($db->get_sql_layer())  			{  				case 'mssql':  				case 'mssql_odbc': @@ -1341,12 +1478,6 @@ class parse_message extends bbcode_firstpass  						ORDER BY LEN(code) DESC';  				break; -				case 'firebird': -					$sql = 'SELECT * -						FROM ' . SMILIES_TABLE . ' -						ORDER BY CHAR_LENGTH(code) DESC'; -				break; -  				// LENGTH supported by MySQL, IBM DB2, Oracle and Access for sure...  				default:  					$sql = 'SELECT * @@ -1400,11 +1531,12 @@ class parse_message extends bbcode_firstpass  	function parse_attachments($form_name, $mode, $forum_id, $submit, $preview, $refresh, $is_message = false)  	{  		global $config, $auth, $user, $phpbb_root_path, $phpEx, $db, $request; +		global $phpbb_container;  		$error = array();  		$num_attachments = sizeof($this->attachment_data); -		$this->filename_data['filecomment'] = utf8_normalize_nfc(request_var('filecomment', '', true)); +		$this->filename_data['filecomment'] = $request->variable('filecomment', '', true);  		$upload = $request->file($form_name);  		$upload_file = (!empty($upload) && $upload['name'] !== 'none' && trim($upload['name'])); @@ -1412,7 +1544,7 @@ class parse_message extends bbcode_firstpass  		$delete_file	= (isset($_POST['delete_file'])) ? true : false;  		// First of all adjust comments if changed -		$actual_comment_list = utf8_normalize_nfc(request_var('comment_list', array(''), true)); +		$actual_comment_list = $request->variable('comment_list', array(''), true);  		foreach ($actual_comment_list as $comment_key => $comment)  		{ @@ -1435,7 +1567,9 @@ class parse_message extends bbcode_firstpass  		{  			if ($num_attachments < $cfg['max_attachments'] || $auth->acl_get('a_') || $auth->acl_get('m_', $forum_id))  			{ -				$filedata = upload_attachment($form_name, $forum_id, false, '', $is_message); +				/** @var \phpbb\attachment\manager $attachment_manager */ +				$attachment_manager = $phpbb_container->get('attachment.manager'); +				$filedata = $attachment_manager->upload($form_name, $forum_id, false, '', $is_message);  				$error = $filedata['error'];  				if ($filedata['post_attach'] && !sizeof($error)) @@ -1465,7 +1599,9 @@ class parse_message extends bbcode_firstpass  					);  					$this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data); -					$this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "'[attachment='.(\\1 + 1).']\\2[/attachment]'", $this->message); +					$this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) { +						return '[attachment='.($match[1] + 1).']' . $match[2] . '[/attachment]'; +					}, $this->message);  					$this->filename_data['filecomment'] = ''; @@ -1498,11 +1634,14 @@ class parse_message extends bbcode_firstpass  			{  				include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx); -				$index = array_keys(request_var('delete_file', array(0 => 0))); +				$index = array_keys($request->variable('delete_file', array(0 => 0)));  				$index = (!empty($index)) ? $index[0] : false;  				if ($index !== false && !empty($this->attachment_data[$index]))  				{ +					/** @var \phpbb\attachment\manager $attachment_manager */ +					$attachment_manager = $phpbb_container->get('attachment.manager'); +  					// delete selected attachment  					if ($this->attachment_data[$index]['is_orphan'])  					{ @@ -1517,11 +1656,11 @@ class parse_message extends bbcode_firstpass  						if ($row)  						{ -							phpbb_unlink($row['physical_filename'], 'file'); +							$attachment_manager->unlink($row['physical_filename'], 'file');  							if ($row['thumbnail'])  							{ -								phpbb_unlink($row['physical_filename'], 'thumbnail'); +								$attachment_manager->unlink($row['physical_filename'], 'thumbnail');  							}  							$db->sql_query('DELETE FROM ' . ATTACHMENTS_TABLE . ' WHERE attach_id = ' . (int) $this->attachment_data[$index]['attach_id']); @@ -1529,11 +1668,13 @@ class parse_message extends bbcode_firstpass  					}  					else  					{ -						delete_attachments('attach', array(intval($this->attachment_data[$index]['attach_id']))); +						$attachment_manager->delete('attach', $this->attachment_data[$index]['attach_id']);  					}  					unset($this->attachment_data[$index]); -					$this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "(\\1 == \$index) ? '' : ((\\1 > \$index) ? '[attachment=' . (\\1 - 1) . ']\\2[/attachment]' : '\\0')", $this->message); +					$this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) use($index) { +						return ($match[1] == $index) ? '' : (($match[1] > $index) ? '[attachment=' . ($match[1] - 1) . ']' . $match[2] . '[/attachment]' : $match[0]); +					}, $this->message);  					// Reindex Array  					$this->attachment_data = array_values($this->attachment_data); @@ -1547,7 +1688,9 @@ class parse_message extends bbcode_firstpass  			{  				if ($num_attachments < $cfg['max_attachments'] || $auth->acl_gets('m_', 'a_', $forum_id))  				{ -					$filedata = upload_attachment($form_name, $forum_id, false, '', $is_message, false, $this->plupload); +					/** @var \phpbb\attachment\manager $attachment_manager */ +					$attachment_manager = $phpbb_container->get('attachment.manager'); +					$filedata = $attachment_manager->upload($form_name, $forum_id, false, '', $is_message);  					$error = array_merge($error, $filedata['error']);  					if (!sizeof($error)) @@ -1577,7 +1720,9 @@ class parse_message extends bbcode_firstpass  						);  						$this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data); -						$this->message = preg_replace('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#e', "'[attachment='.(\\1 + 1).']\\2[/attachment]'", $this->message); +						$this->message = preg_replace_callback('#\[attachment=([0-9]+)\](.*?)\[\/attachment\]#', function ($match) { +							return '[attachment=' . ($match[1] + 1) . ']' . $match[2] . '[/attachment]'; +						}, $this->message);  						$this->filename_data['filecomment'] = '';  						if (isset($this->plupload) && $this->plupload->is_active()) @@ -1621,10 +1766,10 @@ class parse_message extends bbcode_firstpass  	*/  	function get_submitted_attachment_data($check_user_id = false)  	{ -		global $user, $db, $phpbb_root_path, $phpEx, $config; +		global $user, $db;  		global $request; -		$this->filename_data['filecomment'] = utf8_normalize_nfc(request_var('filecomment', '', true)); +		$this->filename_data['filecomment'] = $request->variable('filecomment', '', true);  		$attachment_data = $request->variable('attachment_data', array(0 => array('' => '')), true, \phpbb\request\request_interface::POST);  		$this->attachment_data = array(); @@ -1709,28 +1854,26 @@ class parse_message extends bbcode_firstpass  	*/  	function parse_poll(&$poll)  	{ -		global $auth, $user, $config; +		global $user, $config;  		$poll_max_options = $poll['poll_max_options']; -		// Parse Poll Option text ;) +		// Parse Poll Option text  		$tmp_message = $this->message; -		$this->message = $poll['poll_option_text']; -		$bbcode_bitfield = $this->bbcode_bitfield; -		$poll['poll_option_text'] = $this->parse($poll['enable_bbcode'], ($config['allow_post_links']) ? $poll['enable_urls'] : false, $poll['enable_smilies'], $poll['img_status'], false, false, $config['allow_post_links'], false, 'poll'); +		$poll['poll_options'] = explode("\n", trim($poll['poll_option_text'])); +		$poll['poll_options_size'] = sizeof($poll['poll_options']); -		$bbcode_bitfield = base64_encode(base64_decode($bbcode_bitfield) | base64_decode($this->bbcode_bitfield)); -		$this->message = $tmp_message; +		foreach ($poll['poll_options'] as &$poll_option) +		{ +			$this->message = $poll_option; +			$poll_option = $this->parse($poll['enable_bbcode'], ($config['allow_post_links']) ? $poll['enable_urls'] : false, $poll['enable_smilies'], $poll['img_status'], false, false, $config['allow_post_links'], false, 'poll'); +		} +		unset($poll_option); +		$poll['poll_option_text'] = implode("\n", $poll['poll_options']);  		// Parse Poll Title -		$tmp_message = $this->message;  		$this->message = $poll['poll_title']; -		$this->bbcode_bitfield = $bbcode_bitfield; - -		$poll['poll_options'] = explode("\n", trim($poll['poll_option_text'])); -		$poll['poll_options_size'] = sizeof($poll['poll_options']); -  		if (!$poll['poll_title'] && $poll['poll_options_size'])  		{  			$this->warn_msg[] = $user->lang['NO_POLL_TITLE']; @@ -1748,10 +1891,6 @@ class parse_message extends bbcode_firstpass  			}  		} -		$this->bbcode_bitfield = base64_encode(base64_decode($bbcode_bitfield) | base64_decode($this->bbcode_bitfield)); -		$this->message = $tmp_message; -		unset($tmp_message); -  		if (sizeof($poll['poll_options']) == 1)  		{  			$this->warn_msg[] = $user->lang['TOO_FEW_POLL_OPTIONS']; @@ -1766,6 +1905,65 @@ class parse_message extends bbcode_firstpass  		}  		$poll['poll_max_options'] = ($poll['poll_max_options'] < 1) ? 1 : (($poll['poll_max_options'] > $config['max_poll_options']) ? $config['max_poll_options'] : $poll['poll_max_options']); + +		$this->message = $tmp_message; +	} + +	/** +	* Remove nested quotes at given depth in current parsed message +	* +	* @param  integer $max_depth Depth limit +	* @return null +	*/ +	public function remove_nested_quotes($max_depth) +	{ +		global $phpbb_container; + +		if (preg_match('#^<[rt][ >]#', $this->message)) +		{ +			$this->message = $phpbb_container->get('text_formatter.utils')->remove_bbcode( +				$this->message, +				'quote', +				$max_depth +			); + +			return; +		} + +		// Capture all [quote] and [/quote] tags +		preg_match_all('(\\[/?quote(?:="(.*?)")?:' . $this->bbcode_uid . '\\])', $this->message, $matches, PREG_OFFSET_CAPTURE); + +		// Iterate over the quote tags to mark the ranges that must be removed +		$depth = 0; +		$ranges = array(); +		$start_pos = 0; +		foreach ($matches[0] as $match) +		{ +			if ($match[0][1] === '/') +			{ +				--$depth; +				if ($depth == $max_depth) +				{ +					$end_pos = $match[1] + strlen($match[0]); +					$length = $end_pos - $start_pos; +					$ranges[] = array($start_pos, $length); +				} +			} +			else +			{ +				++$depth; +				if ($depth == $max_depth + 1) +				{ +					$start_pos = $match[1]; +				} +			} +		} + +		foreach (array_reverse($ranges) as $range) +		{ +			list($start_pos, $length) = $range; +			$this->message = substr_replace($this->message, '', $start_pos, $length); +		}  	}  	/** @@ -1779,4 +1977,36 @@ class parse_message extends bbcode_firstpass  	{  		$this->plupload = $plupload;  	} + +	/** +	* Function to perform custom bbcode validation by extensions +	* can be used in bbcode_init() to assign regexp replacement +	* Example: 'regexp' => array('#\[b\](.*?)\[/b\]#uise' => "\$this->validate_bbcode_by_extension('\$1')") +	* +	* Accepts variable number of parameters +	* +	* @return mixed Validation result +	*/ +	public function validate_bbcode_by_extension() +	{ +		global $phpbb_dispatcher; + +		$return = false; +		$params_array = func_get_args(); + +		/** +		* Event to validate bbcode with the custom validating methods +		* provided by extensions +		* +		* @event core.validate_bbcode_by_extension +		* @var array	params_array	Array with the function parameters +		* @var mixed	return			Validation result to return +		* +		* @since 3.1.5-RC1 +		*/ +		$vars = array('params_array', 'return'); +		extract($phpbb_dispatcher->trigger_event('core.validate_bbcode_by_extension', compact($vars))); + +		return $return; +	}  } | 
