diff --git a/res/composer-schema.json b/res/composer-schema.json index f636fc9f5..1d99030ff 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -132,6 +132,12 @@ } } }, + "source": { + "$ref": "#/definitions/source" + }, + "dist": { + "$ref": "#/definitions/dist" + }, "_comment": { "type": ["array", "string"], "description": "A key to store comments in" @@ -953,46 +959,52 @@ } }, "source": { - "type": "object", - "required": ["type", "url", "reference"], - "properties": { - "type": { - "type": "string" - }, - "url": { - "type": "string" - }, - "reference": { - "type": "string" - }, - "mirrors": { - "type": "array" - } - } + "$ref": "#/definitions/source" }, "dist": { - "type": "object", - "required": ["type", "url"], - "properties": { - "type": { - "type": "string" - }, - "url": { - "type": "string" - }, - "reference": { - "type": "string" - }, - "shasum": { - "type": "string" - }, - "mirrors": { - "type": "array" - } - } + "$ref": "#/definitions/dist" } }, "additionalProperties": true + }, + "source": { + "type": "object", + "required": ["type", "url", "reference"], + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "mirrors": { + "type": "array" + } + } + }, + "dist": { + "type": "object", + "required": ["type", "url"], + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + }, + "mirrors": { + "type": "array" + } + } } } } diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index 50c00ac24..6c01e947f 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -114,7 +114,9 @@ abstract class ArchiveDownloader extends FileDownloader } return $promise->then(function () use ($package, $filesystem, $fileName, $temporaryDir, $path): \React\Promise\PromiseInterface { - $filesystem->unlink($fileName); + if (file_exists($fileName)) { + $filesystem->unlink($fileName); + } /** * Returns the folder content, excluding .DS_Store diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index f04007d3b..1feec5b4d 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -179,7 +179,12 @@ class ZipDownloader extends ArchiveDownloader $zipArchive = $this->zipArchiveObject ?: new ZipArchive(); try { - if (true === ($retval = $zipArchive->open($file))) { + if (!file_exists($file) || ($filesize = filesize($file)) === false || $filesize === 0) { + $retval = -1; + } else { + $retval = $zipArchive->open($file); + } + if (true === $retval) { $extractResult = $zipArchive->extractTo($path); if (true === $extractResult) { @@ -241,6 +246,8 @@ class ZipDownloader extends ArchiveDownloader return sprintf("Zip read error (%s)", $file); case ZipArchive::ER_SEEK: return sprintf("Zip seek error (%s)", $file); + case -1: + return sprintf("'%s' is a corrupted zip archive (0 bytes), try again.", $file); default: return sprintf("'%s' is not a valid zip archive, got error code: %s", $file, $retval); } diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 30f35235f..be998f377 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -390,7 +390,7 @@ class GitBitbucketDriver extends VcsDriver } catch (TransportException $e) { $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process, $this->httpDownloader); - if (403 === $e->getCode() || (401 === $e->getCode() && strpos($e->getMessage(), 'Could not authenticate against') === 0)) { + if (in_array($e->getCode(), array(403, 404), true) || (401 === $e->getCode() && strpos($e->getMessage(), 'Could not authenticate against') === 0)) { if (!$this->io->hasAuthentication($this->originUrl) && $bitbucketUtil->authorizeOAuth($this->originUrl) ) { diff --git a/tests/Composer/Test/Downloader/ZipDownloaderTest.php b/tests/Composer/Test/Downloader/ZipDownloaderTest.php index 9c6e7c05d..2d3e319ee 100644 --- a/tests/Composer/Test/Downloader/ZipDownloaderTest.php +++ b/tests/Composer/Test/Downloader/ZipDownloaderTest.php @@ -32,6 +32,8 @@ class ZipDownloaderTest extends TestCase private $config; /** @var \Composer\Package\PackageInterface&\PHPUnit\Framework\MockObject\MockObject */ private $package; + /** @var string */ + private $filename; public function setUp(): void { @@ -41,6 +43,9 @@ class ZipDownloaderTest extends TestCase $dlConfig = $this->getMockBuilder('Composer\Config')->getMock(); $this->httpDownloader = new HttpDownloader($this->io, $dlConfig); $this->package = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock(); + + $this->filename = $this->testDir.'/composer-test.zip'; + file_put_contents($this->filename, 'zip'); } protected function tearDown(): void @@ -126,7 +131,7 @@ class ZipDownloaderTest extends TestCase ->will($this->returnValue(false)); $this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader); - $promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); + $promise = $downloader->extract($this->package, $this->filename, 'vendor/dir'); $this->wait($promise); } @@ -149,7 +154,7 @@ class ZipDownloaderTest extends TestCase ->will($this->throwException(new \ErrorException('Not a directory'))); $this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader); - $promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); + $promise = $downloader->extract($this->package, $this->filename, 'vendor/dir'); $this->wait($promise); } @@ -170,7 +175,7 @@ class ZipDownloaderTest extends TestCase ->will($this->returnValue(true)); $this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader); - $promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); + $promise = $downloader->extract($this->package, $this->filename, 'vendor/dir'); $this->wait($promise); } @@ -199,7 +204,7 @@ class ZipDownloaderTest extends TestCase ->will($this->returnValue(\React\Promise\resolve($procMock))); $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor); - $promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); + $promise = $downloader->extract($this->package, $this->filename, 'vendor/dir'); $this->wait($promise); } @@ -226,7 +231,7 @@ class ZipDownloaderTest extends TestCase ->will($this->returnValue(\React\Promise\resolve($procMock))); $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor); - $promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); + $promise = $downloader->extract($this->package, $this->filename, 'vendor/dir'); $this->wait($promise); } @@ -265,7 +270,7 @@ class ZipDownloaderTest extends TestCase $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor); $this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader); - $promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); + $promise = $downloader->extract($this->package, $this->filename, 'vendor/dir'); $this->wait($promise); } @@ -306,7 +311,7 @@ class ZipDownloaderTest extends TestCase $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor); $this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader); - $promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir'); + $promise = $downloader->extract($this->package, $this->filename, 'vendor/dir'); $this->wait($promise); }