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(); if (!$file) { 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())); } } }