Skip to content

Commit

Permalink
Fix bug where an unhandled exception was caused by an invalid image a…
Browse files Browse the repository at this point in the history
…ttachment (#9475)

GD functions may throw ValueError in some cases since PHP 8.0.
We wrap them in try/catch blocks.
  • Loading branch information
alecpl committed Jul 21, 2024
1 parent 7b68ad1 commit fbdfb03
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 82 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
- Fix bug where some messages could get malformed in an import from a MBOX file (#9510)
- Fix invalid line break characters in multi-line text in Sieve scripts (#9543)
- Fix bug where "with attachment" filter could fail on some fts engines (#9514)
- Fix bug where an unhandled exception was caused by an invalid image attachment (#9475)

## Release 1.6.7

Expand Down
172 changes: 90 additions & 82 deletions program/lib/Roundcube/rcube_image.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,81 +211,85 @@ public function resize($size, $filename = null, $browser_compat = false)

// use GD extension
if ($props['gd_type'] && $props['width'] > 0 && $props['height'] > 0) {
if ($props['gd_type'] == \IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) {
$image = @imagecreatefromjpeg($this->image_file);
$type = 'jpg';
} elseif ($props['gd_type'] == \IMAGETYPE_GIF && function_exists('imagecreatefromgif')) {
$image = @imagecreatefromgif($this->image_file);
$type = 'gif';
} elseif ($props['gd_type'] == \IMAGETYPE_PNG && function_exists('imagecreatefrompng')) {
$image = @imagecreatefrompng($this->image_file);
$type = 'png';
} else {
// @TODO: print error to the log?
return false;
}
try {
if ($props['gd_type'] == \IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) {
$image = imagecreatefromjpeg($this->image_file);
$type = 'jpg';
} elseif ($props['gd_type'] == \IMAGETYPE_GIF && function_exists('imagecreatefromgif')) {
$image = imagecreatefromgif($this->image_file);
$type = 'gif';
} elseif ($props['gd_type'] == \IMAGETYPE_PNG && function_exists('imagecreatefrompng')) {
$image = imagecreatefrompng($this->image_file);
$type = 'png';
} else {
// @TODO: print error to the log?
return false;
}

if ($image === false) {
return false;
}
if ($image === false) {
return false;
}

$scale = $size / max($props['width'], $props['height']);
$scale = $size / max($props['width'], $props['height']);

// Imagemagick resize is implemented in shrinking mode (see -resize argument above)
// we do the same here, if an image is smaller than specified size
// we do nothing but copy original file to destination file
if ($scale >= 1) {
$result = $this->image_file == $filename || copy($this->image_file, $filename);
} else {
$width = intval($props['width'] * $scale);
$height = intval($props['height'] * $scale);
$new_image = imagecreatetruecolor($width, $height);
// Imagemagick resize is implemented in shrinking mode (see -resize argument above)
// we do the same here, if an image is smaller than specified size
// we do nothing but copy original file to destination file
if ($scale >= 1) {
$result = $this->image_file == $filename || copy($this->image_file, $filename);
} else {
$width = intval($props['width'] * $scale);
$height = intval($props['height'] * $scale);
$new_image = imagecreatetruecolor($width, $height);

if ($new_image === false) {
return false;
}
if ($new_image === false) {
return false;
}

// Fix transparency of gif/png image
if ($props['gd_type'] != \IMAGETYPE_JPEG) {
imagealphablending($new_image, false);
imagesavealpha($new_image, true);
$transparent = imagecolorallocatealpha($new_image, 255, 255, 255, 127);
imagefilledrectangle($new_image, 0, 0, $width, $height, $transparent);
}
// Fix transparency of gif/png image
if ($props['gd_type'] != \IMAGETYPE_JPEG) {
imagealphablending($new_image, false);
imagesavealpha($new_image, true);
$transparent = imagecolorallocatealpha($new_image, 255, 255, 255, 127);
imagefilledrectangle($new_image, 0, 0, $width, $height, $transparent);
}

imagecopyresampled($new_image, $image, 0, 0, 0, 0, $width, $height, $props['width'], $props['height']);
$image = $new_image;

// fix orientation of image if EXIF data exists and specifies orientation (GD strips the EXIF data)
if ($this->image_file && $type == 'jpg' && function_exists('exif_read_data')) {
$exif = @exif_read_data($this->image_file);
if ($exif && !empty($exif['Orientation'])) {
switch ($exif['Orientation']) {
case 3:
$image = imagerotate($image, 180, 0);
break;
case 6:
$image = imagerotate($image, -90, 0);
break;
case 8:
$image = imagerotate($image, 90, 0);
break;
imagecopyresampled($new_image, $image, 0, 0, 0, 0, $width, $height, $props['width'], $props['height']);
$image = $new_image;

// fix orientation of image if EXIF data exists and specifies orientation (GD strips the EXIF data)
if ($this->image_file && $type == 'jpg' && function_exists('exif_read_data')) {
$exif = @exif_read_data($this->image_file);
if ($exif && !empty($exif['Orientation'])) {
switch ($exif['Orientation']) {
case 3:
$image = imagerotate($image, 180, 0);
break;
case 6:
$image = imagerotate($image, -90, 0);
break;
case 8:
$image = imagerotate($image, 90, 0);
break;
}
}
}
}

if ($props['gd_type'] == \IMAGETYPE_JPEG) {
$result = imagejpeg($image, $filename, 75);
} elseif ($props['gd_type'] == \IMAGETYPE_GIF) {
$result = imagegif($image, $filename);
} elseif ($props['gd_type'] == \IMAGETYPE_PNG) {
$result = imagepng($image, $filename, 6, \PNG_ALL_FILTERS);
if ($props['gd_type'] == \IMAGETYPE_JPEG) {
$result = imagejpeg($image, $filename, 75);
} elseif ($props['gd_type'] == \IMAGETYPE_GIF) {
$result = imagegif($image, $filename);
} elseif ($props['gd_type'] == \IMAGETYPE_PNG) {
$result = imagepng($image, $filename, 6, \PNG_ALL_FILTERS);
}
}
}

if ($result) {
@chmod($filename, 0600);
return $type;
if ($result) {
@chmod($filename, 0600);
return $type;
}
} catch (Throwable $e) {
rcube::raise_error($e, true, false);
}
}

Expand Down Expand Up @@ -360,25 +364,29 @@ public function convert($type, $filename = null)
}

if ($props['gd_type']) {
if ($props['gd_type'] == \IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) {
$image = imagecreatefromjpeg($this->image_file);
} elseif ($props['gd_type'] == \IMAGETYPE_GIF && function_exists('imagecreatefromgif')) {
$image = imagecreatefromgif($this->image_file);
} elseif ($props['gd_type'] == \IMAGETYPE_PNG && function_exists('imagecreatefrompng')) {
$image = imagecreatefrompng($this->image_file);
} elseif ($props['gd_type'] == \IMAGETYPE_WEBP && function_exists('imagecreatefromwebp')) {
$image = imagecreatefromwebp($this->image_file);
} else {
// @TODO: print error to the log?
return false;
}
try {
if ($props['gd_type'] == \IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) {
$image = imagecreatefromjpeg($this->image_file);
} elseif ($props['gd_type'] == \IMAGETYPE_GIF && function_exists('imagecreatefromgif')) {
$image = imagecreatefromgif($this->image_file);
} elseif ($props['gd_type'] == \IMAGETYPE_PNG && function_exists('imagecreatefrompng')) {
$image = imagecreatefrompng($this->image_file);
} elseif ($props['gd_type'] == \IMAGETYPE_WEBP && function_exists('imagecreatefromwebp')) {
$image = imagecreatefromwebp($this->image_file);
} else {
// @TODO: print error to the log?
return false;
}

if ($type == self::TYPE_JPG) {
$result = imagejpeg($image, $filename, 75);
} elseif ($type == self::TYPE_GIF) {
$result = imagegif($image, $filename);
} elseif ($type == self::TYPE_PNG) {
$result = imagepng($image, $filename, 6, \PNG_ALL_FILTERS);
if ($type == self::TYPE_JPG) {
$result = imagejpeg($image, $filename, 75);
} elseif ($type == self::TYPE_GIF) {
$result = imagegif($image, $filename);
} elseif ($type == self::TYPE_PNG) {
$result = imagepng($image, $filename, 6, \PNG_ALL_FILTERS);
}
} catch (Throwable $e) {
rcube::raise_error($e, true, false);
}

if (!empty($result)) {
Expand Down

0 comments on commit fbdfb03

Please sign in to comment.