Detect and handle corrupt images
authorAlexander Ebert <ebert@woltlab.com>
Tue, 30 Jul 2024 11:21:34 +0000 (13:21 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Tue, 30 Jul 2024 11:21:34 +0000 (13:21 +0200)
wcfsetup/install/files/lib/system/file/processor/FileProcessor.class.php
wcfsetup/install/files/lib/system/file/processor/exception/DamagedImage.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/worker/FileRebuildDataWorker.class.php

index d38534d8b9800e2a4b01556c2675c4dfe619880b..5b6459d5f98f0cd0ea22945d1d00a4059179fea0 100644 (file)
@@ -10,6 +10,8 @@ use wcf\data\file\thumbnail\FileThumbnailList;
 use wcf\data\object\type\ObjectType;
 use wcf\data\object\type\ObjectTypeCache;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\exception\SystemException;
+use wcf\system\file\processor\exception\DamagedImage;
 use wcf\system\image\adapter\ImageAdapter;
 use wcf\system\image\ImageHandler;
 use wcf\system\SingletonFactory;
@@ -73,7 +75,7 @@ final class FileProcessor extends SingletonFactory
             $allowedFileExtensions = \implode(
                 ',',
                 \array_map(
-                    static fn (string $fileExtension) => ".{$fileExtension}",
+                    static fn(string $fileExtension) => ".{$fileExtension}",
                     $allowedFileExtensions
                 )
             );
@@ -134,7 +136,12 @@ final class FileProcessor extends SingletonFactory
         }
 
         $imageAdapter = ImageHandler::getInstance()->getAdapter();
-        $imageAdapter->loadFile($file->getPathname());
+
+        try {
+            $imageAdapter->loadFile($file->getPathname());
+        } catch (SystemException) {
+            throw new DamagedImage($file->fileID);
+        }
 
         $filename = FileUtil::getTemporaryFilename(extension: 'webp');
         $imageAdapter->saveImageAs($imageAdapter->getImage(), $filename, 'webp', 80);
@@ -205,7 +212,12 @@ final class FileProcessor extends SingletonFactory
 
             if ($imageAdapter === null) {
                 $imageAdapter = ImageHandler::getInstance()->getAdapter();
-                $imageAdapter->loadFile($file->getPathname());
+
+                try {
+                    $imageAdapter->loadFile($file->getPathname());
+                } catch (SystemException) {
+                    throw new DamagedImage($file->fileID);
+                }
             }
 
             \assert($imageAdapter instanceof ImageAdapter);
diff --git a/wcfsetup/install/files/lib/system/file/processor/exception/DamagedImage.class.php b/wcfsetup/install/files/lib/system/file/processor/exception/DamagedImage.class.php
new file mode 100644 (file)
index 0000000..5b69754
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+namespace wcf\system\file\processor\exception;
+
+/**
+ * @author Alexander Ebert
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.1
+ */
+final class DamagedImage extends \Exception
+{
+    public function __construct(public readonly int $fileID)
+    {
+        parent::__construct(
+            \sprintf(
+                "The file '%d' is a damaged image.",
+                $this->fileID,
+            ),
+        );
+    }
+}
index 7a15b88f8f58dff9fc39a62cd866990f78e201dd..aa03b53b40bb0c8af1eb3ab8db22752b45d38869 100644 (file)
@@ -2,9 +2,13 @@
 
 namespace wcf\system\worker;
 
+use wcf\data\file\FileEditor;
 use wcf\data\file\FileList;
+use wcf\system\file\processor\exception\DamagedImage;
 use wcf\system\file\processor\FileProcessor;
 
+use function wcf\functions\exception\logThrowable;
+
 /**
  * Worker implementation for updating files.
  *
@@ -41,9 +45,20 @@ final class FileRebuildDataWorker extends AbstractLinearRebuildDataWorker
     {
         parent::execute();
 
+        $damagedFileIDs = [];
         foreach ($this->objectList as $file) {
-            FileProcessor::getInstance()->generateWebpVariant($file);
-            FileProcessor::getInstance()->generateThumbnails($file);
+            try {
+                FileProcessor::getInstance()->generateWebpVariant($file);
+                FileProcessor::getInstance()->generateThumbnails($file);
+            } catch (DamagedImage $e) {
+                logThrowable($e);
+
+                $damagedFileIDs[] = $e->fileID;
+            }
+        }
+
+        if ($damagedFileIDs !== []) {
+            FileEditor::deleteAll($damagedFileIDs);
         }
     }
 }