From dab7606ae895f2dc7903124aafe6d1f6b0d183b5 Mon Sep 17 00:00:00 2001 From: Git'Fellow Date: Thu, 30 Jun 2022 10:53:42 +0200 Subject: [PATCH 1/7] Introduce Update Directory Allow to specify a specific directory as updater working directory --- lib/Updater.php | 181 ++++++++++++++++++++++++------------------------ 1 file changed, 89 insertions(+), 92 deletions(-) diff --git a/lib/Updater.php b/lib/Updater.php index b527e8d7..7ede2018 100644 --- a/lib/Updater.php +++ b/lib/Updater.php @@ -47,7 +47,7 @@ class Updater { public function __construct($baseDir) { $this->baseDir = $baseDir; - if($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { + if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { $configFileName = rtrim($dir, '/') . '/config.php'; } else { $configFileName = $this->baseDir . '/../config/config.php'; @@ -66,8 +66,8 @@ public function __construct($baseDir) { return; } - $dataDir = $this->getDataDirectoryLocation(); - if(empty($dataDir) || !is_string($dataDir)) { + $dataDir = $this->getUpdateDirectoryLocation(); + if (empty($dataDir) || !is_string($dataDir)) { throw new \Exception('Could not read data directory from config.php.'); } @@ -84,16 +84,13 @@ public function __construct($baseDir) { $buildTime = $OC_Build; } - if($version === null) { - return; - } - if($buildTime === null) { + if ($version === null || $buildTime === null) { return; } // normalize version to 3 digits $splittedVersion = explode('.', $version); - if(sizeof($splittedVersion) >= 3) { + if (sizeof($splittedVersion) >= 3) { $splittedVersion = array_slice($splittedVersion, 0, 3); } @@ -188,8 +185,8 @@ public function getConfigOption($key) { * * @return string */ - private function getDataDirectoryLocation() { - return $this->configValues['datadirectory']; + private function getUpdateDirectoryLocation() { + return $this->configValues['updatedirectory'] ?? $this->configValues['datadirectory']; } /** @@ -250,11 +247,11 @@ private function getExpectedElementsList() { */ private function getAppDirectories() { $expected = []; - if($appsPaths = $this->getConfigOption('apps_paths')) { + if ($appsPaths = $this->getConfigOption('apps_paths')) { foreach ($appsPaths as $appsPath) { $parentDir = realpath($this->baseDir . '/../'); $appDir = basename($appsPath['path']); - if(strpos($appsPath['path'], $parentDir) === 0 && $appDir !== 'apps') { + if (strpos($appsPath['path'], $parentDir) === 0 && $appDir !== 'apps') { $expected[] = $appDir; } } @@ -287,7 +284,7 @@ public function checkForExpectedFilesAndFolders() { $expectedElements = $this->getExpectedElementsList(); $unexpectedElements = []; foreach (new \DirectoryIterator($this->baseDir . '/../') as $fileInfo) { - if(array_search($fileInfo->getFilename(), $expectedElements) === false) { + if (array_search($fileInfo->getFilename(), $expectedElements) === false) { $unexpectedElements[] = $fileInfo->getFilename(); } } @@ -310,11 +307,11 @@ public function checkWritePermissions() { $it = new \RecursiveIteratorIterator($filter); foreach ($it as $path => $dir) { - if(!is_writable($path)) { + if (!is_writable($path)) { $notWritablePaths[] = $path; } } - if(count($notWritablePaths) > 0) { + if (count($notWritablePaths) > 0) { throw new UpdateException($notWritablePaths); } @@ -330,7 +327,7 @@ public function checkWritePermissions() { public function setMaintenanceMode($state) { $this->silentLog('[info] setMaintenanceMode("' . ($state ? 'true' : 'false') . '")'); - if($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { + if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { $configFileName = rtrim($dir, '/') . '/config.php'; } else { $configFileName = $this->baseDir . '/../config/config.php'; @@ -370,11 +367,11 @@ public function createBackup() { ]; // Create new folder for the backup - $backupFolderLocation = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid').'/backups/nextcloud-'.$this->getConfigOption('version') . '-' . time() . '/'; + $backupFolderLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid').'/backups/nextcloud-'.$this->getConfigOption('version') . '-' . time() . '/'; $this->silentLog('[info] backup folder location: ' . $backupFolderLocation); $state = mkdir($backupFolderLocation, 0750, true); - if($state === false) { + if ($state === false) { throw new \Exception('Could not create backup folder location'); } @@ -390,35 +387,35 @@ public function createBackup() { $folderStructure = explode('/', $fileName, -1); // Exclude the exclusions - if(isset($folderStructure[0])) { - if(array_search($folderStructure[0], $excludedElements) !== false) { + if (isset($folderStructure[0])) { + if (array_search($folderStructure[0], $excludedElements) !== false) { continue; } } else { - if(array_search($fileName, $excludedElements) !== false) { + if (array_search($fileName, $excludedElements) !== false) { continue; } } // Create folder if it doesn't exist - if(!file_exists($backupFolderLocation . '/' . dirname($fileName))) { + if (!file_exists($backupFolderLocation . '/' . dirname($fileName))) { $state = mkdir($backupFolderLocation . '/' . dirname($fileName), 0750, true); - if($state === false) { + if ($state === false) { throw new \Exception('Could not create folder: '.$backupFolderLocation.'/'.dirname($fileName)); } } // If it is a file copy it - if($fileInfo->isFile()) { + if ($fileInfo->isFile()) { $state = copy($fileInfo->getRealPath(), $backupFolderLocation . $fileName); - if($state === false) { + if ($state === false) { $message = sprintf( 'Could not copy "%s" to "%s"', $fileInfo->getRealPath(), $backupFolderLocation . $fileName ); - if(is_readable($fileInfo->getRealPath()) === false) { + if (is_readable($fileInfo->getRealPath()) === false) { $message = sprintf( '%s. Source %s is not readable', $message, @@ -426,7 +423,7 @@ public function createBackup() { ); } - if(is_writable($backupFolderLocation . $fileName) === false) { + if (is_writable($backupFolderLocation . $fileName) === false) { $message = sprintf( '%s. Destination %s is not writable', $message, @@ -456,7 +453,7 @@ private function getUpdateServerResponse() { $this->silentLog('[info] getUpdateServerResponse()'); $updaterServer = $this->getConfigOption('updater.server.url'); - if($updaterServer === null) { + if ($updaterServer === null) { // FIXME: used deployed URL $updaterServer = 'https://updates.nextcloud.com/updater_server/'; } @@ -486,26 +483,26 @@ private function getUpdateServerResponse() { } $response = curl_exec($curl); - if($response === false) { + if ($response === false) { throw new \Exception('Could not do request to updater server: '.curl_error($curl)); } curl_close($curl); // Response can be empty when no update is available - if($response === '') { + if ($response === '') { return []; } $xml = simplexml_load_string($response); - if($xml === false) { + if ($xml === false) { throw new \Exception('Could not parse updater server XML response'); } $json = json_encode($xml); - if($json === false) { + if ($json === false) { throw new \Exception('Could not JSON encode updater server response'); } $response = json_decode($json, true); - if($response === null) { + if ($response === null) { throw new \Exception('Could not JSON decode updater server response.'); } $this->silentLog('[info] getUpdateServerResponse response: ' . print_r($response, true)); @@ -521,13 +518,13 @@ public function downloadUpdate() { $this->silentLog('[info] downloadUpdate()'); $response = $this->getUpdateServerResponse(); - $storageLocation = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/'; - if(file_exists($storageLocation)) { + $storageLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/'; + if (file_exists($storageLocation)) { $this->silentLog('[info] storage location exists'); $this->recursiveDelete($storageLocation); } $state = mkdir($storageLocation, 0750, true); - if($state === false) { + if ($state === false) { throw new \Exception('Could not mkdir storage location'); } @@ -546,11 +543,11 @@ public function downloadUpdate() { ]); } - if(curl_exec($ch) === false) { + if (curl_exec($ch) === false) { throw new \Exception('Curl error: ' . curl_error($ch)); } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if($httpCode !== 200) { + if ($httpCode !== 200) { $statusCodes = [ 400 => 'Bad request', 401 => 'Unauthorized', @@ -563,14 +560,14 @@ public function downloadUpdate() { ]; $message = 'Download failed'; - if(isset($statusCodes[$httpCode])) { + if (isset($statusCodes[$httpCode])) { $message .= ' - ' . $statusCodes[$httpCode] . ' (HTTP ' . $httpCode . ')'; } else { $message .= ' - HTTP status code: ' . $httpCode; } $curlErrorMessage = curl_error($ch); - if(!empty($curlErrorMessage)) { + if (!empty($curlErrorMessage)) { $message .= ' - curl error message: ' . $curlErrorMessage; } @@ -589,7 +586,7 @@ public function downloadUpdate() { * @throws \Exception */ private function getDownloadedFilePath() { - $storageLocation = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/'; + $storageLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/'; $this->silentLog('[info] storage location: ' . $storageLocation); $filesInStorageLocation = scandir($storageLocation); @@ -597,7 +594,7 @@ private function getDownloadedFilePath() { return $path !== '.' && $path !== '..'; })); // only the downloaded archive - if(count($files) !== 1) { + if (count($files) !== 1) { throw new \Exception('There are more files than the downloaded archive in the downloads/ folder.'); } return $storageLocation . '/' . $files[0]; @@ -611,13 +608,13 @@ private function getDownloadedFilePath() { public function verifyIntegrity() { $this->silentLog('[info] verifyIntegrity()'); - if($this->getCurrentReleaseChannel() === 'daily') { + 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'])) { + if (!isset($response['signature'])) { throw new \Exception('No signature specified for defined update'); } @@ -658,7 +655,7 @@ public function verifyIntegrity() { OPENSSL_ALGO_SHA512 ); - if($validSignature === false) { + if ($validSignature === false) { throw new \Exception('Signature of update is not valid'); } @@ -675,7 +672,7 @@ public function verifyIntegrity() { private function getVersionByVersionFile($versionFile) { require $versionFile; - if(isset($OC_Version)) { + if (isset($OC_Version)) { /** @var array $OC_Version */ return implode('.', $OC_Version); } @@ -696,12 +693,12 @@ public function extractDownload() { $zipState = $zip->open($downloadedFilePath); if ($zipState === true) { $extraction = $zip->extractTo(dirname($downloadedFilePath)); - if($extraction === false) { + if ($extraction === false) { throw new \Exception('Error during unpacking zipfile: '.($zip->getStatusString())); } $zip->close(); $state = unlink($downloadedFilePath); - if($state === false) { + if ($state === false) { throw new \Exception("Can't unlink ". $downloadedFilePath); } } else { @@ -711,7 +708,7 @@ public function extractDownload() { // Ensure that the downloaded version is not lower $downloadedVersion = $this->getVersionByVersionFile(dirname($downloadedFilePath) . '/nextcloud/version.php'); $currentVersion = $this->getVersionByVersionFile($this->baseDir . '/../version.php'); - if(version_compare($downloadedVersion, $currentVersion, '<')) { + if (version_compare($downloadedVersion, $currentVersion, '<')) { throw new \Exception('Downloaded version is lower than installed version'); } @@ -739,14 +736,14 @@ public function replaceEntryPoints() { foreach($filesToReplace as $file) { $this->silentLog('[info] replace ' . $file); $parentDir = dirname($this->baseDir . '/../' . $file); - if(!file_exists($parentDir)) { + if (!file_exists($parentDir)) { $r = mkdir($parentDir); - if($r !== true) { + if ($r !== true) { throw new \Exception('Can\'t create parent directory for entry point: ' . $file); } } $state = file_put_contents($this->baseDir . '/../' . $file, $content); - if($state === false) { + if ($state === false) { throw new \Exception('Can\'t replace entry point: '.$file); } } @@ -761,7 +758,7 @@ public function replaceEntryPoints() { * @throws \Exception */ private function recursiveDelete($folder) { - if(!file_exists($folder)) { + if (!file_exists($folder)) { return; } $iterator = new \RecursiveIteratorIterator( @@ -791,7 +788,7 @@ private function recursiveDelete($folder) { } $state = rmdir($folder); - if($state === false) { + if ($state === false) { throw new \Exception('Could not rmdir ' . $folder); } } @@ -805,12 +802,12 @@ public function deleteOldFiles() { $this->silentLog('[info] deleteOldFiles()'); $shippedAppsFile = $this->baseDir . '/../core/shipped.json'; - if(!file_exists($shippedAppsFile)) { + if (!file_exists($shippedAppsFile)) { throw new \Exception('core/shipped.json is not available'); } - $newShippedAppsFile = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/nextcloud/core/shipped.json'; - if(!file_exists($newShippedAppsFile)) { + $newShippedAppsFile = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/nextcloud/core/shipped.json'; + if (!file_exists($newShippedAppsFile)) { throw new \Exception('core/shipped.json is not available in the new release'); } @@ -824,7 +821,7 @@ public function deleteOldFiles() { } $configSampleFile = $this->baseDir . '/../config/config.sample.php'; - if(file_exists($configSampleFile)) { + if (file_exists($configSampleFile)) { $this->silentLog('[info] config sample exists'); // Delete example config @@ -835,7 +832,7 @@ public function deleteOldFiles() { } $themesReadme = $this->baseDir . '/../themes/README'; - if(file_exists($themesReadme)) { + if (file_exists($themesReadme)) { $this->silentLog('[info] themes README exists'); // Delete themes @@ -871,23 +868,23 @@ public function deleteOldFiles() { $fileName = explode($currentDir, $path)[1]; $folderStructure = explode('/', $fileName, -1); // Exclude the exclusions - if(isset($folderStructure[0])) { - if(array_search($folderStructure[0], $excludedElements) !== false) { + if (isset($folderStructure[0])) { + if (array_search($folderStructure[0], $excludedElements) !== false) { continue; } } else { - if(array_search($fileName, $excludedElements) !== false) { + if (array_search($fileName, $excludedElements) !== false) { continue; } } - if($fileInfo->isFile() || $fileInfo->isLink()) { + if ($fileInfo->isFile() || $fileInfo->isLink()) { $state = unlink($path); - if($state === false) { + if ($state === false) { throw new \Exception('Could not unlink: '.$path); } - } elseif($fileInfo->isDir()) { + } elseif ($fileInfo->isDir()) { $state = rmdir($path); - if($state === false) { + if ($state === false) { throw new \Exception('Could not rmdir: '.$path); } } @@ -922,15 +919,15 @@ private function moveWithExclusions($dataLocation, array $excludedElements) { } } - if($fileInfo->isFile()) { - if(!file_exists($this->baseDir . '/../' . dirname($fileName))) { + if ($fileInfo->isFile()) { + if (!file_exists($this->baseDir . '/../' . dirname($fileName))) { $state = mkdir($this->baseDir . '/../' . dirname($fileName), 0755, true); - if($state === false) { + if ($state === false) { throw new \Exception('Could not mkdir ' . $this->baseDir . '/../' . dirname($fileName)); } } $state = rename($path, $this->baseDir . '/../' . $fileName); - if($state === false) { + if ($state === false) { throw new \Exception( sprintf( 'Could not rename %s to %s', @@ -940,9 +937,9 @@ private function moveWithExclusions($dataLocation, array $excludedElements) { ); } } - if($fileInfo->isDir()) { + if ($fileInfo->isDir()) { $state = rmdir($path); - if($state === false) { + if ($state === false) { throw new \Exception('Could not rmdir ' . $path); } } @@ -967,7 +964,7 @@ public function moveNewVersionInPlace() { 'ocs/v1.php', 'ocs/v2.php', ]; - $storageLocation = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/nextcloud/'; + $storageLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/nextcloud/'; $this->silentLog('[info] storage location: ' . $storageLocation); $this->moveWithExclusions($storageLocation, $excludedElements); @@ -983,15 +980,15 @@ public function moveNewVersionInPlace() { public function finalize() { $this->silentLog('[info] finalize()'); - $storageLocation = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/nextcloud/'; + $storageLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/nextcloud/'; $this->silentLog('[info] storage location: ' . $storageLocation); $this->moveWithExclusions($storageLocation, []); $state = rmdir($storageLocation); - if($state === false) { + if ($state === false) { throw new \Exception('Could not rmdir $storagelocation'); } - $state = unlink($this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/.step'); - if($state === false) { + $state = unlink($this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/.step'); + if ($state === false) { throw new \Exception('Could not rmdir .step'); } @@ -1009,22 +1006,22 @@ public function finalize() { * @throws \Exception */ private function writeStep($state, $step) { - $updaterDir = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid'); - if(!file_exists($updaterDir . '/.step')) { - if(!file_exists($updaterDir)) { + $updaterDir = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid'); + if (!file_exists($updaterDir . '/.step')) { + if (!file_exists($updaterDir)) { $result = mkdir($updaterDir); if ($result === false) { throw new \Exception('Could not create $updaterDir'); } } $result = touch($updaterDir . '/.step'); - if($result === false) { + if ($result === false) { throw new \Exception('Could not create .step'); } } $result = file_put_contents($updaterDir . '/.step', json_encode(['state' => $state, 'step' => $step])); - if($result === false) { + if ($result === false) { throw new \Exception('Could not write to .step'); } } @@ -1054,9 +1051,9 @@ public function endStep($step) { public function currentStep() { $this->silentLog('[info] currentStep()'); - $updaterDir = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid'); + $updaterDir = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid'); $jsonData = []; - if(file_exists($updaterDir. '/.step')) { + if (file_exists($updaterDir. '/.step')) { $state = file_get_contents($updaterDir . '/.step'); if ($state === false) { throw new \Exception('Could not read from .step'); @@ -1079,8 +1076,8 @@ public function currentStep() { public function rollbackChanges($step) { $this->silentLog('[info] rollbackChanges("' . $step . '")'); - $updaterDir = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid'); - if(file_exists($updaterDir . '/.step')) { + $updaterDir = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid'); + if (file_exists($updaterDir . '/.step')) { $this->silentLog('[info] unlink .step'); $state = unlink($updaterDir . '/.step'); if ($state === false) { @@ -1088,7 +1085,7 @@ public function rollbackChanges($step) { } } - if($step >= 7) { + if ($step >= 7) { $this->silentLog('[info] rollbackChanges - step >= 7'); // TODO: If it fails after step 7: Rollback } @@ -1110,7 +1107,7 @@ public function logException(\Exception $e) { $message .= 'Trace:' . PHP_EOL . $e->getTraceAsString() . PHP_EOL; $message .= 'File:' . $e->getFile() . PHP_EOL; $message .= 'Line:' . $e->getLine() . PHP_EOL; - if($e instanceof UpdateException) { + if ($e instanceof UpdateException) { $message .= 'Data:' . PHP_EOL . print_r($e->getData(), true) . PHP_EOL; } $this->log($message); @@ -1123,14 +1120,14 @@ public function logException(\Exception $e) { * @throws LogException */ public function log($message) { - $updaterLogPath = $this->getDataDirectoryLocation() . '/updater.log'; + $updaterLogPath = $this->getUpdateDirectoryLocation() . '/updater.log'; $fh = fopen($updaterLogPath, 'a'); - if($fh === false) { + if ($fh === false) { throw new LogException('Could not open updater.log'); } - if($this->requestID === null) { + if ($this->requestID === null) { $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $charactersLength = strlen($characters); $randomString = ''; @@ -1143,7 +1140,7 @@ public function log($message) { $logLine = date(\DateTime::ISO8601) . ' ' . $this->requestID . ' ' . $message . PHP_EOL; $result = fwrite($fh, $logLine); - if($result === false) { + if ($result === false) { throw new LogException('Could not write to updater.log'); } From 50816ec79eeed7125fea164cf45060d434ded11c Mon Sep 17 00:00:00 2001 From: Git'Fellow Date: Sat, 2 Jul 2022 08:28:47 +0200 Subject: [PATCH 2/7] Update index.php --- index.php | 208 ++++++++++++++++++++++++++---------------------------- 1 file changed, 102 insertions(+), 106 deletions(-) diff --git a/index.php b/index.php index 03b55585..b02a5523 100644 --- a/index.php +++ b/index.php @@ -131,7 +131,7 @@ public function isAuthenticated() { $storedHash = $this->updater->getConfigOption('updater.secret'); // As a sanity check the stored hash or the sent password can never be empty - if($storedHash === '' || $storedHash === null || $this->password === null) { + if ($storedHash === '' || $storedHash === null || $this->password === null) { return false; } @@ -164,7 +164,7 @@ class Updater { public function __construct($baseDir) { $this->baseDir = $baseDir; - if($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { + if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { $configFileName = rtrim($dir, '/') . '/config.php'; } else { $configFileName = $this->baseDir . '/../config/config.php'; @@ -184,7 +184,7 @@ public function __construct($baseDir) { } $dataDir = $this->getDataDirectoryLocation(); - if(empty($dataDir) || !is_string($dataDir)) { + if (empty($dataDir) || !is_string($dataDir)) { throw new \Exception('Could not read data directory from config.php.'); } @@ -201,16 +201,13 @@ public function __construct($baseDir) { $buildTime = $OC_Build; } - if($version === null) { - return; - } - if($buildTime === null) { + if ($version === null || $buildTime === null) { return; } // normalize version to 3 digits $splittedVersion = explode('.', $version); - if(sizeof($splittedVersion) >= 3) { + if (sizeof($splittedVersion) >= 3) { $splittedVersion = array_slice($splittedVersion, 0, 3); } @@ -306,7 +303,7 @@ public function getConfigOption($key) { * @return string */ private function getDataDirectoryLocation() { - return $this->configValues['datadirectory']; + return $this->configValues['updatedirectory'] ?? $this->configValues['datadirectory']; } /** @@ -367,11 +364,11 @@ private function getExpectedElementsList() { */ private function getAppDirectories() { $expected = []; - if($appsPaths = $this->getConfigOption('apps_paths')) { + if ($appsPaths = $this->getConfigOption('apps_paths')) { foreach ($appsPaths as $appsPath) { $parentDir = realpath($this->baseDir . '/../'); $appDir = basename($appsPath['path']); - if(strpos($appsPath['path'], $parentDir) === 0 && $appDir !== 'apps') { + if (strpos($appsPath['path'], $parentDir) === 0 && $appDir !== 'apps') { $expected[] = $appDir; } } @@ -404,7 +401,7 @@ public function checkForExpectedFilesAndFolders() { $expectedElements = $this->getExpectedElementsList(); $unexpectedElements = []; foreach (new \DirectoryIterator($this->baseDir . '/../') as $fileInfo) { - if(array_search($fileInfo->getFilename(), $expectedElements) === false) { + if (array_search($fileInfo->getFilename(), $expectedElements) === false) { $unexpectedElements[] = $fileInfo->getFilename(); } } @@ -427,11 +424,11 @@ public function checkWritePermissions() { $it = new \RecursiveIteratorIterator($filter); foreach ($it as $path => $dir) { - if(!is_writable($path)) { + if (!is_writable($path)) { $notWritablePaths[] = $path; } } - if(count($notWritablePaths) > 0) { + if (count($notWritablePaths) > 0) { throw new UpdateException($notWritablePaths); } @@ -447,7 +444,7 @@ public function checkWritePermissions() { public function setMaintenanceMode($state) { $this->silentLog('[info] setMaintenanceMode("' . ($state ? 'true' : 'false') . '")'); - if($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { + if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { $configFileName = rtrim($dir, '/') . '/config.php'; } else { $configFileName = $this->baseDir . '/../config/config.php'; @@ -491,7 +488,7 @@ public function createBackup() { $this->silentLog('[info] backup folder location: ' . $backupFolderLocation); $state = mkdir($backupFolderLocation, 0750, true); - if($state === false) { + if ($state === false) { throw new \Exception('Could not create backup folder location'); } @@ -507,35 +504,35 @@ public function createBackup() { $folderStructure = explode('/', $fileName, -1); // Exclude the exclusions - if(isset($folderStructure[0])) { - if(array_search($folderStructure[0], $excludedElements) !== false) { + if (isset($folderStructure[0])) { + if (array_search($folderStructure[0], $excludedElements) !== false) { continue; } } else { - if(array_search($fileName, $excludedElements) !== false) { + if (array_search($fileName, $excludedElements) !== false) { continue; } } // Create folder if it doesn't exist - if(!file_exists($backupFolderLocation . '/' . dirname($fileName))) { + if (!file_exists($backupFolderLocation . '/' . dirname($fileName))) { $state = mkdir($backupFolderLocation . '/' . dirname($fileName), 0750, true); - if($state === false) { + if ($state === false) { throw new \Exception('Could not create folder: '.$backupFolderLocation.'/'.dirname($fileName)); } } // If it is a file copy it - if($fileInfo->isFile()) { + if ($fileInfo->isFile()) { $state = copy($fileInfo->getRealPath(), $backupFolderLocation . $fileName); - if($state === false) { + if ($state === false) { $message = sprintf( 'Could not copy "%s" to "%s"', $fileInfo->getRealPath(), $backupFolderLocation . $fileName ); - if(is_readable($fileInfo->getRealPath()) === false) { + if (is_readable($fileInfo->getRealPath()) === false) { $message = sprintf( '%s. Source %s is not readable', $message, @@ -543,7 +540,7 @@ public function createBackup() { ); } - if(is_writable($backupFolderLocation . $fileName) === false) { + if (is_writable($backupFolderLocation . $fileName) === false) { $message = sprintf( '%s. Destination %s is not writable', $message, @@ -573,7 +570,7 @@ private function getUpdateServerResponse() { $this->silentLog('[info] getUpdateServerResponse()'); $updaterServer = $this->getConfigOption('updater.server.url'); - if($updaterServer === null) { + if ($updaterServer === null) { // FIXME: used deployed URL $updaterServer = 'https://updates.nextcloud.com/updater_server/'; } @@ -603,26 +600,26 @@ private function getUpdateServerResponse() { } $response = curl_exec($curl); - if($response === false) { + if ($response === false) { throw new \Exception('Could not do request to updater server: '.curl_error($curl)); } curl_close($curl); // Response can be empty when no update is available - if($response === '') { + if ($response === '') { return []; } $xml = simplexml_load_string($response); - if($xml === false) { + if ($xml === false) { throw new \Exception('Could not parse updater server XML response'); } $json = json_encode($xml); - if($json === false) { + if ($json === false) { throw new \Exception('Could not JSON encode updater server response'); } $response = json_decode($json, true); - if($response === null) { + if ($response === null) { throw new \Exception('Could not JSON decode updater server response.'); } $this->silentLog('[info] getUpdateServerResponse response: ' . print_r($response, true)); @@ -639,12 +636,12 @@ public function downloadUpdate() { $response = $this->getUpdateServerResponse(); $storageLocation = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/'; - if(file_exists($storageLocation)) { + if (file_exists($storageLocation)) { $this->silentLog('[info] storage location exists'); $this->recursiveDelete($storageLocation); } $state = mkdir($storageLocation, 0750, true); - if($state === false) { + if ($state === false) { throw new \Exception('Could not mkdir storage location'); } @@ -663,11 +660,11 @@ public function downloadUpdate() { ]); } - if(curl_exec($ch) === false) { + if (curl_exec($ch) === false) { throw new \Exception('Curl error: ' . curl_error($ch)); } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if($httpCode !== 200) { + if ($httpCode !== 200) { $statusCodes = [ 400 => 'Bad request', 401 => 'Unauthorized', @@ -680,14 +677,14 @@ public function downloadUpdate() { ]; $message = 'Download failed'; - if(isset($statusCodes[$httpCode])) { + if (isset($statusCodes[$httpCode])) { $message .= ' - ' . $statusCodes[$httpCode] . ' (HTTP ' . $httpCode . ')'; } else { $message .= ' - HTTP status code: ' . $httpCode; } $curlErrorMessage = curl_error($ch); - if(!empty($curlErrorMessage)) { + if (!empty($curlErrorMessage)) { $message .= ' - curl error message: ' . $curlErrorMessage; } @@ -714,7 +711,7 @@ private function getDownloadedFilePath() { return $path !== '.' && $path !== '..'; })); // only the downloaded archive - if(count($files) !== 1) { + if (count($files) !== 1) { throw new \Exception('There are more files than the downloaded archive in the downloads/ folder.'); } return $storageLocation . '/' . $files[0]; @@ -728,13 +725,13 @@ private function getDownloadedFilePath() { public function verifyIntegrity() { $this->silentLog('[info] verifyIntegrity()'); - if($this->getCurrentReleaseChannel() === 'daily') { + 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'])) { + if (!isset($response['signature'])) { throw new \Exception('No signature specified for defined update'); } @@ -775,7 +772,7 @@ public function verifyIntegrity() { OPENSSL_ALGO_SHA512 ); - if($validSignature === false) { + if ($validSignature === false) { throw new \Exception('Signature of update is not valid'); } @@ -792,7 +789,7 @@ public function verifyIntegrity() { private function getVersionByVersionFile($versionFile) { require $versionFile; - if(isset($OC_Version)) { + if (isset($OC_Version)) { /** @var array $OC_Version */ return implode('.', $OC_Version); } @@ -813,12 +810,12 @@ public function extractDownload() { $zipState = $zip->open($downloadedFilePath); if ($zipState === true) { $extraction = $zip->extractTo(dirname($downloadedFilePath)); - if($extraction === false) { + if ($extraction === false) { throw new \Exception('Error during unpacking zipfile: '.($zip->getStatusString())); } $zip->close(); $state = unlink($downloadedFilePath); - if($state === false) { + if ($state === false) { throw new \Exception("Can't unlink ". $downloadedFilePath); } } else { @@ -828,7 +825,7 @@ public function extractDownload() { // Ensure that the downloaded version is not lower $downloadedVersion = $this->getVersionByVersionFile(dirname($downloadedFilePath) . '/nextcloud/version.php'); $currentVersion = $this->getVersionByVersionFile($this->baseDir . '/../version.php'); - if(version_compare($downloadedVersion, $currentVersion, '<')) { + if (version_compare($downloadedVersion, $currentVersion, '<')) { throw new \Exception('Downloaded version is lower than installed version'); } @@ -856,14 +853,14 @@ public function replaceEntryPoints() { foreach($filesToReplace as $file) { $this->silentLog('[info] replace ' . $file); $parentDir = dirname($this->baseDir . '/../' . $file); - if(!file_exists($parentDir)) { + if (!file_exists($parentDir)) { $r = mkdir($parentDir); - if($r !== true) { + if ($r !== true) { throw new \Exception('Can\'t create parent directory for entry point: ' . $file); } } $state = file_put_contents($this->baseDir . '/../' . $file, $content); - if($state === false) { + if ($state === false) { throw new \Exception('Can\'t replace entry point: '.$file); } } @@ -878,7 +875,7 @@ public function replaceEntryPoints() { * @throws \Exception */ private function recursiveDelete($folder) { - if(!file_exists($folder)) { + if (!file_exists($folder)) { return; } $iterator = new \RecursiveIteratorIterator( @@ -908,7 +905,7 @@ private function recursiveDelete($folder) { } $state = rmdir($folder); - if($state === false) { + if ($state === false) { throw new \Exception('Could not rmdir ' . $folder); } } @@ -922,12 +919,12 @@ public function deleteOldFiles() { $this->silentLog('[info] deleteOldFiles()'); $shippedAppsFile = $this->baseDir . '/../core/shipped.json'; - if(!file_exists($shippedAppsFile)) { + if (!file_exists($shippedAppsFile)) { throw new \Exception('core/shipped.json is not available'); } $newShippedAppsFile = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/nextcloud/core/shipped.json'; - if(!file_exists($newShippedAppsFile)) { + if (!file_exists($newShippedAppsFile)) { throw new \Exception('core/shipped.json is not available in the new release'); } @@ -941,7 +938,7 @@ public function deleteOldFiles() { } $configSampleFile = $this->baseDir . '/../config/config.sample.php'; - if(file_exists($configSampleFile)) { + if (file_exists($configSampleFile)) { $this->silentLog('[info] config sample exists'); // Delete example config @@ -952,7 +949,7 @@ public function deleteOldFiles() { } $themesReadme = $this->baseDir . '/../themes/README'; - if(file_exists($themesReadme)) { + if (file_exists($themesReadme)) { $this->silentLog('[info] themes README exists'); // Delete themes @@ -988,23 +985,23 @@ public function deleteOldFiles() { $fileName = explode($currentDir, $path)[1]; $folderStructure = explode('/', $fileName, -1); // Exclude the exclusions - if(isset($folderStructure[0])) { - if(array_search($folderStructure[0], $excludedElements) !== false) { + if (isset($folderStructure[0])) { + if (array_search($folderStructure[0], $excludedElements) !== false) { continue; } } else { - if(array_search($fileName, $excludedElements) !== false) { + if (array_search($fileName, $excludedElements) !== false) { continue; } } - if($fileInfo->isFile() || $fileInfo->isLink()) { + if ($fileInfo->isFile() || $fileInfo->isLink()) { $state = unlink($path); - if($state === false) { + if ($state === false) { throw new \Exception('Could not unlink: '.$path); } - } elseif($fileInfo->isDir()) { + } elseif ($fileInfo->isDir()) { $state = rmdir($path); - if($state === false) { + if ($state === false) { throw new \Exception('Could not rmdir: '.$path); } } @@ -1039,15 +1036,15 @@ private function moveWithExclusions($dataLocation, array $excludedElements) { } } - if($fileInfo->isFile()) { - if(!file_exists($this->baseDir . '/../' . dirname($fileName))) { + if ($fileInfo->isFile()) { + if (!file_exists($this->baseDir . '/../' . dirname($fileName))) { $state = mkdir($this->baseDir . '/../' . dirname($fileName), 0755, true); - if($state === false) { + if ($state === false) { throw new \Exception('Could not mkdir ' . $this->baseDir . '/../' . dirname($fileName)); } } $state = rename($path, $this->baseDir . '/../' . $fileName); - if($state === false) { + if ($state === false) { throw new \Exception( sprintf( 'Could not rename %s to %s', @@ -1057,9 +1054,9 @@ private function moveWithExclusions($dataLocation, array $excludedElements) { ); } } - if($fileInfo->isDir()) { + if ($fileInfo->isDir()) { $state = rmdir($path); - if($state === false) { + if ($state === false) { throw new \Exception('Could not rmdir ' . $path); } } @@ -1104,11 +1101,11 @@ public function finalize() { $this->silentLog('[info] storage location: ' . $storageLocation); $this->moveWithExclusions($storageLocation, []); $state = rmdir($storageLocation); - if($state === false) { + if ($state === false) { throw new \Exception('Could not rmdir $storagelocation'); } $state = unlink($this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/.step'); - if($state === false) { + if ($state === false) { throw new \Exception('Could not rmdir .step'); } @@ -1127,21 +1124,21 @@ public function finalize() { */ private function writeStep($state, $step) { $updaterDir = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid'); - if(!file_exists($updaterDir . '/.step')) { - if(!file_exists($updaterDir)) { + if (!file_exists($updaterDir . '/.step')) { + if (!file_exists($updaterDir)) { $result = mkdir($updaterDir); if ($result === false) { throw new \Exception('Could not create $updaterDir'); } } $result = touch($updaterDir . '/.step'); - if($result === false) { + if ($result === false) { throw new \Exception('Could not create .step'); } } $result = file_put_contents($updaterDir . '/.step', json_encode(['state' => $state, 'step' => $step])); - if($result === false) { + if ($result === false) { throw new \Exception('Could not write to .step'); } } @@ -1173,7 +1170,7 @@ public function currentStep() { $updaterDir = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid'); $jsonData = []; - if(file_exists($updaterDir. '/.step')) { + if (file_exists($updaterDir. '/.step')) { $state = file_get_contents($updaterDir . '/.step'); if ($state === false) { throw new \Exception('Could not read from .step'); @@ -1197,7 +1194,7 @@ public function rollbackChanges($step) { $this->silentLog('[info] rollbackChanges("' . $step . '")'); $updaterDir = $this->getDataDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid'); - if(file_exists($updaterDir . '/.step')) { + if (file_exists($updaterDir . '/.step')) { $this->silentLog('[info] unlink .step'); $state = unlink($updaterDir . '/.step'); if ($state === false) { @@ -1205,7 +1202,7 @@ public function rollbackChanges($step) { } } - if($step >= 7) { + if ($step >= 7) { $this->silentLog('[info] rollbackChanges - step >= 7'); // TODO: If it fails after step 7: Rollback } @@ -1227,7 +1224,7 @@ public function logException(\Exception $e) { $message .= 'Trace:' . PHP_EOL . $e->getTraceAsString() . PHP_EOL; $message .= 'File:' . $e->getFile() . PHP_EOL; $message .= 'Line:' . $e->getLine() . PHP_EOL; - if($e instanceof UpdateException) { + if ($e instanceof UpdateException) { $message .= 'Data:' . PHP_EOL . print_r($e->getData(), true) . PHP_EOL; } $this->log($message); @@ -1243,7 +1240,7 @@ public function log($message) { $updaterLogPath = $this->getDataDirectoryLocation() . '/updater.log'; $fh = fopen($updaterLogPath, 'a'); - if($fh === false) { + if ($fh === false) { throw new LogException('Could not open updater.log'); } @@ -1260,7 +1257,7 @@ public function log($message) { $logLine = date(\DateTime::ISO8601) . ' ' . $this->requestID . ' ' . $message . PHP_EOL; $result = fwrite($fh, $logLine); - if($result === false) { + if ($result === false) { throw new LogException('Could not write to updater.log'); } @@ -1311,7 +1308,7 @@ public function logVersion() { try { $updater->log('[info] request to updater'); } catch (\Exception $e) { - if(isset($_POST['step'])) { + if (isset($_POST['step'])) { // mark step as failed http_response_code(500); echo(json_encode(['proceed' => false, 'response' => $e->getMessage()])); @@ -1328,12 +1325,12 @@ public function logVersion() { // Check if already a step is in process $currentStep = $updater->currentStep(); $stepNumber = 0; -if($currentStep !== []) { +if ($currentStep !== []) { $stepState = $currentStep['state']; $stepNumber = $currentStep['step']; $updater->log('[info] Step ' . $stepNumber . ' is in state "' . $stepState . '".'); - if($stepState === 'start') { + if ($stepState === 'start') { die( sprintf( 'Step %s is currently in process. Please reload this page later.', @@ -1343,16 +1340,16 @@ public function logVersion() { } } -if(isset($_POST['step'])) { +if (isset($_POST['step'])) { $updater->log('[info] POST request for step "' . $_POST['step'] . '"'); set_time_limit(0); try { - if(!$auth->isAuthenticated()) { + if (!$auth->isAuthenticated()) { throw new \Exception('Not authenticated'); } $step = (int)$_POST['step']; - if($step > 12 || $step < 1) { + if ($step > 12 || $step < 1) { throw new \Exception('Invalid step'); } @@ -1407,7 +1404,7 @@ public function logVersion() { $message .= ' (and writing to log failed also with: ' . $logE->getMessage() . ')'; } - if(isset($step)) { + if (isset($step)) { $updater->rollbackChanges($step); } http_response_code(500); @@ -1422,7 +1419,7 @@ public function logVersion() { $message .= ' (and writing to log failed also with: ' . $logE->getMessage() . ')'; } - if(isset($step)) { + if (isset($step)) { $updater->rollbackChanges($step); } http_response_code(500); @@ -1435,7 +1432,7 @@ public function logVersion() { $updater->log('[info] show HTML page'); $updater->logVersion(); $updaterUrl = explode('?', $_SERVER['REQUEST_URI'], 2)[0]; -if(strpos($updaterUrl, 'index.php') === false) { +if (strpos($updaterUrl, 'index.php') === false) { $updaterUrl = rtrim($updaterUrl, '/') . '/index.php'; } ?> @@ -1730,7 +1727,7 @@ public function logVersion() {
- isAuthenticated()): ?> + isAuthenticated()): ?>
  • Initializing

    @@ -1740,7 +1737,7 @@ public function logVersion() { updateAvailable() || $stepNumber > 0) { $buttonText = 'Start update'; - if($stepNumber > 0) { + if ($stepNumber > 0) { $buttonText = 'Continue update'; } ?> @@ -1751,53 +1748,53 @@ public function logVersion() {
-
  • +
  • Check for expected files

  • -
  • +
  • Check for write permissions

  • -
  • +
  • Create backup

  • -
  • +
  • Downloading

  • -
  • +
  • Verifying integrity

  • -
  • +
  • Extracting

  • -
  • +
  • Enable maintenance mode

  • -
  • +
  • Replace entry points

  • -
  • +
  • Delete old files

  • -
  • +
  • Move new files in place

  • -
  • +
  • Continue with web based updater

  • -
  • +
  • Done

    @@ -1829,7 +1826,7 @@ public function logVersion() {
  • -isAuthenticated()): ?> +isAuthenticated()) : ?>