Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10555bff76 | ||
|
|
37aa7af4da | ||
|
|
4b75369597 | ||
|
|
9d0e463906 | ||
|
|
a7198f58ce | ||
|
|
47be4b2847 | ||
|
|
b0fae4bd14 | ||
|
|
4711a41436 | ||
|
|
faa71ceaef | ||
|
|
2d30394150 | ||
|
|
99c1182af8 | ||
|
|
d9abe58dd2 | ||
|
|
23034b8ad2 | ||
|
|
1cae5638d3 | ||
|
|
ce9a5f97a3 | ||
|
|
c38b90deef | ||
|
|
13daa7d6fa | ||
|
|
b0e43d332d | ||
|
|
75c8754fb4 | ||
|
|
e0fa64f897 | ||
|
|
ed72fd1766 | ||
|
|
826ae36647 | ||
|
|
9ddf24539e | ||
|
|
3940c1429d | ||
|
|
c236d9eaab | ||
|
|
688994e40c | ||
|
|
9facaee809 | ||
|
|
a7dd5f4685 | ||
|
|
da810ea953 | ||
|
|
51b6e067e8 | ||
|
|
34cf6698bc | ||
|
|
4642160724 | ||
|
|
78a259ef3b | ||
|
|
68cf4ab69a | ||
|
|
d5661d492d | ||
|
|
6900898ae1 | ||
|
|
d90fb7fa68 | ||
|
|
4ea8629fcc | ||
|
|
9d4ff8698d | ||
|
|
b164038846 | ||
|
|
5c46960734 | ||
|
|
a7f4f0c737 | ||
|
|
b64dd501dd | ||
|
|
1679675aa1 | ||
|
|
640466f301 | ||
|
|
9c9771a371 | ||
|
|
1922b3ce65 | ||
|
|
83e819908a | ||
|
|
0924aa644b | ||
|
|
7711ce1d66 | ||
|
|
7dae63e586 | ||
|
|
1bcaa45492 | ||
|
|
66cb114f0d | ||
|
|
1c5d60dcfd | ||
|
|
b6da6356fc | ||
|
|
c09670cc45 | ||
|
|
464f5b7bed | ||
|
|
c799235c24 | ||
|
|
a2860e70a5 | ||
|
|
95a96d46a6 | ||
|
|
81f3dbda31 | ||
|
|
4eb4191843 | ||
|
|
ca433d8a61 | ||
|
|
8f4dfe1514 | ||
|
|
ee42f5168e | ||
|
|
fc8ca57f8c | ||
|
|
7e4bba2d55 | ||
|
|
7e635f9be4 | ||
|
|
e9406a20f2 | ||
|
|
de7729cec8 | ||
|
|
d60e48849b | ||
|
|
908df5a7bb | ||
|
|
c1952afb94 | ||
|
|
7a22e8f4dd | ||
|
|
3ac0da2cdd | ||
|
|
eb816c4cc6 | ||
|
|
64d8bf4fba | ||
|
|
ae6ee95973 | ||
|
|
e9051dc30a | ||
|
|
b6c7c53c3a | ||
|
|
f36bc61fc7 | ||
|
|
c56e0b9dac | ||
|
|
1deb08bf75 |
2
.github/workflows/build-docs.yml
vendored
2
.github/workflows/build-docs.yml
vendored
@@ -11,4 +11,4 @@ jobs:
|
||||
- env:
|
||||
GITHUB_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh workflow run --repo Froxlor/Documentation build-and-deploy -f type=tags ref=${{github.ref_name}}
|
||||
gh workflow run --repo Froxlor/Documentation build-and-deploy.yml -f type=tags ref=${{github.ref_name}}
|
||||
|
||||
@@ -138,6 +138,26 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_req_limit_per_interval' => [
|
||||
'label' => lng('serversettings.req_limit_per_interval'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'req_limit_per_interval',
|
||||
'type' => 'number',
|
||||
'min' => 30,
|
||||
'default' => 60,
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_req_limit_interval' => [
|
||||
'label' => lng('serversettings.req_limit_interval'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'req_limit_interval',
|
||||
'type' => 'number',
|
||||
'min' => 5,
|
||||
'default' => 60,
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'customer_accountprefix' => [
|
||||
'label' => lng('serversettings.accountprefix'),
|
||||
'settinggroup' => 'customer',
|
||||
|
||||
@@ -53,7 +53,7 @@ return [
|
||||
'string_regexp' => '/^(([a-z0-9\-\._]+, ?)*[a-z0-9\-\._]+)?$/i',
|
||||
'string_emptyallowed' => true,
|
||||
'default' => '',
|
||||
'save_method' => 'storeSettingField',
|
||||
'save_method' => 'storeSettingClearCertificates',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
/**
|
||||
|
||||
@@ -77,6 +77,7 @@ if (($page == 'admins' || $page == 'overview') && $userinfo['change_serversettin
|
||||
$result['switched_user'] = CurrentUser::getData();
|
||||
$result['adminsession'] = 1;
|
||||
$result['userid'] = $result['adminid'];
|
||||
session_regenerate_id(true);
|
||||
CurrentUser::setData($result);
|
||||
|
||||
$log->logAction(
|
||||
|
||||
@@ -28,7 +28,7 @@ require __DIR__ . '/lib/init.php';
|
||||
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\Http\HttpClient;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Install\AutoUpdate;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
@@ -132,7 +132,7 @@ elseif ($page == 'getdownload') {
|
||||
elseif ($page == 'extract') {
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
||||
$toExtract = isset($_POST['archive']) ? $_POST['archive'] : null;
|
||||
$localArchive = Froxlor::getInstallDir() . '/updates/' . $toExtract;
|
||||
$localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
|
||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Extracting " . $localArchive . " to " . Froxlor::getInstallDir());
|
||||
$result = AutoUpdate::extractZip($localArchive);
|
||||
if ($result > 0) {
|
||||
@@ -146,7 +146,7 @@ elseif ($page == 'extract') {
|
||||
Response::redirectTo('admin_updates.php');
|
||||
} else {
|
||||
$toExtract = isset($_GET['archive']) ? $_GET['archive'] : null;
|
||||
$localArchive = Froxlor::getInstallDir() . '/updates/' . $toExtract;
|
||||
$localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
|
||||
}
|
||||
|
||||
if (!file_exists($localArchive)) {
|
||||
|
||||
@@ -33,6 +33,7 @@ use Froxlor\Settings;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
use Froxlor\UI\Request;
|
||||
use Froxlor\UI\Response;
|
||||
use Froxlor\Validate\Validate;
|
||||
|
||||
if ($userinfo['change_serversettings'] == '1') {
|
||||
if ($action == 'setconfigured') {
|
||||
@@ -91,6 +92,7 @@ if ($userinfo['change_serversettings'] == '1') {
|
||||
}
|
||||
|
||||
if ($distribution != "" && isset($_POST['finish'])) {
|
||||
$valid_keys = ['http', 'dns', 'smtp', 'mail', 'ftp', 'system', 'distro'];
|
||||
unset($_POST['finish']);
|
||||
unset($_POST['csrf_token']);
|
||||
$params = $_POST;
|
||||
@@ -99,6 +101,20 @@ if ($userinfo['change_serversettings'] == '1') {
|
||||
foreach ($_POST['system'] as $sysdaemon) {
|
||||
$params['system'][] = $sysdaemon;
|
||||
}
|
||||
// validate params
|
||||
foreach ($params as $key => $value) {
|
||||
if (!in_array($key, $valid_keys)) {
|
||||
unset($params[$key]);
|
||||
continue;
|
||||
}
|
||||
if (!is_array($value)) {
|
||||
$params[$key] = Validate::validate($value, $key);
|
||||
} else {
|
||||
foreach ($value as $subkey => $subvalue) {
|
||||
$params[$key][$subkey] = Validate::validate($subvalue, $key.'.'.$subkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
$params_content = json_encode($params);
|
||||
$params_filename = FileDir::makeCorrectFile(Froxlor::getInstallDir() . 'install/' . Froxlor::genSessionId() . '.json');
|
||||
file_put_contents($params_filename, $params_content);
|
||||
|
||||
@@ -93,6 +93,7 @@ if (($page == 'customers' || $page == 'overview') && $userinfo['customers'] != '
|
||||
$result['switched_user'] = CurrentUser::getData();
|
||||
$result['adminsession'] = 0;
|
||||
$result['userid'] = $result['customerid'];
|
||||
session_regenerate_id(true);
|
||||
CurrentUser::setData($result);
|
||||
|
||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "switched user and is now '" . $destination_user . "'");
|
||||
|
||||
@@ -282,6 +282,12 @@ if ($page == 'domains' || $page == 'overview') {
|
||||
}
|
||||
}
|
||||
|
||||
$openbasedir = [
|
||||
0 => lng('domain.docroot'),
|
||||
1 => lng('domain.homedir'),
|
||||
2 => lng('domain.docparent')
|
||||
];
|
||||
|
||||
// create serveralias options
|
||||
$serveraliasoptions = [
|
||||
0 => lng('domains.serveraliasoption_wildcard'),
|
||||
@@ -545,6 +551,12 @@ if ($page == 'domains' || $page == 'overview') {
|
||||
$result['temporary_ssl_redirect'] = $result['ssl_redirect'];
|
||||
$result['ssl_redirect'] = ($result['ssl_redirect'] == 0 ? 0 : 1);
|
||||
|
||||
$openbasedir = [
|
||||
0 => lng('domain.docroot'),
|
||||
1 => lng('domain.homedir'),
|
||||
2 => lng('domain.docparent')
|
||||
];
|
||||
|
||||
$serveraliasoptions = [
|
||||
0 => lng('domains.serveraliasoption_wildcard'),
|
||||
1 => lng('domains.serveraliasoption_www'),
|
||||
|
||||
@@ -53,6 +53,7 @@ if ($action == 'logout') {
|
||||
if (is_array(CurrentUser::getField('switched_user'))) {
|
||||
$result = CurrentUser::getData();
|
||||
$result = $result['switched_user'];
|
||||
session_regenerate_id(true);
|
||||
CurrentUser::setData($result);
|
||||
$target = (isset($_GET['target']) ? $_GET['target'] : 'index');
|
||||
$redirect = "admin_" . $target . ".php";
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Froxlor\Cli\ConfigDiff;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Froxlor\Cli\RunApiCommand;
|
||||
use Froxlor\Cli\ConfigServices;
|
||||
@@ -61,4 +62,5 @@ $application->add(new InstallCommand());
|
||||
$application->add(new MasterCron());
|
||||
$application->add(new UserCommand());
|
||||
$application->add(new ValidateAcmeWebroot());
|
||||
$application->add(new ConfigDiff());
|
||||
$application->run();
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-gmp": "*",
|
||||
"ext-gd": "*",
|
||||
"phpmailer/phpmailer": "~6.0",
|
||||
"monolog/monolog": "^1.24",
|
||||
"robthree/twofactorauth": "^1.6",
|
||||
|
||||
175
composer.lock
generated
175
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "41e7a3bc0e13b47c4f245334b113c3be",
|
||||
"content-hash": "2de39e6b85579ce1f0c2f7a16d57ede3",
|
||||
"packages": [
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
@@ -251,16 +251,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpmailer/phpmailer",
|
||||
"version": "v6.7.1",
|
||||
"version": "v6.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||
"reference": "49cd7ea3d2563f028d7811f06864a53b1f15ff55"
|
||||
"reference": "df16b615e371d81fb79e506277faea67a1be18f1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/49cd7ea3d2563f028d7811f06864a53b1f15ff55",
|
||||
"reference": "49cd7ea3d2563f028d7811f06864a53b1f15ff55",
|
||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1",
|
||||
"reference": "df16b615e371d81fb79e506277faea67a1be18f1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -319,7 +319,7 @@
|
||||
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
||||
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.7.1"
|
||||
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -327,7 +327,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-12-08T13:30:06+00:00"
|
||||
"time": "2023-03-06T14:43:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
@@ -499,16 +499,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v5.4.19",
|
||||
"version": "v5.4.22",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "dccb8d251a9017d5994c988b034d3e18aaabf740"
|
||||
"reference": "3cd51fd2e6c461ca678f84d419461281bd87a0a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/dccb8d251a9017d5994c988b034d3e18aaabf740",
|
||||
"reference": "dccb8d251a9017d5994c988b034d3e18aaabf740",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/3cd51fd2e6c461ca678f84d419461281bd87a0a8",
|
||||
"reference": "3cd51fd2e6c461ca678f84d419461281bd87a0a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -573,12 +573,12 @@
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"cli",
|
||||
"command line",
|
||||
"command-line",
|
||||
"console",
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v5.4.19"
|
||||
"source": "https://github.com/symfony/console/tree/v5.4.22"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -594,7 +594,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-01T08:32:19+00:00"
|
||||
"time": "2023-03-25T09:27:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
@@ -1399,16 +1399,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v5.4.19",
|
||||
"version": "v5.4.22",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "0a01071610fd861cc160dfb7e2682ceec66064cb"
|
||||
"reference": "8036a4c76c0dd29e60b6a7cafcacc50cf088ea62"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/0a01071610fd861cc160dfb7e2682ceec66064cb",
|
||||
"reference": "0a01071610fd861cc160dfb7e2682ceec66064cb",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/8036a4c76c0dd29e60b6a7cafcacc50cf088ea62",
|
||||
"reference": "8036a4c76c0dd29e60b6a7cafcacc50cf088ea62",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1465,7 +1465,7 @@
|
||||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v5.4.19"
|
||||
"source": "https://github.com/symfony/string/tree/v5.4.22"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1481,7 +1481,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-01T08:32:19+00:00"
|
||||
"time": "2023-03-14T06:11:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
@@ -1718,16 +1718,16 @@
|
||||
},
|
||||
{
|
||||
"name": "voku/portable-utf8",
|
||||
"version": "6.0.12",
|
||||
"version": "6.0.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/voku/portable-utf8.git",
|
||||
"reference": "db0583727bb17666bbd2ba238c85babb973fd165"
|
||||
"reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/voku/portable-utf8/zipball/db0583727bb17666bbd2ba238c85babb973fd165",
|
||||
"reference": "db0583727bb17666bbd2ba238c85babb973fd165",
|
||||
"url": "https://api.github.com/repos/voku/portable-utf8/zipball/b8ce36bf26593e5c2e81b1850ef0ffb299d2043f",
|
||||
"reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1793,7 +1793,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/voku/portable-utf8/issues",
|
||||
"source": "https://github.com/voku/portable-utf8/tree/6.0.12"
|
||||
"source": "https://github.com/voku/portable-utf8/tree/6.0.13"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1817,7 +1817,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-11T12:26:16+00:00"
|
||||
"time": "2023-03-08T08:35:38+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
@@ -2030,16 +2030,16 @@
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.11.0",
|
||||
"version": "1.11.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
|
||||
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
|
||||
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
|
||||
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2077,7 +2077,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2085,20 +2085,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-03T13:19:32+00:00"
|
||||
"time": "2023-03-08T13:26:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.15.3",
|
||||
"version": "v4.15.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039"
|
||||
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039",
|
||||
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
|
||||
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2139,22 +2139,22 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
|
||||
},
|
||||
"time": "2023-01-16T22:05:37+00:00"
|
||||
"time": "2023-03-05T19:49:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pdepend/pdepend",
|
||||
"version": "2.12.1",
|
||||
"version": "2.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pdepend/pdepend.git",
|
||||
"reference": "7a892d56ceafd804b4a2ecc85184640937ce9e84"
|
||||
"reference": "31be7cd4f305f3f7b52af99c1cb13fc938d1cfad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pdepend/pdepend/zipball/7a892d56ceafd804b4a2ecc85184640937ce9e84",
|
||||
"reference": "7a892d56ceafd804b4a2ecc85184640937ce9e84",
|
||||
"url": "https://api.github.com/repos/pdepend/pdepend/zipball/31be7cd4f305f3f7b52af99c1cb13fc938d1cfad",
|
||||
"reference": "31be7cd4f305f3f7b52af99c1cb13fc938d1cfad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2190,7 +2190,7 @@
|
||||
"description": "Official version of pdepend to be handled with Composer",
|
||||
"support": {
|
||||
"issues": "https://github.com/pdepend/pdepend/issues",
|
||||
"source": "https://github.com/pdepend/pdepend/tree/2.12.1"
|
||||
"source": "https://github.com/pdepend/pdepend/tree/2.13.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2198,7 +2198,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-09-08T19:30:37+00:00"
|
||||
"time": "2023-02-28T20:56:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@@ -2520,16 +2520,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.10.3",
|
||||
"version": "1.10.14",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "5419375b5891add97dc74be71e6c1c34baaddf64"
|
||||
"reference": "d232901b09e67538e5c86a724be841bea5768a7c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/5419375b5891add97dc74be71e6c1c34baaddf64",
|
||||
"reference": "5419375b5891add97dc74be71e6c1c34baaddf64",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d232901b09e67538e5c86a724be841bea5768a7c",
|
||||
"reference": "d232901b09e67538e5c86a724be841bea5768a7c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2558,8 +2558,11 @@
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"docs": "https://phpstan.org/user-guide/getting-started",
|
||||
"forum": "https://github.com/phpstan/phpstan/discussions",
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.10.3"
|
||||
"security": "https://github.com/phpstan/phpstan/security/policy",
|
||||
"source": "https://github.com/phpstan/phpstan-src"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2575,20 +2578,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-02-25T14:47:13+00:00"
|
||||
"time": "2023-04-19T13:47:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.25",
|
||||
"version": "9.2.26",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954"
|
||||
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e2b40518197a8c0d4b08bc34dfff1c99c508954",
|
||||
"reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
|
||||
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2610,8 +2613,8 @@
|
||||
"phpunit/phpunit": "^9.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-pcov": "*",
|
||||
"ext-xdebug": "*"
|
||||
"ext-pcov": "PHP extension that provides line coverage",
|
||||
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -2644,7 +2647,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.25"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2652,7 +2655,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-02-25T05:32:00+00:00"
|
||||
"time": "2023-03-06T12:58:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@@ -2897,16 +2900,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.6.3",
|
||||
"version": "9.6.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "e7b1615e3e887d6c719121c6d4a44b0ab9645555"
|
||||
"reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e7b1615e3e887d6c719121c6d4a44b0ab9645555",
|
||||
"reference": "e7b1615e3e887d6c719121c6d4a44b0ab9645555",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2",
|
||||
"reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2939,8 +2942,8 @@
|
||||
"sebastian/version": "^3.0.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-soap": "*",
|
||||
"ext-xdebug": "*"
|
||||
"ext-soap": "To be able to generate mocks based on WSDL files",
|
||||
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
|
||||
},
|
||||
"bin": [
|
||||
"phpunit"
|
||||
@@ -2979,7 +2982,8 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.3"
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2995,7 +2999,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-02-04T13:37:15+00:00"
|
||||
"time": "2023-04-14T08:58:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@@ -4082,16 +4086,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/config",
|
||||
"version": "v5.4.19",
|
||||
"version": "v5.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/config.git",
|
||||
"reference": "9bd60843443cda9638efdca7c41eb82ed0026179"
|
||||
"reference": "2a6b1111d038adfa15d52c0871e540f3b352d1e4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/9bd60843443cda9638efdca7c41eb82ed0026179",
|
||||
"reference": "9bd60843443cda9638efdca7c41eb82ed0026179",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/2a6b1111d038adfa15d52c0871e540f3b352d1e4",
|
||||
"reference": "2a6b1111d038adfa15d52c0871e540f3b352d1e4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4141,7 +4145,7 @@
|
||||
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/config/tree/v5.4.19"
|
||||
"source": "https://github.com/symfony/config/tree/v5.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4157,20 +4161,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-08T13:23:55+00:00"
|
||||
"time": "2023-02-14T08:03:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/dependency-injection",
|
||||
"version": "v5.4.20",
|
||||
"version": "v5.4.22",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dependency-injection.git",
|
||||
"reference": "8185ed0df129005a26715902f1a53bad0fe67102"
|
||||
"reference": "e1b7c1432efb4ad1dd89d62906187271e2601ed9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8185ed0df129005a26715902f1a53bad0fe67102",
|
||||
"reference": "8185ed0df129005a26715902f1a53bad0fe67102",
|
||||
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e1b7c1432efb4ad1dd89d62906187271e2601ed9",
|
||||
"reference": "e1b7c1432efb4ad1dd89d62906187271e2601ed9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4230,7 +4234,7 @@
|
||||
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/dependency-injection/tree/v5.4.20"
|
||||
"source": "https://github.com/symfony/dependency-injection/tree/v5.4.22"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4246,20 +4250,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-27T11:08:11+00:00"
|
||||
"time": "2023-03-10T10:02:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v5.4.19",
|
||||
"version": "v5.4.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "648bfaca6a494f3e22378123bcee2894045dc9d8"
|
||||
"reference": "e75960b1bbfd2b8c9e483e0d74811d555ca3de9f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/648bfaca6a494f3e22378123bcee2894045dc9d8",
|
||||
"reference": "648bfaca6a494f3e22378123bcee2894045dc9d8",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/e75960b1bbfd2b8c9e483e0d74811d555ca3de9f",
|
||||
"reference": "e75960b1bbfd2b8c9e483e0d74811d555ca3de9f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4294,7 +4298,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v5.4.19"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v5.4.21"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4310,7 +4314,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-14T19:14:44+00:00"
|
||||
"time": "2023-02-14T08:03:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
@@ -4462,7 +4466,8 @@
|
||||
"ext-json": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-gmp": "*"
|
||||
"ext-gmp": "*",
|
||||
"ext-gd": "*"
|
||||
},
|
||||
"platform-dev": {
|
||||
"ext-pcntl": "*"
|
||||
|
||||
@@ -119,7 +119,7 @@ if ($page == 'overview' || $page == 'accounts') {
|
||||
if (Settings::Get('customer.ftpatdomain') == '1') {
|
||||
$domainlist = [];
|
||||
$result_domains_stmt = Database::prepare("SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "`
|
||||
WHERE `customerid`= :customerid");
|
||||
WHERE `customerid`= :customerid ORDER BY `domain` ASC");
|
||||
Database::pexecute($result_domains_stmt, [
|
||||
"customerid" => $userinfo['customerid']
|
||||
]);
|
||||
@@ -127,7 +127,6 @@ if ($page == 'overview' || $page == 'accounts') {
|
||||
while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$domainlist[$row_domain['domain']] = $idna_convert->decode($row_domain['domain']);
|
||||
}
|
||||
sort($domainlist);
|
||||
}
|
||||
|
||||
if (Settings::Get('system.allow_customer_shell') == '1') {
|
||||
|
||||
@@ -52,6 +52,7 @@ if ($action == 'logout') {
|
||||
if (is_array(CurrentUser::getField('switched_user'))) {
|
||||
$result = CurrentUser::getData();
|
||||
$result = $result['switched_user'];
|
||||
session_regenerate_id(true);
|
||||
CurrentUser::setData($result);
|
||||
$target = (isset($_GET['target']) ? $_GET['target'] : 'index');
|
||||
$redirect = "admin_" . $target . ".php";
|
||||
|
||||
101
index.php
101
index.php
@@ -40,7 +40,6 @@ use Froxlor\UI\Panel\UI;
|
||||
use Froxlor\UI\Response;
|
||||
use Froxlor\User;
|
||||
use Froxlor\Validate\Validate;
|
||||
use Froxlor\Language;
|
||||
|
||||
if ($action == '') {
|
||||
$action = 'login';
|
||||
@@ -53,9 +52,15 @@ if ($action == '2fa_entercode') {
|
||||
Response::redirectTo('index.php');
|
||||
exit();
|
||||
}
|
||||
$smessage = isset($_GET['showmessage']) ? (int)$_GET['showmessage'] : 0;
|
||||
$message = "";
|
||||
if ($smessage > 0) {
|
||||
$message = lng('error.2fa_wrongcode');
|
||||
}
|
||||
// show template to enter code
|
||||
UI::view('login/enter2fa.html.twig', [
|
||||
'pagetitle' => lng('login.2fa')
|
||||
'pagetitle' => lng('login.2fa'),
|
||||
'message' => $message
|
||||
]);
|
||||
} elseif ($action == '2fa_verify') {
|
||||
// verify code from 2fa code-enter form
|
||||
@@ -68,25 +73,25 @@ if ($action == '2fa_entercode') {
|
||||
// verify entered code
|
||||
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
|
||||
$result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3));
|
||||
// get user-data
|
||||
$table = $_SESSION['uidtable_2fa'];
|
||||
$field = $_SESSION['uidfield_2fa'];
|
||||
$uid = $_SESSION['uid_2fa'];
|
||||
$isadmin = $_SESSION['unfo_2fa'];
|
||||
// either the code is valid when using authenticator-app, or we will select userdata by id and entered code
|
||||
// which is temporarily stored for the customer when using email-2fa
|
||||
if ($result) {
|
||||
// get user-data
|
||||
$table = $_SESSION['uidtable_2fa'];
|
||||
$field = $_SESSION['uidfield_2fa'];
|
||||
$uid = $_SESSION['uid_2fa'];
|
||||
$isadmin = $_SESSION['unfo_2fa'];
|
||||
$sel_param = [
|
||||
'uid' => $uid
|
||||
];
|
||||
if ($_SESSION['secret_2fa'] == 'email') {
|
||||
// verify code by selecting user by id and the temp. stored code,
|
||||
// so only if it's the correct code, we get the user-data
|
||||
$sel_stmt = Database::prepare("SELECT * FROM $table WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
|
||||
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
|
||||
$sel_param['code'] = $code;
|
||||
} else {
|
||||
// Authenticator-verification has already happened at this point, so just get the user-data
|
||||
$sel_stmt = Database::prepare("SELECT * FROM $table WHERE `" . $field . "` = :uid");
|
||||
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid");
|
||||
}
|
||||
$userinfo = Database::pexecute_first($sel_stmt, $sel_param);
|
||||
// whoops, no (valid) user? Start again
|
||||
@@ -108,19 +113,54 @@ if ($action == '2fa_entercode') {
|
||||
|
||||
// when using email-2fa, remove the one-time-code
|
||||
if ($userinfo['type_2fa'] == '1') {
|
||||
$del_stmt = Database::prepare("UPDATE $table SET `data_2fa` = '' WHERE `" . $field . "` = :uid");
|
||||
$del_stmt = Database::prepare("UPDATE " . $table . " SET `data_2fa` = '' WHERE `" . $field . "` = :uid");
|
||||
$userinfo = Database::pexecute_first($del_stmt, [
|
||||
'uid' => $uid
|
||||
]);
|
||||
}
|
||||
exit();
|
||||
}
|
||||
// wrong 2fa code - treat like "wrong password"
|
||||
$stmt = Database::prepare("
|
||||
UPDATE " . $table . "
|
||||
SET `lastlogin_fail`= :lastlogin_fail, `loginfail_count`=`loginfail_count`+1
|
||||
WHERE `" . $field . "`= :uid
|
||||
");
|
||||
Database::pexecute($stmt, [
|
||||
"lastlogin_fail" => time(),
|
||||
"uid" => $uid
|
||||
]);
|
||||
|
||||
// get data for processing further
|
||||
$stmt = Database::prepare("
|
||||
SELECT `loginname`, `loginfail_count`, `lastlogin_fail` FROM " . $table . "
|
||||
WHERE `" . $field . "`= :uid
|
||||
");
|
||||
$fail_user = Database::pexecute_first($stmt, [
|
||||
"uid" => $uid
|
||||
]);
|
||||
|
||||
if ($fail_user['loginfail_count'] >= Settings::Get('login.maxloginattempts') && $fail_user['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))) {
|
||||
// Log failed login
|
||||
$rstlog = FroxlorLogger::getInstanceOf([
|
||||
'loginname' => $_SERVER['REMOTE_ADDR']
|
||||
]);
|
||||
$rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, "User '" . $fail_user['loginname'] . "' entered wrong 2fa code too often.");
|
||||
unset($fail_user);
|
||||
Response::redirectTo('index.php', [
|
||||
'showmessage' => '3'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
unset($fail_user);
|
||||
// back to form
|
||||
Response::redirectTo('index.php', [
|
||||
'showmessage' => '2'
|
||||
'action' => '2fa_entercode',
|
||||
'showmessage' => '1'
|
||||
]);
|
||||
exit();
|
||||
} elseif ($action == 'login') {
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
||||
if (!empty($_POST)) {
|
||||
$loginname = Validate::validate($_POST['loginname'], 'loginname');
|
||||
$password = Validate::validate($_POST['password'], 'password');
|
||||
|
||||
@@ -390,13 +430,18 @@ if ($action == '2fa_entercode') {
|
||||
}
|
||||
$lastqrystr = "";
|
||||
if (isset($_REQUEST['qrystr']) && $_REQUEST['qrystr'] != "") {
|
||||
$lastqrystr = htmlspecialchars($_REQUEST['qrystr'], ENT_QUOTES);
|
||||
$lastqrystr = urlencode($_REQUEST['qrystr']);
|
||||
}
|
||||
|
||||
if (!empty($lastscript)) {
|
||||
$_SESSION['lastscript'] = $lastscript;
|
||||
}
|
||||
if (!empty($lastqrystr)) {
|
||||
$_SESSION['lastqrystr'] = $lastqrystr;
|
||||
}
|
||||
|
||||
UI::view('login/login.html.twig', [
|
||||
'pagetitle' => 'Login',
|
||||
'lastscript' => $lastscript,
|
||||
'lastqrystr' => $lastqrystr,
|
||||
'upd_in_progress' => $update_in_progress,
|
||||
'message' => $message,
|
||||
'successmsg' => $successmessage
|
||||
@@ -408,7 +453,7 @@ if ($action == 'forgotpwd') {
|
||||
$adminchecked = false;
|
||||
$message = '';
|
||||
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
||||
if (!empty($_POST)) {
|
||||
$loginname = Validate::validate($_POST['loginname'], 'loginname');
|
||||
$email = Validate::validateEmail($_POST['loginemail']);
|
||||
$result_stmt = Database::prepare("SELECT `adminid`, `customerid`, `customernumber`, `firstname`, `name`, `company`, `email`, `loginname`, `def_language`, `deactivated` FROM `" . TABLE_PANEL_CUSTOMERS . "`
|
||||
@@ -592,7 +637,7 @@ if ($action == 'forgotpwd') {
|
||||
|
||||
UI::view('login/fpwd.html.twig', [
|
||||
'pagetitle' => lng('login.presend'),
|
||||
'action' => $action,
|
||||
'formaction' => 'index.php?action=' . $action,
|
||||
'message' => $message,
|
||||
]);
|
||||
}
|
||||
@@ -615,7 +660,7 @@ if ($action == 'resetpwd') {
|
||||
$check = substr($activationcode, 40, 10);
|
||||
|
||||
if (substr(md5($third . $timestamp), 0, 10) == $check && $timestamp >= time() - 86400) {
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
||||
if (!empty($_POST)) {
|
||||
$stmt = Database::prepare("SELECT `userid`, `admin` FROM `" . TABLE_PANEL_ACTIVATION . "`
|
||||
WHERE `activationcode` = :activationcode");
|
||||
$result = Database::pexecute_first($stmt, [
|
||||
@@ -692,6 +737,7 @@ if ($action == 'resetpwd') {
|
||||
function finishLogin($userinfo)
|
||||
{
|
||||
if (isset($userinfo['userid']) && $userinfo['userid'] != '') {
|
||||
session_regenerate_id(true);
|
||||
CurrentUser::setData($userinfo);
|
||||
|
||||
$language = $userinfo['def_language'] ?? Settings::Get('panel.standardlanguage');
|
||||
@@ -705,29 +751,34 @@ function finishLogin($userinfo)
|
||||
}
|
||||
|
||||
$qryparams = [];
|
||||
if (isset($_POST['qrystr']) && $_POST['qrystr'] != "") {
|
||||
parse_str(urldecode($_POST['qrystr']), $qryparams);
|
||||
if (!empty($_SESSION['lastqrystr'])) {
|
||||
parse_str(urldecode($_SESSION['lastqrystr']), $qryparams);
|
||||
unset($_SESSION['lastqrystr']);
|
||||
}
|
||||
|
||||
if ($userinfo['adminsession'] == '1') {
|
||||
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
|
||||
Response::redirectTo('admin_updates.php?page=overview');
|
||||
} else {
|
||||
if (isset($_POST['script']) && $_POST['script'] != "") {
|
||||
if (preg_match("/customer\_/", $_POST['script']) === 1) {
|
||||
if (!empty($_SESSION['lastscript'])) {
|
||||
$lastscript = $_SESSION['lastscript'];
|
||||
unset($_SESSION['lastscript']);
|
||||
if (preg_match("/customer\_/", $lastscript) === 1) {
|
||||
Response::redirectTo('admin_customers.php', [
|
||||
"page" => "customers"
|
||||
]);
|
||||
} else {
|
||||
Response::redirectTo($_POST['script'], $qryparams);
|
||||
Response::redirectTo($lastscript, $qryparams);
|
||||
}
|
||||
} else {
|
||||
Response::redirectTo('admin_index.php', $qryparams);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isset($_POST['script']) && $_POST['script'] != "") {
|
||||
Response::redirectTo($_POST['script'], $qryparams);
|
||||
if (!empty($_SESSION['lastscript'])) {
|
||||
$lastscript = $_SESSION['lastscript'];
|
||||
unset($_SESSION['lastscript']);
|
||||
Response::redirectTo($lastscript, $qryparams);
|
||||
} else {
|
||||
Response::redirectTo('customer_index.php', $qryparams);
|
||||
}
|
||||
|
||||
@@ -697,8 +697,10 @@ opcache.validate_timestamps'),
|
||||
('system', 'distribution', ''),
|
||||
('system', 'update_channel', 'stable'),
|
||||
('system', 'updatecheck_data', ''),
|
||||
('system', 'update_notify_last', '2.0.13'),
|
||||
('system', 'update_notify_last', '2.0.23'),
|
||||
('system', 'traffictool', 'goaccess'),
|
||||
('system', 'req_limit_per_interval', 60),
|
||||
('system', 'req_limit_interval', 60),
|
||||
('api', 'enabled', '0'),
|
||||
('api', 'customer_default', '1'),
|
||||
('2fa', 'enabled', '1'),
|
||||
@@ -742,8 +744,8 @@ opcache.validate_timestamps'),
|
||||
('panel', 'logo_overridetheme', '0'),
|
||||
('panel', 'logo_overridecustom', '0'),
|
||||
('panel', 'settings_mode', '0'),
|
||||
('panel', 'version', '2.0.13'),
|
||||
('panel', 'db_version', '202302030');
|
||||
('panel', 'version', '2.0.23'),
|
||||
('panel', 'db_version', '202304260');
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `panel_tasks`;
|
||||
@@ -983,7 +985,9 @@ CREATE TABLE IF NOT EXISTS `domain_ssl_settings` (
|
||||
`ssl_cert_chainfile` mediumtext,
|
||||
`ssl_csr_file` mediumtext,
|
||||
`ssl_fullchain_file` mediumtext,
|
||||
`expirationdate` datetime DEFAULT NULL,
|
||||
`validfromdate` datetime DEFAULT NULL,
|
||||
`validtodate` datetime DEFAULT NULL,
|
||||
`issuer` varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY (`domainid`)
|
||||
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
||||
*/
|
||||
|
||||
use Froxlor\Http\RateLimiter;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
use Froxlor\Install\Install;
|
||||
|
||||
@@ -62,6 +63,7 @@ require dirname(__DIR__) . '/lib/tables.inc.php';
|
||||
// init twig
|
||||
UI::initTwig(true);
|
||||
UI::sendHeaders();
|
||||
RateLimiter::run(true);
|
||||
|
||||
$installer = new Install();
|
||||
$installer->handle();
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
||||
*/
|
||||
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\Install\Update;
|
||||
use Froxlor\Settings;
|
||||
|
||||
if (!defined('_CRON_UPDATE')) {
|
||||
if (!defined('AREA') || (defined('AREA') && AREA != 'admin') || !isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) {
|
||||
@@ -82,7 +82,7 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP COLUMN `domains_see_all`;");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Update::showUpdateStep("Checking for multiple mysql-servers to allow acccess to customers for existing databases");
|
||||
Update::showUpdateStep("Checking for multiple mysql-servers to allow access to customers for existing databases");
|
||||
$dbservers_stmt = Database::query("
|
||||
SELECT `customerid`,
|
||||
GROUP_CONCAT(DISTINCT `dbserver` SEPARATOR ',') as allowed_mysqlserver
|
||||
@@ -93,7 +93,8 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
while ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
if (isset($dbserver['allowed_mysqlserver']) && !empty($dbserver['allowed_mysqlserver'])) {
|
||||
$allowed_mysqlserver = json_encode(explode(",", $dbserver['allowed_mysqlserver']));
|
||||
Database::pexecute($upd_stmt, ['allowed_mysql_server' => $allowed_mysqlserver, 'customerid' => $dbserver['customerid']]);
|
||||
Database::pexecute($upd_stmt,
|
||||
['allowed_mysql_server' => $allowed_mysqlserver, 'customerid' => $dbserver['customerid']]);
|
||||
}
|
||||
}
|
||||
Update::lastStepStatus(0);
|
||||
@@ -140,14 +141,15 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
// none of the files existed
|
||||
Update::lastStepStatus(0);
|
||||
} else {
|
||||
Update::lastStepStatus(1, 'manual commands needed', 'Please run the following commands manually:<br><pre>' . $del_list . '</pre>');
|
||||
Update::lastStepStatus(1, 'manual commands needed',
|
||||
'Please run the following commands manually:<br><pre>' . $del_list . '</pre>');
|
||||
}
|
||||
}
|
||||
|
||||
Update::showUpdateStep("Adding new settings");
|
||||
$panel_settings_mode = isset($_POST['panel_settings_mode']) ? (int)$_POST['panel_settings_mode'] : 0;
|
||||
Settings::AddNew("panel.settings_mode", $panel_settings_mode);
|
||||
$system_distribution = isset($_POST['system_distribution']) ? $_POST['system_distribution'] : '';
|
||||
$system_distribution = isset($_POST['system_distribution']) ? $_POST['system_distribution'] : 'bullseye';
|
||||
Settings::AddNew("system.distribution", $system_distribution);
|
||||
Settings::AddNew("system.update_channel", 'stable');
|
||||
Settings::AddNew("system.updatecheck_data", '');
|
||||
@@ -224,7 +226,8 @@ EOF;
|
||||
} else {
|
||||
$cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL;
|
||||
$cron_run_cmd .= FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -r 99';
|
||||
Update::lastStepStatus(1, 'manual commands needed', 'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>');
|
||||
Update::lastStepStatus(1, 'manual commands needed',
|
||||
'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>');
|
||||
}
|
||||
|
||||
Froxlor::updateToDbVersion('202212060');
|
||||
@@ -283,7 +286,8 @@ EOF;
|
||||
} else {
|
||||
$cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL;
|
||||
$cron_run_cmd .= FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -r 99';
|
||||
Update::lastStepStatus(1, 'manual commands needed', 'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>');
|
||||
Update::lastStepStatus(1, 'manual commands needed',
|
||||
'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>');
|
||||
}
|
||||
}
|
||||
Froxlor::updateToVersion('2.0.4');
|
||||
@@ -323,7 +327,7 @@ if (Froxlor::isDatabaseVersion('202212060')) {
|
||||
$system_letsencryptchallengepath_upd = isset($_POST['system_letsencryptchallengepath_upd']) ? $_POST['system_letsencryptchallengepath_upd'] : $acmesh_challenge_dir;
|
||||
if ($acmesh_challenge_dir != $system_letsencryptchallengepath_upd) {
|
||||
Settings::Set('system.letsencryptchallengepath', $system_letsencryptchallengepath_upd);
|
||||
if ((int) Settings::Get('system.leenabled') == 1) {
|
||||
if ((int)Settings::Get('system.leenabled') == 1) {
|
||||
// create JSON string for --apply
|
||||
$dist = Settings::Get('system.distribution');
|
||||
$webserver = Settings::Get('system.webserver');
|
||||
@@ -405,3 +409,106 @@ if (Froxlor::isFroxlorVersion('2.0.12')) {
|
||||
Update::showUpdateStep("Updating from 2.0.12 to 2.0.13", false);
|
||||
Froxlor::updateToVersion('2.0.13');
|
||||
}
|
||||
|
||||
if (Froxlor::isDatabaseVersion('202302030')) {
|
||||
Update::showUpdateStep("Correcting language mapping of templates created pre 2.0.x");
|
||||
// languages from 0.10.x
|
||||
$language_mapping_comp = [
|
||||
'de' => 'Deutsch',
|
||||
'en' => 'English',
|
||||
'fr' => 'Français',
|
||||
'pt' => 'Português',
|
||||
'it' => 'Italiano',
|
||||
'nl' => 'Nederlands',
|
||||
'se' => 'Svenska',
|
||||
'cz' => 'Česká republika'
|
||||
];
|
||||
$upd_tpl_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET `language` = :iso WHERE `language` = :lng");
|
||||
foreach ($language_mapping_comp as $iso => $lang) {
|
||||
Database::pexecute($upd_tpl_stmt, ['iso' => $iso, 'lng' => $lang]);
|
||||
}
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Update::showUpdateStep("Enhancing ssl data table");
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` CHANGE `expirationdate` `validtodate` datetime DEFAULT NULL;");
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `validfromdate` datetime DEFAULT NULL AFTER `ssl_fullchain_file`;");
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `issuer` varchar(255) NOT NULL default '' AFTER `validtodate`;");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Update::showUpdateStep("Filling new ssl data fields with existing certificate data");
|
||||
$crt_upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SET `validfromdate` = :validfromdate, `issuer` = :issuer WHERE `id` = :id");
|
||||
$crt_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`");
|
||||
Database::pexecute($crt_stmt);
|
||||
while ($cert = $crt_stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$cert_content = openssl_x509_parse($cert['ssl_cert_file']);
|
||||
if (is_array($cert_content)) {
|
||||
$validfromdate = empty($cert_content['validFrom_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validFrom_time_t']);
|
||||
$issuer = $cert_content['issuer']['O'] ?? "";
|
||||
Database::pexecute($crt_upd_stmt, ['validfromdate' => $validfromdate, 'issuer' => $issuer, 'id' => $cert['id']]);
|
||||
}
|
||||
}
|
||||
// clear possible user customized columns
|
||||
Database::query("DELETE FROM `" . TABLE_PANEL_USERCOLUMNS . "` WHERE `section` = 'sslcertificates_list'");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToDbVersion('202303150');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.13')) {
|
||||
Update::showUpdateStep("Updating from 2.0.13 to 2.0.14", false);
|
||||
Froxlor::updateToVersion('2.0.14');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.14')) {
|
||||
Update::showUpdateStep("Updating from 2.0.14 to 2.0.15", false);
|
||||
Froxlor::updateToVersion('2.0.15');
|
||||
}
|
||||
|
||||
if (Froxlor::isDatabaseVersion('202303150')) {
|
||||
Update::showUpdateStep("Adding new request rate limit settings");
|
||||
Settings::AddNew("system.req_limit_per_interval", "60");
|
||||
Settings::AddNew("system.req_limit_interval", "60");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToDbVersion('202304260');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.15')) {
|
||||
Update::showUpdateStep("Updating from 2.0.15 to 2.0.16", false);
|
||||
Froxlor::updateToVersion('2.0.16');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.16')) {
|
||||
Update::showUpdateStep("Updating from 2.0.16 to 2.0.17", false);
|
||||
Froxlor::updateToVersion('2.0.17');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.17')) {
|
||||
Update::showUpdateStep("Updating from 2.0.17 to 2.0.18", false);
|
||||
Froxlor::updateToVersion('2.0.18');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.18')) {
|
||||
Update::showUpdateStep("Updating from 2.0.18 to 2.0.19", false);
|
||||
Froxlor::updateToVersion('2.0.19');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.19')) {
|
||||
Update::showUpdateStep("Updating from 2.0.19 to 2.0.20", false);
|
||||
Froxlor::updateToVersion('2.0.20');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.20')) {
|
||||
Update::showUpdateStep("Updating from 2.0.20 to 2.0.21", false);
|
||||
Froxlor::updateToVersion('2.0.21');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.21')) {
|
||||
Update::showUpdateStep("Updating from 2.0.21 to 2.0.22", false);
|
||||
Froxlor::updateToVersion('2.0.22');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.22')) {
|
||||
Update::showUpdateStep("Updating from 2.0.22 to 2.0.23", false);
|
||||
Froxlor::updateToVersion('2.0.23');
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ if (Update::versionInUpdate($current_version, '2.0.0-beta1')) {
|
||||
$config_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/lib/configfiles/');
|
||||
// show list of available distro's
|
||||
$distros = glob($config_dir . '*.xml');
|
||||
$distributions_select[''] = '-';
|
||||
// selection is required $distributions_select[''] = '-';
|
||||
// read in all the distros
|
||||
foreach ($distros as $_distribution) {
|
||||
// get configparser object
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
namespace Froxlor\Api;
|
||||
|
||||
use Exception;
|
||||
use Froxlor\Http\RateLimiter;
|
||||
use Froxlor\Settings;
|
||||
use voku\helper\AntiXSS;
|
||||
|
||||
@@ -52,6 +53,8 @@ class Api
|
||||
if (Settings::Get('api.enabled') != 1) {
|
||||
throw new Exception('API is not enabled. Please contact the administrator if you think this is wrong.', 400);
|
||||
}
|
||||
|
||||
RateLimiter::run();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,12 +39,12 @@ abstract class ApiParameter
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $params
|
||||
* @param array|null $params
|
||||
* optional, array of parameters (var=>value) for the command
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($params = null)
|
||||
public function __construct(array $params = null)
|
||||
{
|
||||
if (!is_null($params)) {
|
||||
$params = $this->trimArray($params);
|
||||
@@ -57,7 +57,7 @@ abstract class ApiParameter
|
||||
*
|
||||
* @param array $input
|
||||
*
|
||||
* @return array
|
||||
* @return string|array
|
||||
*/
|
||||
private function trimArray($input)
|
||||
{
|
||||
@@ -79,9 +79,9 @@ abstract class ApiParameter
|
||||
/**
|
||||
* get specific parameter which also has and unlimited-field
|
||||
*
|
||||
* @param string $param
|
||||
* @param string|null $param
|
||||
* parameter to get out of the request-parameter list
|
||||
* @param string $ul_field
|
||||
* @param string|null $ul_field
|
||||
* parameter to get out of the request-parameter list
|
||||
* @param bool $optional
|
||||
* default: false
|
||||
@@ -91,7 +91,7 @@ abstract class ApiParameter
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getUlParam($param = null, $ul_field = null, $optional = false, $default = 0)
|
||||
protected function getUlParam(string $param = null, string $ul_field = null, bool $optional = false, $default = 0)
|
||||
{
|
||||
$param_value = (int)$this->getParam($param, $optional, $default);
|
||||
$ul_field_value = $this->getBoolParam($ul_field, true, 0);
|
||||
@@ -102,11 +102,11 @@ abstract class ApiParameter
|
||||
}
|
||||
|
||||
/**
|
||||
* get specific parameter from the parameterlist;
|
||||
* get specific parameter from the parameter list;
|
||||
* check for existence and != empty if needed.
|
||||
* Maybe more in the future
|
||||
*
|
||||
* @param string $param
|
||||
* @param string|null $param
|
||||
* parameter to get out of the request-parameter list
|
||||
* @param bool $optional
|
||||
* default: false
|
||||
@@ -116,7 +116,7 @@ abstract class ApiParameter
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getParam($param = null, $optional = false, $default = '')
|
||||
protected function getParam(string $param = null, bool $optional = false, $default = '')
|
||||
{
|
||||
// does it exist?
|
||||
if (!isset($this->cmd_params[$param])) {
|
||||
@@ -128,7 +128,7 @@ abstract class ApiParameter
|
||||
return $default;
|
||||
}
|
||||
// is it empty? - test really on string, as value 0 is being seen as empty by php
|
||||
if ($this->cmd_params[$param] === "") {
|
||||
if (!is_array($this->cmd_params[$param]) && trim($this->cmd_params[$param]) === "") {
|
||||
if ($optional === false) {
|
||||
// get module + function for better error-messages
|
||||
$inmod = $this->getModFunctionString();
|
||||
@@ -142,7 +142,7 @@ abstract class ApiParameter
|
||||
|
||||
/**
|
||||
* returns "module::function()" for better error-messages (missing parameter etc.)
|
||||
* makes debugging a whole lot more comfortable
|
||||
* makes debugging a lot more comfortable
|
||||
*
|
||||
* @param int $level
|
||||
* depth of backtrace, default 2
|
||||
@@ -152,7 +152,7 @@ abstract class ApiParameter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getModFunctionString($level = 1, $max_level = 5, $trace = null)
|
||||
private function getModFunctionString(int $level = 1, int $max_level = 5, $trace = null)
|
||||
{
|
||||
// which class called us
|
||||
$_class = get_called_class();
|
||||
@@ -174,7 +174,7 @@ abstract class ApiParameter
|
||||
/**
|
||||
* getParam wrapper for boolean parameter
|
||||
*
|
||||
* @param string $param
|
||||
* @param string|null $param
|
||||
* parameter to get out of the request-parameter list
|
||||
* @param bool $optional
|
||||
* default: false
|
||||
@@ -183,7 +183,7 @@ abstract class ApiParameter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getBoolParam($param = null, $optional = false, $default = false)
|
||||
protected function getBoolParam(string $param = null, bool $optional = false, $default = false)
|
||||
{
|
||||
$_default = '0';
|
||||
if ($default) {
|
||||
|
||||
@@ -95,7 +95,7 @@ class Admins extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list admins");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list admins");
|
||||
$query_fields = [];
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT *
|
||||
@@ -407,7 +407,7 @@ class Admins extends ApiCommand implements ResourceEntity
|
||||
];
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get admin '" . $result['loginname'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get admin '" . $result['loginname'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'");
|
||||
@@ -584,6 +584,18 @@ class Admins extends ApiCommand implements ResourceEntity
|
||||
$theme = Settings::Get('panel.default_theme');
|
||||
}
|
||||
|
||||
if (empty(trim($name))) {
|
||||
Response::standardError([
|
||||
'stringisempty',
|
||||
'admin.name'
|
||||
], '', true);
|
||||
}
|
||||
if (empty(trim($email))) {
|
||||
Response::standardError([
|
||||
'stringisempty',
|
||||
'admin.email'
|
||||
], '', true);
|
||||
}
|
||||
if (!Validate::validateEmail($email)) {
|
||||
Response::standardError('emailiswrong', $email, true);
|
||||
} else {
|
||||
@@ -705,7 +717,7 @@ class Admins extends ApiCommand implements ResourceEntity
|
||||
WHERE `adminid` = :adminid
|
||||
");
|
||||
Database::pexecute($upd_stmt, $upd_data, true, true);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] edited admin '" . $result['loginname'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] edited admin '" . $result['loginname'] . "'");
|
||||
|
||||
// get all admin-data for return-array
|
||||
$result = $this->apiCall('Admins.get', [
|
||||
|
||||
@@ -97,7 +97,7 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
if (!$has_cert) {
|
||||
$this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added ssl-certificate for '" . $domain['domain'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ssl-certificate for '" . $domain['domain'] . "'");
|
||||
$result = $this->apiCall('Certificates.get', [
|
||||
'id' => $domain['id']
|
||||
]);
|
||||
@@ -127,7 +127,9 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
|
||||
$do_verify = true;
|
||||
$expirationdate = null;
|
||||
$validtodate = null;
|
||||
$validtodate = null;
|
||||
$issuer = "";
|
||||
// no cert-file given -> forget everything
|
||||
if ($ssl_cert_file == '') {
|
||||
$ssl_key_file = '';
|
||||
@@ -168,7 +170,10 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
} else {
|
||||
Response::standardError('sslcertificateinvalidcert', '', true);
|
||||
}
|
||||
$expirationdate = empty($cert_content['validTo_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validTo_time_t']);
|
||||
// get data from certificate to store in the table
|
||||
$validfromdate = empty($cert_content['validFrom_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validFrom_time_t']);
|
||||
$validtodate = empty($cert_content['validTo_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validTo_time_t']);
|
||||
$issuer = $cert_content['issuer']['O'] ?? "";
|
||||
}
|
||||
|
||||
// Add/Update database entry
|
||||
@@ -183,7 +188,9 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
`ssl_key_file` = :ssl_key_file,
|
||||
`ssl_ca_file` = :ssl_ca_file,
|
||||
`ssl_cert_chainfile` = :ssl_cert_chainfile,
|
||||
`expirationdate` = :expirationdate
|
||||
`validfromdate` = :validfromdate,
|
||||
`validtodate` = :validtodate,
|
||||
`issuer` = :issuer
|
||||
" . $qrywhere . " `domainid`= :domainid
|
||||
");
|
||||
$params = [
|
||||
@@ -191,7 +198,9 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
"ssl_key_file" => $ssl_key_file,
|
||||
"ssl_ca_file" => $ssl_ca_file,
|
||||
"ssl_cert_chainfile" => $ssl_cert_chainfile,
|
||||
"expirationdate" => $expirationdate,
|
||||
"validfromdate" => $validfromdate,
|
||||
"validtodate" => $validtodate,
|
||||
"issuer" => $issuer,
|
||||
"domainid" => $domainid
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
@@ -239,7 +248,7 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
$ssl_ca_file = $this->getParam('ssl_ca_file', true, '');
|
||||
$ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, '');
|
||||
$this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, false);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ssl-certificate for '" . $domain['domain'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ssl-certificate for '" . $domain['domain'] . "'");
|
||||
$result = $this->apiCall('Certificates.get', [
|
||||
'id' => $domain['id']
|
||||
]);
|
||||
@@ -299,27 +308,23 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
|
||||
// Set data from certificate
|
||||
$cert['isvalid'] = false;
|
||||
$cert['san'] = null;
|
||||
$cert_data = openssl_x509_parse($cert['ssl_cert_file']);
|
||||
if ($cert_data) {
|
||||
$cert['validfromdate'] = date('Y-m-d H:i:s', $cert_data['validFrom_time_t']);
|
||||
$cert['validtodate'] = date('Y-m-d H:i:s', $cert_data['validTo_time_t']);
|
||||
$cert['isvalid'] = (bool)$cert_data['validTo_time_t'] > time();
|
||||
$cert['issuer'] = $cert_data['issuer']['O'] ?? null;
|
||||
}
|
||||
|
||||
// Set subject alt names from certificate
|
||||
$cert['san'] = null;
|
||||
if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) {
|
||||
$SANs = explode(",", $cert_data['extensions']['subjectAltName']);
|
||||
$SANs = array_map('trim', $SANs);
|
||||
foreach ($SANs as $san) {
|
||||
$san = str_replace("DNS:", "", $san);
|
||||
if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) {
|
||||
$cert['san'][] = $san;
|
||||
// Set subject alt names from certificate
|
||||
if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) {
|
||||
$SANs = explode(",", $cert_data['extensions']['subjectAltName']);
|
||||
$SANs = array_map('trim', $SANs);
|
||||
foreach ($SANs as $san) {
|
||||
$san = str_replace("DNS:", "", $san);
|
||||
if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) {
|
||||
$cert['san'][] = $san;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = $cert;
|
||||
}
|
||||
return $this->response([
|
||||
@@ -465,7 +470,7 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
if ($chk['letsencrypt'] == '1') {
|
||||
Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $chk['domain']);
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] removed ssl-certificate for '" . $chk['domain'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] removed ssl-certificate for '" . $chk['domain'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
throw new Exception("Unable to determine SSL certificate. Maybe no access?", 406);
|
||||
|
||||
@@ -147,7 +147,7 @@ class Cronjobs extends ApiCommand implements ResourceEntity
|
||||
|
||||
// insert task to re-generate the cron.d-file
|
||||
Cronjob::inserttask(TaskId::REBUILD_CRON);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] cronjob with description '" . $result['module'] . '/' . $result['cronfile'] . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] cronjob with description '" . $result['module'] . '/' . $result['cronfile'] . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
$result = $this->apiCall('Cronjobs.get', [
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -177,7 +177,7 @@ class Cronjobs extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list cronjobs");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list cronjobs");
|
||||
$query_fields = [];
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT `c`.* FROM `" . TABLE_PANEL_CRONRUNS . "` `c` " . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());
|
||||
|
||||
@@ -194,7 +194,7 @@ class CustomerBackups extends ApiCommand implements ResourceEntity
|
||||
$result[] = $entry;
|
||||
}
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list customer-backups");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list customer-backups");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
|
||||
@@ -895,7 +895,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
||||
$result['dbspace_used'] = 0;
|
||||
}
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get customer '" . $result['loginname'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get customer '" . $result['loginname'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'");
|
||||
@@ -1327,7 +1327,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
||||
'vu' => $valid_until
|
||||
], true, true);
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
}
|
||||
|
||||
@@ -1538,7 +1538,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
||||
Database::query($admin_update_query);
|
||||
}
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited user '" . $result['loginname'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited user '" . $result['loginname'] . "'");
|
||||
|
||||
/*
|
||||
* move customer to another admin/reseller; #1166
|
||||
@@ -1911,7 +1911,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
||||
// now, recalculate the resource-usage for the old and the new admin
|
||||
User::updateCounters(false);
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'");
|
||||
|
||||
$result = $this->apiCall('Customers.get', [
|
||||
'id' => $c_result['customerid']
|
||||
|
||||
@@ -144,7 +144,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
$id = Database::lastInsertId();
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added directory-option for '" . $userpath . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added directory-option for '" . $userpath . "'");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
|
||||
$result = $this->apiCall('DirOptions.get', [
|
||||
@@ -247,7 +247,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
$params['id'] = $id;
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get directory options for '" . $result['path'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get directory options for '" . $result['path'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = "id #" . $id;
|
||||
@@ -331,7 +331,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
"id" => $id
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited directory options for '" . str_replace($customer['documentroot'], '/', $result['path']) . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited directory options for '" . str_replace($customer['documentroot'], '/', $result['path']) . "'");
|
||||
}
|
||||
|
||||
$result = $this->apiCall('DirOptions.get', [
|
||||
@@ -379,7 +379,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list directory-options");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list directory-options");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
@@ -478,7 +478,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
"customerid" => $customer_data['customerid'],
|
||||
"id" => $id
|
||||
], true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted directory-option for '" . str_replace($customer_data['documentroot'], '/', $result['path']) . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] deleted directory-option for '" . str_replace($customer_data['documentroot'], '/', $result['path']) . "'");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
return $this->response($result);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
$id = Database::lastInsertId();
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added directory-protection for '" . $username . " (" . $path . ")'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added directory-protection for '" . $username . " (" . $path . ")'");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
|
||||
$result = $this->apiCall('DirProtections.get', [
|
||||
@@ -196,7 +196,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
$params['idun'] = ($id <= 0 ? $username : $id);
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get directory protection for '" . $result['path'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get directory protection for '" . $result['path'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "username '" . $username . "'");
|
||||
@@ -279,7 +279,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
}
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated directory-protection '" . $result['username'] . " (" . $result['path'] . ")'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated directory-protection '" . $result['username'] . " (" . $result['path'] . ")'");
|
||||
$result = $this->apiCall('DirProtections.get', [
|
||||
'id' => $result['id']
|
||||
]);
|
||||
@@ -325,7 +325,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list directory-protections");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list directory-protections");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
@@ -413,7 +413,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
"id" => $id
|
||||
]);
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
return $this->response($result);
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@ class DomainZones extends ApiCommand implements ResourceEntity
|
||||
$zone = Dns::createDomainZone($id);
|
||||
$zonefile = (string)$zone;
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get dns-zone for '" . $result['domain'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get dns-zone for '" . $result['domain'] . "'");
|
||||
return $this->response(explode("\n", $zonefile));
|
||||
}
|
||||
|
||||
|
||||
@@ -225,6 +225,8 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
* optional, whether php is enabled for this domain, default 0 (false)
|
||||
* @param bool $openbasedir
|
||||
* optional, whether to activate openbasedir restriction for this domain, default 0 (false)
|
||||
* @param int $openbasedir_path
|
||||
* optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot
|
||||
* @param int $phpsettingid
|
||||
* optional, specify php-configuration that is being used by id, default 1 (system-default)
|
||||
* @param int $mod_fcgid_starter
|
||||
@@ -312,6 +314,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$documentroot = $this->getParam('documentroot', true, '');
|
||||
$phpenabled = $this->getBoolParam('phpenabled', true, 0);
|
||||
$openbasedir = $this->getBoolParam('openbasedir', true, 0);
|
||||
$openbasedir_path = $this->getParam('openbasedir_path', true, 0);
|
||||
$phpsettingid = $this->getParam('phpsettingid', true, 1);
|
||||
$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, -1);
|
||||
$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, -1);
|
||||
@@ -530,6 +533,10 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$mod_fcgid_maxrequests = '-1';
|
||||
}
|
||||
|
||||
if ($openbasedir_path > 2 && $openbasedir_path < 0) {
|
||||
$openbasedir_path = 0;
|
||||
}
|
||||
|
||||
// check non-ssl IP
|
||||
$ipandports = $this->validateIpAddresses($p_ipandports);
|
||||
// check ssl IP
|
||||
@@ -701,6 +708,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
'caneditdomain' => $caneditdomain,
|
||||
'phpenabled' => $phpenabled,
|
||||
'openbasedir' => $openbasedir,
|
||||
'openbasedir_path' => $openbasedir_path,
|
||||
'speciallogfile' => $speciallogfile,
|
||||
'specialsettings' => $specialsettings,
|
||||
'ssl_specialsettings' => $ssl_specialsettings,
|
||||
@@ -754,6 +762,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
`caneditdomain` = :caneditdomain,
|
||||
`phpenabled` = :phpenabled,
|
||||
`openbasedir` = :openbasedir,
|
||||
`openbasedir_path` = :openbasedir_path,
|
||||
`speciallogfile` = :speciallogfile,
|
||||
`specialsettings` = :specialsettings,
|
||||
`ssl_specialsettings` = :ssl_specialsettings,
|
||||
@@ -889,7 +898,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$result['ipsandports'] = $this->getIpsForDomain($result['id']);
|
||||
}
|
||||
$result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get domain '" . $result['domain'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get domain '" . $result['domain'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'");
|
||||
@@ -1101,6 +1110,8 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
* from setting system.apply_phpconfigs_default
|
||||
* @param bool $openbasedir
|
||||
* optional, whether to activate openbasedir restriction for this domain, default 0 (false)
|
||||
* @param int $openbasedir_path
|
||||
* optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot
|
||||
* @param int $phpsettingid
|
||||
* optional, specify php-configuration that is being used by id, default 1 (system-default)
|
||||
* @param int $mod_fcgid_starter
|
||||
@@ -1198,6 +1209,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$phpenabled = $this->getBoolParam('phpenabled', true, $result['phpenabled']);
|
||||
$phpfs = $this->getBoolParam('phpsettingsforsubdomains', true, Settings::Get('system.apply_phpconfigs_default'));
|
||||
$openbasedir = $this->getBoolParam('openbasedir', true, $result['openbasedir']);
|
||||
$openbasedir_path = $this->getParam('openbasedir_path', true, $result['openbasedir_path']);
|
||||
$phpsettingid = $this->getParam('phpsettingid', true, $result['phpsettingid']);
|
||||
$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, $result['mod_fcgid_starter']);
|
||||
$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, $result['mod_fcgid_maxrequests']);
|
||||
@@ -1489,6 +1501,11 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests'];
|
||||
}
|
||||
|
||||
// check changes of openbasedir-path variable
|
||||
if ($openbasedir_path > 2 && $openbasedir_path < 0) {
|
||||
$openbasedir_path = 0;
|
||||
}
|
||||
|
||||
// check non-ssl IP
|
||||
$ipandports = $this->validateIpAddresses($p_ipandports, false, $result['id']);
|
||||
// check ssl IP
|
||||
@@ -1634,7 +1651,31 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';
|
||||
$iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0';
|
||||
|
||||
if ($documentroot != $result['documentroot'] || $ssl_redirect != $result['ssl_redirect'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $phpenabled != $result['phpenabled'] || $openbasedir != $result['openbasedir'] || $phpsettingid != $result['phpsettingid'] || $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || $ssl_specialsettings != $result['ssl_specialsettings'] || $notryfiles != $result['notryfiles'] || $writeaccesslog != $result['writeaccesslog'] || $writeerrorlog != $result['writeerrorlog'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt'] || $http2 != $result['http2'] || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload'] || $ocsp_stapling != $result['ocsp_stapling']) {
|
||||
if ($documentroot != $result['documentroot']
|
||||
|| $ssl_redirect != $result['ssl_redirect']
|
||||
|| $wwwserveralias != $result['wwwserveralias']
|
||||
|| $iswildcarddomain != $result['iswildcarddomain']
|
||||
|| $phpenabled != $result['phpenabled']
|
||||
|| $openbasedir != $result['openbasedir']
|
||||
|| $phpsettingid != $result['phpsettingid']
|
||||
|| $mod_fcgid_starter != $result['mod_fcgid_starter']
|
||||
|| $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests']
|
||||
|| $specialsettings != $result['specialsettings']
|
||||
|| $ssl_specialsettings != $result['ssl_specialsettings']
|
||||
|| $notryfiles != $result['notryfiles']
|
||||
|| $writeaccesslog != $result['writeaccesslog']
|
||||
|| $writeerrorlog != $result['writeerrorlog']
|
||||
|| $aliasdomain != $result['aliasdomain']
|
||||
|| $issubof != $result['ismainbutsubto']
|
||||
|| $email_only != $result['email_only']
|
||||
|| ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1')
|
||||
|| $letsencrypt != $result['letsencrypt']
|
||||
|| $http2 != $result['http2']
|
||||
|| $hsts_maxage != $result['hsts']
|
||||
|| $hsts_sub != $result['hsts_sub']
|
||||
|| $hsts_preload != $result['hsts_preload']
|
||||
|| $ocsp_stapling != $result['ocsp_stapling']
|
||||
) {
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
}
|
||||
|
||||
@@ -1760,7 +1801,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
Database::pexecute($upd_stmt, [
|
||||
'id' => $id
|
||||
], true, true);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] removed specialsettings on all subdomains of domain #" . $id);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] removed specialsettings on all subdomains of domain #" . $id);
|
||||
}
|
||||
|
||||
$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';
|
||||
@@ -1782,7 +1823,8 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$update_data['wwwserveralias'] = $wwwserveralias;
|
||||
$update_data['iswildcarddomain'] = $iswildcarddomain;
|
||||
$update_data['phpenabled'] = $phpenabled;
|
||||
$update_data['openbasedir'] = $openbasedir;
|
||||
$update_data['openbasedir'] = $openbasedir;;
|
||||
$update_data['openbasedir_path'] = $openbasedir_path;
|
||||
$update_data['speciallogfile'] = $speciallogfile;
|
||||
$update_data['phpsettingid'] = $phpsettingid;
|
||||
$update_data['mod_fcgid_starter'] = $mod_fcgid_starter;
|
||||
@@ -1830,6 +1872,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
`iswildcarddomain` = :iswildcarddomain,
|
||||
`phpenabled` = :phpenabled,
|
||||
`openbasedir` = :openbasedir,
|
||||
`openbasedir_path` = :openbasedir_path,
|
||||
`speciallogfile` = :speciallogfile,
|
||||
`phpsettingid` = :phpsettingid,
|
||||
`mod_fcgid_starter` = :mod_fcgid_starter,
|
||||
@@ -1865,6 +1908,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$_update_data['adminid'] = $adminid;
|
||||
$_update_data['phpenabled'] = $phpenabled;
|
||||
$_update_data['openbasedir'] = $openbasedir;
|
||||
$_update_data['openbasedir_path'] = $openbasedir_path;
|
||||
$_update_data['mod_fcgid_starter'] = $mod_fcgid_starter;
|
||||
$_update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests;
|
||||
$_update_data['notryfiles'] = $notryfiles;
|
||||
@@ -1898,6 +1942,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
`adminid` = :adminid,
|
||||
`phpenabled` = :phpenabled,
|
||||
`openbasedir` = :openbasedir,
|
||||
`openbasedir_path` = :openbasedir_path,
|
||||
`mod_fcgid_starter` = :mod_fcgid_starter,
|
||||
`mod_fcgid_maxrequests` = :mod_fcgid_maxrequests,
|
||||
`notryfiles` = :notryfiles,
|
||||
@@ -1914,6 +1959,18 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
");
|
||||
Database::pexecute($_update_stmt, $_update_data, true, true);
|
||||
|
||||
// get current ip<>domain entries
|
||||
$ip_sel_stmt = Database::prepare("
|
||||
SELECT id_ipandports FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id
|
||||
");
|
||||
Database::pexecute($ip_sel_stmt, [
|
||||
'id' => $id
|
||||
], true, true);
|
||||
$current_ips = [];
|
||||
while ($cIP = $ip_sel_stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$current_ips[] = $cIP['id_ipandports'];
|
||||
}
|
||||
|
||||
// Cleanup domain <-> ip mapping
|
||||
$del_stmt = Database::prepare("
|
||||
DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id
|
||||
@@ -1941,6 +1998,12 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
}
|
||||
|
||||
// check ip changes
|
||||
$all_new_ips = array_merge($ipandports, $ssl_ipandports);
|
||||
if (count(array_diff($current_ips, $all_new_ips)) != 0 || count(array_diff($all_new_ips, $current_ips)) != 0) {
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
}
|
||||
|
||||
// Cleanup domain <-> ip mapping for subdomains
|
||||
$domainidsresult_stmt = Database::prepare("
|
||||
SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :id
|
||||
@@ -2158,7 +2221,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
// remove domain from acme.sh / lets encrypt if used
|
||||
Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $result['domain']);
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] deleted domain/subdomains (#" . $result['id'] . ")");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] deleted domain/subdomains (#" . $result['id'] . ")");
|
||||
User::updateCounters();
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
// Using nameserver, insert a task which rebuilds the server config
|
||||
|
||||
@@ -63,7 +63,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
* @param string $alternative_email
|
||||
* optional email address to send account information to, default is the account that is being created
|
||||
* @param int $email_quota
|
||||
* optional quota if enabled in MB, default 0
|
||||
* optional quota if enabled in MB, default setting: system.mail_quota
|
||||
* @param bool $sendinfomail
|
||||
* optional, sends the welcome message to the new account (needed for creation, without the user won't
|
||||
* be able to login before any mail is received), default 1 (true)
|
||||
@@ -85,7 +85,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
$emailaddr = $this->getParam('emailaddr', $ea_optional, '');
|
||||
$email_password = $this->getParam('email_password');
|
||||
$alternative_email = $this->getParam('alternative_email', true, '');
|
||||
$quota = $this->getParam('email_quota', true, 0);
|
||||
$quota = $this->getParam('email_quota', true, Settings::Get('system.mail_quota') ?? 0);
|
||||
$sendinfomail = $this->getBoolParam('sendinfomail', true, 1);
|
||||
|
||||
// validation
|
||||
@@ -99,6 +99,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
Response::standardError('notallowedtouseaccounts', '', true);
|
||||
}
|
||||
|
||||
if (!empty($emailaddr)) {
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$emailaddr = $idna_convert->encode($emailaddr);
|
||||
}
|
||||
|
||||
// get email address
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'id' => $id,
|
||||
@@ -306,7 +311,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email account for '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email account for '" . $result['email_full'] . "'");
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
]);
|
||||
@@ -357,6 +362,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
$ea_optional = $id > 0;
|
||||
$emailaddr = $this->getParam('emailaddr', $ea_optional, '');
|
||||
|
||||
if (!empty($emailaddr)) {
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$emailaddr = $idna_convert->encode($emailaddr);
|
||||
}
|
||||
|
||||
// validation
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'id' => $id,
|
||||
@@ -450,7 +460,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
Admins::increaseUsage($customer['adminid'], 'email_quota_used', '', ($quota - $result['quota']));
|
||||
}
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated email account '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated email account '" . $result['email_full'] . "'");
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
]);
|
||||
@@ -556,7 +566,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
Customers::decreaseUsage($customer['customerid'], 'email_accounts_used');
|
||||
Customers::decreaseUsage($customer['customerid'], 'email_quota_used', '', $quota);
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email account for '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted email account for '" . $result['email_full'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ class EmailDomains extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE,
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO,
|
||||
"[API] list email-domains");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
|
||||
@@ -77,6 +77,11 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$destination = $idna_convert->encode($destination);
|
||||
|
||||
if (!empty($emailaddr)) {
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$emailaddr = $idna_convert->encode($emailaddr);
|
||||
}
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'id' => $id,
|
||||
'emailaddr' => $emailaddr
|
||||
@@ -116,7 +121,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
|
||||
// update customer usage
|
||||
Customers::increaseUsage($customer['customerid'], 'email_forwarders_used');
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email forwarder for '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email forwarder for '" . $result['email_full'] . "'");
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
@@ -293,7 +298,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
|
||||
// update customer usage
|
||||
Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used');
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email forwarder for '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] deleted email forwarder for '" . $result['email_full'] . "'");
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
|
||||
@@ -159,7 +159,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
// update customer usage
|
||||
Customers::increaseUsage($customer['customerid'], 'emails_used');
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email address '" . $email_full . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email address '" . $email_full . "'");
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $email_full
|
||||
@@ -195,11 +195,11 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
FROM `" . TABLE_MAIL_VIRTUAL . "` v
|
||||
LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id`
|
||||
WHERE v.`customerid` IN (" . implode(", ", $customer_ids) . ")
|
||||
AND (v.`id`= :idea OR (v.`email` = :idea OR v.`email_full` = :idea))
|
||||
");
|
||||
AND " . (is_numeric($params['idea']) ? "v.`id`= :idea" : "(v.`email` = :idea OR v.`email_full` = :idea)")
|
||||
);
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get email address '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get email address '" . $result['email_full'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "emailaddr '" . $emailaddr . "'");
|
||||
@@ -294,7 +294,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
"id" => $id
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'");
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
@@ -340,7 +340,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list email-addresses");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list email-addresses");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
@@ -445,7 +445,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
], true, true);
|
||||
Customers::decreaseUsage($customer['customerid'], 'emails_used');
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email address '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted email address '" . $result['email_full'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list fpm-daemons");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list fpm-daemons");
|
||||
$query_fields = [];
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "`" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());
|
||||
@@ -258,7 +258,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
|
||||
$id = Database::lastInsertId();
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
|
||||
$result = $this->apiCall('FpmDaemons.get', [
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -384,7 +384,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
|
||||
Database::pexecute($upd_stmt, $upd_data, true, true);
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
$result = $this->apiCall('FpmDaemons.get', [
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -433,7 +433,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
|
||||
], true, true);
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
throw new Exception("Not allowed to execute given command.", 403);
|
||||
|
||||
@@ -72,7 +72,7 @@ class Froxlor extends ApiCommand
|
||||
|
||||
if (empty($uc_data) || empty($response) || $uc_data['ts'] + self::UPDATE_CHECK_INTERVAL < time() || $uc_data['channel'] != Settings::Get('system.update_channel') || $force_ucheck) {
|
||||
// log our actions
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] checking for updates");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] checking for updates");
|
||||
|
||||
// check for new version
|
||||
$aucheck = AutoUpdate::checkVersion();
|
||||
@@ -142,7 +142,7 @@ class Froxlor extends ApiCommand
|
||||
{
|
||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
|
||||
$json_str = $this->getParam('json_str');
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "User " . $this->getUserDetail('loginname') . " imported settings");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "User " . $this->getUserDetail('loginname') . " imported settings");
|
||||
try {
|
||||
SImExporter::import($json_str);
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
|
||||
@@ -257,7 +257,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
Customers::increaseUsage($customer['customerid'], 'ftp_lastaccountnumber');
|
||||
}
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added ftp-account '" . $username . " (" . $path . ")'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ftp-account '" . $username . " (" . $path . ")'");
|
||||
Cronjob::inserttask(TaskId::CREATE_FTP);
|
||||
|
||||
if ($sendinfomail == 1) {
|
||||
@@ -302,7 +302,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
|
||||
$this->mailer()->clearAddresses();
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] added ftp-user '" . $username . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ftp-user '" . $username . "'");
|
||||
|
||||
$result = $this->apiCall('Ftps.get', [
|
||||
'username' => $username
|
||||
@@ -367,7 +367,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
$params['idun'] = ($id <= 0 ? $username : $id);
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get ftp-user '" . $result['username'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get ftp-user '" . $result['username'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "username '" . $username . "'");
|
||||
@@ -453,7 +453,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
"id" => $id,
|
||||
"password" => $cryptPassword
|
||||
], true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ftp-account password for '" . $result['username'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ftp-account password for '" . $result['username'] . "'");
|
||||
}
|
||||
|
||||
// path update?
|
||||
@@ -471,7 +471,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
"customerid" => $customer['customerid'],
|
||||
"id" => $id
|
||||
], true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ftp-account homdir for '" . $result['username'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ftp-account homdir for '" . $result['username'] . "'");
|
||||
}
|
||||
}
|
||||
// it's the task for "new ftp" but that will
|
||||
@@ -533,7 +533,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list ftp-users");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list ftp-users");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
|
||||
@@ -61,7 +61,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list hosting-plans");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list hosting-plans");
|
||||
$query_fields = [];
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT p.*, a.loginname as adminname
|
||||
@@ -200,8 +200,8 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
$value_arr['logviewenabled'] = $this->getBoolParam('logviewenabled', true, 0);
|
||||
|
||||
// validation
|
||||
$name = Validate::validate(trim($name), 'name', '', '', [], true);
|
||||
$description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_DESC_TEXT);
|
||||
$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);
|
||||
|
||||
if (Settings::Get('system.mail_quota_enabled') != '1') {
|
||||
$value_arr['email_quota'] = -1;
|
||||
@@ -227,7 +227,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
'valuearr' => json_encode($value_arr)
|
||||
];
|
||||
Database::pexecute($ins_stmt, $ins_data, true, true);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] added hosting-plan '" . $name . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] added hosting-plan '" . $name . "'");
|
||||
$result = $this->apiCall('HostingPlans.get', [
|
||||
'planname' => $name
|
||||
]);
|
||||
@@ -264,7 +264,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get hosting-plan '" . $result['name'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get hosting-plan '" . $result['name'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "planname '" . $planname . "'");
|
||||
@@ -382,8 +382,8 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
$value_arr['logviewenabled'] = $this->getBoolParam('logviewenabled', true, $result['logviewenabled']);
|
||||
|
||||
// validation
|
||||
$name = Validate::validate(trim($name), 'name', '', '', [], true);
|
||||
$description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_DESC_TEXT);
|
||||
$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);
|
||||
|
||||
if (Settings::Get('system.mail_quota_enabled') != '1') {
|
||||
$value_arr['email_quota'] = -1;
|
||||
@@ -414,7 +414,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
'id' => $id
|
||||
];
|
||||
Database::pexecute($upd_stmt, $update_data, true, true);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] updated hosting-plan '" . $result['name'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] updated hosting-plan '" . $result['name'] . "'");
|
||||
return $this->response($update_data);
|
||||
}
|
||||
throw new Exception("Not allowed to execute given command.", 403);
|
||||
|
||||
@@ -65,7 +65,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || !empty($this->getUserDetail('ip')))) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list ips and ports");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list ips and ports");
|
||||
$ip_where = "";
|
||||
$append_where = false;
|
||||
if (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) {
|
||||
@@ -175,9 +175,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
|
||||
$docroot = Validate::validate($this->getParam('docroot', true, ''), 'docroot', Validate::REGEX_DIR, '', [], true);
|
||||
|
||||
if ((int)Settings::Get('system.use_ssl') == 1) {
|
||||
$ssl = !empty($this->getBoolParam('ssl', true, 0)) ? intval($this->getBoolParam('ssl', true, 0)) : 0;
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $ssl, ''), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $ssl, ''), 'ssl_key_file', '', '', [], true);
|
||||
$ssl = (bool)$this->getBoolParam('ssl', true, 0);
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', !$ssl, ''), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', !$ssl, ''), 'ssl_key_file', '', '', [], true);
|
||||
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, ''), 'ssl_ca_file', '', '', [], true);
|
||||
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, ''), 'ssl_cert_chainfile', '', '', [], true);
|
||||
$sslss = $this->getParam('ssl_specialsettings', true, '');
|
||||
@@ -335,7 +335,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
|
||||
'id' => $id
|
||||
], true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get ip " . $result['ip'] . " " . $result['port']);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get ip " . $result['ip'] . " " . $result['port']);
|
||||
return $this->response($result);
|
||||
}
|
||||
throw new Exception("IP/port with id #" . $id . " could not be found", 404);
|
||||
@@ -414,9 +414,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
|
||||
$docroot = Validate::validate($this->getParam('docroot', true, $result['docroot']), 'docroot', Validate::REGEX_DIR, '', [], true);
|
||||
|
||||
if ((int)Settings::Get('system.use_ssl') == 1) {
|
||||
$ssl = $this->getBoolParam('ssl', true, $result['ssl']);
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $ssl, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $ssl, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);
|
||||
$ssl = (bool)$this->getBoolParam('ssl', true, $result['ssl']);
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', !$ssl, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', !$ssl, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);
|
||||
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, $result['ssl_ca_file']), 'ssl_ca_file', '', '', [], true);
|
||||
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', [], true);
|
||||
$sslss = $this->getParam('ssl_specialsettings', true, $result['ssl_specialsettings']);
|
||||
|
||||
@@ -26,14 +26,15 @@
|
||||
namespace Froxlor\Api\Commands;
|
||||
|
||||
use Exception;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\PhpHelper;
|
||||
use Froxlor\Api\ApiCommand;
|
||||
use Froxlor\Api\ResourceEntity;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\PhpHelper;
|
||||
use Froxlor\Validate\Validate;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
{
|
||||
@@ -73,8 +74,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
* optional, test connection with given credentials, default is true (yes)
|
||||
*
|
||||
* @access admin
|
||||
* @throws Exception
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
@@ -112,7 +113,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
);
|
||||
if (!empty($mysql_ca)) {
|
||||
$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca;
|
||||
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool) $mysql_verifycert;
|
||||
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_verifycert;
|
||||
}
|
||||
|
||||
$dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";";
|
||||
@@ -167,6 +168,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
$this->addDatabaseFromCustomerAllowedList($newdbserver);
|
||||
}
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] added new database server '" . $description . "' (" . $mysql_host . ")");
|
||||
|
||||
return $this->response(['dbserver' => $newdbserver]);
|
||||
}
|
||||
|
||||
@@ -179,16 +182,16 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
* optional the number of the mysql server (either id or dbserver must be set)
|
||||
*
|
||||
* @access admin
|
||||
* @throws Exception
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->validateAccess();
|
||||
|
||||
$id = (int) $this->getParam('id', true, -1);
|
||||
$id = (int)$this->getParam('id', true, -1);
|
||||
$dn_optional = $id >= 0;
|
||||
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = $id >= 0 ? $id : $dbserver;
|
||||
|
||||
if ($dbserver == 0) {
|
||||
@@ -212,8 +215,12 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
// when removing, remove from list of allowed_mysqlservers from any customers
|
||||
$this->removeDatabaseFromCustomerAllowedList($dbserver);
|
||||
|
||||
$description = $sql_root[$dbserver]['caption'] ?? "unknown";
|
||||
$mysql_host = $sql_root[$dbserver]['host'] ?? "unknown";
|
||||
unset($sql_root[$dbserver]);
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] removed database server '" . $description . "' (" . $mysql_host . ")");
|
||||
|
||||
$this->generateNewUserData($sql, $sql_root);
|
||||
return $this->response(['true']);
|
||||
}
|
||||
@@ -287,14 +294,14 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
* optional the number of the mysql server (either id or dbserver must be set)
|
||||
*
|
||||
* @access admin, customer
|
||||
* @throws Exception
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$id = (int) $this->getParam('id', true, -1);
|
||||
$id = (int)$this->getParam('id', true, -1);
|
||||
$dn_optional = $id >= 0;
|
||||
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = $id >= 0 ? $id : $dbserver;
|
||||
|
||||
$sql_root = [];
|
||||
@@ -317,6 +324,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
|
||||
unset($sql_root[$dbserver]['password']);
|
||||
$sql_root[$dbserver]['id'] = $dbserver;
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get database-server '" . $sql_root[$dbserver]['caption'] . "'");
|
||||
return $this->response($sql_root[$dbserver]);
|
||||
}
|
||||
|
||||
@@ -347,16 +355,16 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
* optional, test connection with given credentials, default is true (yes)
|
||||
*
|
||||
* @access admin
|
||||
* @throws Exception
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$this->validateAccess();
|
||||
|
||||
$id = (int) $this->getParam('id', true, -1);
|
||||
$id = (int)$this->getParam('id', true, -1);
|
||||
$dn_optional = $id >= 0;
|
||||
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = $id >= 0 ? $id : $dbserver;
|
||||
|
||||
$sql_root = [];
|
||||
@@ -417,7 +425,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
);
|
||||
if (!empty($mysql_ca)) {
|
||||
$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca;
|
||||
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool) $mysql_verifycert;
|
||||
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_verifycert;
|
||||
}
|
||||
|
||||
$dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";";
|
||||
@@ -448,6 +456,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
$this->addDatabaseFromCustomerAllowedList($dbserver);
|
||||
}
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] edited database server '" . $description . "' (" . $mysql_host . ")");
|
||||
|
||||
return $this->response(['true']);
|
||||
}
|
||||
|
||||
@@ -472,7 +482,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
WHERE `dbserver` = :dbserver
|
||||
");
|
||||
$result = Database::pexecute_first($result_stmt, ['dbserver' => $dbserver], true, true);
|
||||
return (int) $result['num_dbs'];
|
||||
return (int)$result['num_dbs'];
|
||||
} else {
|
||||
$dbserver = $this->getParam('mysql_server');
|
||||
$customer_ids = $this->getAllowedCustomerIds();
|
||||
@@ -516,7 +526,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
`allowed_mysqlserver` = :am WHERE `customerid` = :cid
|
||||
");
|
||||
while ($customer = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$allowed_mysqls = json_decode(($customer['allowed_mysqlserver'] ?? '[]'), true);
|
||||
$allowed_mysqls = json_decode(($customer['allowed_mysqlserver'] ?: '[]'), true);
|
||||
if (!in_array($dbserver, $allowed_mysqls)) {
|
||||
$allowed_mysqls[] = $dbserver;
|
||||
$allowed_mysqls = json_encode($allowed_mysqls);
|
||||
|
||||
@@ -199,7 +199,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
|
||||
$this->mailer()->clearAddresses();
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] added mysql-database '" . $username . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added mysql-database '" . $username . "'");
|
||||
|
||||
$result = $this->apiCall('Mysqls.get', [
|
||||
'dbname' => $username,
|
||||
@@ -299,7 +299,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
$mbdata = $mbdata_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
Database::needRoot(false);
|
||||
$result['size'] = $mbdata['MB'] ?? 0;
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get database '" . $result['databasename'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get database '" . $result['databasename'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "dbname '" . $dbname . "'");
|
||||
@@ -388,7 +388,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] updated mysql-database '" . $result['databasename'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated mysql-database '" . $result['databasename'] . "'");
|
||||
$result = $this->apiCall('Mysqls.get', [
|
||||
'dbname' => $result['databasename']
|
||||
]);
|
||||
|
||||
@@ -67,7 +67,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list php-configs");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list php-configs");
|
||||
|
||||
$with_subdomains = $this->getBoolParam('with_subdomains', true, false);
|
||||
$query_fields = [];
|
||||
@@ -392,7 +392,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
|
||||
$ins_data['id'] = Database::lastInsertId();
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] php setting with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
|
||||
|
||||
$result = $this->apiCall('PhpSettings.get', [
|
||||
'id' => $ins_data['id']
|
||||
@@ -629,7 +629,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
|
||||
Database::pexecute($upd_stmt, $upd_data, true, true);
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] php setting with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
|
||||
$result = $this->apiCall('PhpSettings.get', [
|
||||
'id' => $id
|
||||
@@ -686,7 +686,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
|
||||
], true, true);
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] php setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
throw new Exception("Not allowed to execute given command.", 403);
|
||||
|
||||
@@ -62,7 +62,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
* optional, overwrites path value with an URL to generate a redirect, alternatively use the path
|
||||
* parameter also for URLs
|
||||
* @param int $openbasedir_path
|
||||
* optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot
|
||||
* optional, either 0 for domains-docroot [default], 1 for customers-homedir or 2 for parent-directory of domains-docroot
|
||||
* @param int $phpsettingid
|
||||
* optional, php-settings-id, if empty the $domain value is used
|
||||
* @param int $redirectcode
|
||||
@@ -104,7 +104,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
$aliasdomain = $this->getParam('alias', true, 0);
|
||||
$path = $this->getParam('path', true, '');
|
||||
$url = $this->getParam('url', true, '');
|
||||
$openbasedir_path = $this->getParam('openbasedir_path', true, 1);
|
||||
$openbasedir_path = $this->getParam('openbasedir_path', true, 0);
|
||||
$phpsettingid = $this->getParam('phpsettingid', true, 0);
|
||||
$redirectcode = $this->getParam('redirectcode', true, Settings::Get('customredirect.default'));
|
||||
$isemaildomain = $this->getParam('isemaildomain', true, 0);
|
||||
@@ -486,7 +486,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
$result['ipsandports'] = $this->getIpsForDomain($result['id']);
|
||||
}
|
||||
$result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get subdomain '" . $result['domain'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get subdomain '" . $result['domain'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'");
|
||||
@@ -856,7 +856,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
Cronjob::inserttask(TaskId::REBUILD_DNS);
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited domain '" . $idna_convert->decode($result['domain']) . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited domain '" . $idna_convert->decode($result['domain']) . "'");
|
||||
}
|
||||
$result = $this->apiCall('SubDomains.get', [
|
||||
'id' => $id
|
||||
|
||||
@@ -92,7 +92,7 @@ class SysLog extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list log-entries");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list log-entries");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
|
||||
@@ -166,7 +166,7 @@ class Traffic extends ApiCommand implements ResourceEntity
|
||||
$row['mail'] *= 1024;
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list traffic");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list traffic");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
|
||||
@@ -122,7 +122,7 @@ class CliCommand extends Command
|
||||
include_once Froxlor::getInstallDir() . '/lib/tables.inc.php';
|
||||
define('_CRON_UPDATE', 1);
|
||||
ob_start([
|
||||
'this',
|
||||
$this,
|
||||
'cleanUpdateOutput'
|
||||
]);
|
||||
include_once Froxlor::getInstallDir() . '/install/updatesql.php';
|
||||
|
||||
178
lib/Froxlor/Cli/ConfigDiff.php
Normal file
178
lib/Froxlor/Cli/ConfigDiff.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Froxlor project.
|
||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can also view it online at
|
||||
* https://files.froxlor.org/misc/COPYING.txt
|
||||
*
|
||||
* @copyright the authors
|
||||
* @author Froxlor team <team@froxlor.org>
|
||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
||||
*/
|
||||
|
||||
namespace Froxlor\Cli;
|
||||
|
||||
use Froxlor\Config\ConfigParser;
|
||||
use Froxlor\FileDir;
|
||||
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;
|
||||
|
||||
final class ConfigDiff extends CliCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setName('froxlor:config-diff')
|
||||
->setDescription('Shows differences in config templates between OS versions')
|
||||
->addArgument('from', InputArgument::OPTIONAL, 'OS version to compare against')
|
||||
->addArgument('to', InputArgument::OPTIONAL, 'OS version to compare from')
|
||||
->addOption('list', 'l', InputOption::VALUE_NONE, 'List all possible OS versions')
|
||||
->addOption('diff-params', '', InputOption::VALUE_REQUIRED, 'Additional parameters for `diff`, e.g. --diff-params="--color=always"');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
require Froxlor::getInstallDir() . '/lib/functions.php';
|
||||
|
||||
$parsers = $versions = [];
|
||||
foreach (glob(Froxlor::getInstallDir() . '/lib/configfiles/*.xml') as $config) {
|
||||
$name = str_replace(".xml", "", strtolower(basename($config)));
|
||||
$parser = new ConfigParser($config);
|
||||
$versions[$name] = $parser->getCompleteDistroName();
|
||||
$parsers[$name] = $parser;
|
||||
}
|
||||
asort($versions);
|
||||
|
||||
if ($input->getOption('list') === true) {
|
||||
$output->writeln('The following OS version templates are available:');
|
||||
foreach ($versions as $k => $v) {
|
||||
$output->writeln(str_pad($k, 20) . $v);
|
||||
}
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
if (!$input->hasArgument('from') || !array_key_exists($input->getArgument('from'), $versions)) {
|
||||
$output->writeln('<error>Missing or invalid "from" argument.</error>');
|
||||
$output->writeln('Available versions: ' . implode(', ', array_keys($versions)));
|
||||
return self::INVALID;
|
||||
}
|
||||
|
||||
if (!$input->hasArgument('to') || !array_key_exists($input->getArgument('to'), $versions)) {
|
||||
$output->writeln('<error>Missing or invalid "to" argument.</error>');
|
||||
$output->writeln('Available versions: ' . implode(', ', array_keys($versions)));
|
||||
return self::INVALID;
|
||||
}
|
||||
|
||||
// Make sure diff is installed
|
||||
$check_diff_installed = FileDir::safe_exec('which diff');
|
||||
if (count($check_diff_installed) === 0) {
|
||||
$output->writeln('<error>Unable to find "diff" installation on your system.</error>');
|
||||
return self::INVALID;
|
||||
}
|
||||
|
||||
$parser_from = $parsers[$input->getArgument('from')];
|
||||
$parser_to = $parsers[$input->getArgument('to')];
|
||||
$tmp_from = tempnam(sys_get_temp_dir(), 'froxlor_config_diff_from');
|
||||
$tmp_to = tempnam(sys_get_temp_dir(), 'froxlor_config_diff_to');
|
||||
$files = [];
|
||||
$titles_by_key = [];
|
||||
|
||||
// Aggregate content for each config file
|
||||
foreach ([[$parser_from, 'from'], [$parser_to, 'to']] as $todo) {
|
||||
foreach ($todo[0]->getServices() as $service_type => $service) {
|
||||
foreach ($service->getDaemons() as $daemon_name => $daemon) {
|
||||
foreach ($daemon->getConfig() as $instruction) {
|
||||
if ($instruction['type'] !== 'file') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($instruction['subcommands'])) {
|
||||
foreach ($instruction['subcommands'] as $subinstruction) {
|
||||
if ($subinstruction['type'] !== 'file') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$content = $subinstruction['content'];
|
||||
}
|
||||
} else {
|
||||
$content = $instruction['content'];
|
||||
}
|
||||
|
||||
if (!isset($content)) {
|
||||
throw new \Exception("Cannot find content for {$instruction['name']}");
|
||||
}
|
||||
|
||||
$key = "{$service_type}_{$daemon_name}_{$instruction['name']}";
|
||||
$titles_by_key[$key] = "{$service->title} : {$daemon->title} : {$instruction['name']}";
|
||||
if (!isset($files[$key])) {
|
||||
$files[$key] = ['from' => '', 'to' => ''];
|
||||
}
|
||||
$files[$key][$todo[1]] = $this->filterContent($content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort($files);
|
||||
|
||||
$diff_params = '';
|
||||
if ($input->hasOption('diff-params') && trim($input->getOption('diff-params')) !== '') {
|
||||
$diff_params = trim($input->getOption('diff-params'));
|
||||
}
|
||||
|
||||
// Run diff on each file and output, if anything changed
|
||||
foreach ($files as $file_key => $content) {
|
||||
file_put_contents($tmp_from, $content['from']);
|
||||
file_put_contents($tmp_to, $content['to']);
|
||||
$diff_output = FileDir::safe_exec("{$check_diff_installed[0]} {$diff_params} {$tmp_from} {$tmp_to}");
|
||||
|
||||
if (count($diff_output) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$output->writeln('<info># ' . $titles_by_key[$file_key] . '</info>');
|
||||
$output->writeln(implode("\n", $diff_output) . "\n");
|
||||
unset($diff_output);
|
||||
}
|
||||
|
||||
// Remove tmp files again
|
||||
unlink($tmp_from);
|
||||
unlink($tmp_to);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function filterContent(string $content): string
|
||||
{
|
||||
$new_content = '';
|
||||
|
||||
foreach (explode("\n", $content) as $n) {
|
||||
$n = trim($n);
|
||||
if (!$n) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($n, '#')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$new_content .= $n . "\n";
|
||||
}
|
||||
|
||||
return $new_content;
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ final class InstallCommand extends Command
|
||||
$_SERVER['SERVER_NAME'] = $host[0] ?? '';
|
||||
$ips = [];
|
||||
exec('hostname -I', $ips);
|
||||
$ips = explode(" ", $ips[0]);
|
||||
$ips = explode(" ", $ips[0] ?? "");
|
||||
// ipv4 address?
|
||||
$_SERVER['SERVER_ADDR'] = filter_var($ips[0] ?? "", FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? ($ips[0] ?? '') : '';
|
||||
if (empty($_SERVER['SERVER_ADDR'])) {
|
||||
|
||||
@@ -62,6 +62,11 @@ final class MasterCron extends CliCommand
|
||||
$result = self::SUCCESS;
|
||||
$result = $this->validateRequirements($input, $output);
|
||||
|
||||
if ($result != self::SUCCESS) {
|
||||
// requirements failed, exit
|
||||
return $result;
|
||||
}
|
||||
|
||||
$jobs = $input->getArgument('job');
|
||||
|
||||
// handle force option
|
||||
@@ -111,8 +116,8 @@ final class MasterCron extends CliCommand
|
||||
]);
|
||||
$this->cronLog->setCronDebugFlag(defined('CRON_DEBUG_FLAG'));
|
||||
|
||||
// check whether there are actual tasks to perform by 'tasks'-cron so
|
||||
// we dont regenerate files unnecessarily
|
||||
// check whether there are actual tasks to perform by 'tasks'-cron, so
|
||||
// we don't regenerate files unnecessarily
|
||||
$tasks_cnt_stmt = Database::query("SELECT COUNT(*) as jobcnt FROM `panel_tasks`");
|
||||
$tasks_cnt = $tasks_cnt_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ final class ValidateAcmeWebroot extends CliCommand
|
||||
'domain' => Settings::Get('system.hostname')
|
||||
];
|
||||
}
|
||||
$upd_stmt = Database::prepare("UPDATE domain_ssl_settings SET expirationdate=NULL WHERE `domainid` = :did");
|
||||
$upd_stmt = Database::prepare("UPDATE domain_ssl_settings SET `validtodate`=NULL WHERE `domainid` = :did");
|
||||
$acmesh_dir = dirname(Settings::Get('system.acmeshpath'));
|
||||
$acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/");
|
||||
$recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), "/");
|
||||
|
||||
@@ -126,6 +126,9 @@ class ApacheFcgi extends Apache
|
||||
|
||||
// mod_proxy stuff for apache-2.4
|
||||
if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') {
|
||||
|
||||
$php_options_text .= ' <Directory "' . FileDir::makeCorrectDir($domain['documentroot']) . '">' . "\n";
|
||||
|
||||
$filesmatch = $phpconfig['fpm_settings']['limit_extensions'];
|
||||
$extensions = explode(" ", $filesmatch);
|
||||
$filesmatch = "";
|
||||
@@ -141,23 +144,19 @@ class ApacheFcgi extends Apache
|
||||
$php_options_text .= ' </FilesMatch>' . "\n";
|
||||
|
||||
$mypath_dir = new Directory($domain['documentroot']);
|
||||
|
||||
// only create the require all granted if there is not active directory-protection
|
||||
// only create the "require all granted" directive if there is no active directory-protection
|
||||
// for this path, as this would be the first require and therefore grant all access
|
||||
if ($mypath_dir->isUserProtected() == false) {
|
||||
$php_options_text .= ' <Directory "' . FileDir::makeCorrectDir($domain['documentroot']) . '">' . "\n";
|
||||
if ($phpconfig['pass_authorizationheader'] == '1') {
|
||||
$php_options_text .= ' CGIPassAuth On' . "\n";
|
||||
}
|
||||
$php_options_text .= ' Require all granted' . "\n";
|
||||
$php_options_text .= ' AllowOverride All' . "\n";
|
||||
$php_options_text .= ' </Directory>' . "\n";
|
||||
} elseif ($phpconfig['pass_authorizationheader'] == '1') {
|
||||
// allow Pass of Authorization header
|
||||
$php_options_text .= ' <Directory "' . FileDir::makeCorrectDir($domain['documentroot']) . '">' . "\n";
|
||||
$php_options_text .= ' CGIPassAuth On' . "\n";
|
||||
$php_options_text .= ' </Directory>' . "\n";
|
||||
}
|
||||
$php_options_text .= ' </Directory>' . "\n";
|
||||
} else {
|
||||
$addheader = "";
|
||||
if ($phpconfig['pass_authorizationheader'] == '1') {
|
||||
@@ -196,6 +195,9 @@ class ApacheFcgi extends Apache
|
||||
}
|
||||
} else {
|
||||
$php_options_text .= ' FcgidIdleTimeout ' . Settings::Get('system.mod_fcgid_idle_timeout') . "\n";
|
||||
if ($phpconfig['pass_authorizationheader'] == '1') {
|
||||
$php_options_text .= ' FcgidPassHeader Authorization' . "\n";
|
||||
}
|
||||
if ((int)Settings::Get('system.mod_fcgid_wrapper') == 0) {
|
||||
$php_options_text .= ' SuexecUserGroup "' . $domain['loginname'] . '" "' . $domain['loginname'] . '"' . "\n";
|
||||
$php_options_text .= ' ScriptAlias /php/ ' . $php->getInterface()->getConfigDir() . "\n";
|
||||
|
||||
@@ -179,7 +179,7 @@ class HttpConfigBase
|
||||
$froxlor_ssl_settings_stmt = Database::prepare("
|
||||
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
|
||||
WHERE `domainid` = '0' AND
|
||||
(`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `expirationdate` IS NULL)
|
||||
(`validtodate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `validtodate` IS NULL)
|
||||
");
|
||||
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
|
||||
if ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) {
|
||||
|
||||
@@ -114,7 +114,9 @@ class AcmeSh extends FroxlorCron
|
||||
`ssl_cert_chainfile` = :chain,
|
||||
`ssl_csr_file` = :csr,
|
||||
`ssl_fullchain_file` = :fullchain,
|
||||
`expirationdate` = :expirationdate
|
||||
`validfromdate` = :validfromdate,
|
||||
`validtodate` = :validtodate,
|
||||
`issuer` = :issuer
|
||||
");
|
||||
|
||||
// prepare domain update sql
|
||||
@@ -136,7 +138,9 @@ class AcmeSh extends FroxlorCron
|
||||
'lepublickey' => Settings::Get('system.lepublickey'),
|
||||
'leregistered' => Settings::Get('system.leregistered'),
|
||||
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
|
||||
'expirationdate' => null,
|
||||
'validfromdate' => null,
|
||||
'validtodate' => null,
|
||||
'issuer' => "",
|
||||
'ssl_cert_file' => null,
|
||||
'ssl_key_file' => null,
|
||||
'ssl_ca_file' => null,
|
||||
@@ -171,7 +175,9 @@ class AcmeSh extends FroxlorCron
|
||||
'lepublickey' => Settings::Get('system.lepublickey'),
|
||||
'leregistered' => Settings::Get('system.leregistered'),
|
||||
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
|
||||
'expirationdate' => is_array($renew_froxlor) ? $renew_froxlor['expirationdate'] : date('Y-m-d H:i:s', 0),
|
||||
'validfromdate' => is_array($renew_froxlor) ? $renew_froxlor['validfromdate'] : date('Y-m-d H:i:s', 0),
|
||||
'validtodate' => is_array($renew_froxlor) ? $renew_froxlor['validtodate'] : date('Y-m-d H:i:s', 0),
|
||||
'issuer' => is_array($renew_froxlor) ? $renew_froxlor['issuer'] : "",
|
||||
'ssl_cert_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_cert_file'] : null,
|
||||
'ssl_key_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_key_file'] : null,
|
||||
'ssl_ca_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_ca_file'] : null,
|
||||
@@ -187,7 +193,7 @@ class AcmeSh extends FroxlorCron
|
||||
'loginname' => $domain['loginname'],
|
||||
'adminsession' => 0
|
||||
]);
|
||||
if (defined('CRON_IS_FORCED') || self::checkFsFilesAreNewer($domain['domain'], $domain['expirationdate'])) {
|
||||
if (defined('CRON_IS_FORCED') || self::checkFsFilesAreNewer($domain['domain'], $domain['validtodate'])) {
|
||||
self::certToDb($domain, $cronlog, []);
|
||||
$changedetected = 1;
|
||||
}
|
||||
@@ -221,16 +227,21 @@ class AcmeSh extends FroxlorCron
|
||||
");
|
||||
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
|
||||
// also check for possible existing certificate
|
||||
if (!$froxlor_ssl && !self::checkFsFilesAreNewer(Settings::Get('system.hostname'), date('Y-m-d H:i:s'))) {
|
||||
if (($froxlor_ssl && empty($froxlor_ssl['validtodate']))
|
||||
|| (!$froxlor_ssl && !self::checkFsFilesAreNewer(Settings::Get('system.hostname'), date('Y-m-d H:i:s')))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function checkFsFilesAreNewer($domain, $cert_date = 0)
|
||||
private static function checkFsFilesAreNewer($domain, $cert_date = 0): bool
|
||||
{
|
||||
$certificate_folder = self::getWorkingDirFromEnv(strtolower($domain));
|
||||
$certificate_folder = self::getCertificateFolder(strtolower($domain));
|
||||
if (empty($certificate_folder)) {
|
||||
return false;
|
||||
}
|
||||
$ssl_file = FileDir::makeCorrectFile($certificate_folder . '/' . strtolower($domain) . '.cer');
|
||||
|
||||
if (is_dir($certificate_folder) && file_exists($ssl_file) && is_readable($ssl_file)) {
|
||||
@@ -242,9 +253,13 @@ class AcmeSh extends FroxlorCron
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getWorkingDirFromEnv($domain = "", $forced_noecc = false)
|
||||
public static function getWorkingDirFromEnv($domain = "", $forced_ecc = false): string
|
||||
{
|
||||
if (Settings::Get('system.leecc') > 0 && !$forced_noecc) {
|
||||
// first try without _ecc either if it's enabled currently or not as
|
||||
// it might have been at some point so there is a chance we have certificates
|
||||
// with and without _ecc - the method getCertificateFolder() will check both
|
||||
// possibilities
|
||||
if ($forced_ecc) {
|
||||
$domain .= "_ecc";
|
||||
}
|
||||
$env_file = FileDir::makeCorrectFile(dirname(self::getAcmeSh()) . '/acme.sh.env');
|
||||
@@ -254,7 +269,7 @@ class AcmeSh extends FroxlorCron
|
||||
cut -d'"' -f2
|
||||
EOC;
|
||||
exec('grep "LE_WORKING_DIR" ' . escapeshellarg($env_file) . ' | ' . $cut, $output);
|
||||
if (is_array($output) && !empty($output) && isset($output[0]) && !empty($output[0])) {
|
||||
if (is_array($output) && !empty($output) && !empty($output[0])) {
|
||||
return FileDir::makeCorrectDir($output[0] . "/" . $domain);
|
||||
}
|
||||
}
|
||||
@@ -279,7 +294,9 @@ EOC;
|
||||
SELECT
|
||||
domssl.`id`,
|
||||
domssl.`domainid`,
|
||||
domssl.`expirationdate`,
|
||||
domssl.`validfromdate`,
|
||||
domssl.`validtodate`,
|
||||
domssl.`issuer`,
|
||||
domssl.`ssl_cert_file`,
|
||||
domssl.`ssl_key_file`,
|
||||
domssl.`ssl_ca_file`,
|
||||
@@ -306,7 +323,7 @@ EOC;
|
||||
AND dom.`letsencrypt` = 1
|
||||
AND dom.`aliasdomain` IS NULL
|
||||
AND dom.`iswildcarddomain` = 0
|
||||
AND domssl.`expirationdate` IS NULL
|
||||
AND domssl.`validtodate` IS NULL
|
||||
");
|
||||
$customer_ssl = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if ($customer_ssl) {
|
||||
@@ -330,7 +347,7 @@ EOC;
|
||||
");
|
||||
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
|
||||
// also check for possible existing certificate
|
||||
if ($froxlor_ssl && self::checkFsFilesAreNewer(Settings::Get('system.hostname'), $froxlor_ssl['expirationdate'])) {
|
||||
if ($froxlor_ssl && self::checkFsFilesAreNewer(Settings::Get('system.hostname'), $froxlor_ssl['validtodate'])) {
|
||||
return $froxlor_ssl;
|
||||
}
|
||||
}
|
||||
@@ -346,7 +363,9 @@ EOC;
|
||||
SELECT
|
||||
domssl.`id`,
|
||||
domssl.`domainid`,
|
||||
domssl.`expirationdate`,
|
||||
domssl.`validfromdate`,
|
||||
domssl.`validtodate`,
|
||||
domssl.`issuer`,
|
||||
domssl.`ssl_cert_file`,
|
||||
domssl.`ssl_key_file`,
|
||||
dom.`domain`,
|
||||
@@ -370,7 +389,7 @@ EOC;
|
||||
if ($renew_certs) {
|
||||
if ($check) {
|
||||
foreach ($renew_certs as $cert) {
|
||||
if (self::checkFsFilesAreNewer($cert['domain'], $cert['expirationdate'])) {
|
||||
if (self::checkFsFilesAreNewer($cert['domain'], $cert['validtodate'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -453,7 +472,7 @@ EOC;
|
||||
// Only issue let's encrypt certificate if no broken ssl_redirect is enabled
|
||||
if ($certrow['ssl_redirect'] != 2) {
|
||||
$do_force = false;
|
||||
if (!empty($certrow['ssl_cert_file']) && empty($certrow['expirationdate'])) {
|
||||
if (!empty($certrow['ssl_cert_file']) && empty($certrow['validtodate'])) {
|
||||
// domain changed (SAN or similar)
|
||||
$do_force = true;
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']);
|
||||
@@ -594,7 +613,9 @@ EOC;
|
||||
'chain' => $return['chain'],
|
||||
'csr' => $return['csr'],
|
||||
'fullchain' => $return['fullchain'],
|
||||
'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t'])
|
||||
'validfromdate' => date('Y-m-d H:i:s', $newcert['validFrom_time_t']),
|
||||
'validtodate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']),
|
||||
'issuer' => $newcert['issuer']['O'] ?? ""
|
||||
]);
|
||||
|
||||
if ($certrow['ssl_redirect'] == 3) {
|
||||
@@ -621,35 +642,21 @@ EOC;
|
||||
*/
|
||||
private static function readCertificateToVar($domain, &$return, &$cronlog)
|
||||
{
|
||||
$certificate_folder = self::getWorkingDirFromEnv($domain);
|
||||
$certificate_folder_noecc = null;
|
||||
if (Settings::Get('system.leecc') > 0) {
|
||||
$certificate_folder_noecc = self::getWorkingDirFromEnv($domain, true);
|
||||
}
|
||||
$certificate_folder = FileDir::makeCorrectDir($certificate_folder);
|
||||
$certificate_folder = self::getCertificateFolder($domain);
|
||||
|
||||
if (is_dir($certificate_folder) || is_dir($certificate_folder_noecc)) {
|
||||
foreach (
|
||||
[
|
||||
'crt' => $domain . '.cer',
|
||||
'key' => $domain . '.key',
|
||||
'chain' => 'ca.cer',
|
||||
'fullchain' => 'fullchain.cer',
|
||||
'csr' => $domain . '.csr'
|
||||
] as $index => $sslfile
|
||||
) {
|
||||
if (!empty($certificate_folder)) {
|
||||
$certificate_files = [
|
||||
'crt' => $domain . '.cer',
|
||||
'key' => $domain . '.key',
|
||||
'chain' => 'ca.cer',
|
||||
'fullchain' => 'fullchain.cer',
|
||||
'csr' => $domain . '.csr'
|
||||
];
|
||||
foreach ($certificate_files as $index => $sslfile) {
|
||||
$ssl_file = FileDir::makeCorrectFile($certificate_folder . '/' . $sslfile);
|
||||
if (file_exists($ssl_file)) {
|
||||
$return[$index] = file_get_contents($ssl_file);
|
||||
} else {
|
||||
if (!empty($certificate_folder_noecc)) {
|
||||
$ssl_file_fb = FileDir::makeCorrectFile($certificate_folder_noecc . '/' . $sslfile);
|
||||
if (file_exists($ssl_file_fb)) {
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "ECC certificates activated but found only non-ecc file");
|
||||
$return[$index] = file_get_contents($ssl_file_fb);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find file '" . $sslfile . "' in '" . $certificate_folder . "'");
|
||||
$return[$index] = null;
|
||||
}
|
||||
@@ -658,4 +665,18 @@ EOC;
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find certificate-folder '" . $certificate_folder . "'");
|
||||
}
|
||||
}
|
||||
|
||||
private static function getCertificateFolder(string $domain): string
|
||||
{
|
||||
$certificate_folder = self::getWorkingDirFromEnv(strtolower($domain));
|
||||
if (file_exists($certificate_folder)) {
|
||||
return $certificate_folder;
|
||||
}
|
||||
$certificate_folder_ecc = self::getWorkingDirFromEnv($domain, true);
|
||||
if (file_exists($certificate_folder_ecc)) {
|
||||
return $certificate_folder_ecc;
|
||||
}
|
||||
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find certificate-folder for domain '" . $domain . "'");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ class Nginx extends HttpConfigBase
|
||||
$this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n";
|
||||
|
||||
// protect bin/
|
||||
$this->nginx_data[$vhost_filename] .= "\t" . 'location ~ ' . rtrim($relpath, "/") . '/(bin|cache|logs|tests|vendor) {' . "\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t" . 'location ~ ^' . rtrim($relpath, "/") . '/(bin|cache|logs|tests|vendor) {' . "\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t" . ' deny all;' . "\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n";
|
||||
}
|
||||
@@ -883,13 +883,7 @@ class Nginx extends HttpConfigBase
|
||||
// remove comments
|
||||
$vhost = implode("\n", preg_replace('/^(\s+)?#(.*)$/', '', explode("\n", $vhost)));
|
||||
// Break blocks into lines
|
||||
$vhost = str_replace([
|
||||
"{",
|
||||
"}"
|
||||
], [
|
||||
" {\n",
|
||||
"\n}"
|
||||
], $vhost);
|
||||
$vhost = preg_replace("/^(\s+)location(.+)\{(.+)\}$/misU", "location $2 {\n $3 \n}", $vhost);
|
||||
// Break into array items
|
||||
$vhost = explode("\n", preg_replace('/[ \t]+/', ' ', trim(preg_replace('/\t+/', '', $vhost))));
|
||||
// Remove empty lines
|
||||
@@ -1040,6 +1034,11 @@ class Nginx extends HttpConfigBase
|
||||
$path_options .= "\t\t" . 'auth_basic_user_file ' . FileDir::makeCorrectFile($single['usrf']) . ';' . "\n";
|
||||
if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {
|
||||
$path_options .= "\t\t" . 'index index.php index.html index.htm;' . "\n";
|
||||
if ($domain['notryfiles'] != 1) {
|
||||
$path_options .= "\t\t" . 'location ~ ^(.+?\.php)(/.*)?$ {' . "\n";
|
||||
$path_options .= "\t\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n";
|
||||
$path_options .= "\t\t" . '}' . "\n\n";
|
||||
}
|
||||
} else {
|
||||
$path_options .= "\t\t" . 'index index.html index.htm;' . "\n";
|
||||
}
|
||||
@@ -1166,7 +1165,6 @@ class Nginx extends HttpConfigBase
|
||||
$phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n";
|
||||
$phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n";
|
||||
$phpopts .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n";
|
||||
$phpopts .= "\t\ttry_files \$fastcgi_script_name =404;\n";
|
||||
$phpopts .= "\t\tfastcgi_pass " . Settings::Get('system.nginx_php_backend') . ";\n";
|
||||
$phpopts .= "\t\tfastcgi_index index.php;\n";
|
||||
if ($domain['ssl'] == '1' && $ssl_vhost) {
|
||||
|
||||
@@ -342,8 +342,17 @@ pm.max_children = 1
|
||||
public function getSocketFile($createifnotexists = true)
|
||||
{
|
||||
$socketdir = FileDir::makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir'));
|
||||
// add fpm-config-id to filename so it's unique for the fpm-daemon and doesn't interfere with running configs when reuilding
|
||||
$socket = strtolower(FileDir::makeCorrectFile($socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['domain'] . '-php-fpm.socket'));
|
||||
// add fpm-config-id to filename, so it's unique for the fpm-daemon and doesn't interfere with running configs when reuilding
|
||||
$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['domain'] . '-php-fpm.socket';
|
||||
if (strlen($socket_filename) > 100) {
|
||||
// respect the unix socket-length limitation
|
||||
$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['id'] . '-php-fpm.socket';
|
||||
if (strlen($socket_filename) > 100) {
|
||||
// even a long loginname it seems
|
||||
$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['guid'] . '-' . $this->domain['id'] . '-php-fpm.socket';
|
||||
}
|
||||
}
|
||||
$socket = strtolower(FileDir::makeCorrectFile($socket_filename));
|
||||
|
||||
if (!is_dir($socketdir) && $createifnotexists) {
|
||||
FileDir::safe_exec('mkdir -p ' . escapeshellarg($socketdir));
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
namespace Froxlor;
|
||||
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\UI\Collection;
|
||||
use Froxlor\Api\Commands\Customers;
|
||||
use Froxlor\Api\Commands\SubDomains;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\UI\Collection;
|
||||
|
||||
/**
|
||||
* Class to manage the current user / session
|
||||
@@ -153,7 +153,7 @@ class CurrentUser
|
||||
} elseif ($resource == 'subdomains') {
|
||||
$parentDomainCollection = (new Collection(SubDomains::class, $_SESSION['userinfo'],
|
||||
['sql_search' => ['d.parentdomainid' => 0]]));
|
||||
$addition = $parentDomainCollection != 0;
|
||||
$addition = $parentDomainCollection->count() != 0;
|
||||
} elseif ($resource == 'domains') {
|
||||
$customerCollection = (new Collection(Customers::class, $_SESSION['userinfo']));
|
||||
$addition = $customerCollection != 0;
|
||||
|
||||
@@ -279,8 +279,7 @@ class Dns
|
||||
}
|
||||
}
|
||||
}
|
||||
$zonerecords[] = new DnsEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'],
|
||||
$entry['ttl']);
|
||||
$zonerecords[] = new DnsEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'] ?? 0, $entry['ttl']);
|
||||
}
|
||||
|
||||
// add missing required entries
|
||||
|
||||
@@ -37,18 +37,18 @@ class PowerDNS
|
||||
/**
|
||||
* remove all records and entries of a given domain
|
||||
*
|
||||
* @param array|null $domain
|
||||
* @param string|null $domain
|
||||
*/
|
||||
public static function cleanDomainZone(array $domain = null)
|
||||
public static function cleanDomainZone(string $domain = null)
|
||||
{
|
||||
if (is_array($domain) && isset($domain['domain'])) {
|
||||
if (!empty($domain)) {
|
||||
$pdns_domains_stmt = self::getDB()->prepare("SELECT `id`, `name` FROM `domains` WHERE `name` = :domain");
|
||||
$del_rec_stmt = self::getDB()->prepare("DELETE FROM `records` WHERE `domain_id` = :did");
|
||||
$del_meta_stmt = self::getDB()->prepare("DELETE FROM `domainmetadata` WHERE `domain_id` = :did");
|
||||
$del_dom_stmt = self::getDB()->prepare("DELETE FROM `domains` WHERE `id` = :did");
|
||||
|
||||
$pdns_domains_stmt->execute([
|
||||
'domain' => $domain['domain']
|
||||
'domain' => $domain
|
||||
]);
|
||||
$pdns_domain = $pdns_domains_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ class Domain
|
||||
$upd_stmt = Database::prepare("UPDATE
|
||||
`" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
|
||||
SET
|
||||
`expirationdate` = null
|
||||
`validtodate` = null
|
||||
WHERE
|
||||
domainid = :domainid
|
||||
");
|
||||
|
||||
@@ -55,6 +55,7 @@ class IpAddr
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getSslIpPortCombinations(): array
|
||||
{
|
||||
@@ -75,7 +76,7 @@ class IpAddr
|
||||
$additional_conditions_params = [];
|
||||
$additional_conditions_array = [];
|
||||
|
||||
if ($userinfo['ip'] != '-1') {
|
||||
if (!empty($userinfo) && $userinfo['ip'] != '-1') {
|
||||
$admin_ip_stmt = Database::prepare("
|
||||
SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = IN (:ipid)
|
||||
");
|
||||
|
||||
@@ -447,7 +447,8 @@ class FileDir
|
||||
$field = [
|
||||
'type' => 'select',
|
||||
'select_var' => $_field,
|
||||
'selected' => $value
|
||||
'selected' => $value,
|
||||
'value' => $value
|
||||
];
|
||||
} else {
|
||||
$field = [
|
||||
|
||||
@@ -31,10 +31,10 @@ final class Froxlor
|
||||
{
|
||||
|
||||
// Main version variable
|
||||
const VERSION = '2.0.13';
|
||||
const VERSION = '2.0.23';
|
||||
|
||||
// Database version (YYYYMMDDC where C is a daily counter)
|
||||
const DBVERSION = '202302030';
|
||||
const DBVERSION = '202304260';
|
||||
|
||||
// Distribution branding-tag (used for Debian etc.)
|
||||
const BRANDING = '';
|
||||
|
||||
55
lib/Froxlor/Http/RateLimiter.php
Normal file
55
lib/Froxlor/Http/RateLimiter.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Froxlor\Http;
|
||||
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
|
||||
class RateLimiter
|
||||
{
|
||||
private static int $limit_per_interval = 60;
|
||||
private static int $reset_time = 0;
|
||||
|
||||
public static function run(bool $install_mode = false)
|
||||
{
|
||||
// default interval = 60 sec
|
||||
self::$reset_time = time() + 60;
|
||||
|
||||
if (!$install_mode) {
|
||||
self::$limit_per_interval = Settings::Get('system.req_limit_per_interval') ?? 60;
|
||||
self::$reset_time = time() + Settings::Get('system.req_limit_interval') ?? 60;
|
||||
}
|
||||
|
||||
// Get the remaining requests and reset time from the headers
|
||||
$remaining = isset($_SESSION['HTTP_X_RATELIMIT_REMAINING']) ? (int)$_SESSION['HTTP_X_RATELIMIT_REMAINING'] : self::$limit_per_interval;
|
||||
$reset = isset($_SESSION['HTTP_X_RATELIMIT_RESET']) ? (int)$_SESSION['HTTP_X_RATELIMIT_RESET'] : self::$reset_time;
|
||||
|
||||
// check if reset time is due
|
||||
if (time() > $reset) {
|
||||
$remaining = self::$limit_per_interval;
|
||||
$reset = self::$reset_time;
|
||||
}
|
||||
|
||||
// If we've hit the limit, return an error
|
||||
if ($remaining <= 0) {
|
||||
header('HTTP/1.1 429 Too Many Requests');
|
||||
header("Retry-After: $reset");
|
||||
UI::twig()->addGlobal('install_mode', '1');
|
||||
echo UI::twig()->render('Froxlor/misc/ratelimithint.html.twig', [
|
||||
'retry' => $reset,
|
||||
'installdir' => Froxlor::getInstallDir()
|
||||
]);
|
||||
die();
|
||||
}
|
||||
|
||||
// Decrement the remaining requests and update the headers
|
||||
$remaining--;
|
||||
$_SESSION['HTTP_X_RATELIMIT_REMAINING'] = $remaining;
|
||||
$_SESSION['HTTP_X_RATELIMIT_RESET'] = $reset;
|
||||
|
||||
header("X-RateLimit-Limit: " . self::$limit_per_interval);
|
||||
header("X-RateLimit-Remaining: " . $remaining);
|
||||
header("X-RateLimit-Reset: " . $reset);
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ class Install
|
||||
public $phpVersion;
|
||||
public $formfield;
|
||||
public string $requiredVersion = '7.4.0';
|
||||
public array $requiredExtensions = ['session', 'ctype', 'xml', 'filter', 'posix', 'mbstring', 'curl', 'gmp', 'json'];
|
||||
public array $requiredExtensions = ['session', 'ctype', 'xml', 'filter', 'posix', 'mbstring', 'curl', 'gmp', 'json', 'gd'];
|
||||
public array $suggestedExtensions = ['bcmath', 'zip'];
|
||||
public array $suggestions = [];
|
||||
public array $criticals = [];
|
||||
|
||||
@@ -448,7 +448,11 @@ class Core
|
||||
$reload = "service php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "-fpm restart";
|
||||
$config_dir = "/etc/php/" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "/fpm/pool.d/";
|
||||
// fcgid
|
||||
$binary = "/usr/bin/php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "-cgi";
|
||||
if ($this->validatedData['distribution'] == 'bookworm') {
|
||||
$binary = "/usr/bin/php-cgi" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;
|
||||
} else {
|
||||
$binary = "/usr/bin/php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "-cgi";
|
||||
}
|
||||
}
|
||||
$db_user->query("UPDATE `" . TABLE_PANEL_FPMDAEMONS . "` SET `reload_cmd` = '" . $reload . "', `config_dir` = '" . $config_dir . "' WHERE `id` ='1';");
|
||||
$db_user->query("UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET `binary` = '" . $binary . "';");
|
||||
|
||||
@@ -68,10 +68,10 @@ class MailLogParser
|
||||
// Parse MDA traffic
|
||||
if (Settings::Get("system.mdaserver") == "dovecot") {
|
||||
$this->parseDovecotLog(Settings::Get("system.mdalog"));
|
||||
$this->parsePostfixLog(Settings::Get("system.mdalog") . ".1");
|
||||
$this->parseDovecotLog(Settings::Get("system.mdalog") . ".1");
|
||||
} elseif (Settings::Get("system.mdaserver") == "courier") {
|
||||
$this->parseCourierLog(Settings::Get("system.mdalog"));
|
||||
$this->parsePostfixLog(Settings::Get("system.mdalog") . ".1");
|
||||
$this->parseCourierLog(Settings::Get("system.mdalog") . ".1");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ class PhpHelper
|
||||
* @return string
|
||||
*/
|
||||
public static function sizeReadable(
|
||||
int $size,
|
||||
$size,
|
||||
?string $max = '',
|
||||
string $system = 'si',
|
||||
string $retstring = '%01.2f %s'
|
||||
@@ -449,7 +449,15 @@ class PhpHelper
|
||||
'ssl_specialsettings',
|
||||
'default_vhostconf_domain',
|
||||
'ssl_default_vhostconf_domain',
|
||||
'filecontent'
|
||||
'filecontent',
|
||||
'admin_password',
|
||||
'password',
|
||||
'new_customer_password',
|
||||
'privileged_password',
|
||||
'email_password',
|
||||
'directory_password',
|
||||
'ftp_password',
|
||||
'mysql_password',
|
||||
];
|
||||
if (!empty($global)) {
|
||||
$tmp = $global;
|
||||
@@ -519,7 +527,12 @@ class PhpHelper
|
||||
} elseif (is_int($value)) {
|
||||
$str .= self::tabPrefix($depth, "'{$key}' => $value,\n");
|
||||
} else {
|
||||
$str .= self::tabPrefix($depth, "'{$key}' => '{$value}',\n");
|
||||
if ($key == 'password') {
|
||||
// special case for passwords (nowdoc)
|
||||
$str .= self::tabPrefix($depth, "'{$key}' => <<<'EOT'\n{$value}\nEOT,\n");
|
||||
} else {
|
||||
$str .= self::tabPrefix($depth, "'{$key}' => '{$value}',\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$str .= self::parseArrayToString($value, $key, ($depth + 1));
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Froxlor;
|
||||
use Exception;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\UI\Form;
|
||||
use Froxlor\Validate\Validate;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
@@ -159,6 +160,9 @@ class SImExporter
|
||||
// re-format the array-key for Form::processForm
|
||||
foreach ($_data as $key => $value) {
|
||||
$index_split = explode('.', $key, 3);
|
||||
if (!isset($current_settings[$index_split[0]][$index_split[1]])) {
|
||||
continue;
|
||||
}
|
||||
if (isset($index_split[2]) && $index_split[2] === 'image_data' && !empty($_data[$index_split[0] . '.' . $index_split[1]])) {
|
||||
$image_data[$key] = $value;
|
||||
} else {
|
||||
@@ -190,42 +194,27 @@ class SImExporter
|
||||
}
|
||||
}
|
||||
|
||||
$img_data = base64_decode($value);
|
||||
$img_filename = Froxlor::getInstallDir() . '/' . str_replace('../', '',
|
||||
explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0]);
|
||||
if (Validate::validateBase64Image($value)) {
|
||||
$img_data = base64_decode($value);
|
||||
$img_filename = explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0];
|
||||
|
||||
file_put_contents($img_filename, $img_data);
|
||||
$spl = explode('.', $img_filename);
|
||||
$file_extension = strtolower(array_pop($spl));
|
||||
unset($spl);
|
||||
|
||||
if (function_exists('finfo_open')) {
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mimetype = finfo_file($finfo, $img_filename);
|
||||
finfo_close($finfo);
|
||||
} else {
|
||||
$mimetype = mime_content_type($img_filename);
|
||||
if (!in_array($file_extension, [
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'png',
|
||||
'gif'
|
||||
])) {
|
||||
throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
|
||||
}
|
||||
$img_filename = 'img/' . bin2hex(random_bytes(16)) . '.' . $file_extension;
|
||||
file_put_contents(Froxlor::getInstallDir() . '/' . $img_filename, $img_data);
|
||||
$img_index = $index_split[0].'.'.$index_split[1];
|
||||
Settings::Set($img_index, $img_filename . '?v=' . time());
|
||||
}
|
||||
if (empty($mimetype)) {
|
||||
$mimetype = 'application/octet-stream';
|
||||
}
|
||||
if (!in_array($mimetype, ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) {
|
||||
@unlink($img_filename);
|
||||
throw new Exception("Uploaded file is not a valid image");
|
||||
}
|
||||
|
||||
$spl = explode('.', $img_filename);
|
||||
$file_extension = strtolower(array_pop($spl));
|
||||
unset($spl);
|
||||
|
||||
if (!in_array($file_extension, [
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'png',
|
||||
'gif'
|
||||
])) {
|
||||
@unlink($img_filename);
|
||||
throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
|
||||
}
|
||||
|
||||
Settings::Set($index, $value);
|
||||
}
|
||||
}
|
||||
// all good
|
||||
|
||||
@@ -36,6 +36,7 @@ use Froxlor\PhpHelper;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\System\Cronjob;
|
||||
use Froxlor\System\IPTools;
|
||||
use Froxlor\Validate\Validate;
|
||||
use PDO;
|
||||
|
||||
class Store
|
||||
@@ -45,10 +46,21 @@ class Store
|
||||
{
|
||||
$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);
|
||||
|
||||
if ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'le_froxlor_enabled' && $newfieldvalue == '0') {
|
||||
Database::query("
|
||||
DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = '0'
|
||||
");
|
||||
if ($returnvalue !== false
|
||||
&& is_array($fielddata)
|
||||
&& isset($fielddata['settinggroup'])
|
||||
&& $fielddata['settinggroup'] == 'system'
|
||||
&& isset($fielddata['varname'])
|
||||
) {
|
||||
if ($fielddata['varname'] == 'le_froxlor_enabled' && $newfieldvalue == '0') {
|
||||
Database::query("
|
||||
DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = '0'
|
||||
");
|
||||
} elseif ($fielddata['varname'] == 'froxloraliases' && $newfieldvalue != $fielddata['value']) {
|
||||
Database::query("
|
||||
UPDATE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SET `validtodate`= NULL WHERE `domainid` = '0'
|
||||
");
|
||||
}
|
||||
}
|
||||
|
||||
return $returnvalue;
|
||||
@@ -415,40 +427,30 @@ class Store
|
||||
}
|
||||
|
||||
// Make sure mime-type matches an image
|
||||
if (function_exists('finfo_open')) {
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mimetype = finfo_file($finfo, $_FILES[$fieldname]['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
} else {
|
||||
$mimetype = mime_content_type($_FILES[$fieldname]['tmp_name']);
|
||||
}
|
||||
if (empty($mimetype)) {
|
||||
$mimetype = 'application/octet-stream';
|
||||
}
|
||||
if (!in_array($mimetype, ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) {
|
||||
throw new \Exception("Uploaded file is not a valid image");
|
||||
}
|
||||
$image_content = file_get_contents($_FILES[$fieldname]['tmp_name']);
|
||||
$value = base64_encode($image_content);
|
||||
if (Validate::validateBase64Image($value)) {
|
||||
$img_filename = $_FILES[$fieldname]['name'];
|
||||
|
||||
// Determine file extension
|
||||
$spl = explode('.', $_FILES[$fieldname]['name']);
|
||||
$file_extension = strtolower(array_pop($spl));
|
||||
unset($spl);
|
||||
$spl = explode('.', $img_filename);
|
||||
$file_extension = strtolower(array_pop($spl));
|
||||
unset($spl);
|
||||
|
||||
if (!in_array($file_extension, [
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'png',
|
||||
'gif'
|
||||
])) {
|
||||
throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
|
||||
if (!in_array($file_extension, [
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'png',
|
||||
'gif'
|
||||
])) {
|
||||
throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
|
||||
}
|
||||
$filename = bin2hex(random_bytes(16)) . '.' . $file_extension;
|
||||
// Move file
|
||||
if (!move_uploaded_file($_FILES[$fieldname]['tmp_name'], $path . $filename)) {
|
||||
throw new Exception("Unable to save image to img folder");
|
||||
}
|
||||
$save_to = 'img/' . $filename . '?v=' . time();
|
||||
}
|
||||
|
||||
// Move file
|
||||
if (!move_uploaded_file($_FILES[$fieldname]['tmp_name'], $path . $fielddata['image_name'] . '.' . $file_extension)) {
|
||||
throw new Exception("Unable to save image to img folder");
|
||||
}
|
||||
|
||||
$save_to = 'img/' . $fielddata['image_name'] . '.' . $file_extension . '?v=' . time();
|
||||
}
|
||||
|
||||
// Delete file?
|
||||
|
||||
@@ -87,26 +87,38 @@ class UI
|
||||
|
||||
return $isHttps && (strcasecmp('on', $isHttps) == 0 || strcasecmp('https', $isHttps) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the cookie host from HTTP_HOST, stripping the port.
|
||||
*/
|
||||
public static function getCookieHost(): ?string
|
||||
{
|
||||
if (empty($_SERVER['HTTP_HOST']))
|
||||
return null;
|
||||
|
||||
$colonPosition = strrpos($_SERVER['HTTP_HOST'], ':');
|
||||
// There's no port in the host
|
||||
if ($colonPosition === false)
|
||||
return $_SERVER['HTTP_HOST'];
|
||||
|
||||
$closingSquareBracketPosition = strrpos($_SERVER['HTTP_HOST'], ']');
|
||||
// The host is an IPv4 address or hostname with port
|
||||
if ($closingSquareBracketPosition === false)
|
||||
return substr($_SERVER['HTTP_HOST'], 0, $colonPosition);
|
||||
|
||||
// The host is an IPv6 address with port
|
||||
return substr($_SERVER['HTTP_HOST'], 0, $closingSquareBracketPosition + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* send various security related headers
|
||||
*/
|
||||
public static function sendHeaders()
|
||||
{
|
||||
if (empty($_SERVER['HTTP_HOST'])) {
|
||||
if (!self::$install_mode) {
|
||||
// fallback to set hostname in settings
|
||||
$_SERVER['HTTP_HOST'] = Settings::Get('system.hostname');
|
||||
} else {
|
||||
// bad request
|
||||
http_response_code(400);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
session_set_cookie_params([
|
||||
'lifetime' => self::$install_mode ? 7200 : 600, // will be renewed based on settings in lib/init.php
|
||||
'path' => '/',
|
||||
'domain' => explode(':', $_SERVER['HTTP_HOST'])[0],
|
||||
'domain' => self::getCookieHost(),
|
||||
'secure' => self::requestIsHttps(),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
@@ -278,7 +290,8 @@ class UI
|
||||
];
|
||||
}
|
||||
|
||||
public static function validateThemeTemplate(string $name, string $theme = "") {
|
||||
public static function validateThemeTemplate(string $name, string $theme = "")
|
||||
{
|
||||
if (empty(trim($theme))) {
|
||||
$theme = self::getTheme();
|
||||
}
|
||||
|
||||
@@ -52,16 +52,16 @@ class Check
|
||||
];
|
||||
|
||||
$check_array = [
|
||||
'system_mod_fcgid_enabled' => [
|
||||
'other_post_field' => 'system_phpfpm_enabled',
|
||||
'system_mod_fcgid' => [
|
||||
'other_post_field' => 'phpfpm_enabled',
|
||||
'other_enabled' => 'phpfpm.enabled',
|
||||
'other_enabled_lng' => 'phpfpmstillenabled',
|
||||
'deactivate' => [
|
||||
'phpfpm.enabled_ownvhost' => 0
|
||||
]
|
||||
],
|
||||
'system_phpfpm_enabled' => [
|
||||
'other_post_field' => 'system_mod_fcgid_enabled',
|
||||
'phpfpm_enabled' => [
|
||||
'other_post_field' => 'system_mod_fcgid',
|
||||
'other_enabled' => 'system.mod_fcgid',
|
||||
'other_enabled_lng' => 'fcgidstillenabled',
|
||||
'deactivate' => [
|
||||
@@ -291,7 +291,7 @@ class Check
|
||||
*/
|
||||
public static function checkLocalGroup($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)
|
||||
{
|
||||
if (empty($newfieldvalue) || $fielddata == $newfieldvalue) {
|
||||
if (empty($newfieldvalue) || $fielddata['value'] == $newfieldvalue) {
|
||||
$returnvalue = [
|
||||
self::FORMFIELDS_PLAUSIBILITY_CHECK_OK
|
||||
];
|
||||
|
||||
@@ -260,7 +260,7 @@ class Validate
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if an emailaddress is in correct format or not
|
||||
* Returns if an email-address is in correct format or not
|
||||
*
|
||||
* @param string $email The email address to check
|
||||
*
|
||||
@@ -334,4 +334,40 @@ class Validate
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* validates whether a given base64 string decodes to an image
|
||||
*
|
||||
* @param string $base64string
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function validateBase64Image(string $base64string) {
|
||||
|
||||
if (!extension_loaded('gd')) {
|
||||
Response::standardError('phpgdextensionnotavailable', null, true);
|
||||
}
|
||||
|
||||
// Decode the base64 string
|
||||
$data = base64_decode($base64string);
|
||||
|
||||
// Create an image from the decoded data
|
||||
$image = @imagecreatefromstring($data);
|
||||
|
||||
// Check if the image was created successfully
|
||||
if (!$image) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the MIME type of the image
|
||||
$mime = image_type_to_mime_type(getimagesizefromstring($data)[2]);
|
||||
|
||||
// Check if the MIME type is a valid image MIME type
|
||||
if (strpos($mime, 'image/') !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If everything is okay, return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2600,7 +2600,6 @@ ServerName "<SERVERNAME> FTP Server"
|
||||
ServerType standalone
|
||||
DeferWelcome off
|
||||
|
||||
MultilineRFC2228 on
|
||||
DefaultServer on
|
||||
ShowSymlinks on
|
||||
|
||||
@@ -2939,7 +2938,6 @@ SQLNamedQuery get-quota-limit SELECT "ftp_users.username AS name, ftp_quotalimit
|
||||
SQLNamedQuery get-quota-tally SELECT "name, quota_type, bytes_in_used,bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,files_xfer_used FROM ftp_quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'"
|
||||
SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used= files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name= '%{6}' AND quota_type = '%{7}'" ftp_quotatallies
|
||||
SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4},%{5}, %{6}, %{7}" ftp_quotatallies
|
||||
|
||||
</IfModule>
|
||||
]]>
|
||||
</content>
|
||||
@@ -2955,7 +2953,7 @@ TLSRSACertificateFile /etc/ssl/certs/proftpd.crt
|
||||
TLSRSACertificateKeyFile /etc/ssl/private/proftpd.key
|
||||
TLSECCertificateFile /etc/ssl/certs/proftpd_ec.crt
|
||||
TLSECCertificateKeyFile /etc/ssl/private/proftpd_ec.key
|
||||
TLSOptions NoCertRequest NoSessionReuseRequired
|
||||
TLSOptions NoSessionReuseRequired
|
||||
TLSVerifyClient off
|
||||
|
||||
# Are clients required to use FTP over TLS when talking to this server?
|
||||
@@ -3317,7 +3315,7 @@ aliases: files
|
||||
<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_configdir}}]]></command>
|
||||
<command><![CDATA[mkdir -p {{settings.system.mod_fcgid_tmpdir}}]]></command>
|
||||
<command><![CDATA[chmod 1777 {{settings.system.mod_fcgid_tmpdir}}]]></command>
|
||||
<command><![CDATA[a2dismod php8.1]]></command>
|
||||
<command><![CDATA[a2dismod php8.2]]></command>
|
||||
</commands>
|
||||
<!-- instead of just restarting apache, we let the cronjob do all the
|
||||
dirty work -->
|
||||
@@ -3350,7 +3348,7 @@ aliases: files
|
||||
</visibility>
|
||||
<visibility mode="true">{{settings.phpfpm.enabled_ownvhost}}
|
||||
</visibility>
|
||||
<command><![CDATA[a2dismod php8.1]]></command>
|
||||
<command><![CDATA[a2dismod php8.2]]></command>
|
||||
</commands>
|
||||
<!-- instead of just restarting apache, we let the cronjob do all the
|
||||
dirty work -->
|
||||
|
||||
@@ -133,7 +133,7 @@ return [
|
||||
'customers' => [
|
||||
'label' => lng('admin.customers'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['customers'],
|
||||
'value' => empty($result['customers']) ? '0' : $result['customers'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
@@ -146,7 +146,7 @@ return [
|
||||
'domains' => [
|
||||
'label' => lng('admin.domains'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['domains'],
|
||||
'value' => empty($result['domains']) ? '0' : $result['domains'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
@@ -159,49 +159,49 @@ return [
|
||||
'diskspace' => [
|
||||
'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')',
|
||||
'type' => 'textul',
|
||||
'value' => $result['diskspace'],
|
||||
'value' => empty($result['diskspace']) ? '0' : $result['diskspace'],
|
||||
'maxlength' => 6,
|
||||
'mandatory' => true
|
||||
],
|
||||
'traffic' => [
|
||||
'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')',
|
||||
'type' => 'textul',
|
||||
'value' => $result['traffic'],
|
||||
'value' => empty($result['traffic']) ? '0' : $result['traffic'],
|
||||
'maxlength' => 4,
|
||||
'mandatory' => true
|
||||
],
|
||||
'subdomains' => [
|
||||
'label' => lng('customer.subdomains'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['subdomains'],
|
||||
'value' => empty($result['subdomains']) ? '0' : $result['subdomains'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
'emails' => [
|
||||
'label' => lng('customer.emails'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['emails'],
|
||||
'value' => empty($result['emails']) ? '0' : $result['emails'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
'email_accounts' => [
|
||||
'label' => lng('customer.accounts'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['email_accounts'],
|
||||
'value' => empty($result['email_accounts']) ? '0' : $result['email_accounts'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
'email_forwarders' => [
|
||||
'label' => lng('customer.forwarders'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['email_forwarders'],
|
||||
'value' => empty($result['email_forwarders']) ? '0' : $result['email_forwarders'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
],
|
||||
'email_quota' => [
|
||||
'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')',
|
||||
'type' => 'textul',
|
||||
'value' => $result['email_quota'],
|
||||
'value' => empty($result['email_quota']) ? '0' : $result['email_quota'],
|
||||
'maxlength' => 9,
|
||||
'visible' => Settings::Get('system.mail_quota_enabled') == '1',
|
||||
'mandatory' => true
|
||||
@@ -209,13 +209,13 @@ return [
|
||||
'ftps' => [
|
||||
'label' => lng('customer.ftps'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['ftps'],
|
||||
'value' => empty($result['ftps']) ? '0' : $result['ftps'],
|
||||
'maxlength' => 9
|
||||
],
|
||||
'mysqls' => [
|
||||
'label' => lng('customer.mysqls'),
|
||||
'type' => 'textul',
|
||||
'value' => $result['mysqls'],
|
||||
'value' => empty($result['mysqls']) ? '0' : $result['mysqls'],
|
||||
'maxlength' => 9,
|
||||
'mandatory' => true
|
||||
]
|
||||
|
||||
@@ -364,6 +364,12 @@ return [
|
||||
'value' => '1',
|
||||
'checked' => true
|
||||
],
|
||||
'openbasedir_path' => [
|
||||
'label' => lng('domain.openbasedirpath'),
|
||||
'type' => 'select',
|
||||
'select_var' => $openbasedir,
|
||||
'selected' => 0
|
||||
],
|
||||
'phpenabled' => [
|
||||
'label' => lng('admin.phpenabled'),
|
||||
'type' => 'checkbox',
|
||||
|
||||
@@ -390,6 +390,12 @@ return [
|
||||
'value' => '1',
|
||||
'checked' => $result['openbasedir']
|
||||
],
|
||||
'openbasedir_path' => [
|
||||
'label' => lng('domain.openbasedirpath'),
|
||||
'type' => 'select',
|
||||
'select_var' => $openbasedir,
|
||||
'selected' => $result['openbasedir_path']
|
||||
],
|
||||
'phpenabled' => [
|
||||
'label' => lng('admin.phpenabled'),
|
||||
'type' => 'checkbox',
|
||||
|
||||
@@ -102,7 +102,7 @@ return [
|
||||
'value' => '5s'
|
||||
],
|
||||
'phpfpm_pass_authorizationheader' => [
|
||||
'visible' => Settings::Get('phpfpm.enabled') == 1 && Settings::Get('system.webserver') == "apache2",
|
||||
'visible' => Settings::Get('system.webserver') == "apache2",
|
||||
'label' => lng('admin.phpsettings.pass_authorizationheader'),
|
||||
'type' => 'checkbox',
|
||||
'value' => '1',
|
||||
|
||||
@@ -105,7 +105,7 @@ return [
|
||||
'value' => $result['fpm_reqslow']
|
||||
],
|
||||
'phpfpm_pass_authorizationheader' => [
|
||||
'visible' => Settings::Get('phpfpm.enabled') == 1 && Settings::Get('system.webserver') == "apache2",
|
||||
'visible' => Settings::Get('system.webserver') == "apache2",
|
||||
'label' => lng('admin.phpsettings.pass_authorizationheader'),
|
||||
'type' => 'checkbox',
|
||||
'value' => '1',
|
||||
|
||||
@@ -57,7 +57,7 @@ return [
|
||||
'label' => lng('panel.path'),
|
||||
'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescriptionSubdomain').(Settings::Get('system.documentroot_use_default_value') == 1 ? lng('panel.pathDescriptionEx') : '') : null),
|
||||
'type' => $pathSelect['type'],
|
||||
'select_var' => $pathSelect['value'],
|
||||
'select_var' => $pathSelect['select_var'] ?? '',
|
||||
'selected' => $pathSelect['value'],
|
||||
'value' => $pathSelect['value'],
|
||||
'note' => $pathSelect['note'] ?? '',
|
||||
|
||||
@@ -43,13 +43,16 @@ return [
|
||||
'email_password' => [
|
||||
'label' => lng('login.password'),
|
||||
'type' => 'password',
|
||||
'autocomplete' => 'off'
|
||||
],
|
||||
'email_password_suggestion' => [
|
||||
'label' => lng('customer.generated_pwd'),
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword()
|
||||
'autocomplete' => 'off',
|
||||
'next_to' => [
|
||||
'email_password_suggestion' => [
|
||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword(),
|
||||
'readonly' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -46,7 +46,7 @@ return [
|
||||
'autocomplete' => 'off',
|
||||
'mandatory' => true,
|
||||
'next_to' => [
|
||||
'admin_password_suggestion' => [
|
||||
'email_password_suggestion' => [
|
||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
|
||||
@@ -25,6 +25,13 @@
|
||||
|
||||
use Froxlor\Froxlor;
|
||||
|
||||
$httpuser = '';
|
||||
$httpgroup = '';
|
||||
if (extension_loaded('posix')) {
|
||||
$httpuser = posix_getpwuid(posix_getuid())['name'] ?? '';
|
||||
$httpgroup = posix_getgrgid(posix_getgid())['name'] ?? '';
|
||||
}
|
||||
|
||||
return [
|
||||
'install' => [
|
||||
'title' => 'install',
|
||||
@@ -194,7 +201,7 @@ return [
|
||||
'placeholder' => lng('admin.webserver_user'),
|
||||
'type' => 'text',
|
||||
'mandatory' => true,
|
||||
'value' => old('httpuser', posix_getpwuid(posix_getuid())['name'] ?? '', 'installation'),
|
||||
'value' => old('httpuser', $httpuser, 'installation'),
|
||||
'advanced' => true,
|
||||
],
|
||||
'httpgroup' => [
|
||||
@@ -202,7 +209,7 @@ return [
|
||||
'placeholder' => lng('admin.webserver_group'),
|
||||
'type' => 'text',
|
||||
'mandatory' => true,
|
||||
'value' => old('httpgroup', posix_getgrgid(posix_getgid())['name'] ?? '', 'installation'),
|
||||
'value' => old('httpgroup', $httpgroup, 'installation'),
|
||||
'advanced' => true,
|
||||
],
|
||||
'activate_newsfeed' => [
|
||||
|
||||
19
lib/init.php
19
lib/init.php
@@ -52,6 +52,7 @@ require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
use Froxlor\CurrentUser;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\Http\RateLimiter;
|
||||
use Froxlor\Idna\IdnaWrapper;
|
||||
use Froxlor\Language;
|
||||
use Froxlor\PhpHelper;
|
||||
@@ -121,6 +122,7 @@ if (!isset($sql) || !is_array($sql)) {
|
||||
|
||||
// send ssl-related headers (later than the others because we need a working database-connection and installation)
|
||||
UI::sendSslHeaders();
|
||||
RateLimiter::run();
|
||||
|
||||
// create a new idna converter
|
||||
$idna_convert = new IdnaWrapper();
|
||||
@@ -179,8 +181,10 @@ if (@file_exists('templates/' . $theme . '/config.json')) {
|
||||
}
|
||||
|
||||
// check for existence of variant in theme
|
||||
if (is_array($_themeoptions) && (!array_key_exists('variants', $_themeoptions) || !array_key_exists($themevariant,
|
||||
$_themeoptions['variants']))) {
|
||||
if (is_array($_themeoptions) && (!array_key_exists('variants', $_themeoptions) || !array_key_exists(
|
||||
$themevariant,
|
||||
$_themeoptions['variants']
|
||||
))) {
|
||||
$themevariant = "default";
|
||||
}
|
||||
|
||||
@@ -214,12 +218,11 @@ UI::twig()->addGlobal('header_logo', $header_logo);
|
||||
if (!CurrentUser::hasSession() && AREA != 'login') {
|
||||
unset($_SESSION['userinfo']);
|
||||
CurrentUser::setData();
|
||||
session_destroy();
|
||||
$params = [
|
||||
"script" => basename($_SERVER["SCRIPT_NAME"]),
|
||||
"qrystr" => $_SERVER["QUERY_STRING"]
|
||||
$_SESSION = [
|
||||
"lastscript" => basename($_SERVER["SCRIPT_NAME"]),
|
||||
"lastqrystr" => $_SERVER["QUERY_STRING"]
|
||||
];
|
||||
Response::redirectTo('index.php', $params);
|
||||
Response::redirectTo('index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
@@ -332,7 +335,7 @@ if (CurrentUser::hasSession()) {
|
||||
$cookie_params = [
|
||||
'expires' => time() + Settings::Get('session.sessiontimeout'),
|
||||
'path' => '/',
|
||||
'domain' => explode(':', $_SERVER['HTTP_HOST'])[0],
|
||||
'domain' => UI::getCookieHost(),
|
||||
'secure' => UI::requestIsHttps(),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
|
||||
@@ -38,7 +38,7 @@ return [
|
||||
'url' => 'customer_email.php?page=emails',
|
||||
'label' => lng('menue.email.emails'),
|
||||
'required_resources' => 'emails',
|
||||
'add_shortlink' => CurrentUser::canAddResource('emails') ? 'customer_email.php?page=emails&action=add' : null,
|
||||
'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('emails') ? 'customer_email.php?page=email_domain&action=add' : null,
|
||||
],
|
||||
[
|
||||
'url' => Settings::Get('panel.webmail_url'),
|
||||
@@ -60,7 +60,7 @@ return [
|
||||
'url' => 'customer_mysql.php?page=mysqls',
|
||||
'label' => lng('menue.mysql.databases'),
|
||||
'required_resources' => 'mysqls',
|
||||
'add_shortlink' => CurrentUser::canAddResource('mysqls')? 'customer_mysql.php?page=mysqls&action=add' : null,
|
||||
'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('mysqls')? 'customer_mysql.php?page=mysqls&action=add' : null,
|
||||
],
|
||||
[
|
||||
'url' => Settings::Get('panel.phpmyadmin_url'),
|
||||
@@ -81,7 +81,7 @@ return [
|
||||
[
|
||||
'url' => 'customer_domains.php?page=domains',
|
||||
'label' => lng('menue.domains.settings'),
|
||||
'add_shortlink' => CurrentUser::canAddResource('subdomains') ? 'customer_domains.php?page=domains&action=add' : null,
|
||||
'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('subdomains') ? 'customer_domains.php?page=domains&action=add' : null,
|
||||
],
|
||||
[
|
||||
'url' => 'customer_domains.php?page=sslcertificates',
|
||||
@@ -98,7 +98,7 @@ return [
|
||||
[
|
||||
'url' => 'customer_ftp.php?page=accounts',
|
||||
'label' => lng('menue.ftp.accounts'),
|
||||
'add_shortlink' => CurrentUser::canAddResource('ftps') ? 'customer_ftp.php?page=accounts&action=add' : null,
|
||||
'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('ftps') ? 'customer_ftp.php?page=accounts&action=add' : null,
|
||||
],
|
||||
[
|
||||
'url' => Settings::Get('panel.webftp_url'),
|
||||
|
||||
@@ -45,30 +45,27 @@ return [
|
||||
'callback' => [SSLCertificate::class, 'domainWithSan'],
|
||||
'searchable' => false,
|
||||
],
|
||||
'c.issuer' => [
|
||||
's.issuer' => [
|
||||
'label' => lng('ssl_certificates.issuer'),
|
||||
'field' => 'issuer',
|
||||
'searchable' => false,
|
||||
],
|
||||
'c.validfromdate' => [
|
||||
's.validfromdate' => [
|
||||
'label' => lng('ssl_certificates.valid_from'),
|
||||
'field' => 'validfromdate',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
],
|
||||
'c.validtodate' => [
|
||||
's.validtodate' => [
|
||||
'label' => lng('ssl_certificates.valid_until'),
|
||||
'field' => 'validtodate',
|
||||
'searchable' => false,
|
||||
'sortable' => false,
|
||||
],
|
||||
],
|
||||
'visible_columns' => Listing::getVisibleColumnsForListing('sslcertificates_list', [
|
||||
'd.domain',
|
||||
'c.domain',
|
||||
'c.issuer',
|
||||
'c.validfromdate',
|
||||
'c.validtodate',
|
||||
's.issuer',
|
||||
's.validfromdate',
|
||||
's.validtodate',
|
||||
]),
|
||||
'actions' => [
|
||||
'edit' => [
|
||||
|
||||
@@ -33,6 +33,7 @@ return [
|
||||
'nl' => 'Niederländisch',
|
||||
'pt' => 'Portugiesisch',
|
||||
'se' => 'Schwedisch',
|
||||
'es' => 'Spanisch',
|
||||
],
|
||||
'2fa' => [
|
||||
'2fa' => '2FA Optionen',
|
||||
@@ -297,7 +298,7 @@ return [
|
||||
'request_terminate_timeout' => 'request_terminate_timeout',
|
||||
'request_slowlog_timeout' => 'request_slowlog_timeout',
|
||||
'activephpconfigs' => 'In Verwendung für PHP-Konfiguration(en)',
|
||||
'pass_authorizationheader' => 'Füge "-pass-header Authorization" / "CGIPassAuth On" in Vhosts ein',
|
||||
'pass_authorizationheader' => 'Übergeben von HTTP AUTH BASIC/DIGEST-Headern von Apache an PHP',
|
||||
],
|
||||
'misc' => 'Sonstiges',
|
||||
'fpmsettings' => [
|
||||
@@ -924,6 +925,8 @@ return [
|
||||
'mysqlserverstillhasdbs' => 'Datenbank-Server kann für den Kunden nicht entfernt werden, da sich dort noch Datenbanken befinden.',
|
||||
'domaincannotbeedited' => 'Keine Berechtigung, um die Domain %s zu bearbeiten',
|
||||
'invalidcronjobintervalvalue' => 'Cronjob Intervall muss einer der folgenden Werte sein: %s',
|
||||
'phpgdextensionnotavailable' => 'Die PHP GD Extension ist nicht verfügbar. Bild-Daten können nicht validiert werden.',
|
||||
'2fa_wrongcode' => 'Der angegebene Code ist nicht korrekt',
|
||||
],
|
||||
'extras' => [
|
||||
'description' => 'Hier können Sie zusätzliche Extras einrichten, wie zum Beispiel einen Verzeichnisschutz.<br />Die Änderungen sind erst nach einer kurzen Zeit wirksam.',
|
||||
@@ -2070,6 +2073,14 @@ Vielen Dank, Ihr Administrator',
|
||||
'toolselect' => 'Traffic Analyzer',
|
||||
],
|
||||
'requires_reconfiguration' => 'Änderungen an dieser Einstellungen benötigen unter Umständen eine erneute Konfiguration der folgenden Dienste:<br><strong>%s</strong>',
|
||||
'req_limit_per_interval' => [
|
||||
'title' => 'Anzahl der HTTP-Anfragen pro Intervall',
|
||||
'description' => 'Erlaubte Anzahl von HTTP-Anfragen pro Intervall (siehe unten) auf froxlor, Standard ist "60"',
|
||||
],
|
||||
'req_limit_interval' => [
|
||||
'title' => 'Rate-Limit-Intervall',
|
||||
'description' => 'Zeit in Sekunden für die maximale Anzahl von HTTP-Anfragen, Standard ist "60".',
|
||||
],
|
||||
],
|
||||
'spf' => [
|
||||
'use_spf' => 'Aktiviere SPF für Domains?',
|
||||
@@ -2177,6 +2188,7 @@ Vielen Dank, Ihr Administrator',
|
||||
'description' => 'Aktualisierung der froxlor Datenbank',
|
||||
'uc_newinfo' => 'Eine neuere %sVersion ist verfügbar: "%s" (Aktuell installierte Version: %s)',
|
||||
'notify_subject' => 'Neues Update verfügbar',
|
||||
'dbupdate_required' => 'Froxlor-Dateien wurden aktualisiert, Datenbank-Aktualisierung notwendig',
|
||||
],
|
||||
'usersettings' => [
|
||||
'custom_notes' => [
|
||||
@@ -2231,8 +2243,8 @@ Vielen Dank, Ihr Administrator',
|
||||
'install' => [
|
||||
'top' => 'Abschluss',
|
||||
'title' => 'Ein letzter Schritt...',
|
||||
'description' => 'Der untenstehende Befehl lädt, installiert und konfiguriert die benötigten Dienste auf dem System aufgrund der Angaben die während des Installationsprozessen gesammelt wurden.',
|
||||
'runcmd' => 'Folgenden Befehl als root-Benutzer in der Shell auf dem Server ausführen:',
|
||||
'description' => 'Der untenstehende Befehl lädt, installiert und konfiguriert die benötigten Dienste auf dem System aufgrund der Angaben die während des Installationsprozessen gesammelt wurden.<br><br><span class="text-danger">Führe die gezeigten Befehle als <b>root</b> in der Shell/Konsole des Servers aus.</span>',
|
||||
'runcmd' => 'Folgende Befehle ausführen, um die Installation abzuschließen:',
|
||||
'manual_config' => 'Ich werden die Dienste manuell konfigurieren, direkt zum Login umleiten',
|
||||
'waitforconfig' => 'Warte auf Abschluss der Dienstkonfiguration...',
|
||||
],
|
||||
|
||||
@@ -33,6 +33,7 @@ return [
|
||||
'nl' => 'Dutch',
|
||||
'pt' => 'Portuguese',
|
||||
'se' => 'Swedish',
|
||||
'es' => 'Spanish',
|
||||
],
|
||||
'2fa' => [
|
||||
'2fa' => '2FA options',
|
||||
@@ -302,7 +303,7 @@ return [
|
||||
'request_terminate_timeout' => 'Request terminate-timeout',
|
||||
'request_slowlog_timeout' => 'Request slowlog-timeout',
|
||||
'activephpconfigs' => 'In use for php-config(s)',
|
||||
'pass_authorizationheader' => 'Add "-pass-header Authorization" / "CGIPassAuth On" to vhosts',
|
||||
'pass_authorizationheader' => 'Passing HTTP AUTH BASIC/DIGEST headers from Apache to PHP',
|
||||
],
|
||||
'misc' => 'Miscellaneous',
|
||||
'fpmsettings' => [
|
||||
@@ -951,7 +952,7 @@ return [
|
||||
'autoupdate_1' => 'PHP setting allow_url_fopen is disabled. Autoupdate needs this setting to be enabled in php.ini',
|
||||
'autoupdate_2' => 'PHP zip extension not found, please ensure it is installed and activated',
|
||||
'autoupdate_4' => 'The froxlor archive could not be stored to the disk :(',
|
||||
'autoupdate_5' => 'version.froxlor.org returned inacceptable values :(',
|
||||
'autoupdate_5' => 'version.froxlor.org returned unacceptable values :(',
|
||||
'autoupdate_6' => 'Whoops, there was no (valid) version given to download :(',
|
||||
'autoupdate_7' => 'The downloaded archive could not be found :(',
|
||||
'autoupdate_8' => 'The archive could not be extracted :(',
|
||||
@@ -993,6 +994,8 @@ return [
|
||||
'mysqlserverstillhasdbs' => 'Cannot remove database server from customers allow-list as there are still databases on it.',
|
||||
'domaincannotbeedited' => 'You are not permitted to edit the domain %s',
|
||||
'invalidcronjobintervalvalue' => 'Cronjob interval must be one of: %s',
|
||||
'phpgdextensionnotavailable' => 'The PHP GD extension is not available. Unable to validate image-data',
|
||||
'2fa_wrongcode' => 'The code entered is not valid',
|
||||
],
|
||||
'extras' => [
|
||||
'description' => 'Here you can add some extras, for example directory protection.<br />The system will need some time to apply the new settings after every change.',
|
||||
@@ -1191,7 +1194,7 @@ Yours sincerely, your administrator',
|
||||
'database_edit' => 'Edit database',
|
||||
'size' => 'Size',
|
||||
'privileged_user' => 'Privileged database user',
|
||||
'privileged_passwd' => 'Password for priviliged user',
|
||||
'privileged_passwd' => 'Password for privileged user',
|
||||
'unprivileged_passwd' => 'Password for unprivileged user',
|
||||
'mysql_ssl_ca_file' => 'SSL server certificate',
|
||||
'mysql_ssl_verify_server_certificate' => 'Verify SSL server certificate'
|
||||
@@ -1260,7 +1263,7 @@ Yours sincerely, your administrator',
|
||||
'reset' => 'Discard changes',
|
||||
'pathDescription' => 'If the directory doesn\'t exist, it will be created automatically.',
|
||||
'pathDescriptionEx' => '<br /><br /><span class="text-danger">Please note:</span> The path <code>/</code> is not allowed due to administrative settings, it will automatically be set to <code>/chosen.subdomain.tld/</code> if not set to another directory.',
|
||||
'pathDescriptionSubdomain' => 'If the directory doesn\'t exist, it will be created automatically.<br /><br />If you want a redirect to another domain than this entry has to start with http:// or https://.<br /><br />If the URL ends with / it is considered a folder, if not, it is treated as file.',
|
||||
'pathDescriptionSubdomain' => 'If the directory doesn\'t exist, it will be created automatically.<br /><br />If you want a redirect to another domain then this entry has to start with http:// or https://.<br /><br />If the URL ends with / it is considered a folder, if not, it is treated as file.',
|
||||
'back' => 'Back',
|
||||
'reseller' => 'reseller',
|
||||
'admin' => 'admin',
|
||||
@@ -2194,6 +2197,14 @@ Yours sincerely, your administrator',
|
||||
'goaccess' => 'goacccess'
|
||||
],
|
||||
'requires_reconfiguration' => 'Changing this settings might require a reconfiguration of the following services:<br><strong>%s</strong>',
|
||||
'req_limit_per_interval' => [
|
||||
'title' => 'Number of HTTP requests per interval',
|
||||
'description' => 'Limit the number of HTTP requests per interval (see below) to froxlor, default is "60"',
|
||||
],
|
||||
'req_limit_interval' => [
|
||||
'title' => 'Rate-limit interval',
|
||||
'description' => 'Specify the time in seconds for the number of HTTP requests, default is "60"',
|
||||
],
|
||||
],
|
||||
'spf' => [
|
||||
'use_spf' => 'Activate SPF for domains?',
|
||||
@@ -2308,6 +2319,7 @@ Yours sincerely, your administrator',
|
||||
'description' => 'Running database updates for your froxlor installation',
|
||||
'uc_newinfo' => 'There is a newer %sversion available: "%s" (Your current version is: %s)',
|
||||
'notify_subject' => 'New update available',
|
||||
'dbupdate_required' => 'Froxlor files have been updated, database update required',
|
||||
],
|
||||
'usersettings' => [
|
||||
'custom_notes' => [
|
||||
@@ -2363,8 +2375,8 @@ Yours sincerely, your administrator',
|
||||
'install' => [
|
||||
'top' => 'Finish setup',
|
||||
'title' => 'One last step...',
|
||||
'description' => 'The command below will download, install and configure required services on your system according to the data you have given in this installation process.',
|
||||
'runcmd' => 'Run the following command as root-user in your shell on this server:',
|
||||
'description' => 'The command below will download, install and configure required services on your system according to the data you have given in this installation process.<br><br><span class="text-danger">Be sure to run the following command as <b>root</b> on the server\'s shell/terminal.</span>',
|
||||
'runcmd' => 'Run the following command to finish the installation:',
|
||||
'manual_config' => 'I will manually configure the services, just take me to the login',
|
||||
'waitforconfig' => 'Waiting for services to be configured...',
|
||||
],
|
||||
|
||||
2405
lng/es.lng.php
Normal file
2405
lng/es.lng.php
Normal file
File diff suppressed because it is too large
Load Diff
7546
package-lock.json
generated
7546
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -42,7 +42,7 @@
|
||||
<label class="form-check-label" for="switchInstallMode">{% if extended is defined and extended %}{{ lng('install.switchmode_basic') }}{% else %}{{ lng('install.switchmode_advanced') }}{% endif %}</label>
|
||||
</div>
|
||||
</div>
|
||||
<p class="lead">{{ section.description }}</p>
|
||||
<p class="lead">{{ section.description|raw }}</p>
|
||||
<hr />
|
||||
|
||||
{% import "Froxlor/form/formfields.html.twig" as formfields %}
|
||||
|
||||
@@ -10,9 +10,16 @@
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ pagetitle }}</h5>
|
||||
|
||||
{% if message is not empty %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading">{{ lng('error.error') }}</h4>
|
||||
<p>{{ message|raw }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="2fa_code" class="col-form-label">{{ lng('login.2facode') }}</label>
|
||||
<input class="form-control" type="text" name="2fa_code" id="2fa_code" value="" autofocus required/>
|
||||
<input class="form-control" type="text" name="2fa_code" id="2fa_code" value="" autocomplete="off" autofocus required/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<form class="col-12 max-w-420 d-flex flex-column" method="post" enctype="application/x-www-form-urlencoded">
|
||||
<form action="{{ formaction }}" class="col-12 max-w-420 d-flex flex-column" method="post" enctype="application/x-www-form-urlencoded">
|
||||
<img class="align-self-center my-5" src="{{ header_logo_login }}" alt="Froxlor Server Management Panel"/>
|
||||
|
||||
<div class="card shadow">
|
||||
@@ -38,8 +38,6 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body d-grid gap-2">
|
||||
<input type="hidden" name="action" value="{{ action }}"/>
|
||||
<input type="hidden" name="send" value="send"/>
|
||||
<button class="btn btn-primary rounded-top-0" type="submit" name="doremind">{{ lng('login.remind') }}</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -39,9 +39,6 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body d-grid gap-2">
|
||||
<input type="hidden" name="script" value="{{ lastscript }}"/>
|
||||
<input type="hidden" name="qrystr" value="{{ lastqrystr|raw }}"/>
|
||||
<input type="hidden" name="send" value="send"/>
|
||||
<button class="btn btn-primary rounded-top-0" type="submit" name="dologin">{{ lng('login.login') }}</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body d-grid gap-2">
|
||||
<input type="hidden" name="action" value="resetpwd"/>
|
||||
<input type="hidden" name="send" value="send"/>
|
||||
<button class="btn btn-primary rounded-top-0" type="submit" name="doremind">{{ lng('login.remind') }}</button>
|
||||
</div>
|
||||
|
||||
|
||||
17
templates/Froxlor/misc/ratelimithint.html.twig
Normal file
17
templates/Froxlor/misc/ratelimithint.html.twig
Normal file
@@ -0,0 +1,17 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-auto">
|
||||
<div class="alert alert-warning fade show" role="alert">
|
||||
<h4 class="alert-heading">
|
||||
Whoops!
|
||||
</h4>
|
||||
<p>It seems like you've hit the rate limit.</p>
|
||||
<p>Please slow down your requests and retry after {{ retry|date('d.m.Y H:i:s') }}</p>
|
||||
<hr>
|
||||
<p class="mt-1 text-center">
|
||||
<a href="" class="btn btn-primary" title="Reload page">Reload</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -4,7 +4,7 @@
|
||||
<h5>
|
||||
<i class="fa-solid fa-gears"></i>
|
||||
{{ lng('admin.serversettings') }}
|
||||
{% if fields._group is defined %} » {{ fields._group.title }}
|
||||
{% if fields._group is defined %} » {{ fields._group.title|raw }}
|
||||
{% endif %}
|
||||
</h5>
|
||||
<span class="text-muted">{{ lng('admin.serversettings_desc') }}</span>
|
||||
@@ -39,7 +39,7 @@
|
||||
<i class="{{ field.icon }} fa-2x me-4" style="width: 1em;"></i>
|
||||
</a>
|
||||
<div>
|
||||
{{ field.title }}
|
||||
{{ field.title|raw }}
|
||||
{% if field.info is defined and field.info is not empty %}
|
||||
{{ field.info|raw }}
|
||||
{% endif %}
|
||||
|
||||
@@ -5,4 +5,8 @@ $(function () {
|
||||
history.back(1);
|
||||
})
|
||||
|
||||
$('#copySysInfo').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText($('#ccSysInfo').text().trim());
|
||||
})
|
||||
});
|
||||
|
||||
@@ -55,6 +55,19 @@
|
||||
<div class="card-header">
|
||||
<i class="fa-solid fa-gears me-1"></i>
|
||||
{{ lng('admin.systemdetails') }}
|
||||
<div class="float-end">
|
||||
<button id="copySysInfo" class="btn btn-outline-dark" style="--bs-btn-padding-y: .25rem; --bs-btn-padding-x: .5rem; --bs-btn-font-size: .5rem;" title="Copy to clipboard"><i class="fa-solid fa-copy"></i></button>
|
||||
</div>
|
||||
<div id="ccSysInfo" class="d-none">
|
||||
- Froxlor: {{ call_static('\\Froxlor\\Froxlor', 'getVersionString') }}
|
||||
- {{ lng('serversettings.update_channel.title') }}: {{ get_setting('system.update_channel') }}
|
||||
- {{ lng('admin.serversoftware') }}: {{ sysinfo.webserver }}
|
||||
- {{ lng('admin.phpversion') }}: {{ sysinfo.phpversion }}
|
||||
- {{ lng('admin.mysqlserverversion') }}: {{ sysinfo.mysqlserverversion }}
|
||||
- {{ lng('admin.webserverinterface') }}: {{ sysinfo.phpsapi }}
|
||||
- Kernel: {{ sysinfo.kernel }}
|
||||
- OS: {{ get_setting('system.distribution') }}
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<a href="{{ link|default('#')|raw }}" class="list-group-item list-group-item-action">
|
||||
<a href="{{ link|default('#')|raw }}" class="list-group-item list-group-item-action" target="_blank">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<b class="mb-1">{{ title }}</b>
|
||||
{% if date is not empty %}
|
||||
|
||||
@@ -19,11 +19,14 @@ class FroxlorTest extends TestCase
|
||||
|
||||
$json_result = Froxlor::getLocal($admin_userdata)->checkUpdate();
|
||||
$result = json_decode($json_result, true)['data'];
|
||||
$this->assertEquals(0, $result['isnewerversion']);
|
||||
if (defined('DEV_FROXLOR') && DEV_FROXLOR == 1) {
|
||||
$this->assertEquals("You already have the latest testing-version of Froxlor installed.", $result['additional_info']);
|
||||
} else {
|
||||
$this->assertEquals("You already have the latest version of Froxlor installed.", $result['additional_info']);
|
||||
$this->assertContains($result['isnewerversion'] ?? -1, [0,1]);
|
||||
$this->assertNotEmpty($result['version']);
|
||||
if ($result['isnewerversion'] == 0) {
|
||||
if (defined('DEV_FROXLOR') && DEV_FROXLOR == 1) {
|
||||
$this->assertEquals("You already have the latest testing-version of Froxlor installed.", $result['additional_info']);
|
||||
} else {
|
||||
$this->assertEquals("You already have the latest version of Froxlor installed.", $result['additional_info']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user