2016-09-21 01:29:50 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorFileImageProxyController
|
|
|
|
extends PhabricatorFileController {
|
|
|
|
|
|
|
|
public function shouldAllowPublic() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
$img_uri = $request->getStr('uri');
|
|
|
|
|
|
|
|
// Validate the URI before doing anything
|
|
|
|
PhabricatorEnv::requireValidRemoteURIForLink($img_uri);
|
|
|
|
$uri = new PhutilURI($img_uri);
|
|
|
|
$proto = $uri->getProtocol();
|
|
|
|
if (!in_array($proto, array('http', 'https'))) {
|
|
|
|
throw new Exception(
|
|
|
|
pht('The provided image URI must be either http or https'));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we already have the specified image URI downloaded
|
|
|
|
$cached_request = id(new PhabricatorFileExternalRequest())->loadOneWhere(
|
|
|
|
'uriIndex = %s',
|
|
|
|
PhabricatorHash::digestForIndex($img_uri));
|
|
|
|
|
|
|
|
if ($cached_request) {
|
|
|
|
return $this->getExternalResponse($cached_request);
|
|
|
|
}
|
|
|
|
|
|
|
|
$ttl = PhabricatorTime::getNow() + phutil_units('7 days in seconds');
|
|
|
|
$external_request = id(new PhabricatorFileExternalRequest())
|
|
|
|
->setURI($img_uri)
|
|
|
|
->setTTL($ttl);
|
|
|
|
|
|
|
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
|
|
|
// Cache missed so we'll need to validate and download the image
|
|
|
|
try {
|
|
|
|
// Rate limit outbound fetches to make this mechanism less useful for
|
|
|
|
// scanning networks and ports.
|
|
|
|
PhabricatorSystemActionEngine::willTakeAction(
|
|
|
|
array($viewer->getPHID()),
|
|
|
|
new PhabricatorFilesOutboundRequestAction(),
|
|
|
|
1);
|
|
|
|
|
|
|
|
$file = PhabricatorFile::newFromFileDownload(
|
|
|
|
$uri,
|
|
|
|
array(
|
|
|
|
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
|
|
|
|
'canCDN' => true,
|
|
|
|
));
|
|
|
|
if (!$file->isViewableImage()) {
|
|
|
|
$mime_type = $file->getMimeType();
|
|
|
|
$engine = new PhabricatorDestructionEngine();
|
|
|
|
$engine->destroyObject($file);
|
|
|
|
$file = null;
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'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 '.
|
|
|
|
'valid image file.',
|
|
|
|
$uri,
|
|
|
|
$mime_type));
|
|
|
|
} else {
|
|
|
|
$file->save();
|
|
|
|
}
|
|
|
|
|
|
|
|
$external_request->setIsSuccessful(true)
|
|
|
|
->setFilePHID($file->getPHID())
|
|
|
|
->save();
|
|
|
|
unset($unguarded);
|
|
|
|
return $this->getExternalResponse($external_request);
|
|
|
|
} catch (HTTPFutureHTTPResponseStatus $status) {
|
|
|
|
$external_request->setIsSuccessful(false)
|
|
|
|
->setResponseMessage($status->getMessage())
|
|
|
|
->save();
|
|
|
|
return $this->getExternalResponse($external_request);
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
// Not actually saving the request in this case
|
|
|
|
$external_request->setResponseMessage($ex->getMessage());
|
|
|
|
return $this->getExternalResponse($external_request);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getExternalResponse(
|
|
|
|
PhabricatorFileExternalRequest $request) {
|
|
|
|
if ($request->getIsSuccessful()) {
|
|
|
|
$file = id(new PhabricatorFileQuery())
|
|
|
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
|
|
|
->withPHIDs(array($request->getFilePHID()))
|
|
|
|
->executeOne();
|
2016-09-26 16:16:48 +02:00
|
|
|
if (!$file) {
|
2016-09-21 01:29:50 +02:00
|
|
|
throw new Exception(pht(
|
|
|
|
'The underlying file does not exist, but the cached request was '.
|
|
|
|
'successful. This likely means the file record was manually deleted '.
|
|
|
|
'by an administrator.'));
|
|
|
|
}
|
|
|
|
return id(new AphrontRedirectResponse())
|
|
|
|
->setIsExternal(true)
|
|
|
|
->setURI($file->getViewURI());
|
|
|
|
} else {
|
|
|
|
throw new Exception(pht(
|
|
|
|
"The request to get the external file from '%s' was unsuccessful:\n %s",
|
|
|
|
$request->getURI(),
|
|
|
|
$request->getResponseMessage()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|