diff --git a/core/modules/image/src/Controller/ImageStyleDownloadController.php b/core/modules/image/src/Controller/ImageStyleDownloadController.php index fb6d96a..afeb20d 100644 --- a/core/modules/image/src/Controller/ImageStyleDownloadController.php +++ b/core/modules/image/src/Controller/ImageStyleDownloadController.php @@ -4,6 +4,7 @@ use Drupal\Component\Utility\Crypt; use Drupal\Core\Image\ImageFactory; +use Drupal\Core\Image\ImageInterface; use Drupal\Core\Lock\LockBackendInterface; use Drupal\image\ImageStyleInterface; use Drupal\system\FileDownloadController; @@ -89,7 +90,7 @@ public function deliver(Request $request, $scheme, ImageStyleInterface $image_st $target = $request->query->get('file'); $image_uri = $scheme . '://' . $target; - $this->validateRequest($request, $scheme, $image_style, $target, $image_uri); + $this->validateRequest($request, $image_style, $scheme, $target); $derivative_uri = $image_style->buildUri($image_uri); $headers = array(); @@ -117,27 +118,35 @@ public function deliver(Request $request, $scheme, ImageStyleInterface $image_st return new Response($this->t('Error generating image, missing source file.'), 404); } - $success = $this->generate($image_style, $image_uri, $derivative_uri); - - if ($success) { - return $this->send($scheme, $derivative_uri, $headers); + try { + $image = $this->generate($image_style, $image_uri, $derivative_uri); + return $this->send($scheme, $image, $headers); } - else { + catch (\RuntimeException $e) { $this->logger->notice('Unable to generate the derived image located at %path.', array('%path' => $derivative_uri)); return new Response($this->t('Error generating image.'), 500); } } /** - * @param $image_uri + * Validate that a source image exists, checking for double extensions. + * + * If the image style converted the extension, it has been added to the + * original file, resulting in filenames like image.png.jpeg. So to find + * the actual source image, we remove the extension and check if that + * image exists. + * + * @param string $image_uri + * The URI to the source image. + * * @return string + * The original $image_uri, or the source with the original extension. + * + * @throws \Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException + * Thrown when no valid source image is found. */ protected function validateSource($image_uri) { if (!file_exists($image_uri)) { - // If the image style converted the extension, it has been added to the - // original file, resulting in filenames like image.png.jpeg. So to find - // the actual source image, we remove the extension and check if that - // image exists. $path_info = pathinfo($image_uri); $converted_image_uri = $path_info['dirname'] . DIRECTORY_SEPARATOR . $path_info['filename']; if (!file_exists($converted_image_uri)) { @@ -151,13 +160,19 @@ protected function validateSource($image_uri) { } /** - * @param $scheme - * @param $derivative_uri - * @param $headers + * Return a response of the derived image. + * + * @param string $scheme + * The URI scheme of $derivative_uri. + * @param \Drupal\core\Image\ImageInterface $image + * The URI of the derived image. + * @param array $headers + * (optional) An array of headers to return in the response. + * * @return \Symfony\Component\HttpFoundation\BinaryFileResponse + * A response with the derived image. */ - protected function send($scheme, $derivative_uri, $headers) { - $image = $this->imageFactory->get($derivative_uri); + protected function send($scheme, ImageInterface $image, $headers = array()) { $uri = $image->getSource(); $headers += array( 'Content-Type' => $image->getMimeType(), @@ -171,10 +186,17 @@ protected function send($scheme, $derivative_uri, $headers) { } /** + * Generate an image derivative. + * * @param \Drupal\image\ImageStyleInterface $image_style - * @param $image_uri - * @param $derivative_uri - * @return bool + * The image style to use for the derivative. + * @param string $image_uri + * The URI of the original image. + * @param string $derivative_uri + * The URI of the derived image. + * + * @return \Drupal\core\Image\ImageInterface + * The generated image derivative. */ protected function generate(ImageStyleInterface $image_style, $image_uri, $derivative_uri) { // Don't start generating the image if the derivative already exists or if @@ -192,34 +214,47 @@ protected function generate(ImageStyleInterface $image_style, $image_uri, $deriv // Try to generate the image, unless another thread just did it while we // were acquiring the lock. $success = file_exists($derivative_uri) || $image_style->createDerivative($image_uri, $derivative_uri); - if (!empty($lock_acquired)) { $this->lock->release($lock_name); } - return $success; + if (!$success) { + throw new \RuntimeException(sprintf('%s was unable to be generated', $derivative_uri)); + } + + return $this->imageFactory->get($derivative_uri); } /** + * Validate an incoming derivative request. + * + * Check that the style is defined, the scheme is valid, and the image + * derivative token is valid. Sites which require image derivatives to be + * generated without a token can set the + * 'image.settings:allow_insecure_derivatives' configuration to TRUE to + * bypass the latter check, but this will increase the site's vulnerability + * to denial-of-service attacks. To prevent this variable from leaving the + * site vulnerable to the most serious attacks, a token is always required + * when a derivative of a style is requested. + * The $target variable for a derivative of a style has + * styles//... as structure, so we check if the $target variable + * starts with styles/. + * * @param \Symfony\Component\HttpFoundation\Request $request - * @param $scheme + * The incoming derivative request. * @param \Drupal\image\ImageStyleInterface $image_style - * @param $target - * @param $image_uri + * The image style to use for the derivative. + * @param string $scheme + * The URI scheme of $target. + * @param string $target + * The path for the generated derivative. + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + * Thrown when the image style, the scheme, or the path token is invalid. */ - protected function validateRequest(Request $request, $scheme, ImageStyleInterface $image_style, $target, $image_uri) { - // Check that the style is defined, the scheme is valid, and the image - // derivative token is valid. Sites which require image derivatives to be - // generated without a token can set the - // 'image.settings:allow_insecure_derivatives' configuration to TRUE to - // bypass the latter check, but this will increase the site's vulnerability - // to denial-of-service attacks. To prevent this variable from leaving the - // site vulnerable to the most serious attacks, a token is always required - // when a derivative of a style is requested. - // The $target variable for a derivative of a style has - // styles//... as structure, so we check if the $target variable - // starts with styles/. + protected function validateRequest(Request $request, ImageStyleInterface $image_style, $scheme, $target) { $valid = !empty($image_style) && file_stream_wrapper_valid_scheme($scheme); + $image_uri = $scheme . '://' . $target; if (!$this->config('image.settings') ->get('allow_insecure_derivatives') || strpos(ltrim($target, '\/'), 'styles/') === 0 ) {