diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index f074b80f4..5d87ad67c 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -13,8 +13,8 @@ namespace Composer\Util; use Composer\Config; -use Composer\Downloader\TransportException; use Composer\IO\IOInterface; +use Composer\Downloader\TransportException; /** * @author François Pluchino @@ -45,9 +45,9 @@ class RemoteFilesystem /** * Constructor. * - * @param IOInterface $io The IO instance - * @param Config $config The config - * @param array $options The options + * @param IOInterface $io The IO instance + * @param Config $config The config + * @param array $options The options * @param bool $disableTls */ public function __construct(IOInterface $io, Config $config = null, array $options = array(), $disableTls = false) @@ -134,16 +134,15 @@ class RemoteFilesystem } /** - * @param array $headers array of returned headers like from getLastHeaders() - * @param string $name header name (case insensitive) - * + * @param array $headers array of returned headers like from getLastHeaders() + * @param string $name header name (case insensitive) * @return string|null */ public function findHeaderValue(array $headers, $name) { $value = null; foreach ($headers as $header) { - if (preg_match('{^' . $name . ':\s*(.+?)\s*$}i', $header, $match)) { + if (preg_match('{^'.$name.':\s*(.+?)\s*$}i', $header, $match)) { $value = $match[1]; } elseif (preg_match('{^HTTP/}i', $header)) { // In case of redirects, http_response_headers contains the headers of all responses @@ -156,8 +155,7 @@ class RemoteFilesystem } /** - * @param array $headers array of returned headers like from getLastHeaders() - * + * @param array $headers array of returned headers like from getLastHeaders() * @return int|null */ public function findStatusCode(array $headers) @@ -167,7 +165,7 @@ class RemoteFilesystem if (preg_match('{^HTTP/\S+ (\d+)}i', $header, $match)) { // In case of redirects, http_response_headers contains the headers of all responses // so we can not return directly and need to keep iterating - $value = (int)$match[1]; + $value = (int) $match[1]; } } @@ -185,6 +183,7 @@ class RemoteFilesystem * * @throws TransportException|\Exception * @throws TransportException When the file could not be downloaded + * * @return bool|string */ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $fileName = null, $progress = true) @@ -211,7 +210,7 @@ class RemoteFilesystem $tempAdditionalOptions = $additionalOptions; if (isset($tempAdditionalOptions['retry-auth-failure'])) { - $this->retryAuthFailure = (bool)$tempAdditionalOptions['retry-auth-failure']; + $this->retryAuthFailure = (bool) $tempAdditionalOptions['retry-auth-failure']; unset($tempAdditionalOptions['retry-auth-failure']); } @@ -228,16 +227,15 @@ class RemoteFilesystem unset($tempAdditionalOptions); $userlandFollow = isset($options['http']['follow_location']) && !$options['http']['follow_location']; - $this->io->writeError((substr($fileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $fileUrl, true, - IOInterface::DEBUG); + $this->io->writeError((substr($fileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $fileUrl, true, IOInterface::DEBUG); if (isset($options['github-token'])) { - $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token=' . $options['github-token']; + $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['github-token']; unset($options['github-token']); } if (isset($options['gitlab-token'])) { - $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token=' . $options['gitlab-token']; + $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['gitlab-token']; unset($options['gitlab-token']); } @@ -250,8 +248,7 @@ class RemoteFilesystem $fileUrl = 'http://' . gethostbyname('packagist.org') . substr($fileUrl, 20); } - $ctx = StreamContextFactory::getContext($fileUrl, $options, - array('notification' => array($this, 'callbackGet'))); + $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet'))); if ($this->progress && !$isRedirect) { $this->io->writeError(" Downloading: Connecting...", false); @@ -306,19 +303,18 @@ class RemoteFilesystem $result = false; } if ($errorMessage && !ini_get('allow_url_fopen')) { - $errorMessage = 'allow_url_fopen must be enabled in php.ini (' . $errorMessage . ')'; + $errorMessage = 'allow_url_fopen must be enabled in php.ini ('.$errorMessage.')'; } restore_error_handler(); if (isset($e) && !$this->retry) { if (!$this->degradedMode && false !== strpos($e->getMessage(), 'Operation timed out')) { $this->degradedMode = true; $this->io->writeError(array( - '' . $e->getMessage() . '', + ''.$e->getMessage().'', 'Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info', )); - return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, - $this->progress); + return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress); } throw $e; @@ -339,8 +335,7 @@ class RemoteFilesystem // fail 4xx and 5xx responses and capture the response if ($statusCode && $statusCode >= 400 && $statusCode <= 599) { if (!$this->retry) { - $e = new TransportException('The "' . $this->fileUrl . '" file could not be downloaded (' . $http_response_header[0] . ')', - $statusCode); + $e = new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.$http_response_header[0].')', $statusCode); $e->setHeaders($http_response_header); $e->setResponse($result); $e->setStatusCode($statusCode); @@ -363,7 +358,7 @@ class RemoteFilesystem $result = zlib_decode($result); } else { // work around issue with gzuncompress & co that do not work with all gzip checksums - $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,' . base64_encode($result)); + $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,'.base64_encode($result)); } if (!$result) { @@ -376,12 +371,11 @@ class RemoteFilesystem $this->degradedMode = true; $this->io->writeError(array( - 'Failed to decode response: ' . $e->getMessage() . '', + 'Failed to decode response: '.$e->getMessage().'', 'Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info', )); - return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, - $this->progress); + return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress); } } } @@ -389,7 +383,7 @@ class RemoteFilesystem // handle copy command if download was successful if (false !== $result && null !== $fileName && !$isRedirect) { if ('' === $result) { - throw new TransportException('"' . $this->fileUrl . '" appears broken, and returned an empty 200 response'); + throw new TransportException('"'.$this->fileUrl.'" appears broken, and returned an empty 200 response'); } $errorMessage = ''; @@ -399,10 +393,10 @@ class RemoteFilesystem } $errorMessage .= preg_replace('{^file_put_contents\(.*?\): }', '', $msg); }); - $result = (bool)file_put_contents($fileName, $result); + $result = (bool) file_put_contents($fileName, $result); restore_error_handler(); if (false === $result) { - throw new TransportException('The "' . $this->fileUrl . '" file could not be written to ' . $fileName . ': ' . $errorMessage); + throw new TransportException('The "'.$this->fileUrl.'" file could not be written to '.$fileName.': '.$errorMessage); } } @@ -443,8 +437,7 @@ class RemoteFilesystem if ($this->retry) { $this->retry = false; - $result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, - $this->progress); + $result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress); if ($this->storeAuth && $this->config) { $authHelper = new AuthHelper($this->io, $this->config); @@ -456,8 +449,7 @@ class RemoteFilesystem } if (false === $result) { - $e = new TransportException('The "' . $this->fileUrl . '" file could not be downloaded: ' . $errorMessage, - $errorCode); + $e = new TransportException('The "'.$this->fileUrl.'" file could not be downloaded: '.$errorMessage, $errorCode); if (!empty($http_response_header[0])) { $e->setHeaders($http_response_header); } @@ -465,12 +457,11 @@ class RemoteFilesystem if (!$this->degradedMode && false !== strpos($e->getMessage(), 'Operation timed out')) { $this->degradedMode = true; $this->io->writeError(array( - '' . $e->getMessage() . '', + ''.$e->getMessage().'', 'Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info', )); - return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, - $this->progress); + return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress); } throw $e; @@ -486,13 +477,12 @@ class RemoteFilesystem /** * Get notification action. * - * @param int $notificationCode The notification code - * @param int $severity The severity level - * @param string $message The message - * @param int $messageCode The message code - * @param int $bytesTransferred The loaded size - * @param int $bytesMax The total size - * + * @param int $notificationCode The notification code + * @param int $severity The severity level + * @param string $message The message + * @param int $messageCode The message code + * @param int $bytesTransferred The loaded size + * @param int $bytesMax The total size * @throws TransportException */ protected function callbackGet($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax) @@ -502,11 +492,10 @@ class RemoteFilesystem if (400 === $messageCode) { // This might happen if your host is secured by ssl client certificate authentication // but you do not send an appropriate certificate - throw new TransportException("The '" . $this->fileUrl . "' URL could not be accessed: " . $message, - $messageCode); + throw new TransportException("The '" . $this->fileUrl . "' URL could not be accessed: " . $message, $messageCode); } - // intentional fallthrough to the next case as the notificationCode - // isn't always consistent and we should inspect the messageCode for 401s + // intentional fallthrough to the next case as the notificationCode + // isn't always consistent and we should inspect the messageCode for 401s case STREAM_NOTIFY_AUTH_REQUIRED: if (401 === $messageCode) { @@ -555,22 +544,20 @@ class RemoteFilesystem protected function promptAuthAndRetry($httpStatus, $reason = null) { if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) { - $message = "\n" . 'Could not fetch ' . $this->fileUrl . ', please create a GitHub OAuth token ' . ($httpStatus === 404 ? 'to access private repos' : 'to go over the API rate limit'); + $message = "\n".'Could not fetch '.$this->fileUrl.', please create a GitHub OAuth token '.($httpStatus === 404 ? 'to access private repos' : 'to go over the API rate limit'); $gitHubUtil = new GitHub($this->io, $this->config, null); if (!$gitHubUtil->authorizeOAuth($this->originUrl) - && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, - $message)) + && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message)) ) { - throw new TransportException('Could not authenticate against ' . $this->originUrl, 401); + throw new TransportException('Could not authenticate against '.$this->originUrl, 401); } } elseif ($this->config && in_array($this->originUrl, $this->config->get('gitlab-domains'), true)) { - $message = "\n" . 'Could not fetch ' . $this->fileUrl . ', enter your ' . $this->originUrl . ' credentials ' . ($httpStatus === 401 ? 'to access private repos' : 'to go over the API rate limit'); + $message = "\n".'Could not fetch '.$this->fileUrl.', enter your ' . $this->originUrl . ' credentials ' .($httpStatus === 401 ? 'to access private repos' : 'to go over the API rate limit'); $gitLabUtil = new GitLab($this->io, $this->config, null); if (!$gitLabUtil->authorizeOAuth($this->originUrl) - && (!$this->io->isInteractive() || !$gitLabUtil->authorizeOAuthInteractively($this->scheme, - $this->originUrl, $message)) + && (!$this->io->isInteractive() || !$gitLabUtil->authorizeOAuthInteractively($this->scheme, $this->originUrl, $message)) ) { - throw new TransportException('Could not authenticate against ' . $this->originUrl, 401); + throw new TransportException('Could not authenticate against '.$this->originUrl, 401); } } else { // 404s are only handled for github @@ -591,12 +578,10 @@ class RemoteFilesystem } // fail if we already have auth if ($this->io->hasAuthentication($this->originUrl)) { - throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", - $httpStatus); + throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus); } - $this->io->overwriteError(' Authentication required (' . parse_url($this->fileUrl, - PHP_URL_HOST) . '):'); + $this->io->overwriteError(' Authentication required ('.parse_url($this->fileUrl, PHP_URL_HOST).'):'); $username = $this->io->ask(' Username: '); $password = $this->io->askAndHideAnswer(' Password: '); $this->io->setAuthentication($this->originUrl, $username, $password); @@ -674,11 +659,11 @@ class RemoteFilesystem $options['github-token'] = $auth['username']; } elseif ($this->config && in_array($originUrl, $this->config->get('gitlab-domains'), true)) { if ($auth['password'] === 'oauth2') { - $headers[] = 'Authorization: Bearer ' . $auth['username']; + $headers[] = 'Authorization: Bearer '.$auth['username']; } } else { $authStr = base64_encode($auth['username'] . ':' . $auth['password']); - $headers[] = 'Authorization: Basic ' . $authStr; + $headers[] = 'Authorization: Basic '.$authStr; } } @@ -700,26 +685,24 @@ class RemoteFilesystem $targetUrl = $locationHeader; } elseif (parse_url($locationHeader, PHP_URL_HOST)) { // Scheme relative; e.g. //example.com/foo - $targetUrl = $this->scheme . ':' . $locationHeader; + $targetUrl = $this->scheme.':'.$locationHeader; } elseif ('/' === $locationHeader[0]) { // Absolute path; e.g. /foo $urlHost = parse_url($this->fileUrl, PHP_URL_HOST); // Replace path using hostname as an anchor. - $targetUrl = preg_replace('{^(.+(?://|@)' . preg_quote($urlHost) . '(?::\d+)?)(?:[/\?].*)?$}', - '\1' . $locationHeader, $this->fileUrl); + $targetUrl = preg_replace('{^(.+(?://|@)'.preg_quote($urlHost).'(?::\d+)?)(?:[/\?].*)?$}', '\1'.$locationHeader, $this->fileUrl); } else { // Relative path; e.g. foo // This actually differs from PHP which seems to add duplicate slashes. - $targetUrl = preg_replace('{^(.+/)[^/?]*(?:\?.*)?$}', '\1' . $locationHeader, $this->fileUrl); + $targetUrl = preg_replace('{^(.+/)[^/?]*(?:\?.*)?$}', '\1'.$locationHeader, $this->fileUrl); } } if (!empty($targetUrl)) { $this->redirects++; - $this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, $targetUrl), true, - IOInterface::DEBUG); + $this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, $targetUrl), true, IOInterface::DEBUG); $additionalOptions['redirects'] = $this->redirects; @@ -727,7 +710,7 @@ class RemoteFilesystem } if (!$this->retry) { - $e = new TransportException('The "' . $this->fileUrl . '" file could not be downloaded, got redirect without Location (' . $http_response_header[0] . ')'); + $e = new TransportException('The "'.$this->fileUrl.'" file could not be downloaded, got redirect without Location ('.$http_response_header[0].')'); $e->setHeaders($http_response_header); $e->setResponse($result); @@ -786,14 +769,15 @@ class RemoteFilesystem /** * CN_match and SNI_server_name are only known once a URL is passed. * They will be set in the getOptionsForUrl() method which receives a URL. + * * cafile or capath can be overridden by passing in those options to constructor. */ $defaults = array( 'ssl' => array( - 'ciphers' => $ciphers, - 'verify_peer' => true, - 'verify_depth' => 7, - 'SNI_enabled' => true, + 'ciphers' => $ciphers, + 'verify_peer' => true, + 'verify_depth' => 7, + 'SNI_enabled' => true, 'capture_peer_cert' => true, ), ); @@ -847,17 +831,24 @@ class RemoteFilesystem /** * This method was adapted from Sslurp. * https://github.com/EvanDotPro/Sslurp + * * (c) Evan Coury + * * For the full copyright and license information, please see below: + * * Copyright (c) 2013, Evan Coury * All rights reserved. + * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: + * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. + * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -913,12 +904,12 @@ class RemoteFilesystem foreach ($caBundlePaths as $caBundle) { $caBundle = dirname($caBundle); - if (is_dir($caBundle) && glob($caBundle . '/*')) { + if (is_dir($caBundle) && glob($caBundle.'/*')) { return $caPath = $caBundle; } } - return $caPath = __DIR__ . '/../../../res/cacert.pem'; // Bundled with Composer, last resort + return $caPath = __DIR__.'/../../../res/cacert.pem'; // Bundled with Composer, last resort } /** @@ -934,7 +925,7 @@ class RemoteFilesystem return $files[$filename]; } - $this->io->writeError('Checking CA file ' . realpath($filename), true, IOInterface::DEBUG); + $this->io->writeError('Checking CA file '.realpath($filename), true, IOInterface::DEBUG); $contents = file_get_contents($filename); // assume the CA is valid if php is vulnerable to @@ -948,7 +939,7 @@ class RemoteFilesystem return $files[$filename] = !empty($contents); } - return $files[$filename] = (bool)openssl_x509_parse($contents); + return $files[$filename] = (bool) openssl_x509_parse($contents); } /** @@ -983,13 +974,11 @@ class RemoteFilesystem )); } - $context = StreamContextFactory::getContext($url, $options, array( - 'options' => array( - 'ssl' => array( - 'capture_peer_cert' => true, - 'verify_peer' => false, // Yes this is fucking insane! But PHP is lame. - ), - ), + $context = StreamContextFactory::getContext($url, $options, array('options' => array( + 'ssl' => array( + 'capture_peer_cert' => true, + 'verify_peer' => false, // Yes this is fucking insane! But PHP is lame. + ), ), )); // Ideally this would just use stream_socket_client() to avoid sending a @@ -1019,11 +1008,11 @@ class RemoteFilesystem private function getUrlAuthority($url) { $defaultPorts = array( - 'ftp' => 21, - 'http' => 80, - 'https' => 443, + 'ftp' => 21, + 'http' => 80, + 'https' => 443, 'ssh2.sftp' => 22, - 'ssh2.scp' => 22, + 'ssh2.scp' => 22, ); $scheme = parse_url($url, PHP_URL_SCHEME); @@ -1038,6 +1027,6 @@ class RemoteFilesystem $defaultPort = $defaultPorts[$scheme]; $port = parse_url($url, PHP_URL_PORT) ?: $defaultPort; - return parse_url($url, PHP_URL_HOST) . ':' . $port; + return parse_url($url, PHP_URL_HOST).':'.$port; } }