diff --git a/core/Command/App/Install.php b/core/Command/App/Install.php index d87439b366480..6c743009995ce 100644 --- a/core/Command/App/Install.php +++ b/core/Command/App/Install.php @@ -45,6 +45,11 @@ protected function configure() { InputArgument::REQUIRED, 'install the specified app' ) + ->addArgument( + 'app-version', + InputArgument::OPTIONAL, + 'version of the app to install' + ) ->addOption( 'keep-disabled', null, @@ -68,6 +73,7 @@ protected function configure() { protected function execute(InputInterface $input, OutputInterface $output): int { $appId = $input->getArgument('app-id'); + $appVersion = $input->getArgument('app-version'); $forceEnable = (bool) $input->getOption('force'); if (\OC_App::getAppPath($appId)) { @@ -78,7 +84,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int try { /** @var Installer $installer */ $installer = \OC::$server->query(Installer::class); - $installer->downloadApp($appId, $input->getOption('allow-unstable')); + $installer->downloadApp($appId, $input->getOption('allow-unstable'), $appVersion); $result = $installer->installApp($appId, $forceEnable); } catch (\Exception $e) { $output->writeln('Error: ' . $e->getMessage()); diff --git a/lib/private/App/AppStore/Fetcher/AppFetcher.php b/lib/private/App/AppStore/Fetcher/AppFetcher.php index 47bdece372db3..5dda5719edf6f 100644 --- a/lib/private/App/AppStore/Fetcher/AppFetcher.php +++ b/lib/private/App/AppStore/Fetcher/AppFetcher.php @@ -142,26 +142,6 @@ protected function fetch($ETag, $content, $allowUnstable = false) { if (empty($releases)) { // Remove apps that don't have a matching release $response['data'][$dataKey] = []; - continue; - } - - // Get the highest version - $versions = []; - foreach ($releases as $release) { - $versions[] = $release['version']; - } - usort($versions, function ($version1, $version2) { - return version_compare($version1, $version2); - }); - $versions = array_reverse($versions); - if (isset($versions[0])) { - $highestVersion = $versions[0]; - foreach ($releases as $release) { - if ((string)$release['version'] === (string)$highestVersion) { - $response['data'][$dataKey]['releases'] = [$release]; - break; - } - } } } diff --git a/lib/private/Installer.php b/lib/private/Installer.php index dc81135b64410..3da8961f270c6 100644 --- a/lib/private/Installer.php +++ b/lib/private/Installer.php @@ -224,7 +224,7 @@ private function splitCerts(string $cert): array { * * @throws \Exception If the installation was not successful */ - public function downloadApp($appId, $allowUnstable = false) { + public function downloadApp($appId, $allowUnstable = false, ?string $appVersion = null) { $appId = strtolower($appId); $apps = $this->appFetcher->get($allowUnstable); @@ -293,11 +293,32 @@ public function downloadApp($appId, $allowUnstable = false) { $tempFile = $this->tempManager->getTemporaryFile('.tar.gz'); $timeout = $this->isCLI ? 0 : 120; $client = $this->clientService->newClient(); - $client->get($app['releases'][0]['download'], ['sink' => $tempFile, 'timeout' => $timeout]); + + $releaseIndex = null; + if ($appVersion !== null) { + foreach ($app['releases'] as $index => $release) { + if ($release['version'] == $appVersion) { + $releaseIndex = $index; + break; + } + } + if ($releaseIndex === null) { + throw new \Exception( + sprintf( + 'App with id %s has no version %s', + $appId, + $appVersion + ) + ); + } + } else { + $releaseIndex = 0; + } + $client->get($app['releases'][$releaseIndex]['download'], ['sink' => $tempFile, 'timeout' => $timeout]); // Check if the signature actually matches the downloaded content $certificate = openssl_get_publickey($app['certificate']); - $verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512); + $verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][$releaseIndex]['signature']), $certificate, OPENSSL_ALGO_SHA512); // PHP 8+ deprecates openssl_free_key and automatically destroys the key instance when it goes out of scope if ((PHP_VERSION_ID < 80000)) { openssl_free_key($certificate);