예제 #1
0
 public function testTwoFactorAuthentication()
 {
     $io = $this->getIOMock();
     $io->expects($this->exactly(2))->method('hasAuthentication')->will($this->onConsecutiveCalls(true, true));
     $io->expects($this->exactly(2))->method('ask')->withConsecutive(array('Username: '******'Authentication Code: '))->will($this->onConsecutiveCalls($this->username, $this->authcode));
     $io->expects($this->once())->method('askAndHideAnswer')->with('Password: '******'', 401);
     $exception->setHeaders(array('X-GitHub-OTP: required; app'));
     $rfs = $this->getRemoteFilesystemMock();
     $rfs->expects($this->at(0))->method('getContents')->will($this->throwException($exception));
     $rfs->expects($this->at(1))->method('getContents')->with($this->equalTo($this->origin), $this->equalTo(sprintf('https://api.%s/authorizations', $this->origin)), $this->isFalse(), $this->callback(function ($array) {
         $headers = GitHubTest::recursiveFind($array, 'header');
         foreach ($headers as $string) {
             if ('X-GitHub-OTP: authcode' === $string) {
                 return true;
             }
         }
         return false;
     }))->willReturn(sprintf('{"token": "%s"}', $this->token));
     $config = $this->getConfigMock();
     $config->expects($this->atLeastOnce())->method('getAuthConfigSource')->willReturn($this->getAuthJsonMock());
     $config->expects($this->atLeastOnce())->method('getConfigSource')->willReturn($this->getConfJsonMock());
     $github = new GitHub($io, $config, null, $rfs);
     $this->assertTrue($github->authorizeOAuthInteractively($this->origin));
 }
 /**
  * @param string $url
  * @return string
  */
 private function curlGet($url)
 {
     if ($this->io->isDebug()) {
         $this->io->write("<info>[Curl]</info> Downloading {$url}");
     }
     list($code, $headers, $body) = $this->curlClient->get($url);
     if ($code != 200) {
         if (isset($headers[0])) {
             $message = $headers[0];
         } else {
             $message = "Unknown error";
         }
         $ex = new TransportException("Unable download {$url} ... {$message}");
         $ex->setHeaders($headers);
         $ex->setResponse($body);
         throw $ex;
     }
     return $body;
 }
예제 #3
0
 private function handleRedirect(array $http_response_header, array $additionalOptions, $result)
 {
     if ($locationHeader = $this->findHeaderValue($http_response_header, 'location')) {
         if (parse_url($locationHeader, PHP_URL_SCHEME)) {
             // Absolute URL; e.g. https://example.com/composer
             $targetUrl = $locationHeader;
         } elseif (parse_url($locationHeader, PHP_URL_HOST)) {
             // Scheme relative; e.g. //example.com/foo
             $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);
         } else {
             // Relative path; e.g. foo
             // This actually differs from PHP which seems to add duplicate slashes.
             $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);
         $additionalOptions['redirects'] = $this->redirects;
         return $this->get($this->originUrl, $targetUrl, $additionalOptions, $this->fileName, $this->progress);
     }
     if (!$this->retry) {
         $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);
         throw $e;
     }
     return false;
 }
예제 #4
0
 /**
  * Get file content or copy action.
  *
  * @param string  $originUrl         The origin URL
  * @param string  $fileUrl           The file URL
  * @param array   $additionalOptions context options
  * @param string  $fileName          the local filename
  * @param boolean $progress          Display the progression
  *
  * @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)
 {
     if (strpos($originUrl, '.github.com') === strlen($originUrl) - 11) {
         $originUrl = 'github.com';
     }
     $this->bytesMax = 0;
     $this->originUrl = $originUrl;
     $this->fileUrl = $fileUrl;
     $this->fileName = $fileName;
     $this->progress = $progress;
     $this->lastProgress = null;
     $this->retryAuthFailure = true;
     $this->lastHeaders = array();
     // capture username/password from URL if there is one
     if (preg_match('{^https?://(.+):(.+)@([^/]+)}i', $fileUrl, $match)) {
         $this->io->setAuthentication($originUrl, urldecode($match[1]), urldecode($match[2]));
     }
     if (isset($additionalOptions['retry-auth-failure'])) {
         $this->retryAuthFailure = (bool) $additionalOptions['retry-auth-failure'];
         unset($additionalOptions['retry-auth-failure']);
     }
     $options = $this->getOptionsForUrl($originUrl, $additionalOptions);
     if ($this->io->isDebug()) {
         $this->io->writeError((substr($fileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $fileUrl);
     }
     if (isset($options['github-token'])) {
         $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token=' . $options['github-token'];
         unset($options['github-token']);
     }
     if (isset($options['http'])) {
         $options['http']['ignore_errors'] = true;
     }
     if ($this->degradedMode && substr($fileUrl, 0, 21) === 'http://packagist.org/') {
         // access packagist using the resolved IPv4 instead of the hostname to force IPv4 protocol
         $fileUrl = 'http://' . gethostbyname('packagist.org') . substr($fileUrl, 20);
     }
     $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
     if ($this->progress) {
         $this->io->writeError("    Downloading: <comment>Connecting...</comment>", false);
     }
     $errorMessage = '';
     $errorCode = 0;
     $result = false;
     set_error_handler(function ($code, $msg) use(&$errorMessage) {
         if ($errorMessage) {
             $errorMessage .= "\n";
         }
         $errorMessage .= preg_replace('{^file_get_contents\\(.*?\\): }', '', $msg);
     });
     try {
         $result = file_get_contents($fileUrl, false, $ctx);
     } catch (\Exception $e) {
         if ($e instanceof TransportException && !empty($http_response_header[0])) {
             $e->setHeaders($http_response_header);
         }
         if ($e instanceof TransportException && $result !== false) {
             $e->setResponse($result);
         }
         $result = false;
     }
     if ($errorMessage && !ini_get('allow_url_fopen')) {
         $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('<error>' . $e->getMessage() . '</error>', '<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>'));
             return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
         }
         throw $e;
     }
     // fail 4xx and 5xx responses and capture the response
     if (!empty($http_response_header[0]) && preg_match('{^HTTP/\\S+ ([45]\\d\\d)}i', $http_response_header[0], $match)) {
         $errorCode = $match[1];
         if (!$this->retry) {
             $e = new TransportException('The "' . $this->fileUrl . '" file could not be downloaded (' . $http_response_header[0] . ')', $errorCode);
             $e->setHeaders($http_response_header);
             $e->setResponse($result);
             throw $e;
         }
         $result = false;
     }
     if ($this->progress && !$this->retry) {
         $this->io->overwriteError("    Downloading: <comment>100%</comment>");
     }
     // decode gzip
     if ($result && extension_loaded('zlib') && substr($fileUrl, 0, 4) === 'http') {
         $decode = false;
         foreach ($http_response_header as $header) {
             if (preg_match('{^content-encoding: *gzip *$}i', $header)) {
                 $decode = true;
             } elseif (preg_match('{^HTTP/}i', $header)) {
                 // In case of redirects, http_response_headers contains the headers of all responses
                 // so we reset the flag when a new response is being parsed as we are only interested in the last response
                 $decode = false;
             }
         }
         if ($decode) {
             try {
                 if (PHP_VERSION_ID >= 50400) {
                     $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));
                 }
                 if (!$result) {
                     throw new TransportException('Failed to decode zlib stream');
                 }
             } catch (\Exception $e) {
                 if ($this->degradedMode) {
                     throw $e;
                 }
                 $this->degradedMode = true;
                 $this->io->writeError(array('<error>Failed to decode response: ' . $e->getMessage() . '</error>', '<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>'));
                 return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
             }
         }
     }
     // handle copy command if download was successful
     if (false !== $result && null !== $fileName) {
         if ('' === $result) {
             throw new TransportException('"' . $this->fileUrl . '" appears broken, and returned an empty 200 response');
         }
         $errorMessage = '';
         set_error_handler(function ($code, $msg) use(&$errorMessage) {
             if ($errorMessage) {
                 $errorMessage .= "\n";
             }
             $errorMessage .= preg_replace('{^file_put_contents\\(.*?\\): }', '', $msg);
         });
         $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);
         }
     }
     if ($this->retry) {
         $this->retry = false;
         $result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
         $authHelper = new AuthHelper($this->io, $this->config);
         $authHelper->storeAuth($this->originUrl, $this->storeAuth);
         $this->storeAuth = false;
         return $result;
     }
     if (false === $result) {
         $e = new TransportException('The "' . $this->fileUrl . '" file could not be downloaded: ' . $errorMessage, $errorCode);
         if (!empty($http_response_header[0])) {
             $e->setHeaders($http_response_header);
         }
         if (!$this->degradedMode && false !== strpos($e->getMessage(), 'Operation timed out')) {
             $this->degradedMode = true;
             $this->io->writeError(array('<error>' . $e->getMessage() . '</error>', '<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>'));
             return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
         }
         throw $e;
     }
     if (!empty($http_response_header[0])) {
         $this->lastHeaders = $http_response_header;
     }
     return $result;
 }
예제 #5
0
 protected function get($originUrl, $fileUrl, $additionalOptions = array(), $fileName = null, $progress = true)
 {
     if (strpos($originUrl, '.github.com') === strlen($originUrl) - 11) {
         $originUrl = 'github.com';
     }
     $this->bytesMax = 0;
     $this->originUrl = $originUrl;
     $this->fileUrl = $fileUrl;
     $this->fileName = $fileName;
     $this->progress = $progress;
     $this->lastProgress = null;
     $this->retryAuthFailure = true;
     $this->lastHeaders = array();
     if (preg_match('{^https?://(.+):(.+)@([^/]+)}i', $fileUrl, $match)) {
         $this->io->setAuthentication($originUrl, urldecode($match[1]), urldecode($match[2]));
     }
     if (isset($additionalOptions['retry-auth-failure'])) {
         $this->retryAuthFailure = (bool) $additionalOptions['retry-auth-failure'];
         unset($additionalOptions['retry-auth-failure']);
     }
     $options = $this->getOptionsForUrl($originUrl, $additionalOptions);
     if ($this->io->isDebug()) {
         $this->io->writeError((substr($fileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $fileUrl);
     }
     if (isset($options['github-token'])) {
         $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token=' . $options['github-token'];
         unset($options['github-token']);
     }
     if (isset($options['http'])) {
         $options['http']['ignore_errors'] = true;
     }
     $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
     if ($this->progress) {
         $this->io->writeError("    Downloading: <comment>Connecting...</comment>", false);
     }
     $errorMessage = '';
     $errorCode = 0;
     $result = false;
     set_error_handler(function ($code, $msg) use(&$errorMessage) {
         if ($errorMessage) {
             $errorMessage .= "\n";
         }
         $errorMessage .= preg_replace('{^file_get_contents\\(.*?\\): }', '', $msg);
     });
     try {
         $result = file_get_contents($fileUrl, false, $ctx);
     } catch (\Exception $e) {
         if ($e instanceof TransportException && !empty($http_response_header[0])) {
             $e->setHeaders($http_response_header);
         }
         if ($e instanceof TransportException && $result !== false) {
             $e->setResponse($result);
         }
         $result = false;
     }
     if ($errorMessage && !ini_get('allow_url_fopen')) {
         $errorMessage = 'allow_url_fopen must be enabled in php.ini (' . $errorMessage . ')';
     }
     restore_error_handler();
     if (isset($e) && !$this->retry) {
         throw $e;
     }
     if (!empty($http_response_header[0]) && preg_match('{^HTTP/\\S+ ([45]\\d\\d)}i', $http_response_header[0], $match)) {
         $errorCode = $match[1];
         if (!$this->retry) {
             $e = new TransportException('The "' . $this->fileUrl . '" file could not be downloaded (' . $http_response_header[0] . ')', $errorCode);
             $e->setHeaders($http_response_header);
             $e->setResponse($result);
             throw $e;
         }
         $result = false;
     }
     if ($result && extension_loaded('zlib') && substr($fileUrl, 0, 4) === 'http') {
         $decode = false;
         foreach ($http_response_header as $header) {
             if (preg_match('{^content-encoding: *gzip *$}i', $header)) {
                 $decode = true;
                 continue;
             } elseif (preg_match('{^HTTP/}i', $header)) {
                 $decode = false;
             }
         }
         if ($decode) {
             if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
                 $result = zlib_decode($result);
             } else {
                 $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,' . base64_encode($result));
             }
             if (!$result) {
                 throw new TransportException('Failed to decode zlib stream');
             }
         }
     }
     if ($this->progress && !$this->retry) {
         $this->io->overwriteError("    Downloading: <comment>100%</comment>");
     }
     if (false !== $result && null !== $fileName) {
         if ('' === $result) {
             throw new TransportException('"' . $this->fileUrl . '" appears broken, and returned an empty 200 response');
         }
         $errorMessage = '';
         set_error_handler(function ($code, $msg) use(&$errorMessage) {
             if ($errorMessage) {
                 $errorMessage .= "\n";
             }
             $errorMessage .= preg_replace('{^file_put_contents\\(.*?\\): }', '', $msg);
         });
         $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);
         }
     }
     if ($this->retry) {
         $this->retry = false;
         $result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
         $authHelper = new AuthHelper($this->io, $this->config);
         $authHelper->storeAuth($this->originUrl, $this->storeAuth);
         $this->storeAuth = false;
         return $result;
     }
     if (false === $result) {
         $e = new TransportException('The "' . $this->fileUrl . '" file could not be downloaded: ' . $errorMessage, $errorCode);
         if (!empty($http_response_header[0])) {
             $e->setHeaders($http_response_header);
         }
         throw $e;
     }
     if (!empty($http_response_header[0])) {
         $this->lastHeaders = $http_response_header;
     }
     return $result;
 }
예제 #6
0
 /**
  * Get file content or copy action.
  *
  * @param string  $originUrl         The origin URL
  * @param string  $fileUrl           The file URL
  * @param array   $additionalOptions context options
  * @param string  $fileName          the local filename
  * @param boolean $progress          Display the progression
  *
  * @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)
 {
     $this->bytesMax = 0;
     $this->originUrl = $originUrl;
     $this->fileUrl = $fileUrl;
     $this->fileName = $fileName;
     $this->progress = $progress;
     $this->lastProgress = null;
     // capture username/password from URL if there is one
     if (preg_match('{^https?://(.+):(.+)@([^/]+)}i', $fileUrl, $match)) {
         $this->io->setAuthentication($originUrl, urldecode($match[1]), urldecode($match[2]));
     }
     $options = $this->getOptionsForUrl($originUrl, $additionalOptions);
     if ($this->io->isDebug()) {
         $this->io->write((substr($fileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $fileUrl);
     }
     if (isset($options['github-token'])) {
         $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token=' . $options['github-token'];
         unset($options['github-token']);
     }
     $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
     if ($this->progress) {
         $this->io->write("    Downloading: <comment>connection...</comment>", false);
     }
     $errorMessage = '';
     $errorCode = 0;
     $result = false;
     set_error_handler(function ($code, $msg) use(&$errorMessage) {
         if ($errorMessage) {
             $errorMessage .= "\n";
         }
         $errorMessage .= preg_replace('{^file_get_contents\\(.*?\\): }', '', $msg);
     });
     try {
         $result = file_get_contents($fileUrl, false, $ctx);
     } catch (\Exception $e) {
         if ($e instanceof TransportException && !empty($http_response_header[0])) {
             $e->setHeaders($http_response_header);
         }
     }
     if ($errorMessage && !ini_get('allow_url_fopen')) {
         $errorMessage = 'allow_url_fopen must be enabled in php.ini (' . $errorMessage . ')';
     }
     restore_error_handler();
     if (isset($e) && !$this->retry) {
         throw $e;
     }
     // fix for 5.4.0 https://bugs.php.net/bug.php?id=61336
     if (!empty($http_response_header[0]) && preg_match('{^HTTP/\\S+ ([45]\\d\\d)}i', $http_response_header[0], $match)) {
         $result = false;
         $errorCode = $match[1];
     }
     // decode gzip
     if ($result && extension_loaded('zlib') && substr($fileUrl, 0, 4) === 'http') {
         $decode = false;
         foreach ($http_response_header as $header) {
             if (preg_match('{^content-encoding: *gzip *$}i', $header)) {
                 $decode = true;
                 continue;
             } elseif (preg_match('{^HTTP/}i', $header)) {
                 $decode = false;
             }
         }
         if ($decode) {
             if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
                 $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));
             }
         }
     }
     if ($this->progress) {
         $this->io->overwrite("    Downloading: <comment>100%</comment>");
     }
     // handle copy command if download was successful
     if (false !== $result && null !== $fileName) {
         if ('' === $result) {
             throw new TransportException('"' . $this->fileUrl . '" appears broken, and returned an empty 200 response');
         }
         $errorMessage = '';
         set_error_handler(function ($code, $msg) use(&$errorMessage) {
             if ($errorMessage) {
                 $errorMessage .= "\n";
             }
             $errorMessage .= preg_replace('{^file_put_contents\\(.*?\\): }', '', $msg);
         });
         $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);
         }
     }
     if ($this->retry) {
         $this->retry = false;
         return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
     }
     if (false === $result) {
         $e = new TransportException('The "' . $this->fileUrl . '" file could not be downloaded: ' . $errorMessage, $errorCode);
         if (!empty($http_response_header[0])) {
             $e->setHeaders($http_response_header);
         }
         throw $e;
     }
     return $result;
 }
 /**
  * Searchs if the registry has a package with the same name exists with a
  * different camelcase.
  *
  * @param Pool               $pool
  * @param string             $name
  * @param TransportException $ex
  */
 protected function fallbackWathProvides(Pool $pool, $name, TransportException $ex)
 {
     $providers = array();
     if (404 === $ex->getCode() && !$this->fallbackProviders) {
         $this->fallbackProviders = true;
         $repoName = Util::convertAliasName($name);
         $results = $this->search($repoName);
         foreach ($results as $item) {
             if ($name === strtolower($item['name'])) {
                 $providers = $this->whatProvides($pool, $item['name']);
                 break;
             }
         }
     }
     $this->fallbackProviders = false;
     $this->providers[$name] = $providers;
 }