From 637a920c28797cebc6ee53b494c39d102d39ae74 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 19 Jul 2016 00:55:59 +0200 Subject: [PATCH] Fixed handling of source bbcodes --- .../system/bbcode/HtmlBBCodeParser.class.php | 268 ++++++++++++------ 1 file changed, 175 insertions(+), 93 deletions(-) diff --git a/wcfsetup/install/files/lib/system/bbcode/HtmlBBCodeParser.class.php b/wcfsetup/install/files/lib/system/bbcode/HtmlBBCodeParser.class.php index c5b1ba57cc..f9a582daea 100644 --- a/wcfsetup/install/files/lib/system/bbcode/HtmlBBCodeParser.class.php +++ b/wcfsetup/install/files/lib/system/bbcode/HtmlBBCodeParser.class.php @@ -46,11 +46,170 @@ class HtmlBBCodeParser extends BBCodeParser { // tags that will eventually be removed within the marker processor $this->buildXMLStructure(); + $this->handleSourceBBCodes(); + $this->buildParsedString(); return $this->parsedText; } + /** + * @inheritDoc + */ + public function buildXMLStructure() { + // stack for open tags + $openTagStack = $openTagDataStack = []; + $newTagArray = []; + $newTextArray = []; + + $i = -1; + foreach ($this->tagArray as $i => $tag) { + if ($tag['closing']) { + // closing tag + if (in_array($tag['name'], $openTagStack) && $this->isAllowed($openTagStack, $tag['name'], true)) { + // close unclosed tags + while (($previousTag = end($openTagStack)) != $tag['name']) { + $nextIndex = count($newTagArray); + + // mark as invalid and do not flag as opened tag + $newTag = $this->buildTag('[/'.end($openTagStack).']'); + $newTag['invalid'] = true; + + $newTagArray[$nextIndex] = $newTag; + if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = ''; + $newTextArray[$nextIndex] .= $this->textArray[$i]; + $this->textArray[$i] = ''; + array_pop($openTagStack); + array_pop($openTagDataStack); + } + + $nextIndex = count($newTagArray); + $newTagArray[$nextIndex] = $tag; + array_pop($openTagStack); + array_pop($openTagDataStack); + if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = ''; + $newTextArray[$nextIndex] .= $this->textArray[$i]; + } + else { + // no such tag open + // handle as plain text + $this->textArray[$i] .= $tag['source']; + $last = count($newTagArray); + if (!isset($newTextArray[$last])) $newTextArray[$last] = ''; + $newTextArray[$last] .= $this->textArray[$i]; + } + } + else { + // opening tag + if ($this->isAllowed($openTagStack, $tag['name']) && $this->isValidTag($tag)) { + $openTagStack[] = $tag['name']; + $openTagDataStack[] = $tag; + $nextIndex = count($newTagArray); + $newTagArray[$nextIndex] = $tag; + if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = ''; + $newTextArray[$nextIndex] .= $this->textArray[$i]; + } + else { + // tag not allowed + $this->textArray[$i] .= $tag['source']; + $last = count($newTagArray); + if (!isset($newTextArray[$last])) $newTextArray[$last] = ''; + $newTextArray[$last] .= $this->textArray[$i]; + } + } + } + + $last = count($newTagArray); + if (!isset($newTextArray[$last])) $newTextArray[$last] = ''; + $newTextArray[$last] .= $this->textArray[$i + 1]; + + // close unclosed open tags + while (end($openTagStack)) { + $nextIndex = count($newTagArray); + + // mark as invalid + $newTag = $this->buildTag('[/'.end($openTagStack).']'); + $newTag['invalid'] = true; + + $newTagArray[$nextIndex] = $newTag; + if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = ''; + array_pop($openTagStack); + array_pop($openTagDataStack); + } + + $this->tagArray = $newTagArray; + $this->textArray = $newTextArray; + } + + /** + * Flags bbcodes inside code bbcodes for reversal, turning them back + * into their source state (= textual representation). + */ + protected function handleSourceBBCodes() { + $sourceBBCodes = $this->getSourceBBCodes(); + + $inCode = ''; + $openTagStack = []; + + for ($i = 0, $length = count($this->tagArray); $i < $length; $i++) { + $tag = $this->tagArray[$i]; + + if (!empty($tag['invalid'])) { + continue; + } + + $name = $tag['name']; + + if ($tag['closing']) { + if ($inCode) { + // matches opening code tag + if ($inCode === $name) { + $inCode = ''; + array_pop($openTagStack); + } + else { + // unrelated tag, flag as invalid + $this->tagArray[$i]['inCode'] = true; + } + + continue; + } + + array_pop($openTagStack); + } + else { + if ($inCode) { + // inside code block, flag as invalid + $this->tagArray[$i]['inCode'] = true; + continue; + } + + // starts a new code block + if (in_array($name, $sourceBBCodes)) { + // look ahead to see if there is a valid closing tag + $hasClosingTag = false; + for ($j = $i + 1; $j < $length; $j++) { + if ($this->tagArray[$j]['name'] === $name && empty($this->tagArray[$j]['invalid'])) { + $hasClosingTag = true; + break; + } + } + + if ($hasClosingTag) { + $inCode = $name; + } + else { + // no closing tag, flag as invalid to avoid the + // entire content afterwards being treated as code + $this->tagArray[$i]['inCode'] = true; + } + } + + $openTagStack[] = $name; + } + } + } + /** * @inheritDoc */ @@ -74,6 +233,16 @@ class HtmlBBCodeParser extends BBCodeParser { $buffer .= $this->textArray[$i]; if ($tag['closing']) { + if (!empty($tag['invalid'])) { + // drop invalid closing tag + continue; + } + else if (!empty($tag['inCode'])) { + // revert bbcodes inside code + $buffer .= $tag['source']; + continue; + } + // get buffered opening tag $openingTag = end($bufferedTagStack); @@ -127,6 +296,12 @@ class HtmlBBCodeParser extends BBCodeParser { } } else { + if (!empty($tag['inCode'])) { + // revert bbcodes inside code + $buffer .= $tag['source']; + continue; + } + // opening tag if ($this->needBuffering($tag)) { // start buffering @@ -143,94 +318,6 @@ class HtmlBBCodeParser extends BBCodeParser { if (isset($this->textArray[$i + 1])) $this->parsedText .= $this->textArray[$i + 1]; } - /** - * @inheritDoc - */ - public function buildXMLStructure() { - // stack for open tags - $openTagStack = $openTagDataStack = []; - $newTagArray = []; - $newTextArray = []; - - $i = -1; - foreach ($this->tagArray as $i => $tag) { - if ($tag['closing']) { - // closing tag - if (in_array($tag['name'], $openTagStack) && $this->isAllowed($openTagStack, $tag['name'], true)) { - // close unclosed tags - while (($previousTag = end($openTagStack)) != $tag['name']) { - $nextIndex = count($newTagArray); - - // mark as invalid and do not flag as opened tag - $newTag = $this->buildTag('[/'.end($openTagStack).']'); - $newTag['invalid'] = true; - - $newTagArray[$nextIndex] = $newTag; - if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = ''; - $newTextArray[$nextIndex] .= $this->textArray[$i]; - $this->textArray[$i] = ''; - array_pop($openTagStack); - array_pop($openTagDataStack); - } - - $nextIndex = count($newTagArray); - $newTagArray[$nextIndex] = $tag; - array_pop($openTagStack); - array_pop($openTagDataStack); - if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = ''; - $newTextArray[$nextIndex] .= $this->textArray[$i]; - } - else { - // no such tag open - // handle as plain text - $this->textArray[$i] .= $tag['source']; - $last = count($newTagArray); - if (!isset($newTextArray[$last])) $newTextArray[$last] = ''; - $newTextArray[$last] .= $this->textArray[$i]; - } - } - else { - // opening tag - if ($this->isAllowed($openTagStack, $tag['name']) && $this->isValidTag($tag)) { - $openTagStack[] = $tag['name']; - $openTagDataStack[] = $tag; - $nextIndex = count($newTagArray); - $newTagArray[$nextIndex] = $tag; - if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = ''; - $newTextArray[$nextIndex] .= $this->textArray[$i]; - } - else { - // tag not allowed - $this->textArray[$i] .= $tag['source']; - $last = count($newTagArray); - if (!isset($newTextArray[$last])) $newTextArray[$last] = ''; - $newTextArray[$last] .= $this->textArray[$i]; - } - } - } - - $last = count($newTagArray); - if (!isset($newTextArray[$last])) $newTextArray[$last] = ''; - $newTextArray[$last] .= $this->textArray[$i + 1]; - - // close unclosed open tags - while (end($openTagStack)) { - $nextIndex = count($newTagArray); - - // mark as invalid - $newTag = $this->buildTag('[/'.end($openTagStack).']'); - $newTag['invalid'] = true; - - $newTagArray[$nextIndex] = $newTag; - if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = ''; - array_pop($openTagStack); - array_pop($openTagDataStack); - } - - $this->tagArray = $newTagArray; - $this->textArray = $newTextArray; - } - /** * Builds the bbcode output. * @@ -376,11 +463,6 @@ class HtmlBBCodeParser extends BBCodeParser { throw new SystemException("Tag mismatch, expected '".$name."', got '".$data['name']."'."); } - if (!empty($data['invalid'])) { - // drop invalid closing tags - return ''; - } - return ''; } -- 2.20.1