Skip to content

Commit

Permalink
[ticket/15851] Automatic update downloader
Browse files Browse the repository at this point in the history
PHPBB3-15851
  • Loading branch information
CHItA authored and marc1706 committed Feb 13, 2025
1 parent c8b1f24 commit 7cb18e8
Show file tree
Hide file tree
Showing 3 changed files with 307 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ protected function set_test_passed($is_passed)
*/
protected function check_php_version()
{
if (version_compare(PHP_VERSION, '7.2.0', '<'))
if (version_compare(PHP_VERSION, '8.1.0', '<'))
{
$this->response_helper->add_error_message('PHP_VERSION_REQD', 'PHP_VERSION_REQD_EXPLAIN');

Expand Down
137 changes: 137 additions & 0 deletions update/controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/

namespace phpbb\update;

use phpbb\filesystem\filesystem_interface;
use phpbb\language\language;

class controller
{
/** @var filesystem_interface Filesystem manager */
private filesystem_interface $filesystem;

/** @var get_updates Updater class */
private get_updates $updater;

/** @var language Translation handler */
private language $language;

/** @var string phpBB root path */
private string $phpbb_root_path;

/**
* Constructor.
*
* @param filesystem_interface $filesystem
* @param get_updates $updater
* @param language $language
* @param string $phpbb_root_path
*/
public function __construct(
filesystem_interface $filesystem,
get_updates $updater,
language $language,
string $phpbb_root_path)
{
$this->filesystem = $filesystem;
$this->language = $language;
$this->updater = $updater;
$this->phpbb_root_path = $phpbb_root_path;
}

/**
* Handle requests.
*
* @param string $download The download URL.
*
* @return string[] Unencoded json response.
*/
public function handle(string $download): array
{
$update_path = $this->phpbb_root_path . 'store/update.zip';
$status = ['status' => 'continue'];
if (!file_exists($update_path))
{
$result = $this->updater->download($download, $update_path);
if (!$result)
{
return [
'status' => 'error',
'error' => $this->language->lang('COULD_NOT_DOWNLOAD_UPDATE_PACKAGE')
];
}

return $status;
}

if (!file_exists($update_path . '.sig'))
{
$result = $this->updater->download($download . '.sig', $update_path . '.sig');
if (!$result)
{
return [
'status' => 'error',
'error' => $this->language->lang('COULD_NOT_DOWNLOAD_UPDATE_SIGNATURE')
];
}
return $status;
}

if (!is_dir($this->phpbb_root_path . 'store/update'))
{
$result = $this->updater->validate($update_path, $update_path . '.sig');
if (!$result)
{
return [
'status' => 'error',
'error' => $this->language->lang('UPDATE_SIGNATURE_INVALID')
];
}

$result = $this->updater->extract($update_path, $this->phpbb_root_path . 'store/update');
if (!$result)
{
return [
'status' => 'error',
'error' => $this->language->lang('COULD_NOT_EXTRACT_UPDATE')
];
}

return $status;
}

if (!is_dir($this->phpbb_root_path . 'install'))
{
$result = $this->updater->copy($this->phpbb_root_path . 'store/update');
if (!$result)
{
return [
'status' => 'error',
'error' => $this->language->lang('COULD_NOT_WRITE_UPDATE_FILES')
];
}

return $status;
}

$this->filesystem->remove([
$this->phpbb_root_path . 'store/update',
$update_path,
$update_path . '.sig'
]);

$status['status'] = 'done';
return $status;
}
}
169 changes: 169 additions & 0 deletions update/get_updates.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/

namespace phpbb\update;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use phpbb\filesystem\exception\filesystem_exception;
use phpbb\filesystem\filesystem_interface;
use SodiumException;
use ZipArchive;

class get_updates
{
/** @var filesystem_interface Filesystem managerr */
private filesystem_interface $filesystem;

/** @var Client HTTP client */
private Client $http_client;

/** @var string Public key to verify package */
private string $public_key;

/** @var string phpBB root path */
private string $phpbb_root_path;

/** @var ZipArchive Zip extractor */
private ZipArchive $zipper;

/**
* Constructor
*
* @param filesystem_interface $filesystem
* @param string $public_key
* @param string $phpbb_root_path
*/
public function __construct(
filesystem_interface $filesystem,
string $public_key,
string $phpbb_root_path)
{
$this->filesystem = $filesystem;
$this->http_client = new Client();
$this->public_key = base64_decode($public_key);
$this->phpbb_root_path = $phpbb_root_path;
$this->zipper = new ZipArchive();
}

/**
* Download the update package.
*
* @param string $url Download link to the update.
* @param string $storage_path Location for the download.
*
* @return bool Whether the download completed successfully.
*/
public function download(string $url, string $storage_path): bool
{
try
{
$this->http_client->request('GET', $url, [
'sink' => $storage_path,
'allow_redirects' => false
]);
}
catch (GuzzleException)
{
return false;
}

return true;
}

/**
* Validate the downloaded file.
*
* @param string $file_path Path to the download.
* @param string $signature_path The signature file.
*
* @return bool Whether the signature is correct or not.
*/
public function validate(string $file_path, string $signature_path): bool
{
if (file_exists($file_path) === false)
{
return false;
}

if (file_exists($signature_path) === false)
{
return false;
}

$raw_signature = file_get_contents($signature_path);

$hash = hash_file('sha384', $file_path, true);
if ($hash === false)
{
return false;
}

$signature = base64_decode($raw_signature);
if ($signature === false)
{
return false;
}

try
{
return sodium_crypto_sign_verify_detached($signature, $hash, $this->public_key);
}
catch (SodiumException)
{
return false;
}
}

/**
* Extract the downloaded archive.
*
* @param string $zip_file Path to the archive.
* @param string $to Path to where to extract the archive to.
*
* @return bool Whether the extraction completed successfully.
*/
public function extract(string $zip_file, string $to): bool
{
if ($this->zipper->open($zip_file) === false)
{
return false;
}

$result = $this->zipper->extractTo($to);
$this->zipper->close();

return $result;
}

/**
* Copy the update package to the root folder.
*
* @param string $src_dir Where to copy from.
*
* @return bool Whether the files were copied successfully.
*/
public function copy(string $src_dir): bool
{
try
{
$this->filesystem->mirror($src_dir, $this->phpbb_root_path);
}
catch (filesystem_exception)
{
return false;
}

return true;
}
}

0 comments on commit 7cb18e8

Please sign in to comment.