Skip to content

Commit 948bdbf

Browse files
committed
Retroactively fix files uploaded while finfo was broken/missing
See https://www.woltlab.com/community/thread/312697/
1 parent 8597632 commit 948bdbf

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

wcfsetup/install/files/lib/system/worker/FileRebuildDataWorker.class.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
namespace wcf\system\worker;
44

5+
use wcf\data\file\File;
56
use wcf\data\file\FileEditor;
67
use wcf\data\file\FileList;
78
use wcf\system\file\processor\exception\DamagedImage;
89
use wcf\system\file\processor\FileProcessor;
10+
use wcf\util\FileUtil;
911

1012
use function wcf\functions\exception\logThrowable;
1113

@@ -45,6 +47,8 @@ public function execute()
4547
{
4648
parent::execute();
4749

50+
$this->fixMimeType();
51+
4852
$damagedFileIDs = [];
4953
foreach ($this->objectList as $file) {
5054
try {
@@ -61,4 +65,92 @@ public function execute()
6165
FileEditor::deleteAll($damagedFileIDs);
6266
}
6367
}
68+
69+
private function fixMimeType(): void
70+
{
71+
$reloadFiles = false;
72+
foreach ($this->objectList as $file) {
73+
// Workaround for images that have been detected but failed to
74+
// determine their dimensions.
75+
$isImageWithoutDimensions = $file->isImage() && $file->width === null;
76+
77+
if ($file->mimeType !== 'application/octet-stream' && !$isImageWithoutDimensions) {
78+
continue;
79+
}
80+
81+
$mimeType = FileUtil::getMimeType($file->getPathname());
82+
if ($file->mimeType === $mimeType && !$isImageWithoutDimensions) {
83+
continue;
84+
}
85+
86+
// When the mime type was incorrectly detected before, for example,
87+
// because fileinfo was not present or malfunctioning, the physical
88+
// location of the file may be incorrect.
89+
//
90+
// The location is determined by the safe file extension, anything
91+
// that ends in `.bin` is piped through PHP instead of being served
92+
// through the web server directly.
93+
$previousFileExtension = File::getSafeFileExtension($file->mimeType, $file->filename);
94+
$detectedFileExtension = File::getSafeFileExtension($mimeType, $file->filename);
95+
96+
$width = $height = null;
97+
if (\str_starts_with($mimeType, 'image/')) {
98+
$data = @\getimagesize($file->getPathname());
99+
if ($data === null) {
100+
// Treat broken images as binary files.
101+
$mimeType === 'application/octet-stream';
102+
$detectedFileExtension = 'bin';
103+
104+
if ($file->mimeType === $mimeType) {
105+
continue;
106+
}
107+
} else {
108+
$width = $data[0];
109+
$height = $data[1];
110+
}
111+
}
112+
113+
if ($previousFileExtension !== $detectedFileExtension) {
114+
$path = $this->getPath($file->fileHash, $detectedFileExtension);
115+
FileUtil::makePath($path);
116+
117+
\rename(
118+
$file->getPathname(),
119+
$path . \sprintf(
120+
'%d-%s.%s',
121+
$file->fileID,
122+
$file->fileHash,
123+
$detectedFileExtension,
124+
),
125+
);
126+
}
127+
128+
(new FileEditor($file))->update([
129+
'fileExtension' => $detectedFileExtension,
130+
'mimeType' => $mimeType,
131+
'width' => $width,
132+
'height' => $height,
133+
]);
134+
135+
$reloadFiles = true;
136+
}
137+
138+
if ($reloadFiles) {
139+
$this->objectList->readObjects();
140+
}
141+
}
142+
143+
private function getPath(string $fileHash, string $fileExtension): string
144+
{
145+
$folderA = \substr($fileHash, 0, 2);
146+
$folderB = \substr($fileHash, 2, 2);
147+
$isStaticFile = $fileExtension !== 'bin';
148+
149+
return \sprintf(
150+
'_data/%s/files/%s/%s/',
151+
$isStaticFile ? 'public' : 'private',
152+
$folderA,
153+
$folderB,
154+
);
155+
}
64156
}

0 commit comments

Comments
 (0)