From 686065c2948b2cd58b0d0a3e59f07bfc0fa973f8 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Mon, 2 Oct 2023 13:49:00 +0200 Subject: [PATCH] some cleanup; hide ssl-related settings when ssl-usage is off when creating/updating domains; add database-update option to update-cli if files are already up-to-date Signed-off-by: Michael Kaufmann --- lib/Froxlor/Api/Commands/Domains.php | 10 +- lib/Froxlor/Api/Commands/HostingPlans.php | 4 +- lib/Froxlor/Cli/CliCommand.php | 17 +-- lib/Froxlor/Cli/ConfigDiff.php | 3 + lib/Froxlor/Cli/ConfigServices.php | 32 +++-- lib/Froxlor/Cli/InstallCommand.php | 15 ++- lib/Froxlor/Cli/MasterCron.php | 124 +++++++++--------- lib/Froxlor/Cli/PhpSessionclean.php | 6 +- lib/Froxlor/Cli/RunApiCommand.php | 15 +-- lib/Froxlor/Cli/SwitchServerIp.php | 7 +- lib/Froxlor/Cli/UpdateCommand.php | 51 ++++--- lib/Froxlor/Cli/UserCommand.php | 14 +- lib/Froxlor/Cli/ValidateAcmeWebroot.php | 15 +-- lib/Froxlor/UI/Callbacks/Domain.php | 2 +- lng/de.lng.php | 1 + lng/en.lng.php | 1 + .../Froxlor/src/js/components/domains.js | 22 +++- 17 files changed, 199 insertions(+), 140 deletions(-) diff --git a/lib/Froxlor/Api/Commands/Domains.php b/lib/Froxlor/Api/Commands/Domains.php index 0647f455..23f25d0c 100644 --- a/lib/Froxlor/Api/Commands/Domains.php +++ b/lib/Froxlor/Api/Commands/Domains.php @@ -316,9 +316,9 @@ class Domains extends ApiCommand implements ResourceEntity $mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, -1); $ssl_redirect = $this->getBoolParam('ssl_redirect', true, 0); $letsencrypt = $this->getBoolParam('letsencrypt', true, 0); + $sslenabled = $this->getBoolParam('sslenabled', true, 1); $dont_use_default_ssl_ipandport_if_empty = $this->getBoolParam('dont_use_default_ssl_ipandport_if_empty', true, 0); $p_ssl_ipandports = $this->getParam('ssl_ipandport', true, $dont_use_default_ssl_ipandport_if_empty ? [] : explode(',', Settings::Get('system.defaultsslip'))); - $sslenabled = $this->getBoolParam('sslenabled', true, 1); $http2 = $this->getBoolParam('http2', true, 0); $hsts_maxage = $this->getParam('hsts_maxage', true, 0); $hsts_sub = $this->getBoolParam('hsts_sub', true, 0); @@ -544,6 +544,10 @@ class Domains extends ApiCommand implements ResourceEntity $ssl_specialsettings = Validate::validate(str_replace("\r\n", "\n", $ssl_specialsettings), 'ssl_specialsettings', '/^[^\0]*$/', '', [], true); } } + if (Settings::Get('system.use_ssl') == "1" && $sslenabled == 1 && empty($ssl_ipandports)) { + // enabled ssl for the domain but no ssl ip/port is selected + Response::standardError('nosslippportgiven', '', true); + } if (Settings::Get('system.use_ssl') == "0" || empty($ssl_ipandports)) { $ssl_redirect = 0; $letsencrypt = 0; @@ -1517,6 +1521,10 @@ class Domains extends ApiCommand implements ResourceEntity if ($remove_ssl_ipandport || (!empty($p_ssl_ipandports) && $p_ssl_ipandports[0] == -1)) { $ssl_ipandports = []; } + if (Settings::Get('system.use_ssl') == "1" && $sslenabled && empty($ssl_ipandports)) { + // enabled ssl for the domain but no ssl ip/port is selected + Response::standardError('nosslippportgiven', '', true); + } if (Settings::Get('system.use_ssl') == "0" || empty($ssl_ipandports)) { $ssl_redirect = 0; $letsencrypt = 0; diff --git a/lib/Froxlor/Api/Commands/HostingPlans.php b/lib/Froxlor/Api/Commands/HostingPlans.php index ce5f4832..5954ff08 100644 --- a/lib/Froxlor/Api/Commands/HostingPlans.php +++ b/lib/Froxlor/Api/Commands/HostingPlans.php @@ -201,7 +201,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity // validation $name = Validate::validate(trim($name), 'name', Validate::REGEX_DESC_TEXT, '', [], true); - $description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_CONF_TEXT); + $description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_DESC_TEXT); if (Settings::Get('system.mail_quota_enabled') != '1') { $value_arr['email_quota'] = -1; @@ -383,7 +383,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity // validation $name = Validate::validate(trim($name), 'name', Validate::REGEX_DESC_TEXT, '', [], true); - $description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_CONF_TEXT); + $description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_DESC_TEXT); if (Settings::Get('system.mail_quota_enabled') != '1') { $value_arr['email_quota'] = -1; diff --git a/lib/Froxlor/Cli/CliCommand.php b/lib/Froxlor/Cli/CliCommand.php index 0db8c5a2..83fcfe70 100644 --- a/lib/Froxlor/Cli/CliCommand.php +++ b/lib/Froxlor/Cli/CliCommand.php @@ -25,19 +25,18 @@ namespace Froxlor\Cli; -use PDO; use Exception; +use Froxlor\Database\Database; use Froxlor\Froxlor; use Froxlor\Settings; -use Froxlor\Database\Database; +use PDO; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class CliCommand extends Command { - protected function validateRequirements(InputInterface $input, OutputInterface $output, bool $ignore_has_updates = false): int + protected function validateRequirements(OutputInterface $output, bool $ignore_has_updates = false): int { if (!file_exists(Froxlor::getInstallDir() . '/lib/userdata.inc.php')) { $output->writeln("Could not find froxlor's userdata.inc.php file. You should use this script only with an installed froxlor system."); @@ -116,9 +115,11 @@ class CliCommand extends Command return $userinfo; } - private function runUpdate(OutputInterface $output): int + protected function runUpdate(OutputInterface $output, bool $manual = false): int { - $output->writeln('Automatic update is activated and we are going to proceed without any notices'); + if (!$manual) { + $output->writeln('Automatic update is activated and we are going to proceed without any notices'); + } include_once Froxlor::getInstallDir() . '/lib/tables.inc.php'; define('_CRON_UPDATE', 1); ob_start([ @@ -127,11 +128,11 @@ class CliCommand extends Command ]); include_once Froxlor::getInstallDir() . '/install/updatesql.php'; ob_end_flush(); - $output->writeln('Automatic update done - you should check your settings to be sure everything is fine'); + $output->writeln('' . ($manual ? 'Database' : 'Automatic') . ' update done - you should check your settings to be sure everything is fine'); return self::SUCCESS; } - private function cleanUpdateOutput($buffer) + private function cleanUpdateOutput($buffer): string { return strip_tags(preg_replace("//", "\n", $buffer)); } diff --git a/lib/Froxlor/Cli/ConfigDiff.php b/lib/Froxlor/Cli/ConfigDiff.php index 82340b6c..d7251617 100644 --- a/lib/Froxlor/Cli/ConfigDiff.php +++ b/lib/Froxlor/Cli/ConfigDiff.php @@ -45,6 +45,9 @@ final class ConfigDiff extends CliCommand ->addOption('diff-params', '', InputOption::VALUE_REQUIRED, 'Additional parameters for `diff`, e.g. --diff-params="--color=always"'); } + /** + * @throws \Exception + */ protected function execute(InputInterface $input, OutputInterface $output): int { require Froxlor::getInstallDir() . '/lib/functions.php'; diff --git a/lib/Froxlor/Cli/ConfigServices.php b/lib/Froxlor/Cli/ConfigServices.php index 4fa1fcda..ba6389ba 100644 --- a/lib/Froxlor/Cli/ConfigServices.php +++ b/lib/Froxlor/Cli/ConfigServices.php @@ -25,6 +25,7 @@ namespace Froxlor\Cli; +use Exception; use Froxlor\Config\ConfigParser; use Froxlor\Database\Database; use Froxlor\FileDir; @@ -40,7 +41,6 @@ use Symfony\Component\Console\Style\SymfonyStyle; final class ConfigServices extends CliCommand { - private $yes_to_all_supported = [ 'bookworm', 'bionic', @@ -62,11 +62,9 @@ final class ConfigServices extends CliCommand ->addOption('yes-to-all', 'A', InputOption::VALUE_NONE, 'Install packages without asking questions (Debian/Ubuntu only currently)'); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $result = self::SUCCESS; - - $result = $this->validateRequirements($input, $output); + $result = $this->validateRequirements($output); require Froxlor::getInstallDir() . '/lib/functions.php'; @@ -93,7 +91,7 @@ final class ConfigServices extends CliCommand if ($result == self::SUCCESS) { $io = new SymfonyStyle($input, $output); if ($input->getOption('create')) { - $result = $this->createConfig($input, $output, $io); + $result = $this->createConfig($output, $io); } elseif ($input->getOption('apply')) { $result = $this->applyConfig($input, $output, $io); } elseif ($input->getOption('list') || $input->getOption('daemon')) { @@ -158,7 +156,10 @@ final class ConfigServices extends CliCommand fclose($fp); } - private function createConfig(InputInterface $input, OutputInterface $output, SymfonyStyle $io) + /** + * @throws Exception + */ + private function createConfig(OutputInterface $output, SymfonyStyle $io): int { $_daemons_config = [ 'distro' => "" @@ -285,7 +286,10 @@ final class ConfigServices extends CliCommand return self::SUCCESS; } - private function applyConfig(InputInterface $input, OutputInterface $output, SymfonyStyle $io) + /** + * @throws Exception + */ + private function applyConfig(InputInterface $input, OutputInterface $output, SymfonyStyle $io): int { $applyFile = $input->getOption('apply'); @@ -429,7 +433,10 @@ final class ConfigServices extends CliCommand } } - private function getReplacerArray() + /** + * @throws Exception + */ + private function getReplacerArray(): array { $customer_tmpdir = '/tmp/'; if (Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_tmpdir') != '') { @@ -438,7 +445,7 @@ final class ConfigServices extends CliCommand $customer_tmpdir = Settings::Get('phpfpm.tmpdir'); } - // try to convert namserver hosts to ip's + // try to convert nameserver hosts to ip's $ns_ips = ""; $known_ns_ips = []; if (Settings::Get('system.nameservers') != '') { @@ -484,12 +491,12 @@ final class ConfigServices extends CliCommand Database::needSqlData(); $sql = Database::getSqlData(); - $replace_arr = [ + return [ '' => $sql['user'], '' => $sql['passwd'], '' => $sql['db'], '' => $sql['host'], - '' => isset($sql['socket']) ? $sql['socket'] : null, + '' => $sql['socket'] ?? null, '' => Settings::Get('system.hostname'), '' => Settings::Get('system.ipaddress'), '' => Settings::Get('system.nameservers'), @@ -508,6 +515,5 @@ final class ConfigServices extends CliCommand '' => Settings::Get('system.ssl_cert_file'), '' => Settings::Get('system.ssl_key_file'), ]; - return $replace_arr; } } diff --git a/lib/Froxlor/Cli/InstallCommand.php b/lib/Froxlor/Cli/InstallCommand.php index 45209dc1..cf00b6ba 100644 --- a/lib/Froxlor/Cli/InstallCommand.php +++ b/lib/Froxlor/Cli/InstallCommand.php @@ -26,13 +26,13 @@ namespace Froxlor\Cli; use Exception; -use Froxlor\Froxlor; use Froxlor\Config\ConfigParser; +use Froxlor\Froxlor; use Froxlor\Install\Install; use Froxlor\Install\Install\Core; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -53,7 +53,10 @@ final class InstallCommand extends Command ->addOption('create-userdata-from-str', 'c', InputOption::VALUE_REQUIRED, 'Creates lib/userdata.inc.php file from string created by web-install process'); } - protected function execute(InputInterface $input, OutputInterface $output) + /** + * @throws Exception + */ + protected function execute(InputInterface $input, OutputInterface $output): int { $result = self::SUCCESS; @@ -137,10 +140,12 @@ final class InstallCommand extends Command $decoded_input = []; } - $result = $this->showStep(0, $extended, $decoded_input); - return $result; + return $this->showStep(0, $extended, $decoded_input); } + /** + * @throws Exception + */ private function showStep(int $step = 0, bool $extended = false, array $decoded_input = []): int { $result = self::SUCCESS; diff --git a/lib/Froxlor/Cli/MasterCron.php b/lib/Froxlor/Cli/MasterCron.php index dd361d50..4364f5a8 100644 --- a/lib/Froxlor/Cli/MasterCron.php +++ b/lib/Froxlor/Cli/MasterCron.php @@ -25,19 +25,20 @@ namespace Froxlor\Cli; -use PDO; -use Froxlor\Froxlor; -use Froxlor\FileDir; -use Froxlor\Settings; -use Froxlor\FroxlorLogger; -use Froxlor\Database\Database; -use Froxlor\System\Cronjob; -use Froxlor\Cron\TaskId; +use Exception; use Froxlor\Cron\CronConfig; use Froxlor\Cron\System\Extrausers; +use Froxlor\Cron\TaskId; +use Froxlor\Database\Database; +use Froxlor\FileDir; +use Froxlor\Froxlor; +use Froxlor\FroxlorLogger; +use Froxlor\Settings; +use Froxlor\System\Cronjob; +use PDO; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\OutputInterface; final class MasterCron extends CliCommand @@ -57,10 +58,12 @@ final class MasterCron extends CliCommand ->addOption('no-fork', 'N', InputOption::VALUE_NONE, 'Do not fork to background (traffic cron only).'); } - protected function execute(InputInterface $input, OutputInterface $output) + /** + * @throws Exception + */ + protected function execute(InputInterface $input, OutputInterface $output): int { - $result = self::SUCCESS; - $result = $this->validateRequirements($input, $output); + $result = $this->validateRequirements($output); if ($result != self::SUCCESS) { // requirements failed, exit @@ -76,7 +79,7 @@ final class MasterCron extends CliCommand Cronjob::inserttask(TaskId::REBUILD_DNS); Cronjob::inserttask(TaskId::CREATE_QUOTA); Cronjob::inserttask(TaskId::REBUILD_CRON); - array_push($jobs, 'tasks'); + $jobs[] = 'tasks'; } define('CRON_IS_FORCED', 1); } @@ -94,7 +97,7 @@ final class MasterCron extends CliCommand foreach ($tasks_to_run as $ttr) { if (in_array($ttr, [TaskId::REBUILD_VHOST, TaskId::REBUILD_DNS, TaskId::CREATE_QUOTA, TaskId::REBUILD_CRON])) { Cronjob::inserttask($ttr); - array_push($jobs, 'tasks'); + $jobs[] = 'tasks'; } else { $output->writeln('Unknown task number "' . $ttr . '"'); } @@ -140,12 +143,12 @@ final class MasterCron extends CliCommand $cronfile::run(); } // free the lockfile - $this->unlockJob($job); + $this->unlockJob(); } } // regenerate nss-extrausers files / invalidate nscd cache (if used) - $this->refreshUsers((int) $tasks_cnt['jobcnt']); + $this->refreshUsers((int)$tasks_cnt['jobcnt']); // we have to check the system's last guid with every cron run // in case the admin installed new software which added a new user @@ -157,13 +160,13 @@ final class MasterCron extends CliCommand CronConfig::checkCrondConfigurationFile(); // check for old/compatibility cronjob file - if (file_exists(Froxlor::getInstallDir().'/scripts/froxlor_master_cronjob.php')) { - @unlink(Froxlor::getInstallDir().'/scripts/froxlor_master_cronjob.php'); - @rmdir(Froxlor::getInstallDir().'/scripts'); + if (file_exists(Froxlor::getInstallDir() . '/scripts/froxlor_master_cronjob.php')) { + @unlink(Froxlor::getInstallDir() . '/scripts/froxlor_master_cronjob.php'); + @rmdir(Froxlor::getInstallDir() . '/scripts'); } // reset cronlog-flag if set to "once" - if ((int) Settings::Get('logger.log_cron') == 1) { + if ((int)Settings::Get('logger.log_cron') == 1) { FroxlorLogger::getInstanceOf()->setCronLog(0); } @@ -173,27 +176,9 @@ final class MasterCron extends CliCommand return $result; } - private function refreshUsers(int $jobcount = 0) - { - if ($jobcount > 0) { - if (Settings::Get('system.nssextrausers') == 1) { - Extrausers::generateFiles($this->cronLog); - return; - } - - // clear NSCD cache if using fcgid or fpm, #1570 - not needed for nss-extrausers - if ((Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) && Settings::Get('system.nssextrausers') == 0) { - $false_val = false; - FileDir::safe_exec('nscd -i passwd 1> /dev/null', $false_val, [ - '>' - ]); - FileDir::safe_exec('nscd -i group 1> /dev/null', $false_val, [ - '>' - ]); - } - } - } - + /** + * @throws Exception + */ private function validateOwnership(OutputInterface $output) { // when using fcgid or fpm for froxlor-vhost itself, we have to check @@ -220,21 +205,6 @@ final class MasterCron extends CliCommand $output->writeln('OK'); } - private function getCronModule(string $cronname, OutputInterface $output) - { - $upd_stmt = Database::prepare(" - SELECT `cronclass` FROM `" . TABLE_PANEL_CRONRUNS . "` WHERE `cronfile` = :cron; - "); - $cron = Database::pexecute_first($upd_stmt, [ - 'cron' => $cronname - ]); - if ($cron) { - return $cron['cronclass']; - } - $output->writeln("Requested cronjob '" . $cronname . "' could not be found."); - return false; - } - private function lockJob(string $job, OutputInterface $output): bool { @@ -247,12 +217,12 @@ final class MasterCron extends CliCommand system("kill -CHLD " . (int)$jobinfo['pid'] . " 1> /dev/null 2> /dev/null", $check_pid_return); if ($check_pid_return == 1) { // Process does not seem to run, most likely it has died - $this->unlockJob($job); + $this->unlockJob(); } else { // cronjob still running, output info and stop $output->writeln([ 'Job "' . $jobinfo['job'] . '" is currently running.', - 'Started: ' . date('d.m.Y H:i', (int) $jobinfo['startts']), + 'Started: ' . date('d.m.Y H:i', (int)$jobinfo['startts']), 'PID: ' . $jobinfo['pid'] . '' ]); return false; @@ -268,8 +238,44 @@ final class MasterCron extends CliCommand return true; } - private function unlockJob(string $job): bool + private function unlockJob(): bool { return @unlink($this->lockFile); } + + private function getCronModule(string $cronname, OutputInterface $output) + { + $upd_stmt = Database::prepare(" + SELECT `cronclass` FROM `" . TABLE_PANEL_CRONRUNS . "` WHERE `cronfile` = :cron; + "); + $cron = Database::pexecute_first($upd_stmt, [ + 'cron' => $cronname + ]); + if ($cron) { + return $cron['cronclass']; + } + $output->writeln("Requested cronjob '" . $cronname . "' could not be found."); + return false; + } + + private function refreshUsers(int $jobcount = 0) + { + if ($jobcount > 0) { + if (Settings::Get('system.nssextrausers') == 1) { + Extrausers::generateFiles($this->cronLog); + return; + } + + // clear NSCD cache if using fcgid or fpm, #1570 - not needed for nss-extrausers + if ((Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) && Settings::Get('system.nssextrausers') == 0) { + $false_val = false; + FileDir::safe_exec('nscd -i passwd 1> /dev/null', $false_val, [ + '>' + ]); + FileDir::safe_exec('nscd -i group 1> /dev/null', $false_val, [ + '>' + ]); + } + } + } } diff --git a/lib/Froxlor/Cli/PhpSessionclean.php b/lib/Froxlor/Cli/PhpSessionclean.php index 9c8fbd33..2993e19f 100644 --- a/lib/Froxlor/Cli/PhpSessionclean.php +++ b/lib/Froxlor/Cli/PhpSessionclean.php @@ -43,9 +43,9 @@ final class PhpSessionclean extends CliCommand $this->addArgument('max-lifetime', InputArgument::OPTIONAL, 'The number of seconds after which data will be seen as "garbage" and potentially cleaned up. Defaults to "1440"'); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $result = $this->validateRequirements($input, $output); + $result = $this->validateRequirements($output); if ($result == self::SUCCESS) { if ((int)Settings::Get('phpfpm.enabled') == 1) { @@ -89,7 +89,7 @@ final class PhpSessionclean extends CliCommand if (count($paths_to_clean) > 0) { foreach ($paths_to_clean as $ptc) { - // find all files older then maxlifetime and delete them + // find all files older than maxlifetime and delete them FileDir::safe_exec("find -O3 \"" . $ptc . "\" -ignore_readdir_race -depth -mindepth 1 -name 'sess_*' -type f -cmin \"+" . $maxlifetime . "\" -delete"); } } diff --git a/lib/Froxlor/Cli/RunApiCommand.php b/lib/Froxlor/Cli/RunApiCommand.php index 5a070f42..9e692de0 100644 --- a/lib/Froxlor/Cli/RunApiCommand.php +++ b/lib/Froxlor/Cli/RunApiCommand.php @@ -26,14 +26,12 @@ namespace Froxlor\Cli; use Exception; -use PDO; -use Symfony\Component\Console\Input\InputInterface; +use Froxlor\Froxlor; use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Froxlor\Database\Database; -use Froxlor\Froxlor; final class RunApiCommand extends CliCommand { @@ -48,11 +46,9 @@ final class RunApiCommand extends CliCommand $this->addOption('show-params', 's', InputOption::VALUE_NONE, 'Show possible parameters for given api-command (given command will *not* be called)'); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $result = self::SUCCESS; - - $result = $this->validateRequirements($input, $output); + $result = $this->validateRequirements($output); require Froxlor::getInstallDir() . '/lib/functions.php'; @@ -110,6 +106,9 @@ final class RunApiCommand extends CliCommand return self::SUCCESS; } + /** + * @throws Exception + */ private function validateCommand(string $command): array { $command = explode(".", $command); diff --git a/lib/Froxlor/Cli/SwitchServerIp.php b/lib/Froxlor/Cli/SwitchServerIp.php index 5e1100cf..53f2b4ea 100644 --- a/lib/Froxlor/Cli/SwitchServerIp.php +++ b/lib/Froxlor/Cli/SwitchServerIp.php @@ -43,11 +43,9 @@ final class SwitchServerIp extends CliCommand ->addOption('list', 'l', InputOption::VALUE_NONE, 'List all IP addresses currently added for this server in froxlor'); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $result = self::SUCCESS; - - $result = $this->validateRequirements($input, $output); + $result = $this->validateRequirements($output); if ($result == self::SUCCESS && $input->getOption('list') == false && $input->getOption('switch') == false) { $output->writeln('Either --list or --switch option must be provided. Nothing to do, exiting.'); @@ -83,6 +81,7 @@ final class SwitchServerIp extends CliCommand $ip_list = $input->getOption('switch'); $has_error = false; + $ips_to_switch = []; foreach ($ip_list as $ips_combo) { $ip_pair = explode(",", $ips_combo); if (count($ip_pair) != 2) { diff --git a/lib/Froxlor/Cli/UpdateCommand.php b/lib/Froxlor/Cli/UpdateCommand.php index c97f6eaf..e49cb99e 100644 --- a/lib/Froxlor/Cli/UpdateCommand.php +++ b/lib/Froxlor/Cli/UpdateCommand.php @@ -27,9 +27,9 @@ namespace Froxlor\Cli; use Exception; use Froxlor\Froxlor; -use Froxlor\Settings; -use Froxlor\Install\Update; use Froxlor\Install\AutoUpdate; +use Froxlor\Install\Update; +use Froxlor\Settings; use Froxlor\System\Mailer; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -44,6 +44,7 @@ final class UpdateCommand extends CliCommand $this->setName('froxlor:update'); $this->setDescription('Check for newer version and update froxlor'); $this->addOption('check-only', 'c', InputOption::VALUE_NONE, 'Only check for newer version and exit') + ->addOption('database', 'd', InputOption::VALUE_NONE, 'Only run database updates in case updates are done via apt or manually.') ->addOption('mail-notify', 'm', InputOption::VALUE_NONE, 'Additionally inform administrator via email if a newer version was found') ->addOption('yes-to-all', 'A', InputOption::VALUE_NONE, 'Do not ask for download, extract and database-update, just do it (if not --check-only is set)') ->addOption('integer-return', 'i', InputOption::VALUE_NONE, 'Return integer whether a new version is available or not (implies --check-only). Useful for programmatic use.'); @@ -53,8 +54,36 @@ final class UpdateCommand extends CliCommand { $result = self::SUCCESS; + // database update only + if ($input->getOption('database')) { + $result = $this->validateRequirements($input, $output, true); + if ($result == self::SUCCESS) { + if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) { + $output->writeln('' . lng('updates.dbupdate_required') . ''); + if ($input->getOption('check-only')) { + $output->writeln('Doing nothing because of "check-only" flag.'); + } else { + $yestoall = $input->getOption('yes-to-all') !== false; + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion('Update database? [no] ', false, '/^(y|j)/i'); + if ($yestoall || $helper->ask($input, $output, $question)) { + $result = $this->runUpdate($output, true); + } + } + return $result; + } + $output->writeln('' . lng('update.noupdatesavail', (Settings::Get('system.update_channel') == 'testing' ? lng('serversettings.uc_testing') . ' ' : '')) . ''); + } + return $result; + } + $result = $this->validateRequirements($input, $output); + if ($result != self::SUCCESS) { + // requirements failed, exit + return $result; + } + require Froxlor::getInstallDir() . '/lib/functions.php'; // version check @@ -182,22 +211,4 @@ final class UpdateCommand extends CliCommand } } } - - private function updateDatabase() - { - include_once Froxlor::getInstallDir() . '/lib/tables.inc.php'; - define('_CRON_UPDATE', 1); - ob_start([ - $this, - 'cleanUpdateOutput' - ]); - include_once Froxlor::getInstallDir() . '/install/updatesql.php'; - ob_end_flush(); - return self::SUCCESS; - } - - private function cleanUpdateOutput($buffer) - { - return strip_tags(preg_replace("//", "\n", $buffer)); - } } diff --git a/lib/Froxlor/Cli/UserCommand.php b/lib/Froxlor/Cli/UserCommand.php index 1a7674b0..3da5c429 100644 --- a/lib/Froxlor/Cli/UserCommand.php +++ b/lib/Froxlor/Cli/UserCommand.php @@ -26,15 +26,15 @@ namespace Froxlor\Cli; use Exception; -use Symfony\Component\Console\Input\InputInterface; +use Froxlor\Api\Commands\Admins; +use Froxlor\Api\Commands\Customers; +use Froxlor\Froxlor; +use Froxlor\System\Crypt; use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Froxlor\Api\Commands\Admins; -use Froxlor\Api\Commands\Customers; -use Froxlor\System\Crypt; -use Froxlor\Froxlor; final class UserCommand extends CliCommand { @@ -50,11 +50,11 @@ final class UserCommand extends CliCommand ->addOption('show-info', 's', InputOption::VALUE_NONE, 'Output information details of given user'); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $result = self::SUCCESS; - $result = $this->validateRequirements($input, $output); + $result = $this->validateRequirements($output); require Froxlor::getInstallDir() . '/lib/functions.php'; diff --git a/lib/Froxlor/Cli/ValidateAcmeWebroot.php b/lib/Froxlor/Cli/ValidateAcmeWebroot.php index 4d906399..ef0b46ae 100644 --- a/lib/Froxlor/Cli/ValidateAcmeWebroot.php +++ b/lib/Froxlor/Cli/ValidateAcmeWebroot.php @@ -48,15 +48,16 @@ final class ValidateAcmeWebroot extends CliCommand $this->addOption('yes-to-all', 'A', InputOption::VALUE_NONE, 'Do not ask for confirmation, update files if necessary'); } - protected function execute(InputInterface $input, OutputInterface $output) + /** + * @throws \Exception + */ + protected function execute(InputInterface $input, OutputInterface $output): int { - $result = self::SUCCESS; - - $result = $this->validateRequirements($input, $output, true); + $result = $this->validateRequirements($output, true); $io = new SymfonyStyle($input, $output); - if ((int) Settings::Get('system.leenabled') == 0) { + if ((int)Settings::Get('system.leenabled') == 0) { $io->info("Let's Encrypt not activated in froxlor settings."); $result = self::INVALID; } @@ -94,7 +95,7 @@ final class ValidateAcmeWebroot extends CliCommand $acmesh_challenge_dir = $recommended; // need to update the corresponding acme-alias config-file $acme_alias_file = Settings::Get('system.letsencryptacmeconf'); - $sed_params = "s@".$former_value."@" . $acmesh_challenge_dir . "@"; + $sed_params = "s@" . $former_value . "@" . $acmesh_challenge_dir . "@"; FileDir::safe_exec('sed -i -e "' . $sed_params . '" ' . escapeshellarg($acme_alias_file)); $count_changes++; } @@ -138,8 +139,6 @@ final class ValidateAcmeWebroot extends CliCommand $io->info("Domain '" . $domain . "' Le_Webroot value is correct"); } break; - } else { - continue; } } } diff --git a/lib/Froxlor/UI/Callbacks/Domain.php b/lib/Froxlor/UI/Callbacks/Domain.php index d476db9b..5fb7729b 100644 --- a/lib/Froxlor/UI/Callbacks/Domain.php +++ b/lib/Froxlor/UI/Callbacks/Domain.php @@ -190,7 +190,7 @@ class Domain // specified certificate for domain if ($attributes['fields']['domain_hascert'] == 1) { $result['icon'] .= ' text-success'; - } // shared certificates (e.g. subdomain if domain where certificate is specified) + } // shared certificates (e.g. subdomain of domain where certificate is specified) elseif ($attributes['fields']['domain_hascert'] == 2) { $result['icon'] .= ' text-warning'; $result['title'] .= "\n" . lng('panel.ssleditor_infoshared'); diff --git a/lng/de.lng.php b/lng/de.lng.php index 5a9b4ac9..ab66b97f 100644 --- a/lng/de.lng.php +++ b/lng/de.lng.php @@ -922,6 +922,7 @@ return [ 'domain_nopunycode' => 'Die Eingabe von Punycode (IDNA) ist nicht notwendig. Die Domain wird automatisch konvertiert.', 'dns_record_toolong' => 'Records/Labels können maximal 63 Zeichen lang sein', 'noipportgiven' => 'Keine IP/Port angegeben', + 'nosslippportgiven' => 'Wenn SSL aktiviert ist, muss eine SSL IP/Port angegeben werden', 'jsonextensionnotfound' => 'Diese Funktion benötigt die PHP json-Erweiterung.', 'cannotdeletesuperadmin' => 'Der erste Administrator kann nicht gelöscht werden.', 'no_wwwcnamae_ifwwwalias' => 'Es kann kein CNAME Eintrag für "www" angelegt werden, da die Domain einen www-Alias aktiviert hat. Ändere diese Einstellung auf "Kein Alias" oder "Wildcard Alias"', diff --git a/lng/en.lng.php b/lng/en.lng.php index 4bbd6f39..010c2dfb 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -993,6 +993,7 @@ return [ 'domain_nopunycode' => 'You must not specify punycode (IDNA). The domain will automatically be converted', 'dns_record_toolong' => 'Records/labels can only be up to 63 characters', 'noipportgiven' => 'No IP/port given', + 'nosslippportgiven' => 'When enabling SSL you need to select a SSL IP/port', 'jsonextensionnotfound' => 'This feature requires the php json-extension.', 'cannotdeletesuperadmin' => 'The first admin cannot be deleted.', 'no_wwwcnamae_ifwwwalias' => 'Cannot set CNAME record for "www" as domain is set to generate a www-alias. Please change settings to either "No alias" or "Wildcard alias"', diff --git a/templates/Froxlor/src/js/components/domains.js b/templates/Froxlor/src/js/components/domains.js index 8d7512a5..f7bfb4d2 100644 --- a/templates/Froxlor/src/js/components/domains.js +++ b/templates/Froxlor/src/js/components/domains.js @@ -1,4 +1,4 @@ -$(function() { +$(function () { // disable unusable php-configuration by customer settings $('#customerid').on('change', function () { @@ -84,4 +84,24 @@ $(function() { $('#section_d').show(); } }) + + /** + * ssl enabled domain - hide unnecessary/unused sections + */ + if ($('#id') && !$('#sslenabled').is(':checked')) { + $('#section_bssl>.formfields>.row').not(":first").addClass("d-none"); + } + + /** + * toggle show/hide of sections in case of ssl enabled flag + */ + $('#sslenabled').on('click', function () { + if ($(this).is(':checked')) { + // show sections + $('#section_bssl>.formfields>.row').removeClass("d-none"); + } else { + // hide unnecessary sections + $('#section_bssl>.formfields>.row').not(":first").addClass("d-none"); + } + }) });