Compare commits

...

59 Commits

Author SHA1 Message Date
Michael Kaufmann
c236d9eaab set version to 2.0.20 for upcoming release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-06-02 20:13:36 +02:00
Michael Kaufmann
688994e40c idna encode umlaut-emailaddresses when adding email-forwarder
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-29 20:52:57 +02:00
Michael Kaufmann
9facaee809 re-enable fcgid/php-fpm activation-validate-check
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-28 15:49:06 +02:00
Michael Kaufmann
a7dd5f4685 show 0 value of resource-fields if value is empty, fixes #1149
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-28 10:46:28 +02:00
Michael Kaufmann
da810ea953 secure filename of local-archive in webupdate
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-25 09:51:55 +02:00
Michael Kaufmann
51b6e067e8 idna encode umlaut-emailaddresses when adding/editing email-account; use correct password-suggestion-layout in change-email-account formfield
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-25 08:26:39 +02:00
Michael Kaufmann
34cf6698bc remove superfluous try_files in nginx config if php-backend (non-fastcgi) is used
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-15 20:14:26 +02:00
Michael Kaufmann
4642160724 add same loginfail restrictions for entering 2fa code as for user/pwd login
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-12 10:36:27 +02:00
Nicolas Thumann
78a259ef3b Fix IPv6 address in cookie domain (#1137)
* Implement getCookieHost to extract cookie host from HTTP_HOST
2023-05-10 08:26:08 +02:00
Nicolas Thumann
68cf4ab69a Fix typo in English privileged_passwd (#1136) 2023-05-09 18:52:43 +02:00
Michael Kaufmann
d5661d492d set version to 2.0.19 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-07 11:07:31 +02:00
Michael Kaufmann
6900898ae1 typo in updater
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-07 11:03:21 +02:00
Michael Kaufmann
d90fb7fa68 fix mysql-pdo check on installation, set version to 2.0.18 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-07 10:54:47 +02:00
Michael Kaufmann
4ea8629fcc set version to 2.0.17 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-06 22:08:43 +02:00
Michael Kaufmann
9d4ff8698d fix ratelimiting when settings do not exist (yet)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-06 22:00:19 +02:00
Michael Kaufmann
b164038846 set version to 2.0.16 for upcoming maintenance release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-05 21:10:01 +02:00
Michael Kaufmann
5c46960734 fix language mixup for rate-limit-interval setting
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-05 13:21:12 +02:00
Michael Kaufmann
a7f4f0c737 output nicer message when hitting rate limit
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-04 10:55:34 +02:00
Michael Kaufmann
b64dd501dd fix missing use-statement
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-02 10:27:28 +02:00
Michael Kaufmann
1679675aa1 introduce http-request rate-limit; smaller fixes
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-02 10:19:53 +02:00
sro0
640466f301 Disable autocomplete on 2FA input element (#1133)
2FA codes change every login. So there is no need to save entered values in browser and suggest them again during future logins.

Co-authored-by: sro0 <>
2023-04-29 09:56:15 +02:00
Michael Kaufmann
9c9771a371 fix generation of current_ips array in Domains-API
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-25 20:09:18 +02:00
Michael Kaufmann
1922b3ce65 set default value for email_quota to settings-default in EmailAccounts.add(); fixes #1132
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-25 19:50:41 +02:00
Michael Kaufmann
83e819908a set default value of 'openbasedir_path' to 0 in SubDomain.add() like we do in Domains.add()
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-25 19:49:09 +02:00
Michael Kaufmann
0924aa644b update dependencies
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-25 19:48:29 +02:00
Lukas Bableck
7711ce1d66 Allow admins to edit openbasedir_path for domains (#1125)
* Add openbasedir_path formfield
* Add openbasedir_path field values to admin_domains page
2023-04-25 19:42:27 +02:00
Michael Kaufmann
7dae63e586 Merge branch 'main' of github.com:Froxlor/Froxlor 2023-04-25 19:40:22 +02:00
Michael Kaufmann
1bcaa45492 add copy-system-details-to-clipboard button on admin dashboard; fixes #1126
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-25 19:36:46 +02:00
Michael Kaufmann
66cb114f0d trigger rebuild of config files after changing only ip-settings in domains
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-25 19:33:52 +02:00
Michael Kaufmann
1c5d60dcfd Add mysql to required extensions 2023-04-23 13:28:33 +02:00
Michael Kaufmann
b6da6356fc Update build-docs.yml 2023-04-23 12:08:19 +02:00
Michael Kaufmann
c09670cc45 make it clearer that the finishing commands have to be exectuted as 'root'; fixes #1128
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-23 11:56:20 +02:00
Michael Kaufmann
464f5b7bed fix adding mysql-server to customers without any prior assigned mysql-server, fixes #1123; fix issues with displaying set value if path-mode is 'dropdown'
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-19 20:58:48 +02:00
Michael Kaufmann
c799235c24 corrected display of special-case titles of settings
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-14 08:23:37 +02:00
Michael Kaufmann
a2860e70a5 strictly check whether field to select is the id or the email-address b/c is cases of email-addresses starting with a digit this is somehow used as value for the id field and return the wrong entity
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-14 08:22:31 +02:00
Michael Kaufmann
95a96d46a6 put php-fpm directives in Directory-directive in apache2; fixes #1120
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-11 09:48:26 +02:00
Michael Kaufmann
81f3dbda31 respect no-try_files setting also in protected directories
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-10 09:33:43 +02:00
Michael Kaufmann
4eb4191843 don't run cron tasks if requirements return non-success; fixes #1122
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-08 10:49:59 +02:00
Michael Kaufmann
ca433d8a61 set version to 2.0.15 for update-bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-07 18:58:19 +02:00
Michael Kaufmann
8f4dfe1514 Fix the fix... 2023-04-07 11:51:44 +02:00
Michael Kaufmann
ee42f5168e Use correct SQL Syntax for older versions 2023-04-07 11:48:43 +02:00
Michael Kaufmann
fc8ca57f8c set version to 2.0.14 for upcoming release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-04-07 08:52:12 +02:00
Michael Kaufmann
7e4bba2d55 corrected mail-log parsing, refs #1119
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-27 12:53:21 +02:00
Michael Kaufmann
7e635f9be4 correctly retriggered certificate issue on froxlor-vhost alias-domain changes, fixes #1115
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-24 09:17:04 +01:00
Michael Kaufmann
e9406a20f2 readd php interpretation to php-enabled customers/domains in directory protection, fixes #1118
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-23 19:18:39 +01:00
Michael Kaufmann
de7729cec8 add certificate metadata to db table
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-23 12:40:01 +01:00
Michael Kaufmann
d60e48849b correct languages for mail/file templates
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-23 08:56:45 +01:00
Michael Kaufmann
908df5a7bb remove sorting from ssl 'issuer' as this data is being read from the certificate content and not the database/table and therefore cannot be sorted using the API, fixes #1116
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-21 14:22:34 +01:00
Michael Kaufmann
c1952afb94 dont sort indexed array as the keys get lost; fixes #1114
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-20 11:12:30 +01:00
Michael Kaufmann
7a22e8f4dd open newsfeed-links in a new tab, fixes #1112
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-18 20:04:02 +01:00
Michael Kaufmann
3ac0da2cdd corrected checkLocalGroup() validation if setting did not change, fixes #1111
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-17 08:27:31 +01:00
dependabot[bot]
eb816c4cc6 Bump webpack from 5.75.0 to 5.76.1 (#1109)
Bumps [webpack](https://github.com/webpack/webpack) from 5.75.0 to 5.76.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.75.0...v5.76.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 13:21:01 +01:00
Michael Kaufmann
64d8bf4fba avoid socket length limitations leading to cut-off/invalid filename for very long domain and/or loginnames, fixes #1108
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-15 13:18:43 +01:00
Michael Kaufmann
ae6ee95973 avoid using posix-extension function before requirement-check can test for it and inform user
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-15 08:54:50 +01:00
Michael Kaufmann
e9051dc30a add spanish language translation reference to german language file
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-08 09:48:13 +01:00
scramatte
b6c7c53c3a Add Spanish language (#1105)
* Add Spanish localization
* add spanish to languages list
2023-03-08 09:43:35 +01:00
Michael Kaufmann
f36bc61fc7 better validation for uploaded/imported image files
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-08 09:33:30 +01:00
Michael Kaufmann
c56e0b9dac add 'Passing HTTP AUTH BASIC' header option when using FCGID; fix typeerror in parameter for Froxlor\Dns\Dns; require php-gd extension for validating uploaded images
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-08 09:33:02 +01:00
Michael Kaufmann
1deb08bf75 use correct parameter in PowerDNS::cleanDomainZone(), fixes #1104
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-04 14:42:31 +01:00
65 changed files with 3596 additions and 7470 deletions

View File

@@ -11,4 +11,4 @@ jobs:
- env: - env:
GITHUB_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN }}
run: | 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}}

View File

@@ -138,6 +138,26 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true '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' => [ 'customer_accountprefix' => [
'label' => lng('serversettings.accountprefix'), 'label' => lng('serversettings.accountprefix'),
'settinggroup' => 'customer', 'settinggroup' => 'customer',

View File

@@ -53,7 +53,7 @@ return [
'string_regexp' => '/^(([a-z0-9\-\._]+, ?)*[a-z0-9\-\._]+)?$/i', 'string_regexp' => '/^(([a-z0-9\-\._]+, ?)*[a-z0-9\-\._]+)?$/i',
'string_emptyallowed' => true, 'string_emptyallowed' => true,
'default' => '', 'default' => '',
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingClearCertificates',
'advanced_mode' => true 'advanced_mode' => true
], ],
/** /**

View File

@@ -28,7 +28,7 @@ require __DIR__ . '/lib/init.php';
use Froxlor\Froxlor; use Froxlor\Froxlor;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\Http\HttpClient; use Froxlor\FileDir;
use Froxlor\Install\AutoUpdate; use Froxlor\Install\AutoUpdate;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
@@ -132,7 +132,7 @@ elseif ($page == 'getdownload') {
elseif ($page == 'extract') { elseif ($page == 'extract') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (isset($_POST['send']) && $_POST['send'] == 'send') {
$toExtract = isset($_POST['archive']) ? $_POST['archive'] : null; $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()); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Extracting " . $localArchive . " to " . Froxlor::getInstallDir());
$result = AutoUpdate::extractZip($localArchive); $result = AutoUpdate::extractZip($localArchive);
if ($result > 0) { if ($result > 0) {
@@ -146,7 +146,7 @@ elseif ($page == 'extract') {
Response::redirectTo('admin_updates.php'); Response::redirectTo('admin_updates.php');
} else { } else {
$toExtract = isset($_GET['archive']) ? $_GET['archive'] : null; $toExtract = isset($_GET['archive']) ? $_GET['archive'] : null;
$localArchive = Froxlor::getInstallDir() . '/updates/' . $toExtract; $localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
} }
if (!file_exists($localArchive)) { if (!file_exists($localArchive)) {

View File

@@ -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 // create serveralias options
$serveraliasoptions = [ $serveraliasoptions = [
0 => lng('domains.serveraliasoption_wildcard'), 0 => lng('domains.serveraliasoption_wildcard'),
@@ -545,6 +551,12 @@ if ($page == 'domains' || $page == 'overview') {
$result['temporary_ssl_redirect'] = $result['ssl_redirect']; $result['temporary_ssl_redirect'] = $result['ssl_redirect'];
$result['ssl_redirect'] = ($result['ssl_redirect'] == 0 ? 0 : 1); $result['ssl_redirect'] = ($result['ssl_redirect'] == 0 ? 0 : 1);
$openbasedir = [
0 => lng('domain.docroot'),
1 => lng('domain.homedir'),
2 => lng('domain.docparent')
];
$serveraliasoptions = [ $serveraliasoptions = [
0 => lng('domains.serveraliasoption_wildcard'), 0 => lng('domains.serveraliasoption_wildcard'),
1 => lng('domains.serveraliasoption_www'), 1 => lng('domains.serveraliasoption_www'),

View File

@@ -45,6 +45,7 @@
"ext-openssl": "*", "ext-openssl": "*",
"ext-fileinfo": "*", "ext-fileinfo": "*",
"ext-gmp": "*", "ext-gmp": "*",
"ext-gd": "*",
"phpmailer/phpmailer": "~6.0", "phpmailer/phpmailer": "~6.0",
"monolog/monolog": "^1.24", "monolog/monolog": "^1.24",
"robthree/twofactorauth": "^1.6", "robthree/twofactorauth": "^1.6",

175
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "41e7a3bc0e13b47c4f245334b113c3be", "content-hash": "2de39e6b85579ce1f0c2f7a16d57ede3",
"packages": [ "packages": [
{ {
"name": "erusev/parsedown", "name": "erusev/parsedown",
@@ -251,16 +251,16 @@
}, },
{ {
"name": "phpmailer/phpmailer", "name": "phpmailer/phpmailer",
"version": "v6.7.1", "version": "v6.8.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git", "url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "49cd7ea3d2563f028d7811f06864a53b1f15ff55" "reference": "df16b615e371d81fb79e506277faea67a1be18f1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/49cd7ea3d2563f028d7811f06864a53b1f15ff55", "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1",
"reference": "49cd7ea3d2563f028d7811f06864a53b1f15ff55", "reference": "df16b615e371d81fb79e506277faea67a1be18f1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -319,7 +319,7 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP", "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": { "support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues", "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": [ "funding": [
{ {
@@ -327,7 +327,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2022-12-08T13:30:06+00:00" "time": "2023-03-06T14:43:22+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
@@ -499,16 +499,16 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v5.4.19", "version": "v5.4.22",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "dccb8d251a9017d5994c988b034d3e18aaabf740" "reference": "3cd51fd2e6c461ca678f84d419461281bd87a0a8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/dccb8d251a9017d5994c988b034d3e18aaabf740", "url": "https://api.github.com/repos/symfony/console/zipball/3cd51fd2e6c461ca678f84d419461281bd87a0a8",
"reference": "dccb8d251a9017d5994c988b034d3e18aaabf740", "reference": "3cd51fd2e6c461ca678f84d419461281bd87a0a8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -573,12 +573,12 @@
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"keywords": [ "keywords": [
"cli", "cli",
"command line", "command-line",
"console", "console",
"terminal" "terminal"
], ],
"support": { "support": {
"source": "https://github.com/symfony/console/tree/v5.4.19" "source": "https://github.com/symfony/console/tree/v5.4.22"
}, },
"funding": [ "funding": [
{ {
@@ -594,7 +594,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-01-01T08:32:19+00:00" "time": "2023-03-25T09:27:28+00:00"
}, },
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
@@ -1399,16 +1399,16 @@
}, },
{ {
"name": "symfony/string", "name": "symfony/string",
"version": "v5.4.19", "version": "v5.4.22",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/string.git", "url": "https://github.com/symfony/string.git",
"reference": "0a01071610fd861cc160dfb7e2682ceec66064cb" "reference": "8036a4c76c0dd29e60b6a7cafcacc50cf088ea62"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/0a01071610fd861cc160dfb7e2682ceec66064cb", "url": "https://api.github.com/repos/symfony/string/zipball/8036a4c76c0dd29e60b6a7cafcacc50cf088ea62",
"reference": "0a01071610fd861cc160dfb7e2682ceec66064cb", "reference": "8036a4c76c0dd29e60b6a7cafcacc50cf088ea62",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1465,7 +1465,7 @@
"utf8" "utf8"
], ],
"support": { "support": {
"source": "https://github.com/symfony/string/tree/v5.4.19" "source": "https://github.com/symfony/string/tree/v5.4.22"
}, },
"funding": [ "funding": [
{ {
@@ -1481,7 +1481,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-01-01T08:32:19+00:00" "time": "2023-03-14T06:11:53+00:00"
}, },
{ {
"name": "twig/twig", "name": "twig/twig",
@@ -1718,16 +1718,16 @@
}, },
{ {
"name": "voku/portable-utf8", "name": "voku/portable-utf8",
"version": "6.0.12", "version": "6.0.13",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/voku/portable-utf8.git", "url": "https://github.com/voku/portable-utf8.git",
"reference": "db0583727bb17666bbd2ba238c85babb973fd165" "reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/voku/portable-utf8/zipball/db0583727bb17666bbd2ba238c85babb973fd165", "url": "https://api.github.com/repos/voku/portable-utf8/zipball/b8ce36bf26593e5c2e81b1850ef0ffb299d2043f",
"reference": "db0583727bb17666bbd2ba238c85babb973fd165", "reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1793,7 +1793,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/voku/portable-utf8/issues", "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": [ "funding": [
{ {
@@ -1817,7 +1817,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-01-11T12:26:16+00:00" "time": "2023-03-08T08:35:38+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [
@@ -2030,16 +2030,16 @@
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
"version": "1.11.0", "version": "1.11.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/myclabs/DeepCopy.git", "url": "https://github.com/myclabs/DeepCopy.git",
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2077,7 +2077,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/myclabs/DeepCopy/issues", "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": [ "funding": [
{ {
@@ -2085,20 +2085,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-03-03T13:19:32+00:00" "time": "2023-03-08T13:26:56+00:00"
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"version": "v4.15.3", "version": "v4.15.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nikic/PHP-Parser.git", "url": "https://github.com/nikic/PHP-Parser.git",
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2139,22 +2139,22 @@
], ],
"support": { "support": {
"issues": "https://github.com/nikic/PHP-Parser/issues", "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", "name": "pdepend/pdepend",
"version": "2.12.1", "version": "2.13.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pdepend/pdepend.git", "url": "https://github.com/pdepend/pdepend.git",
"reference": "7a892d56ceafd804b4a2ecc85184640937ce9e84" "reference": "31be7cd4f305f3f7b52af99c1cb13fc938d1cfad"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pdepend/pdepend/zipball/7a892d56ceafd804b4a2ecc85184640937ce9e84", "url": "https://api.github.com/repos/pdepend/pdepend/zipball/31be7cd4f305f3f7b52af99c1cb13fc938d1cfad",
"reference": "7a892d56ceafd804b4a2ecc85184640937ce9e84", "reference": "31be7cd4f305f3f7b52af99c1cb13fc938d1cfad",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2190,7 +2190,7 @@
"description": "Official version of pdepend to be handled with Composer", "description": "Official version of pdepend to be handled with Composer",
"support": { "support": {
"issues": "https://github.com/pdepend/pdepend/issues", "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": [ "funding": [
{ {
@@ -2198,7 +2198,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-09-08T19:30:37+00:00" "time": "2023-02-28T20:56:15+00:00"
}, },
{ {
"name": "phar-io/manifest", "name": "phar-io/manifest",
@@ -2520,16 +2520,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.10.3", "version": "1.10.14",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "5419375b5891add97dc74be71e6c1c34baaddf64" "reference": "d232901b09e67538e5c86a724be841bea5768a7c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/5419375b5891add97dc74be71e6c1c34baaddf64", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d232901b09e67538e5c86a724be841bea5768a7c",
"reference": "5419375b5891add97dc74be71e6c1c34baaddf64", "reference": "d232901b09e67538e5c86a724be841bea5768a7c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2558,8 +2558,11 @@
"static analysis" "static analysis"
], ],
"support": { "support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues", "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": [ "funding": [
{ {
@@ -2575,20 +2578,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-02-25T14:47:13+00:00" "time": "2023-04-19T13:47:27+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "9.2.25", "version": "9.2.26",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954" "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e2b40518197a8c0d4b08bc34dfff1c99c508954", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954", "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2610,8 +2613,8 @@
"phpunit/phpunit": "^9.3" "phpunit/phpunit": "^9.3"
}, },
"suggest": { "suggest": {
"ext-pcov": "*", "ext-pcov": "PHP extension that provides line coverage",
"ext-xdebug": "*" "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@@ -2644,7 +2647,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "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": [ "funding": [
{ {
@@ -2652,7 +2655,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-02-25T05:32:00+00:00" "time": "2023-03-06T12:58:08+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@@ -2897,16 +2900,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "9.6.3", "version": "9.6.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "e7b1615e3e887d6c719121c6d4a44b0ab9645555" "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e7b1615e3e887d6c719121c6d4a44b0ab9645555", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2",
"reference": "e7b1615e3e887d6c719121c6d4a44b0ab9645555", "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2939,8 +2942,8 @@
"sebastian/version": "^3.0.2" "sebastian/version": "^3.0.2"
}, },
"suggest": { "suggest": {
"ext-soap": "*", "ext-soap": "To be able to generate mocks based on WSDL files",
"ext-xdebug": "*" "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
}, },
"bin": [ "bin": [
"phpunit" "phpunit"
@@ -2979,7 +2982,8 @@
], ],
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "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": [ "funding": [
{ {
@@ -2995,7 +2999,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-02-04T13:37:15+00:00" "time": "2023-04-14T08:58:40+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
@@ -4082,16 +4086,16 @@
}, },
{ {
"name": "symfony/config", "name": "symfony/config",
"version": "v5.4.19", "version": "v5.4.21",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/config.git", "url": "https://github.com/symfony/config.git",
"reference": "9bd60843443cda9638efdca7c41eb82ed0026179" "reference": "2a6b1111d038adfa15d52c0871e540f3b352d1e4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/9bd60843443cda9638efdca7c41eb82ed0026179", "url": "https://api.github.com/repos/symfony/config/zipball/2a6b1111d038adfa15d52c0871e540f3b352d1e4",
"reference": "9bd60843443cda9638efdca7c41eb82ed0026179", "reference": "2a6b1111d038adfa15d52c0871e540f3b352d1e4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -4141,7 +4145,7 @@
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/config/tree/v5.4.19" "source": "https://github.com/symfony/config/tree/v5.4.21"
}, },
"funding": [ "funding": [
{ {
@@ -4157,20 +4161,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-01-08T13:23:55+00:00" "time": "2023-02-14T08:03:56+00:00"
}, },
{ {
"name": "symfony/dependency-injection", "name": "symfony/dependency-injection",
"version": "v5.4.20", "version": "v5.4.22",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/dependency-injection.git", "url": "https://github.com/symfony/dependency-injection.git",
"reference": "8185ed0df129005a26715902f1a53bad0fe67102" "reference": "e1b7c1432efb4ad1dd89d62906187271e2601ed9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8185ed0df129005a26715902f1a53bad0fe67102", "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e1b7c1432efb4ad1dd89d62906187271e2601ed9",
"reference": "8185ed0df129005a26715902f1a53bad0fe67102", "reference": "e1b7c1432efb4ad1dd89d62906187271e2601ed9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -4230,7 +4234,7 @@
"description": "Allows you to standardize and centralize the way objects are constructed in your application", "description": "Allows you to standardize and centralize the way objects are constructed in your application",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/dependency-injection/tree/v5.4.20" "source": "https://github.com/symfony/dependency-injection/tree/v5.4.22"
}, },
"funding": [ "funding": [
{ {
@@ -4246,20 +4250,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-01-27T11:08:11+00:00" "time": "2023-03-10T10:02:45+00:00"
}, },
{ {
"name": "symfony/filesystem", "name": "symfony/filesystem",
"version": "v5.4.19", "version": "v5.4.21",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/filesystem.git", "url": "https://github.com/symfony/filesystem.git",
"reference": "648bfaca6a494f3e22378123bcee2894045dc9d8" "reference": "e75960b1bbfd2b8c9e483e0d74811d555ca3de9f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/648bfaca6a494f3e22378123bcee2894045dc9d8", "url": "https://api.github.com/repos/symfony/filesystem/zipball/e75960b1bbfd2b8c9e483e0d74811d555ca3de9f",
"reference": "648bfaca6a494f3e22378123bcee2894045dc9d8", "reference": "e75960b1bbfd2b8c9e483e0d74811d555ca3de9f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -4294,7 +4298,7 @@
"description": "Provides basic utilities for the filesystem", "description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/filesystem/tree/v5.4.19" "source": "https://github.com/symfony/filesystem/tree/v5.4.21"
}, },
"funding": [ "funding": [
{ {
@@ -4310,7 +4314,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-01-14T19:14:44+00:00" "time": "2023-02-14T08:03:56+00:00"
}, },
{ {
"name": "symfony/polyfill-php81", "name": "symfony/polyfill-php81",
@@ -4462,7 +4466,8 @@
"ext-json": "*", "ext-json": "*",
"ext-openssl": "*", "ext-openssl": "*",
"ext-fileinfo": "*", "ext-fileinfo": "*",
"ext-gmp": "*" "ext-gmp": "*",
"ext-gd": "*"
}, },
"platform-dev": { "platform-dev": {
"ext-pcntl": "*" "ext-pcntl": "*"

View File

@@ -119,7 +119,7 @@ if ($page == 'overview' || $page == 'accounts') {
if (Settings::Get('customer.ftpatdomain') == '1') { if (Settings::Get('customer.ftpatdomain') == '1') {
$domainlist = []; $domainlist = [];
$result_domains_stmt = Database::prepare("SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` $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, [ Database::pexecute($result_domains_stmt, [
"customerid" => $userinfo['customerid'] "customerid" => $userinfo['customerid']
]); ]);
@@ -127,7 +127,6 @@ if ($page == 'overview' || $page == 'accounts') {
while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {
$domainlist[$row_domain['domain']] = $idna_convert->decode($row_domain['domain']); $domainlist[$row_domain['domain']] = $idna_convert->decode($row_domain['domain']);
} }
sort($domainlist);
} }
if (Settings::Get('system.allow_customer_shell') == '1') { if (Settings::Get('system.allow_customer_shell') == '1') {

View File

@@ -53,9 +53,15 @@ if ($action == '2fa_entercode') {
Response::redirectTo('index.php'); Response::redirectTo('index.php');
exit(); exit();
} }
$smessage = isset($_GET['showmessage']) ? (int)$_GET['showmessage'] : 0;
$message = "";
if ($smessage > 0) {
$message = lng('error.2fa_wrongcode');
}
// show template to enter code // show template to enter code
UI::view('login/enter2fa.html.twig', [ UI::view('login/enter2fa.html.twig', [
'pagetitle' => lng('login.2fa') 'pagetitle' => lng('login.2fa'),
'message' => $message
]); ]);
} elseif ($action == '2fa_verify') { } elseif ($action == '2fa_verify') {
// verify code from 2fa code-enter form // verify code from 2fa code-enter form
@@ -68,25 +74,25 @@ if ($action == '2fa_entercode') {
// verify entered code // verify entered code
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname')); $tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
$result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3)); $result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3));
// 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 // get user-data
$table = $_SESSION['uidtable_2fa']; $table = $_SESSION['uidtable_2fa'];
$field = $_SESSION['uidfield_2fa']; $field = $_SESSION['uidfield_2fa'];
$uid = $_SESSION['uid_2fa']; $uid = $_SESSION['uid_2fa'];
$isadmin = $_SESSION['unfo_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) {
$sel_param = [ $sel_param = [
'uid' => $uid 'uid' => $uid
]; ];
if ($_SESSION['secret_2fa'] == 'email') { if ($_SESSION['secret_2fa'] == 'email') {
// verify code by selecting user by id and the temp. stored code, // 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 // 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; $sel_param['code'] = $code;
} else { } else {
// Authenticator-verification has already happened at this point, so just get the user-data // 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); $userinfo = Database::pexecute_first($sel_stmt, $sel_param);
// whoops, no (valid) user? Start again // whoops, no (valid) user? Start again
@@ -108,15 +114,50 @@ if ($action == '2fa_entercode') {
// when using email-2fa, remove the one-time-code // when using email-2fa, remove the one-time-code
if ($userinfo['type_2fa'] == '1') { 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, [ $userinfo = Database::pexecute_first($del_stmt, [
'uid' => $uid 'uid' => $uid
]); ]);
} }
exit(); 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', [ Response::redirectTo('index.php', [
'showmessage' => '2' 'showmessage' => '3'
]);
exit();
}
unset($fail_user);
// back to form
Response::redirectTo('index.php', [
'action' => '2fa_entercode',
'showmessage' => '1'
]); ]);
exit(); exit();
} elseif ($action == 'login') { } elseif ($action == 'login') {

View File

@@ -697,8 +697,10 @@ opcache.validate_timestamps'),
('system', 'distribution', ''), ('system', 'distribution', ''),
('system', 'update_channel', 'stable'), ('system', 'update_channel', 'stable'),
('system', 'updatecheck_data', ''), ('system', 'updatecheck_data', ''),
('system', 'update_notify_last', '2.0.13'), ('system', 'update_notify_last', '2.0.20'),
('system', 'traffictool', 'goaccess'), ('system', 'traffictool', 'goaccess'),
('system', 'req_limit_per_interval', 60),
('system', 'req_limit_interval', 60),
('api', 'enabled', '0'), ('api', 'enabled', '0'),
('api', 'customer_default', '1'), ('api', 'customer_default', '1'),
('2fa', 'enabled', '1'), ('2fa', 'enabled', '1'),
@@ -742,8 +744,8 @@ opcache.validate_timestamps'),
('panel', 'logo_overridetheme', '0'), ('panel', 'logo_overridetheme', '0'),
('panel', 'logo_overridecustom', '0'), ('panel', 'logo_overridecustom', '0'),
('panel', 'settings_mode', '0'), ('panel', 'settings_mode', '0'),
('panel', 'version', '2.0.13'), ('panel', 'version', '2.0.20'),
('panel', 'db_version', '202302030'); ('panel', 'db_version', '202304260');
DROP TABLE IF EXISTS `panel_tasks`; DROP TABLE IF EXISTS `panel_tasks`;
@@ -983,7 +985,9 @@ CREATE TABLE IF NOT EXISTS `domain_ssl_settings` (
`ssl_cert_chainfile` mediumtext, `ssl_cert_chainfile` mediumtext,
`ssl_csr_file` mediumtext, `ssl_csr_file` mediumtext,
`ssl_fullchain_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`), PRIMARY KEY (`id`),
UNIQUE KEY (`domainid`) UNIQUE KEY (`domainid`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci; ) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;

View File

@@ -23,6 +23,7 @@
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2 * @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/ */
use Froxlor\Http\RateLimiter;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\Install\Install; use Froxlor\Install\Install;
@@ -62,6 +63,7 @@ require dirname(__DIR__) . '/lib/tables.inc.php';
// init twig // init twig
UI::initTwig(true); UI::initTwig(true);
UI::sendHeaders(); UI::sendHeaders();
RateLimiter::run(true);
$installer = new Install(); $installer = new Install();
$installer->handle(); $installer->handle();

View File

@@ -23,11 +23,11 @@
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2 * @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/ */
use Froxlor\Froxlor;
use Froxlor\FileDir;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Settings; use Froxlor\FileDir;
use Froxlor\Froxlor;
use Froxlor\Install\Update; use Froxlor\Install\Update;
use Froxlor\Settings;
if (!defined('_CRON_UPDATE')) { if (!defined('_CRON_UPDATE')) {
if (!defined('AREA') || (defined('AREA') && AREA != 'admin') || !isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) { 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`;"); Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP COLUMN `domains_see_all`;");
Update::lastStepStatus(0); 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(" $dbservers_stmt = Database::query("
SELECT `customerid`, SELECT `customerid`,
GROUP_CONCAT(DISTINCT `dbserver` SEPARATOR ',') as allowed_mysqlserver 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)) { while ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {
if (isset($dbserver['allowed_mysqlserver']) && !empty($dbserver['allowed_mysqlserver'])) { if (isset($dbserver['allowed_mysqlserver']) && !empty($dbserver['allowed_mysqlserver'])) {
$allowed_mysqlserver = json_encode(explode(",", $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); Update::lastStepStatus(0);
@@ -140,7 +141,8 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
// none of the files existed // none of the files existed
Update::lastStepStatus(0); Update::lastStepStatus(0);
} else { } 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>');
} }
} }
@@ -224,7 +226,8 @@ EOF;
} else { } else {
$cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL; $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'; $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'); Froxlor::updateToDbVersion('202212060');
@@ -283,7 +286,8 @@ EOF;
} else { } else {
$cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL; $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'; $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'); 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; $system_letsencryptchallengepath_upd = isset($_POST['system_letsencryptchallengepath_upd']) ? $_POST['system_letsencryptchallengepath_upd'] : $acmesh_challenge_dir;
if ($acmesh_challenge_dir != $system_letsencryptchallengepath_upd) { if ($acmesh_challenge_dir != $system_letsencryptchallengepath_upd) {
Settings::Set('system.letsencryptchallengepath', $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 // create JSON string for --apply
$dist = Settings::Get('system.distribution'); $dist = Settings::Get('system.distribution');
$webserver = Settings::Get('system.webserver'); $webserver = Settings::Get('system.webserver');
@@ -405,3 +409,91 @@ if (Froxlor::isFroxlorVersion('2.0.12')) {
Update::showUpdateStep("Updating from 2.0.12 to 2.0.13", false); Update::showUpdateStep("Updating from 2.0.12 to 2.0.13", false);
Froxlor::updateToVersion('2.0.13'); 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&ccedil;ais',
'pt' => 'Portugu&ecirc;s',
'it' => 'Italiano',
'nl' => 'Nederlands',
'se' => 'Svenska',
'cz' => '&#268;esk&aacute; 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');
}

View File

@@ -26,6 +26,7 @@
namespace Froxlor\Api; namespace Froxlor\Api;
use Exception; use Exception;
use Froxlor\Http\RateLimiter;
use Froxlor\Settings; use Froxlor\Settings;
use voku\helper\AntiXSS; use voku\helper\AntiXSS;
@@ -52,6 +53,8 @@ class Api
if (Settings::Get('api.enabled') != 1) { if (Settings::Get('api.enabled') != 1) {
throw new Exception('API is not enabled. Please contact the administrator if you think this is wrong.', 400); throw new Exception('API is not enabled. Please contact the administrator if you think this is wrong.', 400);
} }
RateLimiter::run();
} }
/** /**

View File

@@ -127,7 +127,9 @@ class Certificates extends ApiCommand implements ResourceEntity
} }
$do_verify = true; $do_verify = true;
$expirationdate = null; $validtodate = null;
$validtodate = null;
$issuer = "";
// no cert-file given -> forget everything // no cert-file given -> forget everything
if ($ssl_cert_file == '') { if ($ssl_cert_file == '') {
$ssl_key_file = ''; $ssl_key_file = '';
@@ -168,7 +170,10 @@ class Certificates extends ApiCommand implements ResourceEntity
} else { } else {
Response::standardError('sslcertificateinvalidcert', '', true); 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 // Add/Update database entry
@@ -183,7 +188,9 @@ class Certificates extends ApiCommand implements ResourceEntity
`ssl_key_file` = :ssl_key_file, `ssl_key_file` = :ssl_key_file,
`ssl_ca_file` = :ssl_ca_file, `ssl_ca_file` = :ssl_ca_file,
`ssl_cert_chainfile` = :ssl_cert_chainfile, `ssl_cert_chainfile` = :ssl_cert_chainfile,
`expirationdate` = :expirationdate `validfromdate` = :validfromdate,
`validtodate` = :validtodate,
`issuer` = :issuer
" . $qrywhere . " `domainid`= :domainid " . $qrywhere . " `domainid`= :domainid
"); ");
$params = [ $params = [
@@ -191,7 +198,9 @@ class Certificates extends ApiCommand implements ResourceEntity
"ssl_key_file" => $ssl_key_file, "ssl_key_file" => $ssl_key_file,
"ssl_ca_file" => $ssl_ca_file, "ssl_ca_file" => $ssl_ca_file,
"ssl_cert_chainfile" => $ssl_cert_chainfile, "ssl_cert_chainfile" => $ssl_cert_chainfile,
"expirationdate" => $expirationdate, "validfromdate" => $validfromdate,
"validtodate" => $validtodate,
"issuer" => $issuer,
"domainid" => $domainid "domainid" => $domainid
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
@@ -299,16 +308,12 @@ class Certificates extends ApiCommand implements ResourceEntity
} }
// Set data from certificate // Set data from certificate
$cert['isvalid'] = false;
$cert['san'] = null;
$cert_data = openssl_x509_parse($cert['ssl_cert_file']); $cert_data = openssl_x509_parse($cert['ssl_cert_file']);
if ($cert_data) { 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['isvalid'] = (bool)$cert_data['validTo_time_t'] > time();
$cert['issuer'] = $cert_data['issuer']['O'] ?? null;
}
// Set subject alt names from certificate // Set subject alt names from certificate
$cert['san'] = null;
if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) { if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) {
$SANs = explode(",", $cert_data['extensions']['subjectAltName']); $SANs = explode(",", $cert_data['extensions']['subjectAltName']);
$SANs = array_map('trim', $SANs); $SANs = array_map('trim', $SANs);
@@ -319,7 +324,7 @@ class Certificates extends ApiCommand implements ResourceEntity
} }
} }
} }
}
$result[] = $cert; $result[] = $cert;
} }
return $this->response([ return $this->response([

View File

@@ -225,6 +225,8 @@ class Domains extends ApiCommand implements ResourceEntity
* optional, whether php is enabled for this domain, default 0 (false) * optional, whether php is enabled for this domain, default 0 (false)
* @param bool $openbasedir * @param bool $openbasedir
* optional, whether to activate openbasedir restriction for this domain, default 0 (false) * 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 * @param int $phpsettingid
* optional, specify php-configuration that is being used by id, default 1 (system-default) * optional, specify php-configuration that is being used by id, default 1 (system-default)
* @param int $mod_fcgid_starter * @param int $mod_fcgid_starter
@@ -312,6 +314,7 @@ class Domains extends ApiCommand implements ResourceEntity
$documentroot = $this->getParam('documentroot', true, ''); $documentroot = $this->getParam('documentroot', true, '');
$phpenabled = $this->getBoolParam('phpenabled', true, 0); $phpenabled = $this->getBoolParam('phpenabled', true, 0);
$openbasedir = $this->getBoolParam('openbasedir', true, 0); $openbasedir = $this->getBoolParam('openbasedir', true, 0);
$openbasedir_path = $this->getParam('openbasedir_path', true, 0);
$phpsettingid = $this->getParam('phpsettingid', true, 1); $phpsettingid = $this->getParam('phpsettingid', true, 1);
$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, -1); $mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, -1);
$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', 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'; $mod_fcgid_maxrequests = '-1';
} }
if ($openbasedir_path > 2 && $openbasedir_path < 0) {
$openbasedir_path = 0;
}
// check non-ssl IP // check non-ssl IP
$ipandports = $this->validateIpAddresses($p_ipandports); $ipandports = $this->validateIpAddresses($p_ipandports);
// check ssl IP // check ssl IP
@@ -701,6 +708,7 @@ class Domains extends ApiCommand implements ResourceEntity
'caneditdomain' => $caneditdomain, 'caneditdomain' => $caneditdomain,
'phpenabled' => $phpenabled, 'phpenabled' => $phpenabled,
'openbasedir' => $openbasedir, 'openbasedir' => $openbasedir,
'openbasedir_path' => $openbasedir_path,
'speciallogfile' => $speciallogfile, 'speciallogfile' => $speciallogfile,
'specialsettings' => $specialsettings, 'specialsettings' => $specialsettings,
'ssl_specialsettings' => $ssl_specialsettings, 'ssl_specialsettings' => $ssl_specialsettings,
@@ -754,6 +762,7 @@ class Domains extends ApiCommand implements ResourceEntity
`caneditdomain` = :caneditdomain, `caneditdomain` = :caneditdomain,
`phpenabled` = :phpenabled, `phpenabled` = :phpenabled,
`openbasedir` = :openbasedir, `openbasedir` = :openbasedir,
`openbasedir_path` = :openbasedir_path,
`speciallogfile` = :speciallogfile, `speciallogfile` = :speciallogfile,
`specialsettings` = :specialsettings, `specialsettings` = :specialsettings,
`ssl_specialsettings` = :ssl_specialsettings, `ssl_specialsettings` = :ssl_specialsettings,
@@ -1101,6 +1110,8 @@ class Domains extends ApiCommand implements ResourceEntity
* from setting system.apply_phpconfigs_default * from setting system.apply_phpconfigs_default
* @param bool $openbasedir * @param bool $openbasedir
* optional, whether to activate openbasedir restriction for this domain, default 0 (false) * 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 * @param int $phpsettingid
* optional, specify php-configuration that is being used by id, default 1 (system-default) * optional, specify php-configuration that is being used by id, default 1 (system-default)
* @param int $mod_fcgid_starter * @param int $mod_fcgid_starter
@@ -1198,6 +1209,7 @@ class Domains extends ApiCommand implements ResourceEntity
$phpenabled = $this->getBoolParam('phpenabled', true, $result['phpenabled']); $phpenabled = $this->getBoolParam('phpenabled', true, $result['phpenabled']);
$phpfs = $this->getBoolParam('phpsettingsforsubdomains', true, Settings::Get('system.apply_phpconfigs_default')); $phpfs = $this->getBoolParam('phpsettingsforsubdomains', true, Settings::Get('system.apply_phpconfigs_default'));
$openbasedir = $this->getBoolParam('openbasedir', true, $result['openbasedir']); $openbasedir = $this->getBoolParam('openbasedir', true, $result['openbasedir']);
$openbasedir_path = $this->getParam('openbasedir_path', true, $result['openbasedir_path']);
$phpsettingid = $this->getParam('phpsettingid', true, $result['phpsettingid']); $phpsettingid = $this->getParam('phpsettingid', true, $result['phpsettingid']);
$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, $result['mod_fcgid_starter']); $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']); $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']; $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 // check non-ssl IP
$ipandports = $this->validateIpAddresses($p_ipandports, false, $result['id']); $ipandports = $this->validateIpAddresses($p_ipandports, false, $result['id']);
// check ssl IP // check ssl IP
@@ -1634,7 +1651,31 @@ class Domains extends ApiCommand implements ResourceEntity
$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';
$iswildcarddomain = ($serveraliasoption == '0') ? '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); Cronjob::inserttask(TaskId::REBUILD_VHOST);
} }
@@ -1782,7 +1823,8 @@ class Domains extends ApiCommand implements ResourceEntity
$update_data['wwwserveralias'] = $wwwserveralias; $update_data['wwwserveralias'] = $wwwserveralias;
$update_data['iswildcarddomain'] = $iswildcarddomain; $update_data['iswildcarddomain'] = $iswildcarddomain;
$update_data['phpenabled'] = $phpenabled; $update_data['phpenabled'] = $phpenabled;
$update_data['openbasedir'] = $openbasedir; $update_data['openbasedir'] = $openbasedir;;
$update_data['openbasedir_path'] = $openbasedir_path;
$update_data['speciallogfile'] = $speciallogfile; $update_data['speciallogfile'] = $speciallogfile;
$update_data['phpsettingid'] = $phpsettingid; $update_data['phpsettingid'] = $phpsettingid;
$update_data['mod_fcgid_starter'] = $mod_fcgid_starter; $update_data['mod_fcgid_starter'] = $mod_fcgid_starter;
@@ -1830,6 +1872,7 @@ class Domains extends ApiCommand implements ResourceEntity
`iswildcarddomain` = :iswildcarddomain, `iswildcarddomain` = :iswildcarddomain,
`phpenabled` = :phpenabled, `phpenabled` = :phpenabled,
`openbasedir` = :openbasedir, `openbasedir` = :openbasedir,
`openbasedir_path` = :openbasedir_path,
`speciallogfile` = :speciallogfile, `speciallogfile` = :speciallogfile,
`phpsettingid` = :phpsettingid, `phpsettingid` = :phpsettingid,
`mod_fcgid_starter` = :mod_fcgid_starter, `mod_fcgid_starter` = :mod_fcgid_starter,
@@ -1865,6 +1908,7 @@ class Domains extends ApiCommand implements ResourceEntity
$_update_data['adminid'] = $adminid; $_update_data['adminid'] = $adminid;
$_update_data['phpenabled'] = $phpenabled; $_update_data['phpenabled'] = $phpenabled;
$_update_data['openbasedir'] = $openbasedir; $_update_data['openbasedir'] = $openbasedir;
$_update_data['openbasedir_path'] = $openbasedir_path;
$_update_data['mod_fcgid_starter'] = $mod_fcgid_starter; $_update_data['mod_fcgid_starter'] = $mod_fcgid_starter;
$_update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests; $_update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests;
$_update_data['notryfiles'] = $notryfiles; $_update_data['notryfiles'] = $notryfiles;
@@ -1898,6 +1942,7 @@ class Domains extends ApiCommand implements ResourceEntity
`adminid` = :adminid, `adminid` = :adminid,
`phpenabled` = :phpenabled, `phpenabled` = :phpenabled,
`openbasedir` = :openbasedir, `openbasedir` = :openbasedir,
`openbasedir_path` = :openbasedir_path,
`mod_fcgid_starter` = :mod_fcgid_starter, `mod_fcgid_starter` = :mod_fcgid_starter,
`mod_fcgid_maxrequests` = :mod_fcgid_maxrequests, `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests,
`notryfiles` = :notryfiles, `notryfiles` = :notryfiles,
@@ -1914,6 +1959,18 @@ class Domains extends ApiCommand implements ResourceEntity
"); ");
Database::pexecute($_update_stmt, $_update_data, true, true); 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 // Cleanup domain <-> ip mapping
$del_stmt = Database::prepare(" $del_stmt = Database::prepare("
DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id 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 // Cleanup domain <-> ip mapping for subdomains
$domainidsresult_stmt = Database::prepare(" $domainidsresult_stmt = Database::prepare("
SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :id SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :id

View File

@@ -63,7 +63,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
* @param string $alternative_email * @param string $alternative_email
* optional email address to send account information to, default is the account that is being created * optional email address to send account information to, default is the account that is being created
* @param int $email_quota * @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 * @param bool $sendinfomail
* optional, sends the welcome message to the new account (needed for creation, without the user won't * 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) * 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, ''); $emailaddr = $this->getParam('emailaddr', $ea_optional, '');
$email_password = $this->getParam('email_password'); $email_password = $this->getParam('email_password');
$alternative_email = $this->getParam('alternative_email', true, ''); $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); $sendinfomail = $this->getBoolParam('sendinfomail', true, 1);
// validation // validation
@@ -99,6 +99,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
Response::standardError('notallowedtouseaccounts', '', true); Response::standardError('notallowedtouseaccounts', '', true);
} }
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
// get email address // get email address
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,
@@ -357,6 +362,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
$ea_optional = $id > 0; $ea_optional = $id > 0;
$emailaddr = $this->getParam('emailaddr', $ea_optional, ''); $emailaddr = $this->getParam('emailaddr', $ea_optional, '');
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
// validation // validation
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,

View File

@@ -77,6 +77,11 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
$idna_convert = new IdnaWrapper(); $idna_convert = new IdnaWrapper();
$destination = $idna_convert->encode($destination); $destination = $idna_convert->encode($destination);
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,
'emailaddr' => $emailaddr 'emailaddr' => $emailaddr

View File

@@ -195,8 +195,8 @@ class Emails extends ApiCommand implements ResourceEntity
FROM `" . TABLE_MAIL_VIRTUAL . "` v FROM `" . TABLE_MAIL_VIRTUAL . "` v
LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id` LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id`
WHERE v.`customerid` IN (" . implode(", ", $customer_ids) . ") 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); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { 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_NOTICE, "[API] get email address '" . $result['email_full'] . "'");

View File

@@ -516,7 +516,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
`allowed_mysqlserver` = :am WHERE `customerid` = :cid `allowed_mysqlserver` = :am WHERE `customerid` = :cid
"); ");
while ($customer = $sel_stmt->fetch(PDO::FETCH_ASSOC)) { 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)) { if (!in_array($dbserver, $allowed_mysqls)) {
$allowed_mysqls[] = $dbserver; $allowed_mysqls[] = $dbserver;
$allowed_mysqls = json_encode($allowed_mysqls); $allowed_mysqls = json_encode($allowed_mysqls);

View File

@@ -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 * optional, overwrites path value with an URL to generate a redirect, alternatively use the path
* parameter also for URLs * parameter also for URLs
* @param int $openbasedir_path * @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 * @param int $phpsettingid
* optional, php-settings-id, if empty the $domain value is used * optional, php-settings-id, if empty the $domain value is used
* @param int $redirectcode * @param int $redirectcode
@@ -104,7 +104,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
$aliasdomain = $this->getParam('alias', true, 0); $aliasdomain = $this->getParam('alias', true, 0);
$path = $this->getParam('path', true, ''); $path = $this->getParam('path', true, '');
$url = $this->getParam('url', 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); $phpsettingid = $this->getParam('phpsettingid', true, 0);
$redirectcode = $this->getParam('redirectcode', true, Settings::Get('customredirect.default')); $redirectcode = $this->getParam('redirectcode', true, Settings::Get('customredirect.default'));
$isemaildomain = $this->getParam('isemaildomain', true, 0); $isemaildomain = $this->getParam('isemaildomain', true, 0);

View File

@@ -122,7 +122,7 @@ class CliCommand extends Command
include_once Froxlor::getInstallDir() . '/lib/tables.inc.php'; include_once Froxlor::getInstallDir() . '/lib/tables.inc.php';
define('_CRON_UPDATE', 1); define('_CRON_UPDATE', 1);
ob_start([ ob_start([
'this', $this,
'cleanUpdateOutput' 'cleanUpdateOutput'
]); ]);
include_once Froxlor::getInstallDir() . '/install/updatesql.php'; include_once Froxlor::getInstallDir() . '/install/updatesql.php';

View File

@@ -80,7 +80,7 @@ final class InstallCommand extends Command
$_SERVER['SERVER_NAME'] = $host[0] ?? ''; $_SERVER['SERVER_NAME'] = $host[0] ?? '';
$ips = []; $ips = [];
exec('hostname -I', $ips); exec('hostname -I', $ips);
$ips = explode(" ", $ips[0]); $ips = explode(" ", $ips[0] ?? "");
// ipv4 address? // ipv4 address?
$_SERVER['SERVER_ADDR'] = filter_var($ips[0] ?? "", FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? ($ips[0] ?? '') : ''; $_SERVER['SERVER_ADDR'] = filter_var($ips[0] ?? "", FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? ($ips[0] ?? '') : '';
if (empty($_SERVER['SERVER_ADDR'])) { if (empty($_SERVER['SERVER_ADDR'])) {

View File

@@ -62,6 +62,11 @@ final class MasterCron extends CliCommand
$result = self::SUCCESS; $result = self::SUCCESS;
$result = $this->validateRequirements($input, $output); $result = $this->validateRequirements($input, $output);
if ($result != self::SUCCESS) {
// requirements failed, exit
return $result;
}
$jobs = $input->getArgument('job'); $jobs = $input->getArgument('job');
// handle force option // handle force option
@@ -111,8 +116,8 @@ final class MasterCron extends CliCommand
]); ]);
$this->cronLog->setCronDebugFlag(defined('CRON_DEBUG_FLAG')); $this->cronLog->setCronDebugFlag(defined('CRON_DEBUG_FLAG'));
// check whether there are actual tasks to perform by 'tasks'-cron so // check whether there are actual tasks to perform by 'tasks'-cron, so
// we dont regenerate files unnecessarily // we don't regenerate files unnecessarily
$tasks_cnt_stmt = Database::query("SELECT COUNT(*) as jobcnt FROM `panel_tasks`"); $tasks_cnt_stmt = Database::query("SELECT COUNT(*) as jobcnt FROM `panel_tasks`");
$tasks_cnt = $tasks_cnt_stmt->fetch(PDO::FETCH_ASSOC); $tasks_cnt = $tasks_cnt_stmt->fetch(PDO::FETCH_ASSOC);

View File

@@ -76,7 +76,7 @@ final class ValidateAcmeWebroot extends CliCommand
'domain' => Settings::Get('system.hostname') '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_dir = dirname(Settings::Get('system.acmeshpath'));
$acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/"); $acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/");
$recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), "/"); $recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), "/");

View File

@@ -126,6 +126,9 @@ class ApacheFcgi extends Apache
// mod_proxy stuff for apache-2.4 // mod_proxy stuff for apache-2.4
if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') { 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']; $filesmatch = $phpconfig['fpm_settings']['limit_extensions'];
$extensions = explode(" ", $filesmatch); $extensions = explode(" ", $filesmatch);
$filesmatch = ""; $filesmatch = "";
@@ -141,23 +144,19 @@ class ApacheFcgi extends Apache
$php_options_text .= ' </FilesMatch>' . "\n"; $php_options_text .= ' </FilesMatch>' . "\n";
$mypath_dir = new Directory($domain['documentroot']); $mypath_dir = new Directory($domain['documentroot']);
// only create the "require all granted" directive if there is no active directory-protection
// only create the require all granted if there is not active directory-protection
// for this path, as this would be the first require and therefore grant all access // for this path, as this would be the first require and therefore grant all access
if ($mypath_dir->isUserProtected() == false) { if ($mypath_dir->isUserProtected() == false) {
$php_options_text .= ' <Directory "' . FileDir::makeCorrectDir($domain['documentroot']) . '">' . "\n";
if ($phpconfig['pass_authorizationheader'] == '1') { if ($phpconfig['pass_authorizationheader'] == '1') {
$php_options_text .= ' CGIPassAuth On' . "\n"; $php_options_text .= ' CGIPassAuth On' . "\n";
} }
$php_options_text .= ' Require all granted' . "\n"; $php_options_text .= ' Require all granted' . "\n";
$php_options_text .= ' AllowOverride All' . "\n"; $php_options_text .= ' AllowOverride All' . "\n";
$php_options_text .= ' </Directory>' . "\n";
} elseif ($phpconfig['pass_authorizationheader'] == '1') { } elseif ($phpconfig['pass_authorizationheader'] == '1') {
// allow Pass of Authorization header // allow Pass of Authorization header
$php_options_text .= ' <Directory "' . FileDir::makeCorrectDir($domain['documentroot']) . '">' . "\n";
$php_options_text .= ' CGIPassAuth On' . "\n"; $php_options_text .= ' CGIPassAuth On' . "\n";
$php_options_text .= ' </Directory>' . "\n";
} }
$php_options_text .= ' </Directory>' . "\n";
} else { } else {
$addheader = ""; $addheader = "";
if ($phpconfig['pass_authorizationheader'] == '1') { if ($phpconfig['pass_authorizationheader'] == '1') {
@@ -196,6 +195,9 @@ class ApacheFcgi extends Apache
} }
} else { } else {
$php_options_text .= ' FcgidIdleTimeout ' . Settings::Get('system.mod_fcgid_idle_timeout') . "\n"; $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) { if ((int)Settings::Get('system.mod_fcgid_wrapper') == 0) {
$php_options_text .= ' SuexecUserGroup "' . $domain['loginname'] . '" "' . $domain['loginname'] . '"' . "\n"; $php_options_text .= ' SuexecUserGroup "' . $domain['loginname'] . '" "' . $domain['loginname'] . '"' . "\n";
$php_options_text .= ' ScriptAlias /php/ ' . $php->getInterface()->getConfigDir() . "\n"; $php_options_text .= ' ScriptAlias /php/ ' . $php->getInterface()->getConfigDir() . "\n";

View File

@@ -179,7 +179,7 @@ class HttpConfigBase
$froxlor_ssl_settings_stmt = Database::prepare(" $froxlor_ssl_settings_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
WHERE `domainid` = '0' AND 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); $froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
if ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) { if ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) {

View File

@@ -114,7 +114,9 @@ class AcmeSh extends FroxlorCron
`ssl_cert_chainfile` = :chain, `ssl_cert_chainfile` = :chain,
`ssl_csr_file` = :csr, `ssl_csr_file` = :csr,
`ssl_fullchain_file` = :fullchain, `ssl_fullchain_file` = :fullchain,
`expirationdate` = :expirationdate `validfromdate` = :validfromdate,
`validtodate` = :validtodate,
`issuer` = :issuer
"); ");
// prepare domain update sql // prepare domain update sql
@@ -136,7 +138,9 @@ class AcmeSh extends FroxlorCron
'lepublickey' => Settings::Get('system.lepublickey'), 'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'), 'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'), 'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
'expirationdate' => null, 'validfromdate' => null,
'validtodate' => null,
'issuer' => "",
'ssl_cert_file' => null, 'ssl_cert_file' => null,
'ssl_key_file' => null, 'ssl_key_file' => null,
'ssl_ca_file' => null, 'ssl_ca_file' => null,
@@ -171,7 +175,9 @@ class AcmeSh extends FroxlorCron
'lepublickey' => Settings::Get('system.lepublickey'), 'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'), 'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'), '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_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_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, 'ssl_ca_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_ca_file'] : null,
@@ -187,7 +193,7 @@ class AcmeSh extends FroxlorCron
'loginname' => $domain['loginname'], 'loginname' => $domain['loginname'],
'adminsession' => 0 '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, []); self::certToDb($domain, $cronlog, []);
$changedetected = 1; $changedetected = 1;
} }
@@ -221,7 +227,9 @@ class AcmeSh extends FroxlorCron
"); ");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt); $froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
// also check for possible existing certificate // 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 true;
} }
} }
@@ -279,7 +287,9 @@ EOC;
SELECT SELECT
domssl.`id`, domssl.`id`,
domssl.`domainid`, domssl.`domainid`,
domssl.`expirationdate`, domssl.`validfromdate`,
domssl.`validtodate`,
domssl.`issuer`,
domssl.`ssl_cert_file`, domssl.`ssl_cert_file`,
domssl.`ssl_key_file`, domssl.`ssl_key_file`,
domssl.`ssl_ca_file`, domssl.`ssl_ca_file`,
@@ -306,7 +316,7 @@ EOC;
AND dom.`letsencrypt` = 1 AND dom.`letsencrypt` = 1
AND dom.`aliasdomain` IS NULL AND dom.`aliasdomain` IS NULL
AND dom.`iswildcarddomain` = 0 AND dom.`iswildcarddomain` = 0
AND domssl.`expirationdate` IS NULL AND domssl.`validtodate` IS NULL
"); ");
$customer_ssl = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC); $customer_ssl = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC);
if ($customer_ssl) { if ($customer_ssl) {
@@ -330,7 +340,7 @@ EOC;
"); ");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt); $froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
// also check for possible existing certificate // 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; return $froxlor_ssl;
} }
} }
@@ -346,7 +356,9 @@ EOC;
SELECT SELECT
domssl.`id`, domssl.`id`,
domssl.`domainid`, domssl.`domainid`,
domssl.`expirationdate`, domssl.`validfromdate`,
domssl.`validtodate`,
domssl.`issuer`,
domssl.`ssl_cert_file`, domssl.`ssl_cert_file`,
domssl.`ssl_key_file`, domssl.`ssl_key_file`,
dom.`domain`, dom.`domain`,
@@ -370,7 +382,7 @@ EOC;
if ($renew_certs) { if ($renew_certs) {
if ($check) { if ($check) {
foreach ($renew_certs as $cert) { foreach ($renew_certs as $cert) {
if (self::checkFsFilesAreNewer($cert['domain'], $cert['expirationdate'])) { if (self::checkFsFilesAreNewer($cert['domain'], $cert['validtodate'])) {
return true; return true;
} }
} }
@@ -453,7 +465,7 @@ EOC;
// Only issue let's encrypt certificate if no broken ssl_redirect is enabled // Only issue let's encrypt certificate if no broken ssl_redirect is enabled
if ($certrow['ssl_redirect'] != 2) { if ($certrow['ssl_redirect'] != 2) {
$do_force = false; $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) // domain changed (SAN or similar)
$do_force = true; $do_force = true;
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']);
@@ -594,7 +606,9 @@ EOC;
'chain' => $return['chain'], 'chain' => $return['chain'],
'csr' => $return['csr'], 'csr' => $return['csr'],
'fullchain' => $return['fullchain'], '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) { if ($certrow['ssl_redirect'] == 3) {

View File

@@ -1040,6 +1040,11 @@ class Nginx extends HttpConfigBase
$path_options .= "\t\t" . 'auth_basic_user_file ' . FileDir::makeCorrectFile($single['usrf']) . ';' . "\n"; $path_options .= "\t\t" . 'auth_basic_user_file ' . FileDir::makeCorrectFile($single['usrf']) . ';' . "\n";
if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {
$path_options .= "\t\t" . 'index index.php index.html index.htm;' . "\n"; $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 { } else {
$path_options .= "\t\t" . 'index index.html index.htm;' . "\n"; $path_options .= "\t\t" . 'index index.html index.htm;' . "\n";
} }
@@ -1166,7 +1171,6 @@ class Nginx extends HttpConfigBase
$phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n"; $phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n";
$phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n"; $phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n";
$phpopts .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\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_pass " . Settings::Get('system.nginx_php_backend') . ";\n";
$phpopts .= "\t\tfastcgi_index index.php;\n"; $phpopts .= "\t\tfastcgi_index index.php;\n";
if ($domain['ssl'] == '1' && $ssl_vhost) { if ($domain['ssl'] == '1' && $ssl_vhost) {

View File

@@ -342,8 +342,17 @@ pm.max_children = 1
public function getSocketFile($createifnotexists = true) public function getSocketFile($createifnotexists = true)
{ {
$socketdir = FileDir::makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir')); $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 // 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')); $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) { if (!is_dir($socketdir) && $createifnotexists) {
FileDir::safe_exec('mkdir -p ' . escapeshellarg($socketdir)); FileDir::safe_exec('mkdir -p ' . escapeshellarg($socketdir));

View File

@@ -279,8 +279,7 @@ class Dns
} }
} }
} }
$zonerecords[] = new DnsEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'], $zonerecords[] = new DnsEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'] ?? 0, $entry['ttl']);
$entry['ttl']);
} }
// add missing required entries // add missing required entries

View File

@@ -37,18 +37,18 @@ class PowerDNS
/** /**
* remove all records and entries of a given domain * 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"); $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_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_meta_stmt = self::getDB()->prepare("DELETE FROM `domainmetadata` WHERE `domain_id` = :did");
$del_dom_stmt = self::getDB()->prepare("DELETE FROM `domains` WHERE `id` = :did"); $del_dom_stmt = self::getDB()->prepare("DELETE FROM `domains` WHERE `id` = :did");
$pdns_domains_stmt->execute([ $pdns_domains_stmt->execute([
'domain' => $domain['domain'] 'domain' => $domain
]); ]);
$pdns_domain = $pdns_domains_stmt->fetch(PDO::FETCH_ASSOC); $pdns_domain = $pdns_domains_stmt->fetch(PDO::FETCH_ASSOC);

View File

@@ -350,7 +350,7 @@ class Domain
$upd_stmt = Database::prepare("UPDATE $upd_stmt = Database::prepare("UPDATE
`" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
SET SET
`expirationdate` = null `validtodate` = null
WHERE WHERE
domainid = :domainid domainid = :domainid
"); ");

View File

@@ -447,7 +447,8 @@ class FileDir
$field = [ $field = [
'type' => 'select', 'type' => 'select',
'select_var' => $_field, 'select_var' => $_field,
'selected' => $value 'selected' => $value,
'value' => $value
]; ];
} else { } else {
$field = [ $field = [

View File

@@ -31,10 +31,10 @@ final class Froxlor
{ {
// Main version variable // Main version variable
const VERSION = '2.0.13'; const VERSION = '2.0.20';
// Database version (YYYYMMDDC where C is a daily counter) // Database version (YYYYMMDDC where C is a daily counter)
const DBVERSION = '202302030'; const DBVERSION = '202304260';
// Distribution branding-tag (used for Debian etc.) // Distribution branding-tag (used for Debian etc.)
const BRANDING = ''; const BRANDING = '';

View 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);
}
}

View File

@@ -42,7 +42,7 @@ class Install
public $phpVersion; public $phpVersion;
public $formfield; public $formfield;
public string $requiredVersion = '7.4.0'; 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 $suggestedExtensions = ['bcmath', 'zip'];
public array $suggestions = []; public array $suggestions = [];
public array $criticals = []; public array $criticals = [];

View File

@@ -68,10 +68,10 @@ class MailLogParser
// Parse MDA traffic // Parse MDA traffic
if (Settings::Get("system.mdaserver") == "dovecot") { if (Settings::Get("system.mdaserver") == "dovecot") {
$this->parseDovecotLog(Settings::Get("system.mdalog")); $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") { } elseif (Settings::Get("system.mdaserver") == "courier") {
$this->parseCourierLog(Settings::Get("system.mdalog")); $this->parseCourierLog(Settings::Get("system.mdalog"));
$this->parsePostfixLog(Settings::Get("system.mdalog") . ".1"); $this->parseCourierLog(Settings::Get("system.mdalog") . ".1");
} }
} }

View File

@@ -331,7 +331,7 @@ class PhpHelper
* @return string * @return string
*/ */
public static function sizeReadable( public static function sizeReadable(
int $size, $size,
?string $max = '', ?string $max = '',
string $system = 'si', string $system = 'si',
string $retstring = '%01.2f %s' string $retstring = '%01.2f %s'
@@ -518,9 +518,14 @@ class PhpHelper
$str .= self::tabPrefix($depth, sprintf("'%s' => %s,\n", $key, $value ? 'true' : 'false')); $str .= self::tabPrefix($depth, sprintf("'%s' => %s,\n", $key, $value ? 'true' : 'false'));
} elseif (is_int($value)) { } elseif (is_int($value)) {
$str .= self::tabPrefix($depth, "'{$key}' => $value,\n"); $str .= self::tabPrefix($depth, "'{$key}' => $value,\n");
} else {
if ($key == 'password') {
// special case for passwords (nowdoc)
$str .= self::tabPrefix($depth, "'{$key}' => <<<'EOT'\n{$value}\nEOT,\n");
} else { } else {
$str .= self::tabPrefix($depth, "'{$key}' => '{$value}',\n"); $str .= self::tabPrefix($depth, "'{$key}' => '{$value}',\n");
} }
}
} else { } else {
$str .= self::parseArrayToString($value, $key, ($depth + 1)); $str .= self::parseArrayToString($value, $key, ($depth + 1));
} }

View File

@@ -28,6 +28,7 @@ namespace Froxlor;
use Exception; use Exception;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\UI\Form; use Froxlor\UI\Form;
use Froxlor\Validate\Validate;
use PDO; use PDO;
/** /**
@@ -159,6 +160,9 @@ class SImExporter
// re-format the array-key for Form::processForm // re-format the array-key for Form::processForm
foreach ($_data as $key => $value) { foreach ($_data as $key => $value) {
$index_split = explode('.', $key, 3); $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]])) { if (isset($index_split[2]) && $index_split[2] === 'image_data' && !empty($_data[$index_split[0] . '.' . $index_split[1]])) {
$image_data[$key] = $value; $image_data[$key] = $value;
} else { } else {
@@ -190,26 +194,9 @@ class SImExporter
} }
} }
if (Validate::validateBase64Image($value)) {
$img_data = base64_decode($value); $img_data = base64_decode($value);
$img_filename = Froxlor::getInstallDir() . '/' . str_replace('../', '', $img_filename = explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0];
explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0]);
file_put_contents($img_filename, $img_data);
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 (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); $spl = explode('.', $img_filename);
$file_extension = strtolower(array_pop($spl)); $file_extension = strtolower(array_pop($spl));
@@ -221,11 +208,13 @@ class SImExporter
'png', 'png',
'gif' 'gif'
])) { ])) {
@unlink($img_filename);
throw new Exception("Invalid file-extension, use one of: 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;
Settings::Set($index, $value); 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());
}
} }
} }
// all good // all good

View File

@@ -36,6 +36,7 @@ use Froxlor\PhpHelper;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\System\Cronjob; use Froxlor\System\Cronjob;
use Froxlor\System\IPTools; use Froxlor\System\IPTools;
use Froxlor\Validate\Validate;
use PDO; use PDO;
class Store class Store
@@ -45,10 +46,21 @@ class Store
{ {
$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue); $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') { 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(" Database::query("
DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = '0' 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; return $returnvalue;
@@ -415,22 +427,12 @@ class Store
} }
// Make sure mime-type matches an image // Make sure mime-type matches an image
if (function_exists('finfo_open')) { $image_content = file_get_contents($_FILES[$fieldname]['tmp_name']);
$finfo = finfo_open(FILEINFO_MIME_TYPE); $value = base64_encode($image_content);
$mimetype = finfo_file($finfo, $_FILES[$fieldname]['tmp_name']); if (Validate::validateBase64Image($value)) {
finfo_close($finfo); $img_filename = $_FILES[$fieldname]['name'];
} 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");
}
// Determine file extension $spl = explode('.', $img_filename);
$spl = explode('.', $_FILES[$fieldname]['name']);
$file_extension = strtolower(array_pop($spl)); $file_extension = strtolower(array_pop($spl));
unset($spl); unset($spl);
@@ -442,13 +444,13 @@ class Store
])) { ])) {
throw new Exception("Invalid file-extension, use one of: 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 // Move file
if (!move_uploaded_file($_FILES[$fieldname]['tmp_name'], $path . $fielddata['image_name'] . '.' . $file_extension)) { if (!move_uploaded_file($_FILES[$fieldname]['tmp_name'], $path . $filename)) {
throw new Exception("Unable to save image to img folder"); throw new Exception("Unable to save image to img folder");
} }
$save_to = 'img/' . $filename . '?v=' . time();
$save_to = 'img/' . $fielddata['image_name'] . '.' . $file_extension . '?v=' . time(); }
} }
// Delete file? // Delete file?

View File

@@ -87,26 +87,38 @@ class UI
return $isHttps && (strcasecmp('on', $isHttps) == 0 || strcasecmp('https', $isHttps) == 0); 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 * send various security related headers
*/ */
public static function sendHeaders() 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([ session_set_cookie_params([
'lifetime' => self::$install_mode ? 7200 : 600, // will be renewed based on settings in lib/init.php 'lifetime' => self::$install_mode ? 7200 : 600, // will be renewed based on settings in lib/init.php
'path' => '/', 'path' => '/',
'domain' => explode(':', $_SERVER['HTTP_HOST'])[0], 'domain' => self::getCookieHost(),
'secure' => self::requestIsHttps(), 'secure' => self::requestIsHttps(),
'httponly' => true, 'httponly' => true,
'samesite' => 'Strict' 'samesite' => 'Strict'

View File

@@ -52,16 +52,16 @@ class Check
]; ];
$check_array = [ $check_array = [
'system_mod_fcgid_enabled' => [ 'system_mod_fcgid' => [
'other_post_field' => 'system_phpfpm_enabled', 'other_post_field' => 'phpfpm_enabled',
'other_enabled' => 'phpfpm.enabled', 'other_enabled' => 'phpfpm.enabled',
'other_enabled_lng' => 'phpfpmstillenabled', 'other_enabled_lng' => 'phpfpmstillenabled',
'deactivate' => [ 'deactivate' => [
'phpfpm.enabled_ownvhost' => 0 'phpfpm.enabled_ownvhost' => 0
] ]
], ],
'system_phpfpm_enabled' => [ 'phpfpm_enabled' => [
'other_post_field' => 'system_mod_fcgid_enabled', 'other_post_field' => 'system_mod_fcgid',
'other_enabled' => 'system.mod_fcgid', 'other_enabled' => 'system.mod_fcgid',
'other_enabled_lng' => 'fcgidstillenabled', 'other_enabled_lng' => 'fcgidstillenabled',
'deactivate' => [ 'deactivate' => [
@@ -291,7 +291,7 @@ class Check
*/ */
public static function checkLocalGroup($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues) public static function checkLocalGroup($fieldname, $fielddata, $newfieldvalue, $allnewfieldvalues)
{ {
if (empty($newfieldvalue) || $fielddata == $newfieldvalue) { if (empty($newfieldvalue) || $fielddata['value'] == $newfieldvalue) {
$returnvalue = [ $returnvalue = [
self::FORMFIELDS_PLAUSIBILITY_CHECK_OK self::FORMFIELDS_PLAUSIBILITY_CHECK_OK
]; ];

View File

@@ -334,4 +334,40 @@ class Validate
} }
return false; 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;
}
} }

View File

@@ -133,7 +133,7 @@ return [
'customers' => [ 'customers' => [
'label' => lng('admin.customers'), 'label' => lng('admin.customers'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['customers'], 'value' => empty($result['customers']) ? '0' : $result['customers'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
@@ -146,7 +146,7 @@ return [
'domains' => [ 'domains' => [
'label' => lng('admin.domains'), 'label' => lng('admin.domains'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['domains'], 'value' => empty($result['domains']) ? '0' : $result['domains'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
@@ -159,49 +159,49 @@ return [
'diskspace' => [ 'diskspace' => [
'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')', 'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')',
'type' => 'textul', 'type' => 'textul',
'value' => $result['diskspace'], 'value' => empty($result['diskspace']) ? '0' : $result['diskspace'],
'maxlength' => 6, 'maxlength' => 6,
'mandatory' => true 'mandatory' => true
], ],
'traffic' => [ 'traffic' => [
'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')', 'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')',
'type' => 'textul', 'type' => 'textul',
'value' => $result['traffic'], 'value' => empty($result['traffic']) ? '0' : $result['traffic'],
'maxlength' => 4, 'maxlength' => 4,
'mandatory' => true 'mandatory' => true
], ],
'subdomains' => [ 'subdomains' => [
'label' => lng('customer.subdomains'), 'label' => lng('customer.subdomains'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['subdomains'], 'value' => empty($result['subdomains']) ? '0' : $result['subdomains'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'emails' => [ 'emails' => [
'label' => lng('customer.emails'), 'label' => lng('customer.emails'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['emails'], 'value' => empty($result['emails']) ? '0' : $result['emails'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'email_accounts' => [ 'email_accounts' => [
'label' => lng('customer.accounts'), 'label' => lng('customer.accounts'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['email_accounts'], 'value' => empty($result['email_accounts']) ? '0' : $result['email_accounts'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'email_forwarders' => [ 'email_forwarders' => [
'label' => lng('customer.forwarders'), 'label' => lng('customer.forwarders'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['email_forwarders'], 'value' => empty($result['email_forwarders']) ? '0' : $result['email_forwarders'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'email_quota' => [ 'email_quota' => [
'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')', 'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')',
'type' => 'textul', 'type' => 'textul',
'value' => $result['email_quota'], 'value' => empty($result['email_quota']) ? '0' : $result['email_quota'],
'maxlength' => 9, 'maxlength' => 9,
'visible' => Settings::Get('system.mail_quota_enabled') == '1', 'visible' => Settings::Get('system.mail_quota_enabled') == '1',
'mandatory' => true 'mandatory' => true
@@ -209,13 +209,13 @@ return [
'ftps' => [ 'ftps' => [
'label' => lng('customer.ftps'), 'label' => lng('customer.ftps'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['ftps'], 'value' => empty($result['ftps']) ? '0' : $result['ftps'],
'maxlength' => 9 'maxlength' => 9
], ],
'mysqls' => [ 'mysqls' => [
'label' => lng('customer.mysqls'), 'label' => lng('customer.mysqls'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['mysqls'], 'value' => empty($result['mysqls']) ? '0' : $result['mysqls'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
] ]

View File

@@ -364,6 +364,12 @@ return [
'value' => '1', 'value' => '1',
'checked' => true 'checked' => true
], ],
'openbasedir_path' => [
'label' => lng('domain.openbasedirpath'),
'type' => 'select',
'select_var' => $openbasedir,
'selected' => 0
],
'phpenabled' => [ 'phpenabled' => [
'label' => lng('admin.phpenabled'), 'label' => lng('admin.phpenabled'),
'type' => 'checkbox', 'type' => 'checkbox',

View File

@@ -390,6 +390,12 @@ return [
'value' => '1', 'value' => '1',
'checked' => $result['openbasedir'] 'checked' => $result['openbasedir']
], ],
'openbasedir_path' => [
'label' => lng('domain.openbasedirpath'),
'type' => 'select',
'select_var' => $openbasedir,
'selected' => $result['openbasedir_path']
],
'phpenabled' => [ 'phpenabled' => [
'label' => lng('admin.phpenabled'), 'label' => lng('admin.phpenabled'),
'type' => 'checkbox', 'type' => 'checkbox',

View File

@@ -102,7 +102,7 @@ return [
'value' => '5s' 'value' => '5s'
], ],
'phpfpm_pass_authorizationheader' => [ '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'), 'label' => lng('admin.phpsettings.pass_authorizationheader'),
'type' => 'checkbox', 'type' => 'checkbox',
'value' => '1', 'value' => '1',

View File

@@ -105,7 +105,7 @@ return [
'value' => $result['fpm_reqslow'] 'value' => $result['fpm_reqslow']
], ],
'phpfpm_pass_authorizationheader' => [ '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'), 'label' => lng('admin.phpsettings.pass_authorizationheader'),
'type' => 'checkbox', 'type' => 'checkbox',
'value' => '1', 'value' => '1',

View File

@@ -57,7 +57,7 @@ return [
'label' => lng('panel.path'), '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), 'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescriptionSubdomain').(Settings::Get('system.documentroot_use_default_value') == 1 ? lng('panel.pathDescriptionEx') : '') : null),
'type' => $pathSelect['type'], 'type' => $pathSelect['type'],
'select_var' => $pathSelect['value'], 'select_var' => $pathSelect['select_var'] ?? '',
'selected' => $pathSelect['value'], 'selected' => $pathSelect['value'],
'value' => $pathSelect['value'], 'value' => $pathSelect['value'],
'note' => $pathSelect['note'] ?? '', 'note' => $pathSelect['note'] ?? '',

View File

@@ -43,13 +43,16 @@ return [
'email_password' => [ 'email_password' => [
'label' => lng('login.password'), 'label' => lng('login.password'),
'type' => 'password', 'type' => 'password',
'autocomplete' => 'off' 'autocomplete' => 'off',
], 'next_to' => [
'email_password_suggestion' => [ 'email_password_suggestion' => [
'label' => lng('customer.generated_pwd'), 'next_to_prefix' => lng('customer.generated_pwd') . ':',
'type' => 'text', 'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''), 'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword() 'value' => Crypt::generatePassword(),
'readonly' => true
]
]
] ]
] ]
] ]

View File

@@ -46,7 +46,7 @@ return [
'autocomplete' => 'off', 'autocomplete' => 'off',
'mandatory' => true, 'mandatory' => true,
'next_to' => [ 'next_to' => [
'admin_password_suggestion' => [ 'email_password_suggestion' => [
'next_to_prefix' => lng('customer.generated_pwd') . ':', 'next_to_prefix' => lng('customer.generated_pwd') . ':',
'type' => 'text', 'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''), 'visible' => (Settings::Get('panel.password_regex') == ''),

View File

@@ -25,6 +25,13 @@
use Froxlor\Froxlor; use Froxlor\Froxlor;
$httpuser = '';
$httpgroup = '';
if (extension_loaded('posix')) {
$httpuser = posix_getpwuid(posix_getuid())['name'] ?? '';
$httpgroup = posix_getgrgid(posix_getgid())['name'] ?? '';
}
return [ return [
'install' => [ 'install' => [
'title' => 'install', 'title' => 'install',
@@ -194,7 +201,7 @@ return [
'placeholder' => lng('admin.webserver_user'), 'placeholder' => lng('admin.webserver_user'),
'type' => 'text', 'type' => 'text',
'mandatory' => true, 'mandatory' => true,
'value' => old('httpuser', posix_getpwuid(posix_getuid())['name'] ?? '', 'installation'), 'value' => old('httpuser', $httpuser, 'installation'),
'advanced' => true, 'advanced' => true,
], ],
'httpgroup' => [ 'httpgroup' => [
@@ -202,7 +209,7 @@ return [
'placeholder' => lng('admin.webserver_group'), 'placeholder' => lng('admin.webserver_group'),
'type' => 'text', 'type' => 'text',
'mandatory' => true, 'mandatory' => true,
'value' => old('httpgroup', posix_getgrgid(posix_getgid())['name'] ?? '', 'installation'), 'value' => old('httpgroup', $httpgroup, 'installation'),
'advanced' => true, 'advanced' => true,
], ],
'activate_newsfeed' => [ 'activate_newsfeed' => [

View File

@@ -52,6 +52,7 @@ require dirname(__DIR__) . '/vendor/autoload.php';
use Froxlor\CurrentUser; use Froxlor\CurrentUser;
use Froxlor\Froxlor; use Froxlor\Froxlor;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\Http\RateLimiter;
use Froxlor\Idna\IdnaWrapper; use Froxlor\Idna\IdnaWrapper;
use Froxlor\Language; use Froxlor\Language;
use Froxlor\PhpHelper; 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) // send ssl-related headers (later than the others because we need a working database-connection and installation)
UI::sendSslHeaders(); UI::sendSslHeaders();
RateLimiter::run();
// create a new idna converter // create a new idna converter
$idna_convert = new IdnaWrapper(); $idna_convert = new IdnaWrapper();
@@ -332,7 +334,7 @@ if (CurrentUser::hasSession()) {
$cookie_params = [ $cookie_params = [
'expires' => time() + Settings::Get('session.sessiontimeout'), 'expires' => time() + Settings::Get('session.sessiontimeout'),
'path' => '/', 'path' => '/',
'domain' => explode(':', $_SERVER['HTTP_HOST'])[0], 'domain' => UI::getCookieHost(),
'secure' => UI::requestIsHttps(), 'secure' => UI::requestIsHttps(),
'httponly' => true, 'httponly' => true,
'samesite' => 'Strict' 'samesite' => 'Strict'

View File

@@ -45,30 +45,27 @@ return [
'callback' => [SSLCertificate::class, 'domainWithSan'], 'callback' => [SSLCertificate::class, 'domainWithSan'],
'searchable' => false, 'searchable' => false,
], ],
'c.issuer' => [ 's.issuer' => [
'label' => lng('ssl_certificates.issuer'), 'label' => lng('ssl_certificates.issuer'),
'field' => 'issuer', 'field' => 'issuer',
'searchable' => false,
], ],
'c.validfromdate' => [ 's.validfromdate' => [
'label' => lng('ssl_certificates.valid_from'), 'label' => lng('ssl_certificates.valid_from'),
'field' => 'validfromdate', 'field' => 'validfromdate',
'searchable' => false, 'searchable' => false,
'sortable' => false,
], ],
'c.validtodate' => [ 's.validtodate' => [
'label' => lng('ssl_certificates.valid_until'), 'label' => lng('ssl_certificates.valid_until'),
'field' => 'validtodate', 'field' => 'validtodate',
'searchable' => false, 'searchable' => false,
'sortable' => false,
], ],
], ],
'visible_columns' => Listing::getVisibleColumnsForListing('sslcertificates_list', [ 'visible_columns' => Listing::getVisibleColumnsForListing('sslcertificates_list', [
'd.domain', 'd.domain',
'c.domain', 'c.domain',
'c.issuer', 's.issuer',
'c.validfromdate', 's.validfromdate',
'c.validtodate', 's.validtodate',
]), ]),
'actions' => [ 'actions' => [
'edit' => [ 'edit' => [

View File

@@ -33,6 +33,7 @@ return [
'nl' => 'Niederländisch', 'nl' => 'Niederländisch',
'pt' => 'Portugiesisch', 'pt' => 'Portugiesisch',
'se' => 'Schwedisch', 'se' => 'Schwedisch',
'es' => 'Spanisch',
], ],
'2fa' => [ '2fa' => [
'2fa' => '2FA Optionen', '2fa' => '2FA Optionen',
@@ -297,7 +298,7 @@ return [
'request_terminate_timeout' => 'request_terminate_timeout', 'request_terminate_timeout' => 'request_terminate_timeout',
'request_slowlog_timeout' => 'request_slowlog_timeout', 'request_slowlog_timeout' => 'request_slowlog_timeout',
'activephpconfigs' => 'In Verwendung für PHP-Konfiguration(en)', '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', 'misc' => 'Sonstiges',
'fpmsettings' => [ 'fpmsettings' => [
@@ -924,6 +925,8 @@ return [
'mysqlserverstillhasdbs' => 'Datenbank-Server kann für den Kunden nicht entfernt werden, da sich dort noch Datenbanken befinden.', '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', 'domaincannotbeedited' => 'Keine Berechtigung, um die Domain %s zu bearbeiten',
'invalidcronjobintervalvalue' => 'Cronjob Intervall muss einer der folgenden Werte sein: %s', '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' => [ '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.', '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', 'toolselect' => 'Traffic Analyzer',
], ],
'requires_reconfiguration' => 'Änderungen an dieser Einstellungen benötigen unter Umständen eine erneute Konfiguration der folgenden Dienste:<br><strong>%s</strong>', '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' => [ 'spf' => [
'use_spf' => 'Aktiviere SPF für Domains?', 'use_spf' => 'Aktiviere SPF für Domains?',
@@ -2177,6 +2188,7 @@ Vielen Dank, Ihr Administrator',
'description' => 'Aktualisierung der froxlor Datenbank', 'description' => 'Aktualisierung der froxlor Datenbank',
'uc_newinfo' => 'Eine neuere %sVersion ist verfügbar: "%s" (Aktuell installierte Version: %s)', 'uc_newinfo' => 'Eine neuere %sVersion ist verfügbar: "%s" (Aktuell installierte Version: %s)',
'notify_subject' => 'Neues Update verfügbar', 'notify_subject' => 'Neues Update verfügbar',
'dbupdate_required' => 'Froxlor-Dateien wurden aktualisiert, Datenbank-Aktualisierung notwendig',
], ],
'usersettings' => [ 'usersettings' => [
'custom_notes' => [ 'custom_notes' => [
@@ -2231,8 +2243,8 @@ Vielen Dank, Ihr Administrator',
'install' => [ 'install' => [
'top' => 'Abschluss', 'top' => 'Abschluss',
'title' => 'Ein letzter Schritt...', '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.', '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' => 'Folgenden Befehl als root-Benutzer in der Shell auf dem Server ausführen:', 'runcmd' => 'Folgende Befehle ausführen, um die Installation abzuschließen:',
'manual_config' => 'Ich werden die Dienste manuell konfigurieren, direkt zum Login umleiten', 'manual_config' => 'Ich werden die Dienste manuell konfigurieren, direkt zum Login umleiten',
'waitforconfig' => 'Warte auf Abschluss der Dienstkonfiguration...', 'waitforconfig' => 'Warte auf Abschluss der Dienstkonfiguration...',
], ],

View File

@@ -33,6 +33,7 @@ return [
'nl' => 'Dutch', 'nl' => 'Dutch',
'pt' => 'Portuguese', 'pt' => 'Portuguese',
'se' => 'Swedish', 'se' => 'Swedish',
'es' => 'Spanish',
], ],
'2fa' => [ '2fa' => [
'2fa' => '2FA options', '2fa' => '2FA options',
@@ -302,7 +303,7 @@ return [
'request_terminate_timeout' => 'Request terminate-timeout', 'request_terminate_timeout' => 'Request terminate-timeout',
'request_slowlog_timeout' => 'Request slowlog-timeout', 'request_slowlog_timeout' => 'Request slowlog-timeout',
'activephpconfigs' => 'In use for php-config(s)', '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', 'misc' => 'Miscellaneous',
'fpmsettings' => [ '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_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_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_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_6' => 'Whoops, there was no (valid) version given to download :(',
'autoupdate_7' => 'The downloaded archive could not be found :(', 'autoupdate_7' => 'The downloaded archive could not be found :(',
'autoupdate_8' => 'The archive could not be extracted :(', '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.', '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', 'domaincannotbeedited' => 'You are not permitted to edit the domain %s',
'invalidcronjobintervalvalue' => 'Cronjob interval must be one of: %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' => [ '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.', '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', 'database_edit' => 'Edit database',
'size' => 'Size', 'size' => 'Size',
'privileged_user' => 'Privileged database user', 'privileged_user' => 'Privileged database user',
'privileged_passwd' => 'Password for priviliged user', 'privileged_passwd' => 'Password for privileged user',
'unprivileged_passwd' => 'Password for unprivileged user', 'unprivileged_passwd' => 'Password for unprivileged user',
'mysql_ssl_ca_file' => 'SSL server certificate', 'mysql_ssl_ca_file' => 'SSL server certificate',
'mysql_ssl_verify_server_certificate' => 'Verify SSL server certificate' 'mysql_ssl_verify_server_certificate' => 'Verify SSL server certificate'
@@ -2194,6 +2197,14 @@ Yours sincerely, your administrator',
'goaccess' => 'goacccess' 'goaccess' => 'goacccess'
], ],
'requires_reconfiguration' => 'Changing this settings might require a reconfiguration of the following services:<br><strong>%s</strong>', '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' => [ 'spf' => [
'use_spf' => 'Activate SPF for domains?', 'use_spf' => 'Activate SPF for domains?',
@@ -2308,6 +2319,7 @@ Yours sincerely, your administrator',
'description' => 'Running database updates for your froxlor installation', 'description' => 'Running database updates for your froxlor installation',
'uc_newinfo' => 'There is a newer %sversion available: "%s" (Your current version is: %s)', 'uc_newinfo' => 'There is a newer %sversion available: "%s" (Your current version is: %s)',
'notify_subject' => 'New update available', 'notify_subject' => 'New update available',
'dbupdate_required' => 'Froxlor files have been updated, database update required',
], ],
'usersettings' => [ 'usersettings' => [
'custom_notes' => [ 'custom_notes' => [
@@ -2363,8 +2375,8 @@ Yours sincerely, your administrator',
'install' => [ 'install' => [
'top' => 'Finish setup', 'top' => 'Finish setup',
'title' => 'One last step...', '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.', '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 as root-user in your shell on this server:', 'runcmd' => 'Run the following command to finish the installation:',
'manual_config' => 'I will manually configure the services, just take me to the login', 'manual_config' => 'I will manually configure the services, just take me to the login',
'waitforconfig' => 'Waiting for services to be configured...', 'waitforconfig' => 'Waiting for services to be configured...',
], ],

2405
lng/es.lng.php Normal file

File diff suppressed because it is too large Load Diff

7546
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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> <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>
</div> </div>
<p class="lead">{{ section.description }}</p> <p class="lead">{{ section.description|raw }}</p>
<hr /> <hr />
{% import "Froxlor/form/formfields.html.twig" as formfields %} {% import "Froxlor/form/formfields.html.twig" as formfields %}

View File

@@ -10,9 +10,16 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">{{ pagetitle }}</h5> <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"> <div class="mb-3">
<label for="2fa_code" class="col-form-label">{{ lng('login.2facode') }}</label> <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>
</div> </div>

View 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 %}

View File

@@ -4,7 +4,7 @@
<h5> <h5>
<i class="fa-solid fa-gears"></i> <i class="fa-solid fa-gears"></i>
{{ lng('admin.serversettings') }} {{ lng('admin.serversettings') }}
{% if fields._group is defined %}&nbsp;&raquo;&nbsp;{{ fields._group.title }} {% if fields._group is defined %}&nbsp;&raquo;&nbsp;{{ fields._group.title|raw }}
{% endif %} {% endif %}
</h5> </h5>
<span class="text-muted">{{ lng('admin.serversettings_desc') }}</span> <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> <i class="{{ field.icon }} fa-2x me-4" style="width: 1em;"></i>
</a> </a>
<div> <div>
{{ field.title }} {{ field.title|raw }}
{% if field.info is defined and field.info is not empty %} {% if field.info is defined and field.info is not empty %}
{{ field.info|raw }} {{ field.info|raw }}
{% endif %} {% endif %}

View File

@@ -5,4 +5,8 @@ $(function () {
history.back(1); history.back(1);
}) })
$('#copySysInfo').on('click', function (e) {
e.preventDefault();
navigator.clipboard.writeText($('#ccSysInfo').text().trim());
})
}); });

View File

@@ -55,6 +55,19 @@
<div class="card-header"> <div class="card-header">
<i class="fa-solid fa-gears me-1"></i> <i class="fa-solid fa-gears me-1"></i>
{{ lng('admin.systemdetails') }} {{ 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> </div>
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-start"> <li class="list-group-item d-flex justify-content-between align-items-start">

View File

@@ -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"> <div class="d-flex w-100 justify-content-between">
<b class="mb-1">{{ title }}</b> <b class="mb-1">{{ title }}</b>
{% if date is not empty %} {% if date is not empty %}