Skip to content

Commit cff4727

Browse files
committed
fix(previews): Don't crash on animated WEBP images
Fixes #30029 and #37263 libgd handles animated WEBP images poorly and generates a meaningless error message as a result. We were returning a 500 error for these preview requests (web) and a fatal error at the command-line (occ). Now we bypass libgd if the we detect an animated WEBP image (and simply don't generate the preview). No more 500 error. Should fix occ too. Signed-off-by: Josh Richards <[email protected]>
1 parent 32e8605 commit cff4727

File tree

1 file changed

+49
-2
lines changed

1 file changed

+49
-2
lines changed

lib/private/legacy/OC_Image.php

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -748,9 +748,56 @@ public function loadFromFile($imagePath = false) {
748748
if (!$this->checkImageSize($imagePath)) {
749749
return false;
750750
}
751-
$this->resource = @imagecreatefromwebp($imagePath);
751+
752+
// Check for animated header before generating preview since libgd does not handle them well
753+
// Adapted from here: https://stackoverflow.com/a/68491679/4085517 (stripped to only to check for animations + added additional error checking)
754+
// Header format details here: https://developers.google.com/speed/webp/docs/riff_container
755+
756+
// Load up the header data, if any
757+
$fp = fopen($imagePath, 'rb');
758+
if (!$fp) {
759+
return false;
760+
}
761+
$data = fread($fp, 90);
762+
if (!$data) {
763+
return false;
764+
}
765+
fclose($fp);
766+
unset($fp);
767+
768+
$headerFormat = 'A4Riff/' . // get n string
769+
'I1Filesize/' . // get integer (file size but not actual size)
770+
'A4Webp/' . // get n string
771+
'A4Vp/' . // get n string
772+
'A74Chunk';
773+
774+
$header = unpack($headerFormat, $data);
775+
unset($data, $headerFormat);
776+
if (!$header) {
777+
return false;
778+
}
779+
780+
// Check if we're really dealing with a valid WEBP header rather than just one suffixed ".webp"
781+
if (!isset($header['Riff']) || strtoupper($header['Riff']) !== 'RIFF') {
782+
return false;
783+
}
784+
if (!isset($header['Webp']) || strtoupper($header['Webp']) !== 'WEBP') {
785+
return false;
786+
}
787+
if (!isset($header['Vp']) || strpos(strtoupper($header['Vp']), 'VP8') === false) {
788+
return false;
789+
}
790+
791+
// Check for animation indicators
792+
if (strpos(strtoupper($header['Chunk']), 'ANIM') !== false || strpos(strtoupper($header['Chunk']), 'ANMF') !== false) {
793+
// Animated so don't let it reach libgd
794+
$this->logger->debug('OC_Image->loadFromFile, animated WEBP images not supported: ' . $imagePath, ['app' => 'core']);
795+
} else {
796+
// We're safe so give it to libgd
797+
$this->resource = @imagecreatefromwebp($imagePath);
798+
}
752799
} else {
753-
$this->logger->debug('OC_Image->loadFromFile, webp images not supported: ' . $imagePath, ['app' => 'core']);
800+
$this->logger->debug('OC_Image->loadFromFile, WEBP images not supported: ' . $imagePath, ['app' => 'core']);
754801
}
755802
break;
756803
/*

0 commit comments

Comments
 (0)