diff options
author | Git'Fellow <carlos@reendex.com> | 2022-06-30 11:53:42 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-30 11:53:42 +0300 |
commit | dab7606ae895f2dc7903124aafe6d1f6b0d183b5 (patch) | |
tree | 7e51a274b6f420a3fd9c8dff6a977269c8e91f9c | |
parent | bd6a4114864faec390044e8bb4f4505db73bfbce (diff) |
Introduce Update Directory
Allow to specify a specific directory as updater working directory
-rw-r--r-- | lib/Updater.php | 181 |
1 files changed, 89 insertions, 92 deletions
diff --git a/lib/Updater.php b/lib/Updater.php index b527e8d..7ede201 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 @@ class Updater { 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 @@ class Updater { $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 @@ class Updater { * * @return string */ - private function getDataDirectoryLocation() { - return $this->configValues['datadirectory']; + private function getUpdateDirectoryLocation() { + return $this->configValues['updatedirectory'] ?? $this->configValues['datadirectory']; } /** @@ -250,11 +247,11 @@ class Updater { */ 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 @@ class Updater { $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 @@ class Updater { $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 @@ class Updater { 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 @@ class Updater { ]; // 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 @@ class Updater { $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 @@ class Updater { ); } - 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 @@ class Updater { $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 @@ class Updater { } $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 @@ class Updater { $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 @@ class Updater { ]); } - 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 @@ class Updater { ]; $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 @@ class Updater { * @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 @@ class Updater { 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 @@ class Updater { 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 @@ EOF; OPENSSL_ALGO_SHA512 ); - if($validSignature === false) { + if ($validSignature === false) { throw new \Exception('Signature of update is not valid'); } @@ -675,7 +672,7 @@ EOF; 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 @@ EOF; $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 @@ EOF; // 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 @@ EOF; 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 @@ EOF; * @throws \Exception */ private function recursiveDelete($folder) { - if(!file_exists($folder)) { + if (!file_exists($folder)) { return; } $iterator = new \RecursiveIteratorIterator( @@ -791,7 +788,7 @@ EOF; } $state = rmdir($folder); - if($state === false) { + if ($state === false) { throw new \Exception('Could not rmdir ' . $folder); } } @@ -805,12 +802,12 @@ EOF; $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 @@ EOF; } $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 @@ EOF; } $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 @@ EOF; $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 @@ EOF; } } - 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 @@ EOF; ); } } - 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 @@ EOF; '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 @@ EOF; 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 @@ EOF; * @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 @@ EOF; 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 @@ EOF; 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 @@ EOF; } } - if($step >= 7) { + if ($step >= 7) { $this->silentLog('[info] rollbackChanges - step >= 7'); // TODO: If it fails after step 7: Rollback } @@ -1110,7 +1107,7 @@ EOF; $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 @@ EOF; * @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 @@ EOF; $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'); } |