Browse Source

Improve handling of non-standard ports for GitLab and GitHub installs, fixes #8173

Jordi Boggiano 5 years ago
parent
commit
0261ce8092

+ 2 - 2
src/Composer/Downloader/FileDownloader.php

@@ -125,7 +125,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
         $fileName = $this->getFileName($package, $path);
 
         $processedUrl = $this->processUrl($package, $url);
-        $hostname = parse_url($processedUrl, PHP_URL_HOST);
+        $origin = RemoteFilesystem::getOrigin($processedUrl);
 
         $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $processedUrl);
         if ($this->eventDispatcher) {
@@ -150,7 +150,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
                 $retries = 3;
                 while ($retries--) {
                     try {
-                        $rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getTransportOptions());
+                        $rfs->copy($origin, $processedUrl, $fileName, $this->outputProgress, $package->getTransportOptions());
                         break;
                     } catch (TransportException $e) {
                         // if we got an http response with a proper code, then requesting again will probably not help, abort

+ 6 - 6
src/Composer/Repository/ComposerRepository.php

@@ -206,8 +206,8 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
         if ($this->searchUrl && $mode === self::SEARCH_FULLTEXT) {
             $url = str_replace(array('%query%', '%type%'), array($query, $type), $this->searchUrl);
 
-            $hostname = parse_url($url, PHP_URL_HOST) ?: $url;
-            $json = $this->rfs->getContents($hostname, $url, false);
+            $origin = RemoteFilesystem::getOrigin($url);
+            $json = $this->rfs->getContents($origin, $url, false);
             $search = JsonFile::parseJson($json, $url);
 
             if (empty($search['results'])) {
@@ -681,10 +681,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
                     $this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
                 }
 
-                $hostname = parse_url($filename, PHP_URL_HOST) ?: $filename;
+                $origin = RemoteFilesystem::getOrigin($filename);
                 $rfs = $preFileDownloadEvent->getRemoteFilesystem();
 
-                $json = $rfs->getContents($hostname, $filename, false);
+                $json = $rfs->getContents($origin, $filename, false);
                 if ($sha256 && $sha256 !== hash('sha256', $json)) {
                     // undo downgrade before trying again if http seems to be hijacked or modifying content somehow
                     if ($this->allowSslDowngrade) {
@@ -760,10 +760,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
                     $this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
                 }
 
-                $hostname = parse_url($filename, PHP_URL_HOST) ?: $filename;
+                $origin = RemoteFilesystem::getOrigin($filename);
                 $rfs = $preFileDownloadEvent->getRemoteFilesystem();
                 $options = array('http' => array('header' => array('If-Modified-Since: '.$lastModifiedTime)));
-                $json = $rfs->getContents($hostname, $filename, false, $options);
+                $json = $rfs->getContents($origin, $filename, false, $options);
                 if ($json === '' && $rfs->findStatusCode($rfs->getLastHeaders()) === 304) {
                     return true;
                 }

+ 4 - 0
src/Composer/Repository/Vcs/GitHubDriver.php

@@ -304,6 +304,10 @@ class GitHubDriver extends VcsDriver
      */
     protected function generateSshUrl()
     {
+        if (false !== strpos($this->originUrl, ':')) {
+            return 'ssh://git@' . $this->originUrl . '/'.$this->owner.'/'.$this->repository.'.git';
+        }
+
         return 'git@' . $this->originUrl . ':'.$this->owner.'/'.$this->repository.'.git';
     }
 

+ 14 - 14
src/Composer/Repository/Vcs/GitLabDriver.php

@@ -67,9 +67,9 @@ class GitLabDriver extends VcsDriver
     private $isPrivate = true;
 
     /**
-     * @var int port number
+     * @var bool true if the origin has a port number or a path component in it
      */
-    protected $portNumber;
+    private $hasNonstandardOrigin = false;
 
     const URL_REGEX = '#^(?:(?P<scheme>https?)://(?P<domain>.+?)(?::(?P<port>[0-9]+))?/|git@(?P<domain2>[^:]+):)(?P<parts>.+)/(?P<repo>[^/]+?)(?:\.git|/)?$#';
 
@@ -94,11 +94,10 @@ class GitLabDriver extends VcsDriver
             ? $match['scheme']
             : (isset($this->repoConfig['secure-http']) && $this->repoConfig['secure-http'] === false ? 'http' : 'https')
         ;
-        $this->originUrl = $this->determineOrigin($configuredDomains, $guessedDomain, $urlParts);
+        $this->originUrl = $this->determineOrigin($configuredDomains, $guessedDomain, $urlParts, $match['port']);
 
-        if (!empty($match['port']) && true === is_numeric($match['port'])) {
-            // If it is an HTTP based URL, and it has a port
-            $this->portNumber = (int) $match['port'];
+        if (false !== strpos($this->originUrl, ':') || false !== strpos($this->originUrl, '/')) {
+            $this->hasNonstandardOrigin = true;
         }
 
         $this->namespace = implode('/', $urlParts);
@@ -259,10 +258,7 @@ class GitLabDriver extends VcsDriver
      */
     public function getApiUrl()
     {
-        $domainName = $this->originUrl;
-        $portNumber = (true === is_numeric($this->portNumber)) ? sprintf(':%s', $this->portNumber) : '';
-
-        return $this->scheme.'://'.$domainName.$portNumber.'/api/v4/projects/'.$this->urlEncodeAll($this->namespace).'%2F'.$this->urlEncodeAll($this->repository);
+        return $this->scheme.'://'.$this->originUrl.'/api/v4/projects/'.$this->urlEncodeAll($this->namespace).'%2F'.$this->urlEncodeAll($this->repository);
     }
 
     /**
@@ -360,6 +356,10 @@ class GitLabDriver extends VcsDriver
      */
     protected function generateSshUrl()
     {
+        if ($this->hasNonstandardOrigin) {
+            return 'ssh://git@'.$this->originUrl.'/'.$this->namespace.'/'.$this->repository.'.git';
+        }
+
         return 'git@' . $this->originUrl . ':'.$this->namespace.'/'.$this->repository.'.git';
     }
 
@@ -458,7 +458,7 @@ class GitLabDriver extends VcsDriver
         $guessedDomain = !empty($match['domain']) ? $match['domain'] : $match['domain2'];
         $urlParts = explode('/', $match['parts']);
 
-        if (false === self::determineOrigin((array) $config->get('gitlab-domains'), $guessedDomain, $urlParts)) {
+        if (false === self::determineOrigin((array) $config->get('gitlab-domains'), $guessedDomain, $urlParts, $match['port'])) {
             return false;
         }
 
@@ -492,16 +492,16 @@ class GitLabDriver extends VcsDriver
      * @param  array       $urlParts
      * @return bool|string
      */
-    private static function determineOrigin(array $configuredDomains, $guessedDomain, array &$urlParts)
+    private static function determineOrigin(array $configuredDomains, $guessedDomain, array &$urlParts, $portNumber)
     {
-        if (in_array($guessedDomain, $configuredDomains)) {
+        if (in_array($guessedDomain, $configuredDomains) || ($portNumber && in_array($guessedDomain.':'.$portNumber, $configuredDomains))) {
             return $guessedDomain;
         }
 
         while (null !== ($part = array_shift($urlParts))) {
             $guessedDomain .= '/' . $part;
 
-            if (in_array($guessedDomain, $configuredDomains)) {
+            if (in_array($guessedDomain, $configuredDomains) || ($portNumber && in_array(preg_replace('{/}', ':'.$portNumber.'/', $guessedDomain, 1), $configuredDomains))) {
                 return $guessedDomain;
             }
         }

+ 10 - 1
src/Composer/Util/GitLab.php

@@ -53,7 +53,10 @@ class GitLab
      */
     public function authorizeOAuth($originUrl)
     {
-        if (!in_array($originUrl, $this->config->get('gitlab-domains'), true)) {
+        // before composer 1.9, origin URLs had no port number in them
+        $bcOriginUrl = preg_replace('{:\d+}', '', $originUrl);
+
+        if (!in_array($originUrl, $this->config->get('gitlab-domains'), true) && !in_array($bcOriginUrl, $this->config->get('gitlab-domains'), true)) {
             return false;
         }
 
@@ -73,6 +76,12 @@ class GitLab
             return true;
         }
 
+        if (isset($authTokens[$bcOriginUrl])) {
+            $this->io->setAuthentication($originUrl, $authTokens[$bcOriginUrl], 'private-token');
+
+            return true;
+        }
+
         return false;
     }
 

+ 13 - 0
src/Composer/Util/RemoteFilesystem.php

@@ -1110,4 +1110,17 @@ class RemoteFilesystem
             $io->writeError('<'.$type.'>'.ucfirst($type).' from '.$url.': '.$data[$type].'</'.$type.'>');
         }
     }
+
+    public static function getOrigin($urlOrPath)
+    {
+        $hostPort = parse_url($urlOrPath, PHP_URL_HOST);
+        if (!$hostPort) {
+            return $urlOrPath;
+        }
+        if (parse_url($urlOrPath, PHP_URL_PORT)) {
+            $hostPort .= ':'.parse_url($urlOrPath, PHP_URL_PORT);
+        }
+
+        return $hostPort;
+    }
 }