Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/updater.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMorris Jobke <hey@morrisjobke.de>2017-04-17 05:48:53 +0300
committerGitHub <noreply@github.com>2017-04-17 05:48:53 +0300
commit3c28f8d0777e12b78824cab13adc28d3e7bae7a8 (patch)
treed4cee111e6dae931101701e13facbd44f846dd2e
parent28e49e6fcd35671afc9f5c9704d0a4a02e62cec1 (diff)
parent069d3694e9c00e54f13bc7a5535ccbdccc6f18b0 (diff)
Merge pull request #96 from nextcloud/verify-integrity
Verify integrity of downloaded update
-rw-r--r--index.php189
-rw-r--r--lib/UpdateCommand.php41
-rw-r--r--lib/Updater.php107
-rw-r--r--tests/features/bootstrap/FeatureContext.php72
-rw-r--r--tests/features/cli.feature14
-rw-r--r--tests/features/master.feature1
-rw-r--r--tests/features/stable10.feature17
-rw-r--r--tests/features/stable11.feature12
-rw-r--r--tests/features/stable9.feature12
-rwxr-xr-xupdater.pharbin591718 -> 595635 bytes
-rw-r--r--vendor/autoload.php2
-rw-r--r--vendor/composer/ClassLoader.php46
-rw-r--r--vendor/composer/LICENSE2
-rw-r--r--vendor/composer/autoload_real.php2
-rw-r--r--vendor/composer/installed.json8
15 files changed, 413 insertions, 112 deletions
diff --git a/index.php b/index.php
index 3f14aa5..4307f00 100644
--- a/index.php
+++ b/index.php
@@ -1,6 +1,6 @@
<?php
/**
- * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
+ * @copyright Copyright (c) 2016-2017 Lukas Reschke <lukas@statuscode.ch>
* @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
@@ -217,6 +217,15 @@ class Updater {
}
/**
+ * Returns currently used release channel
+ *
+ * @return string
+ */
+ private function getCurrentReleaseChannel() {
+ return !is_null($this->getConfigOption('updater.release.channel')) ? $this->getConfigOption('updater.release.channel') : 'stable';
+ }
+
+ /**
* @return string
* @throws \Exception
*/
@@ -230,7 +239,7 @@ class Updater {
if ($version !== '' && $version !== $this->currentVersion) {
$this->updateAvailable = true;
- $releaseChannel = !is_null($this->getConfigOption('updater.release.channel')) ? $this->getConfigOption('updater.release.channel') : 'stable';
+ $releaseChannel = $this->getCurrentReleaseChannel();
$updateText = 'Update to ' . $versionString . ' available. (channel: "' . htmlentities($releaseChannel) . '")<br /><span class="light">Following file will be downloaded automatically:</span> <code class="light">' . $response['url'] . '</code>';
} else {
$updateText = 'No update available.';
@@ -239,7 +248,7 @@ class Updater {
if ($this->updateAvailable && isset($response['autoupdater']) && !($response['autoupdater'] === 1 || $response['autoupdater'] === '1')) {
$this->updateAvailable = false;
- $updateText .= '<br />The updater is disabled for this update - please update manually.' . $response['autoupdater'];
+ $updateText .= '<br />The updater is disabled for this update - please update manually.';
}
$this->silentLog('[info] end of checkForUpdate() ' . $updateText);
@@ -498,7 +507,7 @@ class Updater {
}
$this->silentLog('[info] updaterServer: ' . $updaterServer);
- $releaseChannel = !is_null($this->getConfigOption('updater.release.channel')) ? $this->getConfigOption('updater.release.channel') : 'stable';
+ $releaseChannel = $this->getCurrentReleaseChannel();
$this->silentLog('[info] releaseChannel: ' . $releaseChannel);
$this->silentLog('[info] internal version: ' . $this->getConfigOption('version'));
@@ -597,13 +606,10 @@ class Updater {
}
/**
- * Extracts the download
- *
- * @throws \Exception
+ * @return string
+ * @throws Exception
*/
- public function extractDownload() {
- $this->silentLog('[info] extractDownload()');
-
+ private function getDownloadedFilePath() {
$storageLocation = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/';
$this->silentLog('[info] storage location: ' . $storageLocation);
@@ -612,15 +618,88 @@ class Updater {
if(count($files) !== 3) {
throw new \Exception('Not exact 3 files existent in folder');
}
+ return $storageLocation . '/' . $files[2];
+ }
+
+ /**
+ * Verifies the integrity of the downloaded file
+ *
+ * @throws \Exception
+ */
+ public function verifyIntegrity() {
+ $this->silentLog('[info] verifyIntegrity()');
+
+ if($this->getCurrentReleaseChannel() === 'daily') {
+ $this->silentLog('[info] current channel is "daily" which is not signed. Skipping verification.');
+ return;
+ }
+
+ $response = $this->getUpdateServerResponse();
+ if(!isset($response['signature'])) {
+ throw new \Exception('No signature specified for defined update');
+ }
+
+ $certificate = <<<EOF
+-----BEGIN CERTIFICATE-----
+MIIEojCCA4qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwezELMAkGA1UEBhMCREUx
+GzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzEXMBUGA1UECgwOTmV4dGNsb3Vk
+IEdtYkgxNjA0BgNVBAMMLU5leHRjbG91ZCBDb2RlIFNpZ25pbmcgSW50ZXJtZWRp
+YXRlIEF1dGhvcml0eTAeFw0xNjA2MTIyMTA1MDZaFw00MTA2MDYyMTA1MDZaMGYx
+CzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxEjAQBgNV
+BAcMCVN0dXR0Z2FydDEXMBUGA1UECgwOTmV4dGNsb3VkIEdtYkgxDTALBgNVBAMM
+BGNvcmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUxcrn2DC892IX
+8+dJjZVh9YeHF65n2ha886oeAizOuHBdWBfzqt+GoUYTOjqZF93HZMcwy0P+xyCf
+Qqak5Ke9dybN06RXUuGP45k9UYBp03qzlUzCDalrkj+Jd30LqcSC1sjRTsfuhc+u
+vH1IBuBnf7SMUJUcoEffbmmpAPlEcLHxlUGlGnz0q1e8UFzjbEFj3JucMO4ys35F
+qZS4dhvCngQhRW3DaMlQLXEUL9k3kFV+BzlkPzVZEtSmk4HJujFCnZj1vMcjQBg/
+Bqq1HCmUB6tulnGcxUzt/Z/oSIgnuGyENeke077W3EyryINL7EIyD4Xp7sxLizTM
+FCFCjjH1AgMBAAGjggFDMIIBPzAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG
+QDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp
+ZmljYXRlMB0GA1UdDgQWBBQwc1H9AL8pRlW2e5SLCfPPqtqc0DCBpQYDVR0jBIGd
+MIGagBRt6m6qqTcsPIktFz79Ru7DnnjtdKF+pHwwejELMAkGA1UEBhMCREUxGzAZ
+BgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzESMBAGA1UEBwwJU3R1dHRnYXJ0MRcw
+FQYDVQQKDA5OZXh0Y2xvdWQgR21iSDEhMB8GA1UEAwwYTmV4dGNsb3VkIFJvb3Qg
+QXV0aG9yaXR5ggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH
+AwEwDQYJKoZIhvcNAQELBQADggEBADZ6+HV/+0NEH3nahTBFxO6nKyR/VWigACH0
+naV0ecTcoQwDjKDNNFr+4S1WlHdwITlnNabC7v9rZ/6QvbkrOTuO9fOR6azp1EwW
+2pixWqj0Sb9/dSIVRpSq+jpBE6JAiX44dSR7zoBxRB8DgVO2Afy0s80xEpr5JAzb
+NYuPS7M5UHdAv2dr16fDcDIvn+vk92KpNh1NTeZFjBbRVQ9DXrgkRGW34TK8uSLI
+YG6jnfJ6eJgTaO431ywWPXNg1mUMaT/+QBOgB299QVCKQU+lcZWptQt+RdsJUm46
+NY/nARy4Oi4uOe88SuWITj9KhrFmEvrUlgM8FvoXA1ldrR7KiEg=
+-----END CERTIFICATE-----
+EOF;
+
+ $validSignature = (bool)openssl_verify(
+ file_get_contents($this->getDownloadedFilePath()),
+ base64_decode($response['signature']),
+ $certificate,
+ OPENSSL_ALGO_SHA512
+ );
+
+ if($validSignature === false) {
+ throw new \Exception('Signature of update is not valid');
+ }
+
+ $this->silentLog('[info] end of verifyIntegrity()');
+ }
+
+ /**
+ * Extracts the download
+ *
+ * @throws \Exception
+ */
+ public function extractDownload() {
+ $this->silentLog('[info] extractDownload()');
+ $downloadedFilePath = $this->getDownloadedFilePath();
$zip = new \ZipArchive;
- $zipState = $zip->open($storageLocation . '/' . $files[2]);
+ $zipState = $zip->open($downloadedFilePath);
if ($zipState === true) {
- $zip->extractTo($storageLocation);
+ $zip->extractTo(dirname($downloadedFilePath));
$zip->close();
- $state = unlink($storageLocation . '/' . $files[2]);
+ $state = unlink($downloadedFilePath);
if($state === false) {
- throw new \Exception('Cant unlink '. $storageLocation . '/' . $files[2]);
+ throw new \Exception('Cant unlink '. $downloadedFilePath);
}
} else {
throw new \Exception('Cant handle ZIP file. Error code is: '.$zipState);
@@ -1104,7 +1183,7 @@ if(isset($_POST['step'])) {
}
$step = (int)$_POST['step'];
- if($step > 11 || $step < 1) {
+ if($step > 12 || $step < 1) {
throw new \Exception('Invalid step');
}
@@ -1126,21 +1205,24 @@ if(isset($_POST['step'])) {
$updater->downloadUpdate();
break;
case 6:
- $updater->extractDownload();
+ $updater->verifyIntegrity();
break;
case 7:
- $updater->replaceEntryPoints();
+ $updater->extractDownload();
break;
case 8:
- $updater->deleteOldFiles();
+ $updater->replaceEntryPoints();
break;
case 9:
- $updater->moveNewVersionInPlace();
+ $updater->deleteOldFiles();
break;
case 10:
- $updater->setMaintenanceMode(false);
+ $updater->moveNewVersionInPlace();
break;
case 11:
+ $updater->setMaintenanceMode(false);
+ break;
+ case 12:
$updater->finalize();
break;
}
@@ -1476,30 +1558,34 @@ if(strpos($updaterUrl, 'index.php') === false) {
<h2>Downloading</h2>
<div class="output hidden"></div>
</li>
- <li id="step-extract" class="step <?php if($stepNumber >= 6) { echo 'passed-step'; }?>">
+ <li id="step-verify-integrity" class="step <?php if($stepNumber >= 6) { echo 'passed-step'; }?>">
+ <h2>Verifying integrity</h2>
+ <div class="output hidden"></div>
+ </li>
+ <li id="step-extract" class="step <?php if($stepNumber >= 7) { echo 'passed-step'; }?>">
<h2>Extracting</h2>
<div class="output hidden"></div>
</li>
- <li id="step-entrypoints" class="step <?php if($stepNumber >= 7) { echo 'passed-step'; }?>">
+ <li id="step-entrypoints" class="step <?php if($stepNumber >= 8) { echo 'passed-step'; }?>">
<h2>Replace entry points</h2>
<div class="output hidden"></div>
</li>
- <li id="step-delete" class="step <?php if($stepNumber >= 8) { echo 'passed-step'; }?>">
+ <li id="step-delete" class="step <?php if($stepNumber >= 9) { echo 'passed-step'; }?>">
<h2>Delete old files</h2>
<div class="output hidden"></div>
</li>
- <li id="step-move" class="step <?php if($stepNumber >= 9) { echo 'passed-step'; }?>">
+ <li id="step-move" class="step <?php if($stepNumber >= 10) { echo 'passed-step'; }?>">
<h2>Move new files in place</h2>
<div class="output hidden"></div>
</li>
- <li id="step-maintenance-mode" class="step <?php if($stepNumber >= 10) { echo 'passed-step'; }?>">
+ <li id="step-maintenance-mode" class="step <?php if($stepNumber >= 11) { echo 'passed-step'; }?>">
<h2>Keep maintenance mode active?</h2>
<div class="output hidden">
<button id="maintenance-enable">Yes (for usage with command line tool)</button>
<button id="maintenance-disable">No (for usage of the web based updater)</button>
</div>
</li>
- <li id="step-done" class="step <?php if($stepNumber >= 11) { echo 'passed-step'; }?>">
+ <li id="step-done" class="step <?php if($stepNumber >= 12) { echo 'passed-step'; }?>">
<h2>Done</h2>
<div class="output hidden">
<a class="button" href="<?php echo str_replace('/index.php', '/../', $updaterUrl); ?>">Go back to your Nextcloud instance to finish the update</a>
@@ -1705,56 +1791,69 @@ if(strpos($updaterUrl, 'index.php') === false) {
5: function (response) {
if (response.proceed === true) {
successStep('step-download');
- currentStep('step-extract');
+ currentStep('step-verify-integrity');
performStep(6, performStepCallbacks[6]);
} else {
- errorStep('step-download', 5);
+ errorStep('step-verify-integrity', 5);
if(response.response) {
- addStepText('step-download', escapeHTML(response.response));
+ addStepText('step-verify-integrity', escapeHTML(response.response));
}
}
},
6: function (response) {
if (response.proceed === true) {
+ successStep('step-verify-integrity');
+ currentStep('step-extract');
+ performStep(7, performStepCallbacks[7]);
+ } else {
+ errorStep('step-verify-integrity', 6);
+
+ if(response.response) {
+ addStepText('step-verify-integrity', escapeHTML(response.response));
+ }
+ }
+ },
+ 7: function (response) {
+ if (response.proceed === true) {
successStep('step-extract');
currentStep('step-entrypoints');
- performStep(7, performStepCallbacks[7]);
+ performStep(8, performStepCallbacks[8]);
} else {
- errorStep('step-extract', 6);
+ errorStep('step-extract', 7);
if(response.response) {
addStepText('step-extract', escapeHTML(response.response));
}
}
},
- 7: function (response) {
+ 8: function (response) {
if (response.proceed === true) {
successStep('step-entrypoints');
currentStep('step-delete');
- performStep(8, performStepCallbacks[8]);
+ performStep(9, performStepCallbacks[9]);
} else {
- errorStep('step-entrypoints', 7);
+ errorStep('step-entrypoints', 8);
if(response.response) {
addStepText('step-entrypoints', escapeHTML(response.response));
}
}
},
- 8: function (response) {
+ 9: function (response) {
if (response.proceed === true) {
successStep('step-delete');
currentStep('step-move');
- performStep(9, performStepCallbacks[9]);
+ performStep(10, performStepCallbacks[10]);
} else {
- errorStep('step-delete', 8);
+ errorStep('step-delete', 9);
if(response.response) {
addStepText('step-delete', escapeHTML(response.response));
}
}
},
- 9: function (response) {
+ 10: function (response) {
if (response.proceed === true) {
successStep('step-move');
@@ -1764,27 +1863,27 @@ if(strpos($updaterUrl, 'index.php') === false) {
.getElementsByClassName('output')[0];
el.classList.remove('hidden');
} else {
- errorStep('step-move', 9);
+ errorStep('step-move', 10);
if(response.response) {
addStepText('step-move', escapeHTML(response.response));
}
}
},
- 10: function (response) {
+ 11: function (response) {
if (response.proceed === true) {
successStep('step-maintenance-mode');
currentStep('step-done');
- performStep(11, performStepCallbacks[11]);
+ performStep(12, performStepCallbacks[12]);
} else {
- errorStep('step-maintenance-mode', 10);
+ errorStep('step-maintenance-mode', 11);
if(response.response) {
addStepText('step-maintenance-mode', escapeHTML(response.response));
}
}
},
- 11: function (response) {
+ 12: function (response) {
if (response.proceed === true) {
successStep('step-done');
@@ -1833,11 +1932,11 @@ if(strpos($updaterUrl, 'index.php') === false) {
el.innerHTML = 'Maintenance mode will kept active.<br>Now trigger the migration via command line: <code>./occ upgrade</code><br>';
successStep('step-maintenance-mode');
currentStep('step-done');
- performStep(11, performStepCallbacks[11]);
+ performStep(12, performStepCallbacks[12]);
} else {
el.innerHTML = 'Maintenance mode will get disabled.<br>';
currentStep('step-maintenance-mode');
- performStep(10, performStepCallbacks[10]);
+ performStep(11, performStepCallbacks[11]);
}
}
diff --git a/lib/UpdateCommand.php b/lib/UpdateCommand.php
index 8bc4f74..c132f4f 100644
--- a/lib/UpdateCommand.php
+++ b/lib/UpdateCommand.php
@@ -1,6 +1,7 @@
<?php
/**
* @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de>
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
@@ -44,12 +45,13 @@ class UpdateCommand extends Command {
3 => 'Enable maintenance mode',
4 => 'Create backup',
5 => 'Downloading',
- 6 => 'Extracting',
- 7 => 'Replace entry points',
- 8 => 'Delete old files',
- 9 => 'Move new files in place',
- 10 => 'Keep maintenance mode active?',
- 11 => 'Done',
+ 6 => 'Verify integrity',
+ 7 => 'Extracting',
+ 8 => 'Replace entry points',
+ 9 => 'Delete old files',
+ 10 => 'Move new files in place',
+ 11 => 'Keep maintenance mode active?',
+ 12 => 'Done',
];
protected function configure() {
@@ -198,7 +200,7 @@ class UpdateCommand extends Command {
// print already executed steps
for($i = 1; $i <= $stepNumber; $i++) {
- if ($i === 10) {
+ if ($i === 11) {
// no need to ask for maintenance mode on CLI - skip it
continue;
}
@@ -206,10 +208,10 @@ class UpdateCommand extends Command {
}
$i = $stepNumber;
- while ($i < 11) {
+ while ($i < 12) {
$i++;
- if ($i === 10) {
+ if ($i === 11) {
// no need to ask for maintenance mode on CLI - skip it
continue;
}
@@ -266,7 +268,7 @@ class UpdateCommand extends Command {
}
$output->writeln('');
- if ($i === 11) {
+ if ($i === 12) {
$this->updater->log('[info] update of code successful.');
$output->writeln('Update of code successful.');
@@ -296,7 +298,7 @@ class UpdateCommand extends Command {
if ($input->isInteractive()) {
$helper = $this->getHelper('question');
- $question = new ConfirmationQuestion($this->checkTexts[10] . ' [y/N] ', false);
+ $question = new ConfirmationQuestion($this->checkTexts[11] . ' [y/N] ', false);
if ($helper->ask($input, $output, $question)) {
$output->writeln('Maintenance mode kept active');
@@ -338,7 +340,7 @@ class UpdateCommand extends Command {
protected function executeStep($step) {
$this->updater->log('[info] executeStep request for step "' . $step . '"');
try {
- if($step > 11 || $step < 1) {
+ if($step > 12 || $step < 1) {
throw new \Exception('Invalid step');
}
@@ -360,22 +362,25 @@ class UpdateCommand extends Command {
$this->updater->downloadUpdate();
break;
case 6:
- $this->updater->extractDownload();
+ $this->updater->verifyIntegrity();
break;
case 7:
- $this->updater->replaceEntryPoints();
+ $this->updater->extractDownload();
break;
case 8:
- $this->updater->deleteOldFiles();
+ $this->updater->replaceEntryPoints();
break;
case 9:
- $this->updater->moveNewVersionInPlace();
+ $this->updater->deleteOldFiles();
break;
case 10:
+ $this->updater->moveNewVersionInPlace();
+ break;
+ case 11:
// this is not needed in the CLI updater
//$this->updater->setMaintenanceMode(false);
break;
- case 11:
+ case 12:
$this->updater->finalize();
break;
}
@@ -415,7 +420,7 @@ class UpdateCommand extends Command {
protected function showCurrentStatus(OutputInterface $output, $stepNumber) {
$output->writeln('Steps that will be executed:');
for ($i = 1; $i < sizeof($this->checkTexts); $i++) {
- if ($i === 10) {
+ if ($i === 11) {
// no need to ask for maintenance mode on CLI - skip it
continue;
}
diff --git a/lib/Updater.php b/lib/Updater.php
index 5cfa34a..fc0e58a 100644
--- a/lib/Updater.php
+++ b/lib/Updater.php
@@ -1,6 +1,6 @@
<?php
/**
- * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
+ * @copyright Copyright (c) 2016-2017 Lukas Reschke <lukas@statuscode.ch>
* @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
@@ -102,6 +102,15 @@ class Updater {
}
/**
+ * Returns currently used release channel
+ *
+ * @return string
+ */
+ private function getCurrentReleaseChannel() {
+ return !is_null($this->getConfigOption('updater.release.channel')) ? $this->getConfigOption('updater.release.channel') : 'stable';
+ }
+
+ /**
* @return string
* @throws \Exception
*/
@@ -115,7 +124,7 @@ class Updater {
if ($version !== '' && $version !== $this->currentVersion) {
$this->updateAvailable = true;
- $releaseChannel = !is_null($this->getConfigOption('updater.release.channel')) ? $this->getConfigOption('updater.release.channel') : 'stable';
+ $releaseChannel = $this->getCurrentReleaseChannel();
$updateText = 'Update to ' . $versionString . ' available. (channel: "' . htmlentities($releaseChannel) . '")<br /><span class="light">Following file will be downloaded automatically:</span> <code class="light">' . $response['url'] . '</code>';
} else {
$updateText = 'No update available.';
@@ -124,7 +133,7 @@ class Updater {
if ($this->updateAvailable && isset($response['autoupdater']) && !($response['autoupdater'] === 1 || $response['autoupdater'] === '1')) {
$this->updateAvailable = false;
- $updateText .= '<br />The updater is disabled for this update - please update manually.' . $response['autoupdater'];
+ $updateText .= '<br />The updater is disabled for this update - please update manually.';
}
$this->silentLog('[info] end of checkForUpdate() ' . $updateText);
@@ -383,7 +392,7 @@ class Updater {
}
$this->silentLog('[info] updaterServer: ' . $updaterServer);
- $releaseChannel = !is_null($this->getConfigOption('updater.release.channel')) ? $this->getConfigOption('updater.release.channel') : 'stable';
+ $releaseChannel = $this->getCurrentReleaseChannel();
$this->silentLog('[info] releaseChannel: ' . $releaseChannel);
$this->silentLog('[info] internal version: ' . $this->getConfigOption('version'));
@@ -482,13 +491,10 @@ class Updater {
}
/**
- * Extracts the download
- *
- * @throws \Exception
+ * @return string
+ * @throws Exception
*/
- public function extractDownload() {
- $this->silentLog('[info] extractDownload()');
-
+ private function getDownloadedFilePath() {
$storageLocation = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/';
$this->silentLog('[info] storage location: ' . $storageLocation);
@@ -497,15 +503,88 @@ class Updater {
if(count($files) !== 3) {
throw new \Exception('Not exact 3 files existent in folder');
}
+ return $storageLocation . '/' . $files[2];
+ }
+
+ /**
+ * Verifies the integrity of the downloaded file
+ *
+ * @throws \Exception
+ */
+ public function verifyIntegrity() {
+ $this->silentLog('[info] verifyIntegrity()');
+
+ if($this->getCurrentReleaseChannel() === 'daily') {
+ $this->silentLog('[info] current channel is "daily" which is not signed. Skipping verification.');
+ return;
+ }
+
+ $response = $this->getUpdateServerResponse();
+ if(!isset($response['signature'])) {
+ throw new \Exception('No signature specified for defined update');
+ }
+
+ $certificate = <<<EOF
+-----BEGIN CERTIFICATE-----
+MIIEojCCA4qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwezELMAkGA1UEBhMCREUx
+GzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzEXMBUGA1UECgwOTmV4dGNsb3Vk
+IEdtYkgxNjA0BgNVBAMMLU5leHRjbG91ZCBDb2RlIFNpZ25pbmcgSW50ZXJtZWRp
+YXRlIEF1dGhvcml0eTAeFw0xNjA2MTIyMTA1MDZaFw00MTA2MDYyMTA1MDZaMGYx
+CzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxEjAQBgNV
+BAcMCVN0dXR0Z2FydDEXMBUGA1UECgwOTmV4dGNsb3VkIEdtYkgxDTALBgNVBAMM
+BGNvcmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUxcrn2DC892IX
+8+dJjZVh9YeHF65n2ha886oeAizOuHBdWBfzqt+GoUYTOjqZF93HZMcwy0P+xyCf
+Qqak5Ke9dybN06RXUuGP45k9UYBp03qzlUzCDalrkj+Jd30LqcSC1sjRTsfuhc+u
+vH1IBuBnf7SMUJUcoEffbmmpAPlEcLHxlUGlGnz0q1e8UFzjbEFj3JucMO4ys35F
+qZS4dhvCngQhRW3DaMlQLXEUL9k3kFV+BzlkPzVZEtSmk4HJujFCnZj1vMcjQBg/
+Bqq1HCmUB6tulnGcxUzt/Z/oSIgnuGyENeke077W3EyryINL7EIyD4Xp7sxLizTM
+FCFCjjH1AgMBAAGjggFDMIIBPzAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG
+QDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp
+ZmljYXRlMB0GA1UdDgQWBBQwc1H9AL8pRlW2e5SLCfPPqtqc0DCBpQYDVR0jBIGd
+MIGagBRt6m6qqTcsPIktFz79Ru7DnnjtdKF+pHwwejELMAkGA1UEBhMCREUxGzAZ
+BgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzESMBAGA1UEBwwJU3R1dHRnYXJ0MRcw
+FQYDVQQKDA5OZXh0Y2xvdWQgR21iSDEhMB8GA1UEAwwYTmV4dGNsb3VkIFJvb3Qg
+QXV0aG9yaXR5ggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH
+AwEwDQYJKoZIhvcNAQELBQADggEBADZ6+HV/+0NEH3nahTBFxO6nKyR/VWigACH0
+naV0ecTcoQwDjKDNNFr+4S1WlHdwITlnNabC7v9rZ/6QvbkrOTuO9fOR6azp1EwW
+2pixWqj0Sb9/dSIVRpSq+jpBE6JAiX44dSR7zoBxRB8DgVO2Afy0s80xEpr5JAzb
+NYuPS7M5UHdAv2dr16fDcDIvn+vk92KpNh1NTeZFjBbRVQ9DXrgkRGW34TK8uSLI
+YG6jnfJ6eJgTaO431ywWPXNg1mUMaT/+QBOgB299QVCKQU+lcZWptQt+RdsJUm46
+NY/nARy4Oi4uOe88SuWITj9KhrFmEvrUlgM8FvoXA1ldrR7KiEg=
+-----END CERTIFICATE-----
+EOF;
+
+ $validSignature = (bool)openssl_verify(
+ file_get_contents($this->getDownloadedFilePath()),
+ base64_decode($response['signature']),
+ $certificate,
+ OPENSSL_ALGO_SHA512
+ );
+
+ if($validSignature === false) {
+ throw new \Exception('Signature of update is not valid');
+ }
+
+ $this->silentLog('[info] end of verifyIntegrity()');
+ }
+
+ /**
+ * Extracts the download
+ *
+ * @throws \Exception
+ */
+ public function extractDownload() {
+ $this->silentLog('[info] extractDownload()');
+ $downloadedFilePath = $this->getDownloadedFilePath();
$zip = new \ZipArchive;
- $zipState = $zip->open($storageLocation . '/' . $files[2]);
+ $zipState = $zip->open($downloadedFilePath);
if ($zipState === true) {
- $zip->extractTo($storageLocation);
+ $zip->extractTo(dirname($downloadedFilePath));
$zip->close();
- $state = unlink($storageLocation . '/' . $files[2]);
+ $state = unlink($downloadedFilePath);
if($state === false) {
- throw new \Exception('Cant unlink '. $storageLocation . '/' . $files[2]);
+ throw new \Exception('Cant unlink '. $downloadedFilePath);
}
} else {
throw new \Exception('Cant handle ZIP file. Error code is: '.$zipState);
diff --git a/tests/features/bootstrap/FeatureContext.php b/tests/features/bootstrap/FeatureContext.php
index 0f05432..7d8ef5a 100644
--- a/tests/features/bootstrap/FeatureContext.php
+++ b/tests/features/bootstrap/FeatureContext.php
@@ -1,12 +1,11 @@
<?php
-use Behat\Behat\Context\Context;
-use Behat\Behat\Tester\Exception\PendingException;
+use Behat\Behat\Context\SnippetAcceptingContext;
/**
* Defines application features from the specific context.
*/
-class FeatureContext implements Context
+class FeatureContext implements SnippetAcceptingContext
{
protected $buildDir;
protected $serverDir;
@@ -171,6 +170,57 @@ class FeatureContext implements Context
$this->CLIReturnCode = $returnCode;
}
+ /**
+ * @param $version
+ * @return string
+ */
+ public function getSignatureForVersion($version) {
+ $signatures = [
+ '9.0.54' => 'wVgtLXquICXsmfJwRUD8tJiFzYJAIdQfjAcOzvKGDYh96NMT6MGVEMYQgAfyYvq0
+tuAcqsU87CDc1IQ14y2GmkSnwnXJrCXEJFaYqBGyXJtbzRukby5k+IVx2NTBaNjL
+XMC1irGa7tnCC/pyn9K+RDDHSTa3aQ7W0Z2MIq+TpNuASwshOGaep9IP7bmVvEsS
+CC8df8qp8xBkUA6PLxDkrHHGe1dTauuMYc0hUkzclC+fD2wFzV4ks1RpX6V4dLlZ
+4+nzlDeepIAVOnoaIaxLv4DmITD5Mg408z/CCB2YBBntFC9wIlfErr9X4JqQWVEQ
+Xpo6Rlr6bSFMHcDn3Bjn+Q==',
+ '9.0.55RC1' => 'uUGqJ6shKJCxoP9UvPclGewzN3jxX1blMyB27RsLN/jEf1Y6nnxz3kVyeXQcUuvV
+P54w4cyKBnj8+mMJV57bnIUf42B1GFIiHTILpauDcC6KqsEt/kGIUDiHtZjZAJKr
+jzuUjSAXKjTxeAQv0l9H7nQaa7Xs7LgWTV9LycQVLksYoV0MDBMOjBuxH017jgQH
+AAzqdiQguM2pv5+j6AZcP6q1YRueLePTcM2q+AlDB52LYA1Dfmj81jsP1J9WmbXJ
+IfbjOzrkifG5mJB+Q7/HOJjQYevaxjL6prSp0yym7nqSy63vH8GDxKa+gIK5Gfj+
+0mo/sSnvB5vobDDb/TO8og==',
+ '10.0.1' => 'w64N/qoNW2jiStnNXWJBhVkD0eLs75ELvbAnDY2wCyFM5TlqTeKlBnwK7zuIOzwD
+h5/k0tExXra1fBPic6qUF30Z9n1O6C60z0zKQYOaHrR4I4EN1SdHFsjbLyXxm/Ua
+F3DwREXcYMT4r7XAl/PfX0zkpIMMmCu5GEd1GlxReY8sUcVcJmMf1/7x2NYNuQgV
+UY4SEY7zFbybOcT8XfXNxod0isDkkOSj8B1TtTjlBZ6wyHSTVBtKg2fVKcTdOvxp
+IlWHCJCnDFD9Nwz5bBTUd8ssNYqFBVNT2viyR696ObpIE1f7AJHpsgY9FJ4psDDf
+D0kiMCecZa45XkBwFvg41Q==',
+ '10.0.2RC1' => 'ScJSe22yoHYtwFH8nydZoWOgxRgWq6JlthFw7/BvTvUGMejaB4hD/s5hFKE4Luvs
+GjV3/UtKpnBUpZSByQyEM/BbD+fyhbU3L+v6CQQYgjbtcRTyNmqdsFwZ+T1MXhTZ
+Be10XmB2QB6Xi0jvxjD9d3AYUFPH8Chy8rF6Wth7V7Eexyuh+4secrvuv7pqbuNt
+AmVRSYigLgIB5oVDd5BSvpeDe4ZhKN8qE1fDDTQX29iPvTn6M/FJZ3pH3ZrLRzgl
+9n97PTVFgtC2NdLYc7GD9bHxTUL1/iKvp7s9I6Tp6oxyBfOJwC4AqRTwURnG1ouZ
+EjMJzAibeG1AVDDbaU6irQ==',
+ '11.0.0beta' => 'LflrJaVMjHH1ntRlQ1mkgvQ86VTSFxvw77kcPt33tWVk6GLSMxpy+/71V3nGNHM2
+STd1czLzm+AUWdWWVluAci6XjJ7x+Tlsg5S7+ofuiwO1/m1nf5yXeHEf9q9ATqzy
+UkzEhCEbMF+9Zxd5WZZkexUZyVf/cfx8MBEsvsqqysHGgZJKmoJEwN2jCnKYIQzL
+yAvOWXjOA0xarzXxwdlTnkFEnzBvFeYAThcfBDdUBRbyCqVbpU7IBQoXZYCSAtac
+FBKNj6b48LDvE4BDnid8y/91q88WdL4QLy4wVFVPxbMIObfDeAnyLvG+p7La7V5G
+iPnnYL0Y2XIhbshkwWhS/Q==',
+ '11.0.0beta2' => 'WAzWUB8rgr/deeOnlBn084ysJe3Z8J2dSunkJNlNEtbbCBr2ALwZh6Xe41MXr2F/
+S6riPZk2uRmx5W2XZ+IUHSTVlAOKOQaflTlo3xConuKfcyojJ3PGsO8x7awtRNA7
+2GBBBTNKY9/q8eONztsBWY7O2pWMTqJKEuXiXQyAQ3IkqxQkMWoyxKU/zGWBdZad
+QYwN9zVszIoWo31nlVz7QSzUZ4iyEVnbY4gT5EVPKfflIPQblp2bN485pYamuPe5
+CYB6S4AUzuyYS2zxt7MMdoaWHXY8hxdst1AF/kGKZ5Bdct4qVw7pup77c+uMNBvT
+jFSE6+KKI+HAE132eaXY5A==',
+ ];
+
+ if(isset($signatures[$version])) {
+ return $signatures[$version];
+ }
+
+ return '';
+ }
+
/**
* @Given /there is an update to version ([0-9.]+) available/
*/
@@ -188,6 +238,7 @@ class FeatureContext implements Context
<url>https://download.nextcloud.com/server/releases/nextcloud-' . $version . '.zip</url>
<web>https://docs.nextcloud.org/server/10/admin_manual/maintenance/manual_upgrade.html</web>
<autoupdater>' . $this->autoupdater . '</autoupdater>
+ <signature>'.$this->getSignatureForVersion($version).'</signature>
</nextcloud>
';
file_put_contents($this->updateServerDir . 'index.php', $content);
@@ -195,7 +246,7 @@ class FeatureContext implements Context
}
/**
- * @Given /there is an update to prerelease version of (.*) available/
+ * @Given there is an update to prerelease version :version available
*/
public function thereIsAnUpdateToPrereleaseVersionAvailable($version)
{
@@ -211,6 +262,7 @@ class FeatureContext implements Context
<url>https://download.nextcloud.com/server/prereleases/nextcloud-' . $version . '.zip</url>
<web>https://docs.nextcloud.org/server/10/admin_manual/maintenance/manual_upgrade.html</web>
<autoupdater>1</autoupdater>
+ <signature>'.$this->getSignatureForVersion($version).'</signature>
</nextcloud>
';
file_put_contents($this->updateServerDir . 'index.php', $content);
@@ -302,6 +354,18 @@ class FeatureContext implements Context
}
/**
+ * @Given the current channel is :channel
+ * @param string $channel
+ */
+ public function theCurrentChannelIs($channel)
+ {
+
+ chdir($this->serverDir . 'nextcloud');
+ shell_exec('chmod +x occ');
+ exec('./occ config:system:set --value '.$channel.' updater.release.channel');
+ }
+
+ /**
* @Then /upgrade is (not required|required)/
*/
public function upgradeIs($state)
diff --git a/tests/features/cli.feature b/tests/features/cli.feature
index 0750a84..adb8f92 100644
--- a/tests/features/cli.feature
+++ b/tests/features/cli.feature
@@ -34,6 +34,20 @@ Feature: CLI updater
#And maintenance mode should be off
And upgrade is not required
+ Scenario: Update without valid signature is being offered - 10.0.0 to 10.0.2
+ Given the current installed version is 10.0.0
+ # This works because 10.0.2 is not in the signature list
+ And there is an update to version 10.0.2 available
+ When the CLI updater is run
+ Then the return code should not be 0
+ And the output should contain "Signature of update is not valid"
+ And the installed version should be 10.0.0
+# known issue:
+ And maintenance mode should be on
+# TODO - it should be:
+#And maintenance mode should be off
+ And upgrade is not required
+
Scenario: Update is available but autoupdate is disabled - 10.0.0 to 10.0.1
Given the current installed version is 10.0.0
And the autoupdater is disabled
diff --git a/tests/features/master.feature b/tests/features/master.feature
index 171b3d3..5fd4817 100644
--- a/tests/features/master.feature
+++ b/tests/features/master.feature
@@ -2,6 +2,7 @@ Feature: CLI updater - master base
Scenario: Update is available - master to master daily
Given the current installed version is master
+ And the current channel is "daily"
And there is an update to daily version of master available
And the version number is decreased in the config.php to enforce upgrade
When the CLI updater is run successfully
diff --git a/tests/features/stable10.feature b/tests/features/stable10.feature
index ad6ccb2..bcc5367 100644
--- a/tests/features/stable10.feature
+++ b/tests/features/stable10.feature
@@ -11,7 +11,7 @@ Feature: CLI updater - stable10 base
Scenario: Update is available - 10.0.0 to 11.0beta
Given the current installed version is 10.0.0
- And there is an update to prerelease version of 11.0.0beta available
+ And there is an update to prerelease version "11.0.0beta" available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 11.0.0
@@ -20,7 +20,7 @@ Feature: CLI updater - stable10 base
Scenario: Update is available - 10.0.1 to 11.0beta
Given the current installed version is 10.0.1
- And there is an update to prerelease version of 11.0.0beta available
+ And there is an update to prerelease version "11.0.0beta" available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 11.0.0
@@ -29,6 +29,7 @@ Feature: CLI updater - stable10 base
Scenario: Update is available - 10.0.0 to stable10 daily
Given the current installed version is 10.0.0
+ And the current channel is "daily"
And there is an update to daily version of stable10 available
When the CLI updater is run successfully
And the output should contain "Update successful"
@@ -38,6 +39,7 @@ Feature: CLI updater - stable10 base
Scenario: Update is available - 10.0.1 to stable10 daily
Given the current installed version is 10.0.1
+ And the current channel is "daily"
And there is an update to daily version of stable10 available
When the CLI updater is run successfully
And the output should contain "Update successful"
@@ -47,6 +49,7 @@ Feature: CLI updater - stable10 base
Scenario: Update is available - stable10 to stable10 daily
Given the current installed version is stable10
+ And the current channel is "daily"
And there is an update to daily version of stable10 available
And the version number is decreased in the config.php to enforce upgrade
When the CLI updater is run successfully
@@ -57,7 +60,7 @@ Feature: CLI updater - stable10 base
Scenario: Update is available - stable10 to 11.0.0 beta 2
Given the current installed version is stable10
- And there is an update to prerelease version of 11.0.0beta2 available
+ And there is an update to prerelease version "11.0.0beta2" available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 11.0.0.5
@@ -66,7 +69,7 @@ Feature: CLI updater - stable10 base
Scenario: Update is available - 10.0.0 to 11.0.0 beta 2
Given the current installed version is 10.0.0
- And there is an update to prerelease version of 11.0.0beta2 available
+ And there is an update to prerelease version "11.0.0beta2" available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 11.0.0.5
@@ -75,7 +78,7 @@ Feature: CLI updater - stable10 base
Scenario: Update is available - 10.0.1 to 11.0.0 beta 2
Given the current installed version is 10.0.1
- And there is an update to prerelease version of 11.0.0beta2 available
+ And there is an update to prerelease version "11.0.0beta2" available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 11.0.0.5
@@ -84,7 +87,7 @@ Feature: CLI updater - stable10 base
Scenario: Update is available - 10.0.0 to 10.0.2RC1
Given the current installed version is 10.0.0
- And there is an update to prerelease version of 10.0.2RC1 available
+ And there is an update to prerelease version "10.0.2RC1" available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 10.0.2
@@ -93,7 +96,7 @@ Feature: CLI updater - stable10 base
Scenario: Update is available - 10.0.1 to 10.0.2RC1
Given the current installed version is 10.0.1
- And there is an update to prerelease version of 10.0.2RC1 available
+ And there is an update to prerelease version "10.0.2RC1" available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 10.0.2
diff --git a/tests/features/stable11.feature b/tests/features/stable11.feature
index d1364ba..c96f1dc 100644
--- a/tests/features/stable11.feature
+++ b/tests/features/stable11.feature
@@ -2,7 +2,7 @@ Feature: CLI updater - stable11 base
Scenario: Update is available - 11.0.0 beta to 11.0.0 beta 2
Given the current installed version is 11.0.0beta
- And there is an update to prerelease version of 11.0.0beta2 available
+ And there is an update to prerelease version "11.0.0beta2" available
And the version number is decreased in the config.php to enforce upgrade
When the CLI updater is run successfully
And the output should contain "Update successful"
@@ -10,8 +10,9 @@ Feature: CLI updater - stable11 base
And maintenance mode should be off
And upgrade is not required
- Scenario: Update is available - 11.0.0 to master daily
- Given the current installed version is 11.0.0
+ Scenario: Update is available - 11.0.2 to master daily
+ Given the current installed version is 11.0.2
+ And the current channel is "daily"
And there is an update to daily version of master available
When the CLI updater is run successfully
And the output should contain "Update successful"
@@ -19,8 +20,9 @@ Feature: CLI updater - stable11 base
And maintenance mode should be off
And upgrade is not required
- Scenario: Update is available - 11.0.1 to master daily
- Given the current installed version is 11.0.1
+ Scenario: Update is available - 11.0.2 to master daily
+ Given the current installed version is 11.0.2
+ And the current channel is "daily"
And there is an update to daily version of master available
When the CLI updater is run successfully
And the output should contain "Update successful"
diff --git a/tests/features/stable9.feature b/tests/features/stable9.feature
index ed4b9b3..a23ce5a 100644
--- a/tests/features/stable9.feature
+++ b/tests/features/stable9.feature
@@ -38,6 +38,7 @@ Feature: CLI updater - stable9 base
Scenario: Update is available - 9.0.53 to stable9 daily
Given the current installed version is 9.0.53
+ And the current channel is "daily"
And there is an update to daily version of stable9 available
When the CLI updater is run successfully
And the output should contain "Update successful"
@@ -56,7 +57,7 @@ Feature: CLI updater - stable9 base
Scenario: Update is available - 9.0.54 to 9.0.55 RC1
Given the current installed version is 9.0.54
- And there is an update to prerelease version 9.0.55RC1 available
+ And there is an update to prerelease version "9.0.55RC1" available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 9.0.55
@@ -65,7 +66,7 @@ Feature: CLI updater - stable9 base
Scenario: Update is available - 9.0.55 RC1 to 9.0.55 RC1 to check if the updater will run on the RC onwards
Given the current installed version is 9.0.55RC1
- And there is an update to prerelease version 9.0.55RC1 available
+ And there is an update to prerelease version "9.0.55RC1" available
And the version number is decreased in the config.php to enforce upgrade
When the CLI updater is run successfully
And the output should contain "Update successful"
@@ -75,6 +76,7 @@ Feature: CLI updater - stable9 base
Scenario: Update is available - 9.0.54 to stable9 daily
Given the current installed version is 9.0.54
+ And the current channel is "daily"
And there is an update to daily version of stable9 available
And the version number is decreased in the config.php to enforce upgrade
When the CLI updater is run successfully
@@ -85,6 +87,7 @@ Feature: CLI updater - stable9 base
Scenario: Update is available - stable9 to stable9 daily
Given the current installed version is stable9
+ And the current channel is "daily"
And there is an update to daily version of stable9 available
And the version number is decreased in the config.php to enforce upgrade
When the CLI updater is run successfully
@@ -131,7 +134,7 @@ Feature: CLI updater - stable9 base
Scenario: Update is available - 9.0.53 to 10.0.2RC1
Given the current installed version is 9.0.53
- And there is an update to prerelease version 10.0.2RC1 available
+ And there is an update to prerelease version "10.0.2RC1" available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 10.0.2
@@ -140,7 +143,7 @@ Feature: CLI updater - stable9 base
Scenario: Update is available - 9.0.54 to 10.0.2RC1
Given the current installed version is 9.0.54
- And there is an update to prerelease version 10.0.2RC1 available
+ And there is an update to prerelease version "10.0.2RC1" available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 10.0.2
@@ -149,6 +152,7 @@ Feature: CLI updater - stable9 base
Scenario: Update is available - 9.0.54 to stable10 daily
Given the current installed version is 9.0.54
+ And the current channel is "daily"
And there is an update to daily version of stable10 available
When the CLI updater is run successfully
And the output should contain "Update successful"
diff --git a/updater.phar b/updater.phar
index fcd8ca0..818cc9f 100755
--- a/updater.phar
+++ b/updater.phar
Binary files differ
diff --git a/vendor/autoload.php b/vendor/autoload.php
index e277114..c749f66 100644
--- a/vendor/autoload.php
+++ b/vendor/autoload.php
@@ -2,6 +2,6 @@
// autoload.php @generated by Composer
-require_once __DIR__ . '/composer' . '/autoload_real.php';
+require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit994b8d870ddb923ebc3ff0ceaaaa96a6::getLoader();
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
index ac67d30..2c72175 100644
--- a/vendor/composer/ClassLoader.php
+++ b/vendor/composer/ClassLoader.php
@@ -55,6 +55,7 @@ class ClassLoader
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
+ private $apcuPrefix;
public function getPrefixes()
{
@@ -272,6 +273,26 @@ class ClassLoader
}
/**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
@@ -313,11 +334,6 @@ class ClassLoader
*/
public function findFile($class)
{
- // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
- if ('\\' == $class[0]) {
- $class = substr($class, 1);
- }
-
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
@@ -325,6 +341,12 @@ class ClassLoader
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
$file = $this->findFileWithExtension($class, '.php');
@@ -333,6 +355,10 @@ class ClassLoader
$file = $this->findFileWithExtension($class, '.hh');
}
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
@@ -348,9 +374,13 @@ class ClassLoader
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
- foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
- if (0 === strpos($class, $prefix)) {
- foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath.'\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ $length = $this->prefixLengthsPsr4[$first][$search];
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
index 1a28124..f27399a 100644
--- a/vendor/composer/LICENSE
+++ b/vendor/composer/LICENSE
@@ -1,5 +1,5 @@
-Copyright (c) 2016 Nils Adermann, Jordi Boggiano
+Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
index 38d2ec6..b42ef68 100644
--- a/vendor/composer/autoload_real.php
+++ b/vendor/composer/autoload_real.php
@@ -23,7 +23,7 @@ class ComposerAutoloaderInit994b8d870ddb923ebc3ff0ceaaaa96a6
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit994b8d870ddb923ebc3ff0ceaaaa96a6', 'loadClassLoader'));
- $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index c084769..a0c7fd4 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -17,7 +17,7 @@
"require": {
"php": ">=5.3.0"
},
- "time": "2016-10-10 12:19:37",
+ "time": "2016-10-10T12:19:37+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -74,7 +74,7 @@
"symfony/class-loader": "~2.8|~3.0",
"symfony/http-kernel": "~2.8|~3.0"
},
- "time": "2016-09-06 11:02:40",
+ "time": "2016-09-06T11:02:40+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -128,7 +128,7 @@
"suggest": {
"ext-mbstring": "For best performance"
},
- "time": "2016-05-18 14:26:46",
+ "time": "2016-05-18T14:26:46+00:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -198,7 +198,7 @@
"symfony/event-dispatcher": "",
"symfony/process": ""
},
- "time": "2016-10-06 01:44:51",
+ "time": "2016-10-06T01:44:51+00:00",
"type": "library",
"extra": {
"branch-alias": {