Changeset View
Changeset View
Standalone View
Standalone View
src/applications/files/storage/PhabricatorFile.php
| Show First 20 Lines • Show All 445 Lines • ▼ Show 20 Lines | if (!$engine_identifier || strlen($engine_identifier) > 32) { | ||||
| "identifier '{$engine_identifier}': it must be nonempty ". | "identifier '{$engine_identifier}': it must be nonempty ". | ||||
| "and no longer than 32 characters."); | "and no longer than 32 characters."); | ||||
| } | } | ||||
| return array($engine_identifier, $data_handle); | return array($engine_identifier, $data_handle); | ||||
| } | } | ||||
| /** | |||||
| * Download a remote resource over HTTP and save the response body as a file. | |||||
| * | |||||
| * This method respects `security.outbound-blacklist`, and protects against | |||||
| * HTTP redirection (by manually following "Location" headers and verifying | |||||
| * each destination). It does not protect against DNS rebinding. See | |||||
| * discussion in T6755. | |||||
| */ | |||||
| public static function newFromFileDownload($uri, array $params = array()) { | public static function newFromFileDownload($uri, array $params = array()) { | ||||
| $timeout = 5; | |||||
| $redirects = array(); | |||||
| $current = $uri; | |||||
| while (true) { | |||||
| try { | |||||
| if (count($redirects) > 10) { | |||||
| throw new Exception( | |||||
| pht('Too many redirects trying to fetch remote URI.')); | |||||
| } | |||||
| PhabricatorEnv::requireValidRemoteURIForFetch( | PhabricatorEnv::requireValidRemoteURIForFetch( | ||||
| $uri, | $current, | ||||
| array( | array( | ||||
| 'http', | 'http', | ||||
| 'https', | 'https', | ||||
| )); | )); | ||||
| $timeout = 5; | list($status, $body, $headers) = id(new HTTPSFuture($current)) | ||||
| list($file_data) = id(new HTTPSFuture($uri)) | ->setFollowLocation(false) | ||||
| ->setTimeout($timeout) | ->setTimeout($timeout) | ||||
| ->resolvex(); | ->resolve(); | ||||
| if ($status->isRedirect()) { | |||||
| // This is an HTTP 3XX status, so look for a "Location" header. | |||||
| $location = null; | |||||
| foreach ($headers as $header) { | |||||
| list($name, $value) = $header; | |||||
| if (phutil_utf8_strtolower($name) == 'location') { | |||||
| $location = $value; | |||||
| break; | |||||
| } | |||||
| } | |||||
| // HTTP 3XX status with no "Location" header, just treat this like | |||||
| // a normal HTTP error. | |||||
| if ($location === null) { | |||||
| throw $status; | |||||
| } | |||||
| if (isset($redirects[$location])) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Encountered loop while following redirects.')); | |||||
| } | |||||
| $redirects[$location] = $location; | |||||
| $current = $location; | |||||
| // We'll fall off the bottom and go try this URI now. | |||||
| } else if ($status->isError()) { | |||||
| // This is something other than an HTTP 2XX or HTTP 3XX status, so | |||||
| // just bail out. | |||||
| throw $status; | |||||
| } else { | |||||
| // This is HTTP 2XX, so use the the response body to save the | |||||
| // file data. | |||||
| $params = $params + array( | $params = $params + array( | ||||
| 'name' => basename($uri), | 'name' => basename($uri), | ||||
| ); | ); | ||||
| return self::newFromFileData($file_data, $params); | return self::newFromFileData($body, $params); | ||||
| } | |||||
| } catch (Exception $ex) { | |||||
| if ($redirects) { | |||||
| throw new PhutilProxyException( | |||||
| pht( | |||||
| 'Failed to fetch remote URI "%s" after following %s redirect(s) '. | |||||
| '(%s): %s', | |||||
| $uri, | |||||
| new PhutilNumber(count($redirects)), | |||||
| implode(' > ', array_keys($redirects)), | |||||
| $ex->getMessage()), | |||||
| $ex); | |||||
| } else { | |||||
| throw $ex; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| public static function normalizeFileName($file_name) { | public static function normalizeFileName($file_name) { | ||||
| $pattern = "@[\\x00-\\x19#%&+!~'\$\"\/=\\\\?<> ]+@"; | $pattern = "@[\\x00-\\x19#%&+!~'\$\"\/=\\\\?<> ]+@"; | ||||
| $file_name = preg_replace($pattern, '_', $file_name); | $file_name = preg_replace($pattern, '_', $file_name); | ||||
| $file_name = preg_replace('@_+@', '_', $file_name); | $file_name = preg_replace('@_+@', '_', $file_name); | ||||
| $file_name = trim($file_name, '_'); | $file_name = trim($file_name, '_'); | ||||
| ▲ Show 20 Lines • Show All 822 Lines • Show Last 20 Lines | |||||