diff options
author | JoshyPHP <s9e.dev@gmail.com> | 2015-05-30 01:02:12 +0200 |
---|---|---|
committer | JoshyPHP <s9e.dev@gmail.com> | 2015-06-27 02:52:26 +0200 |
commit | 817db2f13526842e04aeabe4fcd6d809dce2d0a2 (patch) | |
tree | 50bfc83d6b64cc3543a446ee22b5eb32c143d6eb | |
parent | 7d7b536874aa7eae7a6c5451d1ed8ee1dc62a1df (diff) | |
download | forums-817db2f13526842e04aeabe4fcd6d809dce2d0a2.tar forums-817db2f13526842e04aeabe4fcd6d809dce2d0a2.tar.gz forums-817db2f13526842e04aeabe4fcd6d809dce2d0a2.tar.bz2 forums-817db2f13526842e04aeabe4fcd6d809dce2d0a2.tar.xz forums-817db2f13526842e04aeabe4fcd6d809dce2d0a2.zip |
[ticket/13880] Automatically remove quotes that are nested too deep
PHPBB3-13880
-rw-r--r-- | phpBB/includes/message_parser.php | 72 | ||||
-rw-r--r-- | phpBB/posting.php | 12 | ||||
-rw-r--r-- | tests/functional/posting_test.php | 81 |
3 files changed, 142 insertions, 23 deletions
diff --git a/phpBB/includes/message_parser.php b/phpBB/includes/message_parser.php index 42ca9bf09d..6f462b7a9e 100644 --- a/phpBB/includes/message_parser.php +++ b/phpBB/includes/message_parser.php @@ -791,28 +791,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']) - { - if ($config['max_quote_depth'] == 1) - { - // Depth 1 - no nesting is allowed - $error_ary['quote_depth'] = $user->lang('QUOTE_NO_NESTING'); - } - else - { - // 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]) @@ -1277,6 +1255,12 @@ class parse_message extends bbcode_firstpass return $update_this_message ? $this->warn_msg : $return_message; } + // 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. // The maximum length check happened before any parsings. if ($mode === 'post' && utf8_clean_string($this->message) === '') @@ -1856,6 +1840,50 @@ class parse_message extends bbcode_firstpass } /** + * 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) + { + // 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); + } + } + + /** * Setter function for passing the plupload object * * @param \phpbb\plupload\plupload $plupload The plupload object diff --git a/phpBB/posting.php b/phpBB/posting.php index 651f674ef9..d994811a91 100644 --- a/phpBB/posting.php +++ b/phpBB/posting.php @@ -1579,11 +1579,21 @@ if (!sizeof($error) && $preview) } } +// Remove quotes that would become nested too deep before decoding the text +$generate_quote = ($mode == 'quote' && !$submit && !$preview && !$refresh); +if ($generate_quote && $config['max_quote_depth'] > 0) +{ + $tmp_bbcode_uid = $message_parser->bbcode_uid; + $message_parser->bbcode_uid = $post_data['bbcode_uid']; + $message_parser->remove_nested_quotes($config['max_quote_depth'] - 1); + $message_parser->bbcode_uid = $tmp_bbcode_uid; +} + // Decode text for message display $post_data['bbcode_uid'] = ($mode == 'quote' && !$preview && !$refresh && !sizeof($error)) ? $post_data['bbcode_uid'] : $message_parser->bbcode_uid; $message_parser->decode_message($post_data['bbcode_uid']); -if ($mode == 'quote' && !$submit && !$preview && !$refresh) +if ($generate_quote) { if ($config['allow_bbcode']) { diff --git a/tests/functional/posting_test.php b/tests/functional/posting_test.php index fd802eed45..33632a01e1 100644 --- a/tests/functional/posting_test.php +++ b/tests/functional/posting_test.php @@ -59,4 +59,85 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case 'Your subject contains the following unsupported characters' ); } + + /** + * @testdox max_quote_depth is applied to the text populating the posting form + */ + public function test_quote_depth_form() + { + $text = '0[quote]1[quote]2[/quote]1[/quote]0'; + $expected = array( + 0 => '[quote="admin"]0[quote]1[quote]2[/quote]1[/quote]0[/quote]', + 1 => '[quote="admin"]00[/quote]', + 2 => '[quote="admin"]0[quote]11[/quote]0[/quote]', + 3 => '[quote="admin"]0[quote]1[quote]2[/quote]1[/quote]0[/quote]', + ); + + $this->login(); + $topic = $this->create_topic(2, 'Test Topic 1', 'Test topic'); + $post = $this->create_post(2, $topic['topic_id'], 'Re: Test Topic 1', $text); + $quote_url = "posting.php?mode=quote&f=2&t={$post['topic_id']}&p={$post['post_id']}&sid={$this->sid}"; + + $this->admin_login(); + foreach ($expected as $quote_depth => $expected_text) + { + $this->set_quote_depth($quote_depth); + $crawler = self::request('GET', $quote_url); + $this->assertContains($expected_text, $crawler->filter('textarea#message')->text()); + } + } + + /** + * @testdox max_quote_depth is applied to the submitted text + */ + public function test_quote_depth_submit() + { + $text = 'depth:0[quote]depth:1[quote]depth:2[quote]depth:3[/quote][/quote][/quote]'; + $contains = array( + 0 => array('depth:0', 'depth:1', 'depth:2', 'depth:3'), + 1 => array('depth:0', 'depth:1'), + 2 => array('depth:0', 'depth:1', 'depth:2'), + 3 => array('depth:0', 'depth:1', 'depth:2', 'depth:3'), + ); + $not_contains = array( + 0 => array(), + 1 => array('depth:2', 'depth:3'), + 2 => array('depth:3'), + 3 => array(), + ); + + $this->login(); + $this->admin_login(); + $topic = $this->create_topic(2, 'Test Topic 1', 'Test topic'); + + for ($quote_depth = 0; $quote_depth <= 2; ++$quote_depth) + { + $this->set_quote_depth($quote_depth); + + $post = $this->create_post(2, $topic['topic_id'], 'Re: Test Topic 1', $text); + $url = "viewtopic.php?p={$post['post_id']}&sid={$this->sid}"; + + $crawler = self::request('GET', $url); + $text_content = $crawler->filter('#p' . $post['post_id'])->text(); + foreach ($contains[$quote_depth] as $contains_text) + { + $this->assertContains($contains_text, $text_content); + } + foreach ($not_contains[$quote_depth] as $not_contains_text) + { + $this->assertNotContains($not_contains_text, $text_content); + } + } + } + + protected function set_quote_depth($depth) + { + $crawler = self::request('GET', 'adm/index.php?sid=' . $this->sid . '&i=acp_board&mode=post'); + $form = $crawler->selectButton('Submit')->form(); + $values = $form->getValues(); + $values['config[max_quote_depth]'] = $depth; + $form->setValues($values); + $crawler = self::submit($form); + $this->assertEquals(1, $crawler->filter('.successbox')->count()); + } } |