Avoid invalid HTML caused by nested link elements
authorAlexander Ebert <ebert@woltlab.com>
Tue, 4 Apr 2017 11:05:00 +0000 (13:05 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Tue, 4 Apr 2017 11:05:00 +0000 (13:05 +0200)
wcfsetup/install/files/lib/system/bbcode/AttachmentBBCode.class.php
wcfsetup/install/files/lib/system/bbcode/HtmlBBCodeParser.class.php
wcfsetup/install/files/lib/util/DOMUtil.class.php

index 8709fc7e0e858cf29e2163eb7b21fc2bdf0623e1..b1abcfd01db6f3ae4fe82d4cd99486232860983f 100644 (file)
@@ -38,6 +38,17 @@ class AttachmentBBCode extends AbstractBBCode {
                        $attachmentID = $openingTag['attributes'][0];
                }
                
+               $hasParentLink = false;
+               if (!empty($closingTag['__parents'])) {
+                       /** @var \DOMElement $parent */
+                       foreach ($closingTag['__parents'] as $parent) {
+                               if ($parent->nodeName === 'a') {
+                                       $hasParentLink = true;
+                                       break;
+                               }
+                       }
+               }
+               
                // get embedded object
                $attachment = MessageEmbeddedObjectManager::getInstance()->getObject('com.woltlab.wcf.attachment', $attachmentID);
                if ($attachment === null) {
@@ -92,7 +103,7 @@ class AttachmentBBCode extends AbstractBBCode {
                                        $title = StringUtil::encodeHTML($attachment->filename);
                                        
                                        $result = '<img src="' . $source . '" alt="">';
-                                       if ($attachment->width > ATTACHMENT_THUMBNAIL_WIDTH || $attachment->height > ATTACHMENT_THUMBNAIL_HEIGHT) {
+                                       if (!$hasParentLink && ($attachment->width > ATTACHMENT_THUMBNAIL_WIDTH || $attachment->height > ATTACHMENT_THUMBNAIL_HEIGHT)) {
                                                $result = '<a href="' . $source . '" title="' . $title . '" class="embeddedAttachmentLink jsImageViewer' . ($class ? ' '.$class : '') . '">' . $result . '</a>';
                                        }
                                        else {
@@ -120,7 +131,7 @@ class AttachmentBBCode extends AbstractBBCode {
                                        }
                                        
                                        $result = '<img src="'.StringUtil::encodeHTML(LinkHandler::getInstance()->getLink('Attachment', $linkParameters)).'"'.($imageClasses ? ' class="'.$imageClasses.'"' : '').' style="width: '.($attachment->hasThumbnail() ? $attachment->thumbnailWidth : $attachment->width).'px; height: '.($attachment->hasThumbnail() ? $attachment->thumbnailHeight : $attachment->height).'px;" alt="">';
-                                       if ($attachment->hasThumbnail() && $attachment->canDownload()) {
+                                       if (!$hasParentLink && $attachment->hasThumbnail() && $attachment->canDownload()) {
                                                $result = '<a href="'.StringUtil::encodeHTML(LinkHandler::getInstance()->getLink('Attachment', ['object' => $attachment])).'" title="'.StringUtil::encodeHTML($attachment->filename).'" class="embeddedAttachmentLink jsImageViewer' . ($class ? ' '.$class : '') . '">'.$result.'</a>';
                                        }
                                }
index 50a4e3224436aa26ecf62b79cebf141772fb0c14..79ccffd55a49ae397db80fc8b38eead90e179449 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace wcf\system\bbcode;
 use wcf\system\exception\SystemException;
+use wcf\util\DOMUtil;
 use wcf\util\JSON;
 use wcf\util\StringUtil;
 
@@ -287,7 +288,7 @@ class HtmlBBCodeParser extends BBCodeParser {
                        }
                        
                        $openingTag = ['attributes' => $attributes, 'name' => $name];
-                       $closingTag = ['name' => $name];
+                       $closingTag = ['name' => $name, '__parents' => DOMUtil::getReadonlyParentTree($element)];
                        
                        if ($bbcode->getProcessor()) {
                                /** @var IBBCode $processor */
index f1ad00aa521073a925b805e04a22a2999a555d81..172f625bcb88751c543cf7babc5eec4dbf43e081 100644 (file)
@@ -171,6 +171,27 @@ final class DOMUtil {
                return $reverseOrder ? array_reverse($parents) : $parents;
        }
        
+       /**
+        * Returns a cloned parent tree that is virtually readonly. In fact it can be
+        * modified, but all changes are non permanent and do not affect the source
+        * document at all.
+        * 
+        * @param       \DOMNode        $node           node
+        * @return      \DOMElement[]   list of parent elements
+        */
+       public static function getReadonlyParentTree(\DOMNode $node) {
+               $tree = [];
+               /** @var \DOMElement $parent */
+               foreach (self::getParents($node) as $parent) {
+                       // do not include <body>, <html> and the document itself
+                       if ($parent->nodeName === 'body') break;
+                       
+                       $tree[] = $parent->cloneNode(false);
+               }
+               
+               return $tree;
+       }
+       
        /**
         * Determines the relative position of two nodes to each other.
         *