Skip to content

Commit 1ed21d3

Browse files
committed
feat: allow to provide manual URL
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
1 parent 73b07ec commit 1ed21d3

File tree

8 files changed

+131
-36
lines changed

8 files changed

+131
-36
lines changed

index.php

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -553,11 +553,9 @@ private function getUpdateServerResponse() {
553553
*
554554
* @throws \Exception
555555
*/
556-
public function downloadUpdate() {
556+
public function downloadUpdate(?string $url = null) {
557557
$this->silentLog('[info] downloadUpdate()');
558558

559-
$response = $this->getUpdateServerResponse();
560-
561559
$storageLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/';
562560
if (file_exists($storageLocation)) {
563561
$this->silentLog('[info] storage location exists');
@@ -568,8 +566,26 @@ public function downloadUpdate() {
568566
throw new \Exception('Could not mkdir storage location');
569567
}
570568

571-
$fp = fopen($storageLocation . basename($response['url']), 'w+');
572-
$ch = curl_init($response['url']);
569+
$downloadURL = '';
570+
if ($url) {
571+
// If a URL is provided, use it directly
572+
$downloadURL = $url;
573+
} else {
574+
// Otherwise, get the download URLs from the update server
575+
$response = $this->getUpdateServerResponse();
576+
577+
if (!isset($response['url']) || !is_string($response['url'])) {
578+
throw new \Exception('Response from update server is missing url');
579+
}
580+
$downloadURL = $response['url'];
581+
}
582+
583+
if (!$downloadURL) {
584+
throw new \Exception('No download URL provided or available from update server');
585+
}
586+
587+
$fp = fopen($storageLocation . basename($downloadURL), 'w+');
588+
$ch = curl_init($downloadURL);
573589
curl_setopt_array($ch, [
574590
CURLOPT_FILE => $fp,
575591
CURLOPT_USERAGENT => 'Nextcloud Updater',
@@ -611,7 +627,7 @@ public function downloadUpdate() {
611627
$message .= ' - curl error message: ' . $curlErrorMessage;
612628
}
613629

614-
$message .= ' - URL: ' . htmlentities($response['url']);
630+
$message .= ' - URL: ' . htmlentities($downloadURL);
615631

616632
throw new \Exception($message);
617633
}
@@ -645,14 +661,24 @@ private function getDownloadedFilePath() {
645661
*
646662
* @throws \Exception
647663
*/
648-
public function verifyIntegrity() {
664+
public function verifyIntegrity(?string $urlOverride = null): void {
649665
$this->silentLog('[info] verifyIntegrity()');
650666

651667
if ($this->getCurrentReleaseChannel() === 'daily') {
652668
$this->silentLog('[info] current channel is "daily" which is not signed. Skipping verification.');
653669
return;
654670
}
655671

672+
if ($urlOverride) {
673+
$this->silentLog('[info] custom download url provided, cannot verify signature');
674+
return;
675+
}
676+
677+
if ($urlOverride) {
678+
$this->silentLog('[info] custom download url provided, cannot verify signature');
679+
return;
680+
}
681+
656682
$response = $this->getUpdateServerResponse();
657683
if (empty($response['signature'])) {
658684
throw new \Exception('No signature specified for defined update');

lib/UpdateCommand.php

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class UpdateCommand extends Command {
4141
protected $skipBackup = false;
4242

4343
protected bool $skipUpgrade = false;
44+
protected string $urlOverride = '';
4445

4546
/** @var array strings of text for stages of updater */
4647
protected $checkTexts = [
@@ -65,7 +66,8 @@ protected function configure() {
6566
->setDescription('Updates the code of an Nextcloud instance')
6667
->setHelp("This command fetches the latest code that is announced via the updater server and safely replaces the existing code with the new one.")
6768
->addOption('no-backup', null, InputOption::VALUE_NONE, 'Skip backup of current Nextcloud version')
68-
->addOption('no-upgrade', null, InputOption::VALUE_NONE, "Don't automatically run occ upgrade");
69+
->addOption('no-upgrade', null, InputOption::VALUE_NONE, "Don't automatically run occ upgrade")
70+
->addOption('url', null, InputOption::VALUE_OPTIONAL, 'The URL of the Nextcloud release to download');
6971
}
7072

7173
public static function getUpdaterVersion(): string {
@@ -78,8 +80,9 @@ public static function getUpdaterVersion(): string {
7880
}
7981

8082
protected function execute(InputInterface $input, OutputInterface $output) {
81-
$this->skipBackup = $input->getOption('no-backup');
82-
$this->skipUpgrade = $input->getOption('no-upgrade');
83+
$this->skipBackup = (bool)$input->getOption('no-backup');
84+
$this->skipUpgrade = (bool)$input->getOption('no-upgrade');
85+
$this->urlOverride = (string)$input->getOption('url');
8386

8487
$version = static::getUpdaterVersion();
8588
$output->writeln('Nextcloud Updater - version: ' . $version);
@@ -152,7 +155,12 @@ protected function execute(InputInterface $input, OutputInterface $output) {
152155
$output->writeln('Current version is ' . $this->updater->getCurrentVersion() . '.');
153156

154157
// needs to be called that early because otherwise updateAvailable() returns false
155-
$updateString = $this->updater->checkForUpdate();
158+
if ($this->urlOverride) {
159+
$this->updater->log('[info] Using URL override: ' . $this->urlOverride);
160+
$updateString = 'Update check forced with URL override: ' . $this->urlOverride;
161+
} else {
162+
$updateString = $this->updater->checkForUpdate();
163+
}
156164

157165
$output->writeln('');
158166

@@ -165,9 +173,11 @@ protected function execute(InputInterface $input, OutputInterface $output) {
165173

166174
$output->writeln('');
167175

168-
if (!$this->updater->updateAvailable() && $stepNumber === 0) {
169-
$output->writeln('Nothing to do.');
170-
return 0;
176+
if (!$this->urlOverride) {
177+
if (!$this->updater->updateAvailable() && $stepNumber === 0) {
178+
$output->writeln('Nothing to do.');
179+
return 0;
180+
}
171181
}
172182

173183
$questionText = 'Start update';
@@ -374,10 +384,10 @@ protected function executeStep($step) {
374384
}
375385
break;
376386
case 4:
377-
$this->updater->downloadUpdate();
387+
$this->updater->downloadUpdate($this->urlOverride);
378388
break;
379389
case 5:
380-
$this->updater->verifyIntegrity();
390+
$this->updater->verifyIntegrity($this->urlOverride);
381391
break;
382392
case 6:
383393
$this->updater->extractDownload();

lib/Updater.php

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -517,11 +517,9 @@ private function getUpdateServerResponse() {
517517
*
518518
* @throws \Exception
519519
*/
520-
public function downloadUpdate() {
520+
public function downloadUpdate(?string $url = null) {
521521
$this->silentLog('[info] downloadUpdate()');
522522

523-
$response = $this->getUpdateServerResponse();
524-
525523
$storageLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/';
526524
if (file_exists($storageLocation)) {
527525
$this->silentLog('[info] storage location exists');
@@ -532,8 +530,26 @@ public function downloadUpdate() {
532530
throw new \Exception('Could not mkdir storage location');
533531
}
534532

535-
$fp = fopen($storageLocation . basename($response['url']), 'w+');
536-
$ch = curl_init($response['url']);
533+
$downloadURL = '';
534+
if ($url) {
535+
// If a URL is provided, use it directly
536+
$downloadURL = $url;
537+
} else {
538+
// Otherwise, get the download URLs from the update server
539+
$response = $this->getUpdateServerResponse();
540+
541+
if (!isset($response['url']) || !is_string($response['url'])) {
542+
throw new \Exception('Response from update server is missing url');
543+
}
544+
$downloadURL = $response['url'];
545+
}
546+
547+
if (!$downloadURL) {
548+
throw new \Exception('No download URL provided or available from update server');
549+
}
550+
551+
$fp = fopen($storageLocation . basename($downloadURL), 'w+');
552+
$ch = curl_init($downloadURL);
537553
curl_setopt_array($ch, [
538554
CURLOPT_FILE => $fp,
539555
CURLOPT_USERAGENT => 'Nextcloud Updater',
@@ -575,7 +591,7 @@ public function downloadUpdate() {
575591
$message .= ' - curl error message: ' . $curlErrorMessage;
576592
}
577593

578-
$message .= ' - URL: ' . htmlentities($response['url']);
594+
$message .= ' - URL: ' . htmlentities($downloadURL);
579595

580596
throw new \Exception($message);
581597
}
@@ -609,14 +625,24 @@ private function getDownloadedFilePath() {
609625
*
610626
* @throws \Exception
611627
*/
612-
public function verifyIntegrity() {
628+
public function verifyIntegrity(?string $urlOverride = null): void {
613629
$this->silentLog('[info] verifyIntegrity()');
614630

615631
if ($this->getCurrentReleaseChannel() === 'daily') {
616632
$this->silentLog('[info] current channel is "daily" which is not signed. Skipping verification.');
617633
return;
618634
}
619635

636+
if ($urlOverride) {
637+
$this->silentLog('[info] custom download url provided, cannot verify signature');
638+
return;
639+
}
640+
641+
if ($urlOverride) {
642+
$this->silentLog('[info] custom download url provided, cannot verify signature');
643+
return;
644+
}
645+
620646
$response = $this->getUpdateServerResponse();
621647
if (empty($response['signature'])) {
622648
throw new \Exception('No signature specified for defined update');

updater.phar

2.41 KB
Binary file not shown.

vendor/autoload.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@
1414
echo $err;
1515
}
1616
}
17-
trigger_error(
18-
$err,
19-
E_USER_ERROR
20-
);
17+
throw new RuntimeException($err);
2118
}
2219

2320
require_once __DIR__ . '/composer/autoload_real.php';

vendor/composer/InstalledVersions.php

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,23 @@
2626
*/
2727
class InstalledVersions
2828
{
29+
/**
30+
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
31+
* @internal
32+
*/
33+
private static $selfDir = null;
34+
2935
/**
3036
* @var mixed[]|null
3137
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
3238
*/
3339
private static $installed;
3440

41+
/**
42+
* @var bool
43+
*/
44+
private static $installedIsLocalDir;
45+
3546
/**
3647
* @var bool|null
3748
*/
@@ -309,6 +320,24 @@ public static function reload($data)
309320
{
310321
self::$installed = $data;
311322
self::$installedByVendor = array();
323+
324+
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
325+
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
326+
// so we have to assume it does not, and that may result in duplicate data being returned when listing
327+
// all installed packages for example
328+
self::$installedIsLocalDir = false;
329+
}
330+
331+
/**
332+
* @return string
333+
*/
334+
private static function getSelfDir()
335+
{
336+
if (self::$selfDir === null) {
337+
self::$selfDir = strtr(__DIR__, '\\', '/');
338+
}
339+
340+
return self::$selfDir;
312341
}
313342

314343
/**
@@ -322,19 +351,27 @@ private static function getInstalled()
322351
}
323352

324353
$installed = array();
354+
$copiedLocalDir = false;
325355

326356
if (self::$canGetVendors) {
357+
$selfDir = self::getSelfDir();
327358
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
359+
$vendorDir = strtr($vendorDir, '\\', '/');
328360
if (isset(self::$installedByVendor[$vendorDir])) {
329361
$installed[] = self::$installedByVendor[$vendorDir];
330362
} elseif (is_file($vendorDir.'/composer/installed.php')) {
331363
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
332364
$required = require $vendorDir.'/composer/installed.php';
333-
$installed[] = self::$installedByVendor[$vendorDir] = $required;
334-
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
335-
self::$installed = $installed[count($installed) - 1];
365+
self::$installedByVendor[$vendorDir] = $required;
366+
$installed[] = $required;
367+
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
368+
self::$installed = $required;
369+
self::$installedIsLocalDir = true;
336370
}
337371
}
372+
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
373+
$copiedLocalDir = true;
374+
}
338375
}
339376
}
340377

@@ -350,7 +387,7 @@ private static function getInstalled()
350387
}
351388
}
352389

353-
if (self::$installed !== array()) {
390+
if (self::$installed !== array() && !$copiedLocalDir) {
354391
$installed[] = self::$installed;
355392
}
356393

vendor/composer/installed.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
'name' => '__root__',
44
'pretty_version' => 'dev-master',
55
'version' => 'dev-master',
6-
'reference' => 'b3b17cf837de98718ef2bc2277fbc1db24f4288f',
6+
'reference' => 'c3ba72da63d6b7f90b3a2a757303726ac9420efa',
77
'type' => 'library',
88
'install_path' => __DIR__ . '/../../',
99
'aliases' => array(),
@@ -13,7 +13,7 @@
1313
'__root__' => array(
1414
'pretty_version' => 'dev-master',
1515
'version' => 'dev-master',
16-
'reference' => 'b3b17cf837de98718ef2bc2277fbc1db24f4288f',
16+
'reference' => 'c3ba72da63d6b7f90b3a2a757303726ac9420efa',
1717
'type' => 'library',
1818
'install_path' => __DIR__ . '/../../',
1919
'aliases' => array(),

vendor/composer/platform_check.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
2020
}
2121
}
22-
trigger_error(
23-
'Composer detected issues in your platform: ' . implode(' ', $issues),
24-
E_USER_ERROR
22+
throw new \RuntimeException(
23+
'Composer detected issues in your platform: ' . implode(' ', $issues)
2524
);
2625
}

0 commit comments

Comments
 (0)