Ignore unclosed bbcodes
authorAlexander Ebert <ebert@woltlab.com>
Mon, 18 Jul 2016 13:25:28 +0000 (15:25 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 18 Jul 2016 13:25:37 +0000 (15:25 +0200)
wcfsetup/install/files/lib/system/bbcode/HtmlBBCodeParser.class.php

index d3acbf260e127ef285d1fbfdba33bb494f9a0798..de926a6e54a1c0d9871c9f3904573e221898729a 100644 (file)
@@ -41,7 +41,11 @@ class HtmlBBCodeParser extends BBCodeParser {
                // difference to the original implementation: sourcecode bbcodes are handled too
                $this->buildTagArray(false);
                
+               // difference to the original implementation: we don't care for unclosed tags,
+               // they'll be marked as invalid and removed at the end, leaving lonely opening
+               // tags that will eventually be removed within the marker processor
                $this->buildXMLStructure();
+               
                $this->buildParsedString();
                
                return $this->parsedText;
@@ -139,6 +143,94 @@ 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.
         * 
@@ -284,6 +376,11 @@ class HtmlBBCodeParser extends BBCodeParser {
                        throw new SystemException("Tag mismatch, expected '".$name."', got '".$data['name']."'.");
                }
                
+               if (!empty($data['invalid'])) {
+                       // drop invalid closing tags
+                       return '';
+               }
+               
                return '<woltlab-metacode-marker data-uuid="' . $data['uuid'] . '" />';
        }