diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index ae2b199b2..ef6c13cc9 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -26,6 +26,10 @@ abstract class FileDownloader implements DownloaderInterface { protected $io; private $bytesMax; + private $firstCall; + private $url; + private $fileUrl; + private $fileName; /** * Constructor. @@ -50,6 +54,10 @@ abstract class FileDownloader implements DownloaderInterface */ public function download(PackageInterface $package, $path) { + $this->firstCall = true; + $this->url = $package->getSourceUrl(); + $this->fileUrl = $package->getDistUrl(); + // init the progress bar $this->bytesMax = 0; @@ -66,6 +74,7 @@ abstract class FileDownloader implements DownloaderInterface } $fileName = rtrim($path.'/'.md5(time().rand()).'.'.pathinfo($url, PATHINFO_EXTENSION), '.'); + $this->fileName = $fileName; $this->io->write(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); @@ -78,35 +87,8 @@ abstract class FileDownloader implements DownloaderInterface } } - // Handle system proxy - $params = array('http' => array()); - - if (isset($_SERVER['HTTP_PROXY'])) { - // http(s):// is not supported in proxy - $proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $_SERVER['HTTP_PROXY']); - - if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) { - throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); - } - - $params['http'] = array( - 'proxy' => $proxy, - 'request_fulluri' => true, - ); - } - - if ($this->io->hasAuthorization($package->getSourceUrl())) { - $auth = $this->io->getAuthorization($package->getSourceUrl()); - $authStr = base64_encode($auth['username'] . ':' . $auth['password']); - $params['http'] = array_merge($params['http'], array('header' => "Authorization: Basic $authStr\r\n")); - } - - $ctx = stream_context_create($params); - stream_context_set_params($ctx, array("notification" => array($this, 'callbackGet'))); - - $this->io->overwrite(" Downloading: connection...", false); - @copy($url, $fileName, $ctx); - $this->io->overwrite(" Downloading"); + $this->copy($this->url, $this->fileName, $this->fileUrl); + $this->io->write(''); if (!file_exists($fileName)) { throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the' @@ -170,12 +152,39 @@ abstract class FileDownloader implements DownloaderInterface protected function callbackGet($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax) { switch ($notificationCode) { - case STREAM_NOTIFY_AUTH_REQUIRED: - throw new \LogicException("Authorization is required"); - break; + case STREAM_NOTIFY_AUTH_REQUIRED: + case STREAM_NOTIFY_FAILURE: + // for private repository returning 404 error when the authorization is incorrect + $auth = $this->io->getAuthorization($this->url); + $ps = $this->firstCall && 404 === $messageCode + && null === $this->io->getLastUsername() + && null === $auth['username']; + + if (404 === $messageCode && !$this->firstCall) { + throw new \RuntimeException("The '" . $this->fileUrl . "' URL not found"); + } + + $this->firstCall = false; + + // get authorization informations + if (401 === $messageCode || $ps) { + if (!$this->io->isInteractive()) { + $mess = "The '" . $this->fileUrl . "' URL not found"; + + if (401 === $code || $ps) { + $mess = "The '" . $this->fileUrl . "' URL required the authorization.\nYou must be used the interactive console"; + } + + throw new \RuntimeException($mess); + } - case STREAM_NOTIFY_FAILURE: - throw new \LogicException("File not found"); + $this->io->overwrite(' Authorization required:'); + $username = $this->io->ask(' Username: '); + $password = $this->io->askAndHideAnswer(' Password: '); + $this->io->setAuthorization($this->url, $username, $password); + + $this->copy($this->url, $this->fileName, $this->fileUrl); + } break; case STREAM_NOTIFY_FILE_SIZE_IS: @@ -203,6 +212,39 @@ abstract class FileDownloader implements DownloaderInterface } } + protected function copy($url, $fileName, $fileUrl) + { + // Handle system proxy + $params = array('http' => array()); + + if (isset($_SERVER['HTTP_PROXY'])) { + // http(s):// is not supported in proxy + $proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $_SERVER['HTTP_PROXY']); + + if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) { + throw new \RuntimeException('You must enable the openssl extension to use a proxy over https'); + } + + $params['http'] = array( + 'proxy' => $proxy, + 'request_fulluri' => true, + ); + } + + if ($this->io->hasAuthorization($url)) { + $auth = $this->io->getAuthorization($url); + $authStr = base64_encode($auth['username'] . ':' . $auth['password']); + $params['http'] = array_merge($params['http'], array('header' => "Authorization: Basic $authStr\r\n")); + } + + $ctx = stream_context_create($params); + stream_context_set_params($ctx, array("notification" => array($this, 'callbackGet'))); + + $this->io->overwrite(" Downloading: connection...", false); + @copy($fileUrl, $fileName, $ctx); + $this->io->overwrite(" Downloading", false); + } + /** * Extract file to directory * diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 6addf26e7..5a25fc1be 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -136,7 +136,7 @@ abstract class VcsDriver throw new \RuntimeException($mess); } - $this->io->write("Authorization for " . $this->contentUrl . ":"); + $this->io->write(array('', "Authorization for " . $this->contentUrl . ":")); $username = $this->io->ask(' Username: '); $password = $this->io->askAndHideAnswer(' Password: '); $this->io->setAuthorization($this->url, $username, $password);