Differential D19195 Diff 45974 src/applications/files/controller/PhabricatorFileImageProxyController.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/files/controller/PhabricatorFileImageProxyController.php
| <?php | <?php | ||||
| final class PhabricatorFileImageProxyController | final class PhabricatorFileImageProxyController | ||||
| extends PhabricatorFileController { | extends PhabricatorFileController { | ||||
| public function shouldAllowPublic() { | public function shouldAllowPublic() { | ||||
| return true; | return true; | ||||
| } | } | ||||
| public function handleRequest(AphrontRequest $request) { | public function handleRequest(AphrontRequest $request) { | ||||
| $show_prototypes = PhabricatorEnv::getEnvConfig( | |||||
| 'phabricator.show-prototypes'); | |||||
| if (!$show_prototypes) { | |||||
| throw new Exception( | |||||
| pht('Show prototypes is disabled. | |||||
| Set `phabricator.show-prototypes` to `true` to use the image proxy')); | |||||
| } | |||||
| $viewer = $request->getViewer(); | $viewer = $request->getViewer(); | ||||
| $img_uri = $request->getStr('uri'); | $img_uri = $request->getStr('uri'); | ||||
| // Validate the URI before doing anything | // Validate the URI before doing anything | ||||
| PhabricatorEnv::requireValidRemoteURIForLink($img_uri); | PhabricatorEnv::requireValidRemoteURIForLink($img_uri); | ||||
| $uri = new PhutilURI($img_uri); | $uri = new PhutilURI($img_uri); | ||||
| $proto = $uri->getProtocol(); | $proto = $uri->getProtocol(); | ||||
| if (!in_array($proto, array('http', 'https'))) { | |||||
| $allowed_protocols = array( | |||||
| 'http', | |||||
| 'https', | |||||
| ); | |||||
| if (!in_array($proto, $allowed_protocols)) { | |||||
| throw new Exception( | throw new Exception( | ||||
| pht('The provided image URI must be either http or https')); | pht( | ||||
| 'The provided image URI must use one of these protocols: %s.', | |||||
| implode(', ', $allowed_protocols))); | |||||
| } | } | ||||
| // Check if we already have the specified image URI downloaded | // Check if we already have the specified image URI downloaded | ||||
| $cached_request = id(new PhabricatorFileExternalRequest())->loadOneWhere( | $cached_request = id(new PhabricatorFileExternalRequest())->loadOneWhere( | ||||
| 'uriIndex = %s', | 'uriIndex = %s', | ||||
| PhabricatorHash::digestForIndex($img_uri)); | PhabricatorHash::digestForIndex($img_uri)); | ||||
| if ($cached_request) { | if ($cached_request) { | ||||
| return $this->getExternalResponse($cached_request); | return $this->getExternalResponse($cached_request); | ||||
| } | } | ||||
| $ttl = PhabricatorTime::getNow() + phutil_units('7 days in seconds'); | $ttl = PhabricatorTime::getNow() + phutil_units('7 days in seconds'); | ||||
| $external_request = id(new PhabricatorFileExternalRequest()) | $external_request = id(new PhabricatorFileExternalRequest()) | ||||
| ->setURI($img_uri) | ->setURI($img_uri) | ||||
| ->setTTL($ttl); | ->setTTL($ttl); | ||||
| // Cache missed, so we'll need to validate and download the image. | |||||
| $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | ||||
| $save_request = false; | $save_request = false; | ||||
| // Cache missed so we'll need to validate and download the image | |||||
| try { | try { | ||||
| // Rate limit outbound fetches to make this mechanism less useful for | // Rate limit outbound fetches to make this mechanism less useful for | ||||
| // scanning networks and ports. | // scanning networks and ports. | ||||
| PhabricatorSystemActionEngine::willTakeAction( | PhabricatorSystemActionEngine::willTakeAction( | ||||
| array($viewer->getPHID()), | array($viewer->getPHID()), | ||||
| new PhabricatorFilesOutboundRequestAction(), | new PhabricatorFilesOutboundRequestAction(), | ||||
| 1); | 1); | ||||
| $file = PhabricatorFile::newFromFileDownload( | $file = PhabricatorFile::newFromFileDownload( | ||||
| $uri, | $uri, | ||||
| array( | array( | ||||
| 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, | 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, | ||||
| 'canCDN' => true, | 'canCDN' => true, | ||||
| )); | )); | ||||
| if (!$file->isViewableImage()) { | if (!$file->isViewableImage()) { | ||||
| $mime_type = $file->getMimeType(); | $mime_type = $file->getMimeType(); | ||||
| $engine = new PhabricatorDestructionEngine(); | $engine = new PhabricatorDestructionEngine(); | ||||
| $engine->destroyObject($file); | $engine->destroyObject($file); | ||||
| $file = null; | $file = null; | ||||
| throw new Exception( | throw new Exception( | ||||
| pht( | pht( | ||||
| 'The URI "%s" does not correspond to a valid image file, got '. | 'The URI "%s" does not correspond to a valid image file (got '. | ||||
| 'a file with MIME type "%s". You must specify the URI of a '. | 'a file with MIME type "%s"). You must specify the URI of a '. | ||||
| 'valid image file.', | 'valid image file.', | ||||
| $uri, | $uri, | ||||
| $mime_type)); | $mime_type)); | ||||
| } else { | |||||
| $file->save(); | |||||
| } | } | ||||
| $file->save(); | |||||
| $external_request | $external_request | ||||
| ->setIsSuccessful(1) | ->setIsSuccessful(1) | ||||
| ->setFilePHID($file->getPHID()); | ->setFilePHID($file->getPHID()); | ||||
| $save_request = true; | $save_request = true; | ||||
| } catch (HTTPFutureHTTPResponseStatus $status) { | } catch (HTTPFutureHTTPResponseStatus $status) { | ||||
| $external_request | $external_request | ||||
| ->setIsSuccessful(0) | ->setIsSuccessful(0) | ||||
| ->setResponseMessage($status->getMessage()) | ->setResponseMessage($status->getMessage()); | ||||
| ->save(); | |||||
| $save_request = true; | $save_request = true; | ||||
| } catch (Exception $ex) { | } catch (Exception $ex) { | ||||
| // Not actually saving the request in this case | // Not actually saving the request in this case | ||||
| $external_request->setResponseMessage($ex->getMessage()); | $external_request->setResponseMessage($ex->getMessage()); | ||||
| } | } | ||||
| if ($save_request) { | if ($save_request) { | ||||
| ▲ Show 20 Lines • Show All 53 Lines • Show Last 20 Lines | |||||