Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6fae81f1b | ||
|
|
bab6c3da5b | ||
|
|
c494838069 | ||
|
|
d191693f74 | ||
|
|
aaac84245e | ||
|
|
46a46816b0 | ||
|
|
ce4f64e73a | ||
|
|
b7439d0f3b | ||
|
|
a9da57f6fb | ||
|
|
8ab2e43426 | ||
|
|
8932174df8 | ||
|
|
eb6ea8195d | ||
|
|
a9feb97c27 | ||
|
|
0a7ca058aa | ||
|
|
d9032f3790 | ||
|
|
76793c8992 | ||
|
|
6068daece2 | ||
|
|
0624292b49 | ||
|
|
afc3b68abf | ||
|
|
830f43a9db | ||
|
|
9253a94aad | ||
|
|
e9d3de0c25 | ||
|
|
bbda491e82 | ||
|
|
a0f179a7e7 | ||
|
|
5afc5272d1 | ||
|
|
8f5bd789a4 | ||
|
|
ff64740880 | ||
|
|
0a221d0479 | ||
|
|
097cde13ad | ||
|
|
f90dc5854d | ||
|
|
8eb38a8a28 | ||
|
|
e17135f0c3 | ||
|
|
c5017786e0 | ||
|
|
2e18d7c581 | ||
|
|
ceb7f5b23d | ||
|
|
ebed800dec | ||
|
|
32344e39cf | ||
|
|
e35092c31f | ||
|
|
606377f1d9 | ||
|
|
b9baeb76d5 | ||
|
|
2f2d72851b | ||
|
|
a2925af73a | ||
|
|
1008c015a5 | ||
|
|
2eda4ae972 | ||
|
|
38b2dbd81b | ||
|
|
aab98e4dae | ||
|
|
4b930375b7 | ||
|
|
c7245d0b9b | ||
|
|
ec42003367 | ||
|
|
fde43f8060 | ||
|
|
a43d53d540 | ||
|
|
3638dc08ea | ||
|
|
c2d166c866 | ||
|
|
0fb9357e87 | ||
|
|
26c3c87d28 | ||
|
|
0aa3e2f7b1 | ||
|
|
9dec83fff2 | ||
|
|
a839d76d1f | ||
|
|
079047b9fe | ||
|
|
2bb863baac | ||
|
|
8be7372d73 | ||
|
|
dcaff3f7de | ||
|
|
b6dadc0d8f | ||
|
|
665b879ac5 | ||
|
|
60f51fd746 | ||
|
|
5bb450bccc | ||
|
|
604078ddc6 | ||
|
|
b018319b8a | ||
|
|
13aa07ed1a | ||
|
|
4db5b09111 | ||
|
|
4f114738e7 | ||
|
|
2c9f9ebfe2 | ||
|
|
ee986e519e | ||
|
|
103d321003 | ||
|
|
99b3deda91 | ||
|
|
a47f8ed7ee | ||
|
|
ce841e8aa4 | ||
|
|
86130616dd | ||
|
|
5622ce5011 | ||
|
|
00bdadb6e5 | ||
|
|
05223369c5 | ||
|
|
b45ac3de3c | ||
|
|
11a5c38476 |
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -15,7 +15,8 @@ assignees: ''
|
|||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
**System information**
|
**System information**
|
||||||
* Froxlor version: $version/$gitSHA1
|
* Froxlor version: \$version/\$gitSHA1
|
||||||
|
* PHP sapi & version: php-fpm 8.3 / fcgid 8.0 / etc.
|
||||||
* Web server: apache2/nginx/lighttpd
|
* Web server: apache2/nginx/lighttpd
|
||||||
* DNS server: Bind/PowerDNS (standalone)/PowerDNS (Bind-backend)
|
* DNS server: Bind/PowerDNS (standalone)/PowerDNS (Bind-backend)
|
||||||
* POP/IMAP server: Courier/Dovecot
|
* POP/IMAP server: Courier/Dovecot
|
||||||
|
|||||||
8
.github/workflows/build-mariadb.yml
vendored
8
.github/workflows/build-mariadb.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
php-versions: [ '7.4', '8.2' ]
|
php-versions: [ '7.4', '8.3' ]
|
||||||
mariadb-version: [ 10.11, 10.5 ]
|
mariadb-version: [ 10.11, 10.5 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-versions }}
|
php-version: ${{ matrix.php-versions }}
|
||||||
tools: composer:v2
|
tools: composer:v2
|
||||||
extensions: mbstring, xml, ctype, pdo_mysql, mysql, curl, json, zip, session, filter, posix, openssl, fileinfo, bcmath, gmp, gnupg
|
extensions: mbstring, xml, ctype, pdo_mysql, mysql, curl, json, zip, session, filter, posix, openssl, fileinfo, bcmath, gmp
|
||||||
|
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: sudo apt-get install -y ant
|
run: sudo apt-get install -y ant
|
||||||
@@ -72,7 +72,7 @@ jobs:
|
|||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '20.x'
|
node-version: '22.x'
|
||||||
|
|
||||||
- name: Install npm dependencies
|
- name: Install npm dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
@@ -120,7 +120,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Deploy nightly to server
|
- name: Deploy nightly to server
|
||||||
uses: easingthemes/ssh-deploy@main
|
uses: easingthemes/ssh-deploy@main
|
||||||
env:
|
with:
|
||||||
ARGS: "-rltDzvO --chown=${{ secrets.WEB_USER }}:${{ secrets.WEB_USER }}"
|
ARGS: "-rltDzvO --chown=${{ secrets.WEB_USER }}:${{ secrets.WEB_USER }}"
|
||||||
SOURCE: "dist/"
|
SOURCE: "dist/"
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
|
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
|
||||||
|
|||||||
4
.github/workflows/build-mysql.yml
vendored
4
.github/workflows/build-mysql.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
php-versions: ['7.4', '8.2']
|
php-versions: ['7.4', '8.3']
|
||||||
mysql-version: [8.0, 5.7]
|
mysql-version: [8.0, 5.7]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-versions }}
|
php-version: ${{ matrix.php-versions }}
|
||||||
tools: composer:v2
|
tools: composer:v2
|
||||||
extensions: mbstring, xml, ctype, pdo_mysql, mysql, curl, json, zip, session, filter, posix, openssl, fileinfo, bcmath, gmp, gnupg
|
extensions: mbstring, xml, ctype, pdo_mysql, mysql, curl, json, zip, session, filter, posix, openssl, fileinfo, bcmath, gmp
|
||||||
|
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: sudo apt-get install -y ant
|
run: sudo apt-get install -y ant
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -10,6 +10,7 @@ logs/*
|
|||||||
.settings/
|
.settings/
|
||||||
.test/
|
.test/
|
||||||
*.diff
|
*.diff
|
||||||
|
*.patch
|
||||||
*~
|
*~
|
||||||
.well-known
|
.well-known
|
||||||
.idea
|
.idea
|
||||||
@@ -23,4 +24,5 @@ templates/*
|
|||||||
!templates/index.html
|
!templates/index.html
|
||||||
!templates/Froxlor/
|
!templates/Froxlor/
|
||||||
templates/Froxlor/build/
|
templates/Froxlor/build/
|
||||||
|
templates/Froxlor/hot
|
||||||
!templates/misc/
|
!templates/misc/
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ Developed by experienced server administrators, this panel simplifies the effort
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Fast install
|
### Fast install
|
||||||
|
|
||||||
1. Ensure that your webserver serves /var/www/html
|
1. Ensure that your webserver serves /var/www/html
|
||||||
2. Extract froxlor into /var/www/html
|
2. Extract froxlor into /var/www/html
|
||||||
3. Point your browser to http://[ip-of-webserver]/froxlor
|
3. Point your browser to http://[ip-of-webserver]/froxlor
|
||||||
@@ -24,6 +25,7 @@ If you have chosen to do the configuration by hand during the installation, you
|
|||||||
3. Follow the steps for your services
|
3. Follow the steps for your services
|
||||||
|
|
||||||
### Detailed installation
|
### Detailed installation
|
||||||
|
|
||||||
https://docs.froxlor.org/latest/general/installation/
|
https://docs.froxlor.org/latest/general/installation/
|
||||||
|
|
||||||
## Help
|
## Help
|
||||||
@@ -49,6 +51,7 @@ May be found in [COPYING](COPYING)
|
|||||||
## Downloads
|
## Downloads
|
||||||
|
|
||||||
### Tarball
|
### Tarball
|
||||||
|
|
||||||
https://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](https://files.froxlor.org/releases/froxlor-latest.tar.gz.md5) [SHA1](https://files.froxlor.org/releases/froxlor-latest.tar.gz.sha1)
|
https://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](https://files.froxlor.org/releases/froxlor-latest.tar.gz.md5) [SHA1](https://files.froxlor.org/releases/froxlor-latest.tar.gz.sha1)
|
||||||
|
|
||||||
### Debian / Ubuntu repository
|
### Debian / Ubuntu repository
|
||||||
|
|||||||
@@ -257,7 +257,8 @@ return [
|
|||||||
'varname' => 'mail_smtp_user',
|
'varname' => 'mail_smtp_user',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
'default' => '',
|
'default' => '',
|
||||||
'save_method' => 'storeSettingField'
|
'save_method' => 'storeSettingField',
|
||||||
|
'autocomplete' => 'off'
|
||||||
],
|
],
|
||||||
'system_mail_smtp_passwd' => [
|
'system_mail_smtp_passwd' => [
|
||||||
'label' => lng('serversettings.mail_smtp_passwd'),
|
'label' => lng('serversettings.mail_smtp_passwd'),
|
||||||
@@ -265,7 +266,8 @@ return [
|
|||||||
'varname' => 'mail_smtp_passwd',
|
'varname' => 'mail_smtp_passwd',
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'default' => '',
|
'default' => '',
|
||||||
'save_method' => 'storeSettingField'
|
'save_method' => 'storeSettingField',
|
||||||
|
'autocomplete' => 'new-password'
|
||||||
],
|
],
|
||||||
'system_apply_specialsettings_default' => [
|
'system_apply_specialsettings_default' => [
|
||||||
'label' => lng('serversettings.apply_specialsettings_default'),
|
'label' => lng('serversettings.apply_specialsettings_default'),
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ return [
|
|||||||
],
|
],
|
||||||
'requires_reconf' => ['http']
|
'requires_reconf' => ['http']
|
||||||
],
|
],
|
||||||
'system_apache_24' => [
|
'system_apache24' => [
|
||||||
'label' => lng('serversettings.apache_24'),
|
'label' => lng('serversettings.apache_24'),
|
||||||
'settinggroup' => 'system',
|
'settinggroup' => 'system',
|
||||||
'varname' => 'apache24',
|
'varname' => 'apache24',
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ return [
|
|||||||
'dovecot' => 'dovecot (imap/pop3)',
|
'dovecot' => 'dovecot (imap/pop3)',
|
||||||
'proftpd' => 'proftpd (ftp)',
|
'proftpd' => 'proftpd (ftp)',
|
||||||
],
|
],
|
||||||
'save_method' => 'storeSettingField',
|
'save_method' => 'storeSettingFieldInsertUpdateServicesTask',
|
||||||
'advanced_mode' => true
|
'advanced_mode' => true
|
||||||
],
|
],
|
||||||
'system_le_renew_hook' => [
|
'system_le_renew_hook' => [
|
||||||
@@ -278,7 +278,7 @@ return [
|
|||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
'string_regexp' => '/^[a-z0-9\/\._\- ]+$/i',
|
'string_regexp' => '/^[a-z0-9\/\._\- ]+$/i',
|
||||||
'default' => 'systemctl restart postfix dovecot proftpd',
|
'default' => 'systemctl restart postfix dovecot proftpd',
|
||||||
'save_method' => 'storeSettingField',
|
'save_method' => 'storeSettingFieldInsertUpdateServicesTask',
|
||||||
'advanced_mode' => true,
|
'advanced_mode' => true,
|
||||||
'required_otp' => true
|
'required_otp' => true
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -58,6 +58,51 @@ return [
|
|||||||
'save_method' => 'storeSettingField',
|
'save_method' => 'storeSettingField',
|
||||||
'required_otp' => true
|
'required_otp' => true
|
||||||
],
|
],
|
||||||
|
'antispam_default_bypass_spam' => [
|
||||||
|
'label' => lng('antispam.default_bypass_spam'),
|
||||||
|
'settinggroup' => 'antispam',
|
||||||
|
'varname' => 'default_bypass_spam',
|
||||||
|
'type' => 'select',
|
||||||
|
'default' => 2,
|
||||||
|
'select_var' => [
|
||||||
|
1 => lng('antispam.default_select.on_changeable'),
|
||||||
|
2 => lng('antispam.default_select.off_changeable'),
|
||||||
|
3 => lng('antispam.default_select.on_unchangeable'),
|
||||||
|
4 => lng('antispam.default_select.off_unchangeable'),
|
||||||
|
],
|
||||||
|
'save_method' => 'storeSettingField',
|
||||||
|
'advanced_mode' => true
|
||||||
|
],
|
||||||
|
'antispam_default_spam_rewrite_subject' => [
|
||||||
|
'label' => lng('antispam.default_spam_rewrite_subject'),
|
||||||
|
'settinggroup' => 'antispam',
|
||||||
|
'varname' => 'default_spam_rewrite_subject',
|
||||||
|
'type' => 'select',
|
||||||
|
'default' => 1,
|
||||||
|
'select_var' => [
|
||||||
|
1 => lng('antispam.default_select.on_changeable'),
|
||||||
|
2 => lng('antispam.default_select.off_changeable'),
|
||||||
|
3 => lng('antispam.default_select.on_unchangeable'),
|
||||||
|
4 => lng('antispam.default_select.off_unchangeable'),
|
||||||
|
],
|
||||||
|
'save_method' => 'storeSettingField',
|
||||||
|
'advanced_mode' => true
|
||||||
|
],
|
||||||
|
'antispam_default_policy_greylist' => [
|
||||||
|
'label' => lng('antispam.default_policy_greylist'),
|
||||||
|
'settinggroup' => 'antispam',
|
||||||
|
'varname' => 'default_policy_greylist',
|
||||||
|
'type' => 'select',
|
||||||
|
'default' => 1,
|
||||||
|
'select_var' => [
|
||||||
|
1 => lng('antispam.default_select.on_changeable'),
|
||||||
|
2 => lng('antispam.default_select.off_changeable'),
|
||||||
|
3 => lng('antispam.default_select.on_unchangeable'),
|
||||||
|
4 => lng('antispam.default_select.off_unchangeable'),
|
||||||
|
],
|
||||||
|
'save_method' => 'storeSettingField',
|
||||||
|
'advanced_mode' => true
|
||||||
|
],
|
||||||
'antispam_dkim_keylength' => [
|
'antispam_dkim_keylength' => [
|
||||||
'label' => lng('antispam.dkim_keylength'),
|
'label' => lng('antispam.dkim_keylength'),
|
||||||
'settinggroup' => 'antispam',
|
'settinggroup' => 'antispam',
|
||||||
@@ -84,7 +129,7 @@ return [
|
|||||||
'settinggroup' => 'spf',
|
'settinggroup' => 'spf',
|
||||||
'varname' => 'spf_entry',
|
'varname' => 'spf_entry',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
'string_regexp' => '/^v=spf[a-z0-9:~?\s.-]+$/i',
|
'string_regexp' => '/^v=spf[a-z0-9:~?\s\.\-\/]+$/i',
|
||||||
'default' => 'v=spf1 a mx -all',
|
'default' => 'v=spf1 a mx -all',
|
||||||
'save_method' => 'storeSettingField'
|
'save_method' => 'storeSettingField'
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -307,17 +307,20 @@ if (($page == 'customers' || $page == 'overview') && $userinfo['customers'] != '
|
|||||||
$hosting_plans[$row['id']] = $row['name'];
|
$hosting_plans[$row['id']] = $row['name'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$available_admins_stmt = Database::prepare("
|
$admin_select = [];
|
||||||
SELECT * FROM `" . TABLE_PANEL_ADMINS . "`
|
if ($userinfo['customers_see_all'] == '1') {
|
||||||
WHERE (`customers` = '-1' OR `customers` > `customers_used`)
|
$available_admins_stmt = Database::prepare("
|
||||||
AND adminid <> :currentadmin
|
SELECT * FROM `" . TABLE_PANEL_ADMINS . "`
|
||||||
");
|
WHERE (`customers` = '-1' OR `customers` > `customers_used`)
|
||||||
Database::pexecute($available_admins_stmt, ['currentadmin' => $result['adminid']]);
|
AND adminid <> :currentadmin
|
||||||
$admin_select = [
|
");
|
||||||
0 => "---"
|
Database::pexecute($available_admins_stmt, ['currentadmin' => $result['adminid']]);
|
||||||
];
|
$admin_select = [
|
||||||
while ($available_admin = $available_admins_stmt->fetch()) {
|
0 => "---"
|
||||||
$admin_select[$available_admin['adminid']] = $available_admin['name'] . " (" . $available_admin['loginname'] . ")";
|
];
|
||||||
|
while ($available_admin = $available_admins_stmt->fetch()) {
|
||||||
|
$admin_select[$available_admin['adminid']] = $available_admin['name'] . " (" . $available_admin['loginname'] . ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$customer_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/customer/formfield.customer_edit.php';
|
$customer_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/customer/formfield.customer_edit.php';
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ if ($page == 'domains' || $page == 'overview') {
|
|||||||
$alias_check = $alias_check['count'];
|
$alias_check = $alias_check['count'];
|
||||||
|
|
||||||
$domain_emails_result_stmt = Database::prepare("
|
$domain_emails_result_stmt = Database::prepare("
|
||||||
SELECT `email`, `email_full`, `destination`, `popaccountid` AS `number_email_forwarders`
|
SELECT `email`, `email_full`, `destination`, `popaccountid`
|
||||||
FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid` = :customerid AND `domainid` = :id
|
FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid` = :customerid AND `domainid` = :id
|
||||||
");
|
");
|
||||||
Database::pexecute($domain_emails_result_stmt, [
|
Database::pexecute($domain_emails_result_stmt, [
|
||||||
@@ -593,6 +593,23 @@ if ($page == 'domains' || $page == 'overview') {
|
|||||||
}
|
}
|
||||||
echo 0;
|
echo 0;
|
||||||
exit();
|
exit();
|
||||||
|
} elseif ($action == 'jqEmaildomainNote') {
|
||||||
|
$domainid = intval(Request::post('id'));
|
||||||
|
$newval = intval(Request::post('newval'));
|
||||||
|
try {
|
||||||
|
$json_result = Domains::getLocal($userinfo, [
|
||||||
|
'id' => $domainid
|
||||||
|
])->get();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Response::dynamicError($e->getMessage());
|
||||||
|
}
|
||||||
|
$result = json_decode($json_result, true)['data'];
|
||||||
|
if ((int)$newval == 0 && $newval != $result['isemaildomain']) {
|
||||||
|
echo json_encode(['changed' => true, 'info' => lng('admin.emaildomainwarning')]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
echo 0;
|
||||||
|
exit();
|
||||||
} elseif ($action == 'import') {
|
} elseif ($action == 'import') {
|
||||||
if (Request::post('send') == 'send') {
|
if (Request::post('send') == 'send') {
|
||||||
$separator = Validate::validate(Request::post('separator'), 'separator');
|
$separator = Validate::validate(Request::post('separator'), 'separator');
|
||||||
|
|||||||
@@ -46,7 +46,6 @@
|
|||||||
"ext-fileinfo": "*",
|
"ext-fileinfo": "*",
|
||||||
"ext-gmp": "*",
|
"ext-gmp": "*",
|
||||||
"ext-gd": "*",
|
"ext-gd": "*",
|
||||||
"ext-gnupg": "*",
|
|
||||||
"phpmailer/phpmailer": "~6.0",
|
"phpmailer/phpmailer": "~6.0",
|
||||||
"monolog/monolog": "^1.24",
|
"monolog/monolog": "^1.24",
|
||||||
"robthree/twofactorauth": "^1.6",
|
"robthree/twofactorauth": "^1.6",
|
||||||
@@ -73,9 +72,15 @@
|
|||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-bcmath": "*",
|
"ext-bcmath": "*",
|
||||||
"ext-zip": "*",
|
"ext-zip": "*",
|
||||||
|
"ext-gnupg": "*",
|
||||||
"ext-apcu": "*",
|
"ext-apcu": "*",
|
||||||
"ext-readline": "*"
|
"ext-readline": "*"
|
||||||
},
|
},
|
||||||
|
"config": {
|
||||||
|
"platform": {
|
||||||
|
"php": "7.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Froxlor\\": [
|
"Froxlor\\": [
|
||||||
@@ -84,6 +89,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"dev": [
|
||||||
|
"Composer\\Config::disableProcessTimeout",
|
||||||
|
"npx concurrently -c \"#93c5fd,#fdba74\" \"php -S 127.0.0.1:8000\" \"npm run dev\" --names=server,vite"
|
||||||
|
],
|
||||||
"post-install-cmd": "if [ -f ./vendor/bin/phpcs ]; then \"vendor/bin/phpcs\" --config-set installed_paths vendor/phpcompatibility/php-compatibility ; fi",
|
"post-install-cmd": "if [ -f ./vendor/bin/phpcs ]; then \"vendor/bin/phpcs\" --config-set installed_paths vendor/phpcompatibility/php-compatibility ; fi",
|
||||||
"post-update-cmd" : "if [ -f ./vendor/bin/phpcs ]; then \"vendor/bin/phpcs\" --config-set installed_paths vendor/phpcompatibility/php-compatibility ; fi"
|
"post-update-cmd" : "if [ -f ./vendor/bin/phpcs ]; then \"vendor/bin/phpcs\" --config-set installed_paths vendor/phpcompatibility/php-compatibility ; fi"
|
||||||
}
|
}
|
||||||
|
|||||||
604
composer.lock
generated
604
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -141,7 +141,6 @@ if ($page == 'overview' || $page == 'domains') {
|
|||||||
WHERE `customerid` = :customerid
|
WHERE `customerid` = :customerid
|
||||||
AND `parentdomainid` = '0'
|
AND `parentdomainid` = '0'
|
||||||
AND `email_only` = '0'
|
AND `email_only` = '0'
|
||||||
AND `caneditdomain` = '1'
|
|
||||||
AND `deactivated` = '0'
|
AND `deactivated` = '0'
|
||||||
ORDER BY `domain` ASC");
|
ORDER BY `domain` ASC");
|
||||||
Database::pexecute($stmt, [
|
Database::pexecute($stmt, [
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ use Froxlor\Api\Commands\EmailAccounts;
|
|||||||
use Froxlor\Api\Commands\EmailDomains;
|
use Froxlor\Api\Commands\EmailDomains;
|
||||||
use Froxlor\Api\Commands\EmailForwarders;
|
use Froxlor\Api\Commands\EmailForwarders;
|
||||||
use Froxlor\Api\Commands\Emails;
|
use Froxlor\Api\Commands\Emails;
|
||||||
use Froxlor\Cron\Mail\Rspamd;
|
|
||||||
use Froxlor\CurrentUser;
|
use Froxlor\CurrentUser;
|
||||||
use Froxlor\Database\Database;
|
use Froxlor\Database\Database;
|
||||||
use Froxlor\FroxlorLogger;
|
use Froxlor\FroxlorLogger;
|
||||||
@@ -105,7 +104,7 @@ if ($page == 'email_domain') {
|
|||||||
$email_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.emails.php';
|
$email_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.emails.php';
|
||||||
$collection = (new Collection(Emails::class, $userinfo, $sql_search))
|
$collection = (new Collection(Emails::class, $userinfo, $sql_search))
|
||||||
->withPagination($email_list_data['email_list']['columns'],
|
->withPagination($email_list_data['email_list']['columns'],
|
||||||
$email_list_data['email_list']['default_sorting']);
|
$email_list_data['email_list']['default_sorting'], ['domainid=' . $email_domainid]);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Response::dynamicError($e->getMessage());
|
Response::dynamicError($e->getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ if ($page == 'overview' || $page == 'mysqls') {
|
|||||||
$new_password = Crypt::validatePassword(Request::post('mysql_password'));
|
$new_password = Crypt::validatePassword(Request::post('mysql_password'));
|
||||||
foreach ($allowed_mysqlservers as $dbserver) {
|
foreach ($allowed_mysqlservers as $dbserver) {
|
||||||
// require privileged access for target db-server
|
// require privileged access for target db-server
|
||||||
Database::needRoot(true, $dbserver, false);
|
Database::needRoot(true, $dbserver, true);
|
||||||
// get DbManager
|
// get DbManager
|
||||||
$dbm = new DbManager($log);
|
$dbm = new DbManager($log);
|
||||||
// give permission to the user on every access-host we have
|
// give permission to the user on every access-host we have
|
||||||
|
|||||||
@@ -391,6 +391,9 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES
|
|||||||
('antispam', 'config_file', '/etc/rspamd/local.d/froxlor_settings.conf'),
|
('antispam', 'config_file', '/etc/rspamd/local.d/froxlor_settings.conf'),
|
||||||
('antispam', 'reload_command', 'service rspamd restart'),
|
('antispam', 'reload_command', 'service rspamd restart'),
|
||||||
('antispam', 'dkim_keylength', '1024'),
|
('antispam', 'dkim_keylength', '1024'),
|
||||||
|
('antispam', 'default_bypass_spam', '2'),
|
||||||
|
('antispam', 'default_spam_rewrite_subject', '1'),
|
||||||
|
('antispam', 'default_policy_greylist', '1'),
|
||||||
('admin', 'show_news_feed', '0'),
|
('admin', 'show_news_feed', '0'),
|
||||||
('admin', 'show_version_login', '0'),
|
('admin', 'show_version_login', '0'),
|
||||||
('admin', 'show_version_footer', '0'),
|
('admin', 'show_version_footer', '0'),
|
||||||
@@ -730,8 +733,8 @@ opcache.validate_timestamps'),
|
|||||||
('panel', 'logo_overridecustom', '0'),
|
('panel', 'logo_overridecustom', '0'),
|
||||||
('panel', 'settings_mode', '0'),
|
('panel', 'settings_mode', '0'),
|
||||||
('panel', 'menu_collapsed', '1'),
|
('panel', 'menu_collapsed', '1'),
|
||||||
('panel', 'version', '2.2.2'),
|
('panel', 'version', '2.2.7'),
|
||||||
('panel', 'db_version', '202409280');
|
('panel', 'db_version', '202412030');
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `panel_tasks`;
|
DROP TABLE IF EXISTS `panel_tasks`;
|
||||||
|
|||||||
@@ -24,7 +24,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use Froxlor\Database\Database;
|
use Froxlor\Database\Database;
|
||||||
|
use Froxlor\Database\DbManager;
|
||||||
use Froxlor\Froxlor;
|
use Froxlor\Froxlor;
|
||||||
|
use Froxlor\FroxlorLogger;
|
||||||
use Froxlor\Install\Update;
|
use Froxlor\Install\Update;
|
||||||
use Froxlor\Settings;
|
use Froxlor\Settings;
|
||||||
|
|
||||||
@@ -183,3 +185,73 @@ if (Froxlor::isFroxlorVersion('2.2.1')) {
|
|||||||
Update::showUpdateStep("Updating from 2.2.1 to 2.2.2", false);
|
Update::showUpdateStep("Updating from 2.2.1 to 2.2.2", false);
|
||||||
Froxlor::updateToVersion('2.2.2');
|
Froxlor::updateToVersion('2.2.2');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isFroxlorVersion('2.2.2')) {
|
||||||
|
Update::showUpdateStep("Updating from 2.2.2 to 2.2.3", false);
|
||||||
|
Froxlor::updateToVersion('2.2.3');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isFroxlorVersion('2.2.3')) {
|
||||||
|
Update::showUpdateStep("Updating from 2.2.3 to 2.2.4", false);
|
||||||
|
Froxlor::updateToVersion('2.2.4');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isFroxlorVersion('2.2.4')) {
|
||||||
|
Update::showUpdateStep("Updating from 2.2.4 to 2.2.5", false);
|
||||||
|
Froxlor::updateToVersion('2.2.5');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isDatabaseVersion('202409280')) {
|
||||||
|
|
||||||
|
Update::showUpdateStep("Adding new antispam settings");
|
||||||
|
Settings::AddNew("antispam.default_bypass_spam", "2");
|
||||||
|
Settings::AddNew("antispam.default_spam_rewrite_subject", "1");
|
||||||
|
Settings::AddNew("antispam.default_policy_greylist", "1");
|
||||||
|
Update::lastStepStatus(0);
|
||||||
|
|
||||||
|
Froxlor::updateToDbVersion('202411200');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isDatabaseVersion('202411200')) {
|
||||||
|
|
||||||
|
Update::showUpdateStep("Adjusting customer mysql global user");
|
||||||
|
// get all customers that are not deactivated and that have at least one database (hence a global database-user)
|
||||||
|
$customers = Database::query("
|
||||||
|
SELECT DISTINCT c.loginname, c.allowed_mysqlserver
|
||||||
|
FROM `" . TABLE_PANEL_CUSTOMERS . "` c
|
||||||
|
LEFT JOIN `" . TABLE_PANEL_DATABASES . "` d ON c.customerid = d.customerid
|
||||||
|
WHERE c.deactivated = '0' AND d.id IS NOT NULL
|
||||||
|
");
|
||||||
|
while ($customer = $customers->fetch(\PDO::FETCH_ASSOC)) {
|
||||||
|
$current_allowed_mysqlserver = !empty($customer['allowed_mysqlserver']) ? json_decode($customer['allowed_mysqlserver'], true) : [];
|
||||||
|
foreach ($current_allowed_mysqlserver as $dbserver) {
|
||||||
|
// require privileged access for target db-server
|
||||||
|
Database::needRoot(true, $dbserver, false);
|
||||||
|
// get DbManager
|
||||||
|
$dbm = new DbManager(FroxlorLogger::getInstanceOf());
|
||||||
|
foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {
|
||||||
|
if ($dbm->getManager()->userExistsOnHost($customer['loginname'], $mysql_access_host)) {
|
||||||
|
// deactivate temporarily
|
||||||
|
$dbm->getManager()->disableUser($customer['loginname'], $mysql_access_host);
|
||||||
|
// re-enable
|
||||||
|
$dbm->getManager()->enableUser($customer['loginname'], $mysql_access_host, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dbm->getManager()->flushPrivileges();
|
||||||
|
Database::needRoot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Update::lastStepStatus(0);
|
||||||
|
|
||||||
|
Froxlor::updateToDbVersion('202412030');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isFroxlorVersion('2.2.5')) {
|
||||||
|
Update::showUpdateStep("Updating from 2.2.5 to 2.2.6", false);
|
||||||
|
Froxlor::updateToVersion('2.2.6');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isFroxlorVersion('2.2.6')) {
|
||||||
|
Update::showUpdateStep("Updating from 2.2.6 to 2.2.7", false);
|
||||||
|
Froxlor::updateToVersion('2.2.7');
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ abstract class ApiParameter
|
|||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function __construct(array $params = null)
|
public function __construct(?array $params = null)
|
||||||
{
|
{
|
||||||
if (!is_null($params)) {
|
if (!is_null($params)) {
|
||||||
$params = $this->trimArray($params);
|
$params = $this->trimArray($params);
|
||||||
@@ -91,7 +91,7 @@ abstract class ApiParameter
|
|||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected function getUlParam(string $param = null, string $ul_field = null, bool $optional = false, $default = 0)
|
protected function getUlParam(?string $param = null, ?string $ul_field = null, bool $optional = false, $default = 0)
|
||||||
{
|
{
|
||||||
$param_value = (int)$this->getParam($param, $optional, $default);
|
$param_value = (int)$this->getParam($param, $optional, $default);
|
||||||
$ul_field_value = $this->getBoolParam($ul_field, true, 0);
|
$ul_field_value = $this->getBoolParam($ul_field, true, 0);
|
||||||
@@ -116,7 +116,7 @@ abstract class ApiParameter
|
|||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected function getParam(string $param = null, bool $optional = false, $default = '')
|
protected function getParam(?string $param = null, bool $optional = false, $default = '')
|
||||||
{
|
{
|
||||||
// does it exist?
|
// does it exist?
|
||||||
if (!isset($this->cmd_params[$param])) {
|
if (!isset($this->cmd_params[$param])) {
|
||||||
@@ -183,7 +183,7 @@ abstract class ApiParameter
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function getBoolParam(string $param = null, bool $optional = false, $default = false)
|
protected function getBoolParam(?string $param = null, bool $optional = false, $default = false)
|
||||||
{
|
{
|
||||||
$_default = '0';
|
$_default = '0';
|
||||||
if ($default) {
|
if ($default) {
|
||||||
|
|||||||
@@ -287,6 +287,15 @@ class Admins extends ApiCommand implements ResourceEntity
|
|||||||
'login' => $loginname
|
'login' => $loginname
|
||||||
], true, true);
|
], true, true);
|
||||||
|
|
||||||
|
// Check for existing email address
|
||||||
|
// do not check via api as we skip any permission checks for this task
|
||||||
|
$email_check_admin_stmt = Database::prepare("
|
||||||
|
SELECT `email` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `email` = :email
|
||||||
|
");
|
||||||
|
$email_check_admin = Database::pexecute_first($email_check_admin_stmt, [
|
||||||
|
'email' => $email
|
||||||
|
], true, true);
|
||||||
|
|
||||||
if (($loginname_check && strtolower($loginname_check['loginname']) == strtolower($loginname)) || ($loginname_check_admin && strtolower($loginname_check_admin['loginname']) == strtolower($loginname))) {
|
if (($loginname_check && strtolower($loginname_check['loginname']) == strtolower($loginname)) || ($loginname_check_admin && strtolower($loginname_check_admin['loginname']) == strtolower($loginname))) {
|
||||||
Response::standardError('loginnameexists', $loginname, true);
|
Response::standardError('loginnameexists', $loginname, true);
|
||||||
} elseif (preg_match('/^' . preg_quote(Settings::Get('customer.accountprefix'), '/') . '([0-9]+)/', $loginname)) {
|
} elseif (preg_match('/^' . preg_quote(Settings::Get('customer.accountprefix'), '/') . '([0-9]+)/', $loginname)) {
|
||||||
@@ -298,6 +307,8 @@ class Admins extends ApiCommand implements ResourceEntity
|
|||||||
Response::standardError('loginnameiswrong', $loginname, true);
|
Response::standardError('loginnameiswrong', $loginname, true);
|
||||||
} elseif (!Validate::validateEmail($email)) {
|
} elseif (!Validate::validateEmail($email)) {
|
||||||
Response::standardError('emailiswrong', $email, true);
|
Response::standardError('emailiswrong', $email, true);
|
||||||
|
} elseif ($email_check_admin && strtolower($email_check_admin['email']) == strtolower($email)) {
|
||||||
|
Response::standardError('emailexists', $email, true);
|
||||||
} else {
|
} else {
|
||||||
if ($customers_see_all != '1') {
|
if ($customers_see_all != '1') {
|
||||||
$customers_see_all = '0';
|
$customers_see_all = '0';
|
||||||
@@ -610,8 +621,20 @@ class Admins extends ApiCommand implements ResourceEntity
|
|||||||
'admin.email'
|
'admin.email'
|
||||||
], '', true);
|
], '', true);
|
||||||
}
|
}
|
||||||
|
// Check for existing email address
|
||||||
|
// do not check via api as we skip any permission checks for this task
|
||||||
|
$email_check_admin_stmt = Database::prepare("
|
||||||
|
SELECT `email` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `email` = :email and `adminid` <> :adminid
|
||||||
|
");
|
||||||
|
$email_check_admin = Database::pexecute_first($email_check_admin_stmt, [
|
||||||
|
'email' => $email,
|
||||||
|
'adminid' => $id,
|
||||||
|
], true, true);
|
||||||
|
|
||||||
if (!Validate::validateEmail($email)) {
|
if (!Validate::validateEmail($email)) {
|
||||||
Response::standardError('emailiswrong', $email, true);
|
Response::standardError('emailiswrong', $email, true);
|
||||||
|
} elseif ($email_check_admin && strtolower($email_check_admin['email']) == strtolower($email)) {
|
||||||
|
Response::standardError('emailexists', $email, true);
|
||||||
} else {
|
} else {
|
||||||
if ($deactivated != '1') {
|
if ($deactivated != '1') {
|
||||||
$deactivated = '0';
|
$deactivated = '0';
|
||||||
|
|||||||
@@ -505,6 +505,15 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
'login' => $loginname
|
'login' => $loginname
|
||||||
], true, true);
|
], true, true);
|
||||||
|
|
||||||
|
// Check for existing email address
|
||||||
|
// do not check via api as we skip any permission checks for this task
|
||||||
|
$email_check_admin_stmt = Database::prepare("
|
||||||
|
SELECT `email` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `email` = :email
|
||||||
|
");
|
||||||
|
$email_check_admin = Database::pexecute_first($email_check_admin_stmt, [
|
||||||
|
'email' => $email
|
||||||
|
], true, true);
|
||||||
|
|
||||||
$mysql_maxlen = Database::getSqlUsernameLength() - strlen(Settings::Get('customer.mysqlprefix'));
|
$mysql_maxlen = Database::getSqlUsernameLength() - strlen(Settings::Get('customer.mysqlprefix'));
|
||||||
if (($loginname_check && strtolower($loginname_check['loginname']) == strtolower($loginname)) || ($loginname_check_admin && strtolower($loginname_check_admin['loginname']) == strtolower($loginname))) {
|
if (($loginname_check && strtolower($loginname_check['loginname']) == strtolower($loginname)) || ($loginname_check_admin && strtolower($loginname_check_admin['loginname']) == strtolower($loginname))) {
|
||||||
Response::standardError('loginnameexists', $loginname, true);
|
Response::standardError('loginnameexists', $loginname, true);
|
||||||
@@ -514,6 +523,8 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
} else {
|
} else {
|
||||||
Response::standardError('loginnameiswrong', $loginname, true);
|
Response::standardError('loginnameiswrong', $loginname, true);
|
||||||
}
|
}
|
||||||
|
} elseif ($email_check_admin && strtolower($email_check_admin['email']) == strtolower($email)) {
|
||||||
|
Response::standardError('emailexistsanon', $email, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$guid = intval(Settings::Get('system.lastguid')) + 1;
|
$guid = intval(Settings::Get('system.lastguid')) + 1;
|
||||||
@@ -828,7 +839,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
try {
|
try {
|
||||||
$this->mailer()->Subject = $mail_subject;
|
$this->mailer()->Subject = $mail_subject;
|
||||||
$this->mailer()->AltBody = $mail_body;
|
$this->mailer()->AltBody = $mail_body;
|
||||||
$this->mailer()->msgHTML(str_replace("\n", "<br />", $mail_body));
|
$this->mailer()->Body = str_replace("\n", "<br />", $mail_body);
|
||||||
$this->mailer()->addAddress($email, User::getCorrectUserSalutation([
|
$this->mailer()->addAddress($email, User::getCorrectUserSalutation([
|
||||||
'firstname' => $firstname,
|
'firstname' => $firstname,
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
@@ -1106,7 +1117,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
$email = $this->getParam('email', true, $idna_convert->decode($result['email']));
|
$email = $this->getParam('email', true, $idna_convert->decode($result['email']));
|
||||||
$name = $this->getParam('name', true, $result['name']);
|
$name = $this->getParam('name', true, $result['name']);
|
||||||
$firstname = $this->getParam('firstname', true, $result['firstname']);
|
$firstname = $this->getParam('firstname', true, $result['firstname']);
|
||||||
$company_required = (!empty($name) && empty($firstname)) || (empty($name) && !empty($firstname)) || (empty($name) && empty($firstname));
|
$company_required = ((!empty($name) && empty($firstname)) || (empty($name) && !empty($firstname)) || (empty($name) && empty($firstname))) && empty($result['company']);
|
||||||
$company = $this->getParam('company', !$company_required, $result['company']);
|
$company = $this->getParam('company', !$company_required, $result['company']);
|
||||||
$street = $this->getParam('street', true, $result['street']);
|
$street = $this->getParam('street', true, $result['street']);
|
||||||
$zipcode = $this->getParam('zipcode', true, $result['zipcode']);
|
$zipcode = $this->getParam('zipcode', true, $result['zipcode']);
|
||||||
@@ -1243,6 +1254,18 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
], '', true);
|
], '', true);
|
||||||
} elseif (!Validate::validateEmail($email)) {
|
} elseif (!Validate::validateEmail($email)) {
|
||||||
Response::standardError('emailiswrong', $email, true);
|
Response::standardError('emailiswrong', $email, true);
|
||||||
|
} else {
|
||||||
|
// Check for existing email address
|
||||||
|
// do not check via api as we skip any permission checks for this task
|
||||||
|
$email_check_admin_stmt = Database::prepare("
|
||||||
|
SELECT `email` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `email` = :email
|
||||||
|
");
|
||||||
|
$email_check_admin = Database::pexecute_first($email_check_admin_stmt, [
|
||||||
|
'email' => $email
|
||||||
|
], true, true);
|
||||||
|
if ($email_check_admin && strtolower($email_check_admin['email']) == strtolower($email)) {
|
||||||
|
Response::standardError('emailexistsanon', $email, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1347,7 +1370,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
$current_allowed_mysqlserver = isset($result['allowed_mysqlserver']) && !empty($result['allowed_mysqlserver']) ? json_decode($result['allowed_mysqlserver'], true) : [];
|
$current_allowed_mysqlserver = isset($result['allowed_mysqlserver']) && !empty($result['allowed_mysqlserver']) ? json_decode($result['allowed_mysqlserver'], true) : [];
|
||||||
foreach ($current_allowed_mysqlserver as $dbserver) {
|
foreach ($current_allowed_mysqlserver as $dbserver) {
|
||||||
// require privileged access for target db-server
|
// require privileged access for target db-server
|
||||||
Database::needRoot(true, $dbserver, false);
|
Database::needRoot(true, $dbserver, true);
|
||||||
// get DbManager
|
// get DbManager
|
||||||
$dbm = new DbManager($this->logger());
|
$dbm = new DbManager($this->logger());
|
||||||
foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {
|
foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ class DomainZones extends ApiCommand implements ResourceEntity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif ($type == 'CAA' && !empty($content)) {
|
} elseif ($type == 'CAA' && !empty($content)) {
|
||||||
$re = '/(?\'critical\'\d)\h*(?\'type\'iodef|issue|issuewild)\h*(?\'value\'(?\'issuevalue\'"(?\'domain\'(?=.{3,128}$)(?>(?>[a-zA-Z0-9]+[a-zA-Z0-9-]*[a-zA-Z0-9]+|[a-zA-Z0-9]+)\.)*(?>[a-zA-Z]{2,}|[a-zA-Z0-9]{2,}\.[a-zA-Z]{2,}))[;\h]*(?\'parameters\'(?>[a-zA-Z0-9]{1,60}=[a-zA-Z0-9]{1,60}\h*)+)?")|(?\'iodefvalue\'"(?\'url\'(mailto:.*|http:\/\/.*|https:\/\/.*))"))/';
|
$re = '/(?\'critical\'\d+)\h*(?\'type\'iodef|issue|issuewild)\h*(?\'value\'(?\'issuevalue\'"(?\'domain\'(?=.{3,128}$)(?>(?>[a-zA-Z0-9]+[a-zA-Z0-9-]*[a-zA-Z0-9]+|[a-zA-Z0-9]+)\.)*(?>[a-zA-Z]{2,}|[a-zA-Z0-9]{2,}\.[a-zA-Z]{2,}))[;\h]*(?\'parameters\'(?>[a-zA-Z0-9]{1,60}=[a-zA-Z0-9:\.\/\-]{1,60}\h*)+)?")|(?\'iodefvalue\'"(?\'url\'(mailto:.*|http:\/\/.*|https:\/\/.*))"))/';
|
||||||
preg_match($re, $content, $matches);
|
preg_match($re, $content, $matches);
|
||||||
|
|
||||||
if (empty($matches)) {
|
if (empty($matches)) {
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
* @param string $zonefile
|
* @param string $zonefile
|
||||||
* optional, custom dns zone filename (only of nameserver is activated), default empty (auto-generated)
|
* optional, custom dns zone filename (only of nameserver is activated), default empty (auto-generated)
|
||||||
* @param bool $dkim
|
* @param bool $dkim
|
||||||
* optional, currently not in use, default 0 (false)
|
* optional, whether this domain should use dkim if antispam is activated, default 0 (false)
|
||||||
* @param string $specialsettings
|
* @param string $specialsettings
|
||||||
* optional, custom webserver vhost-content which is added to the generated vhost, default empty
|
* optional, custom webserver vhost-content which is added to the generated vhost, default empty
|
||||||
* @param string $ssl_specialsettings
|
* @param string $ssl_specialsettings
|
||||||
@@ -474,7 +474,6 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
}
|
}
|
||||||
$caneditdomain = '1';
|
$caneditdomain = '1';
|
||||||
$zonefile = '';
|
$zonefile = '';
|
||||||
$dkim = '0';
|
|
||||||
$specialsettings = '';
|
$specialsettings = '';
|
||||||
$ssl_specialsettings = '';
|
$ssl_specialsettings = '';
|
||||||
$include_specialsettings = 0;
|
$include_specialsettings = 0;
|
||||||
@@ -550,8 +549,11 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Settings::Get('system.use_ssl') == "1" && $sslenabled == 1 && empty($ssl_ipandports)) {
|
if (Settings::Get('system.use_ssl') == "1" && $sslenabled == 1 && empty($ssl_ipandports)) {
|
||||||
// enabled ssl for the domain but no ssl ip/port is selected
|
// if this is a customer standard-subdomain, we simply ignore this and disable ssl-related settings (see if-statement below)
|
||||||
Response::standardError('nosslippportgiven', '', true);
|
if (!$is_stdsubdomain) {
|
||||||
|
// enabled ssl for the domain but no ssl ip/port is selected
|
||||||
|
Response::standardError('nosslippportgiven', '', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Settings::Get('system.use_ssl') == "0" || empty($ssl_ipandports)) {
|
if (Settings::Get('system.use_ssl') == "0" || empty($ssl_ipandports)) {
|
||||||
$ssl_redirect = 0;
|
$ssl_redirect = 0;
|
||||||
@@ -593,12 +595,18 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
$ssl_redirect = 2;
|
$ssl_redirect = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preg_match('/^https?\:\/\//', $documentroot)) {
|
// Check if given documentroot is either a valid URL or a valid path
|
||||||
if (strstr($documentroot, ":") !== false) {
|
if (preg_match('/^https?\:\/\//', $documentroot)) {
|
||||||
Response::standardError('pathmaynotcontaincolon', '', true);
|
$encoded = $idna_convert->encode($documentroot);
|
||||||
} else {
|
if (!Validate::validateUrl($encoded, true)) {
|
||||||
$documentroot = FileDir::makeCorrectDir($documentroot);
|
Response::standardError('invaliddocumentrooturl', '', true);
|
||||||
}
|
}
|
||||||
|
$documentroot = $encoded;
|
||||||
|
} else {
|
||||||
|
if (strpos($documentroot, ':') !== false) {
|
||||||
|
Response::standardError('pathmaynotcontaincolon', '', true);
|
||||||
|
}
|
||||||
|
$documentroot = FileDir::makeCorrectDir($documentroot);
|
||||||
}
|
}
|
||||||
|
|
||||||
$domain_check_stmt = Database::prepare("
|
$domain_check_stmt = Database::prepare("
|
||||||
@@ -1063,6 +1071,9 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
* (default yes), 3 = always, default 0 (never)
|
* (default yes), 3 = always, default 0 (never)
|
||||||
* @param bool $isemaildomain
|
* @param bool $isemaildomain
|
||||||
* optional, allow email usage with this domain, default 0 (false)
|
* optional, allow email usage with this domain, default 0 (false)
|
||||||
|
* @param bool $emaildomainverified
|
||||||
|
* optional, when setting $isemaildomain to false, this needs to be set to true to confirm the action in case email addresses exist for this domain,
|
||||||
|
* default 0 (false)
|
||||||
* @param bool $email_only
|
* @param bool $email_only
|
||||||
* optional, restrict domain to email usage, default 0 (false)
|
* optional, restrict domain to email usage, default 0 (false)
|
||||||
* @param int $selectserveralias
|
* @param int $selectserveralias
|
||||||
@@ -1085,7 +1096,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
* @param string $zonefile
|
* @param string $zonefile
|
||||||
* optional, custom dns zone filename (only of nameserver is activated), default empty (auto-generated)
|
* optional, custom dns zone filename (only of nameserver is activated), default empty (auto-generated)
|
||||||
* @param bool $dkim
|
* @param bool $dkim
|
||||||
* optional, currently not in use, default 0 (false)
|
* optional, whether this domain should use dkim if antispam is activated, default 0 (false)
|
||||||
* @param string $specialsettings
|
* @param string $specialsettings
|
||||||
* optional, custom webserver vhost-content which is added to the generated vhost, default empty
|
* optional, custom webserver vhost-content which is added to the generated vhost, default empty
|
||||||
* @param string $ssl_specialsettings
|
* @param string $ssl_specialsettings
|
||||||
@@ -1190,6 +1201,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
|
|
||||||
$subcanemaildomain = $this->getParam('subcanemaildomain', true, $result['subcanemaildomain']);
|
$subcanemaildomain = $this->getParam('subcanemaildomain', true, $result['subcanemaildomain']);
|
||||||
$isemaildomain = $this->getBoolParam('isemaildomain', true, $result['isemaildomain']);
|
$isemaildomain = $this->getBoolParam('isemaildomain', true, $result['isemaildomain']);
|
||||||
|
$emaildomainverified = $this->getBoolParam('emaildomainverified', true, 0);
|
||||||
$email_only = $this->getBoolParam('email_only', true, $result['email_only']);
|
$email_only = $this->getBoolParam('email_only', true, $result['email_only']);
|
||||||
$p_serveraliasoption = $this->getParam('selectserveralias', true, -1);
|
$p_serveraliasoption = $this->getParam('selectserveralias', true, -1);
|
||||||
$speciallogfile = $this->getBoolParam('speciallogfile', true, $result['speciallogfile']);
|
$speciallogfile = $this->getBoolParam('speciallogfile', true, $result['speciallogfile']);
|
||||||
@@ -1273,7 +1285,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
|
|
||||||
// count where we are used in email-accounts
|
// count where we are used in email-accounts
|
||||||
$domain_emails_result_stmt = Database::prepare("
|
$domain_emails_result_stmt = Database::prepare("
|
||||||
SELECT `email`, `email_full`, `destination`, `popaccountid` AS `number_email_forwarders`
|
SELECT `email`, `email_full`, `destination`, `popaccountid`
|
||||||
FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid` = :customerid AND `domainid` = :id
|
FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid` = :customerid AND `domainid` = :id
|
||||||
");
|
");
|
||||||
Database::pexecute($domain_emails_result_stmt, [
|
Database::pexecute($domain_emails_result_stmt, [
|
||||||
@@ -1296,6 +1308,10 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($emails > 0 && (int)$isemaildomain == 0 && (int)$result['isemaildomain'] == 1 && (int)$emaildomainverified == 0) {
|
||||||
|
Response::standardError('emaildomainstillhasaddresses', '', true);
|
||||||
|
}
|
||||||
|
|
||||||
// handle change of customer (move domain from customer to customer)
|
// handle change of customer (move domain from customer to customer)
|
||||||
if ($customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') {
|
if ($customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') {
|
||||||
// check whether target customer has enough resources
|
// check whether target customer has enough resources
|
||||||
@@ -1404,10 +1420,6 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preg_match('/^https?\:\/\//', $documentroot) && strstr($documentroot, ":") !== false) {
|
|
||||||
Response::standardError('pathmaynotcontaincolon', '', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->getUserDetail('change_serversettings') == '1') {
|
if ($this->getUserDetail('change_serversettings') == '1') {
|
||||||
if (Settings::Get('system.bind_enable') == '1') {
|
if (Settings::Get('system.bind_enable') == '1') {
|
||||||
$zonefile = Validate::validate($zonefile, 'zonefile', '', '', [], true);
|
$zonefile = Validate::validate($zonefile, 'zonefile', '', '', [], true);
|
||||||
@@ -1452,7 +1464,6 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
} else {
|
} else {
|
||||||
$isbinddomain = $result['isbinddomain'];
|
$isbinddomain = $result['isbinddomain'];
|
||||||
$zonefile = $result['zonefile'];
|
$zonefile = $result['zonefile'];
|
||||||
$dkim = $result['dkim'];
|
|
||||||
$specialsettings = $result['specialsettings'];
|
$specialsettings = $result['specialsettings'];
|
||||||
$ssl_specialsettings = $result['ssl_specialsettings'];
|
$ssl_specialsettings = $result['ssl_specialsettings'];
|
||||||
$include_specialsettings = $result['include_specialsettings'];
|
$include_specialsettings = $result['include_specialsettings'];
|
||||||
@@ -1571,15 +1582,25 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated
|
// Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated
|
||||||
if (($result['letsencrypt'] != $letsencrypt || $result['ssl_redirect'] != $ssl_redirect) && $ssl_redirect > 0 && $letsencrypt == 1) {
|
if ($result['letsencrypt'] != $letsencrypt && $ssl_redirect > 0 && $letsencrypt == 1) {
|
||||||
$ssl_redirect = 2;
|
$ssl_redirect = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preg_match('/^https?\:\/\//', $documentroot)) {
|
$idna_convert = new IdnaWrapper();
|
||||||
if ($documentroot != $result['documentroot']) {
|
if ($documentroot != $result['documentroot']) {
|
||||||
|
if (preg_match('/^https?\:\/\//', $documentroot)) {
|
||||||
|
$encoded = $idna_convert->encode($documentroot);
|
||||||
|
if (!Validate::validateUrl($encoded, true)) {
|
||||||
|
Response::standardError('invaliddocumentrooturl', '', true);
|
||||||
|
}
|
||||||
|
$documentroot = $encoded;
|
||||||
|
} else {
|
||||||
if (substr($documentroot, 0, 1) != "/") {
|
if (substr($documentroot, 0, 1) != "/") {
|
||||||
$documentroot = $customer['documentroot'] . '/' . $documentroot;
|
$documentroot = $customer['documentroot'] . '/' . $documentroot;
|
||||||
}
|
}
|
||||||
|
if (strpos($documentroot, ':') !== false) {
|
||||||
|
Response::standardError('pathmaynotcontaincolon', '', true);
|
||||||
|
}
|
||||||
$documentroot = FileDir::makeCorrectDir($documentroot);
|
$documentroot = FileDir::makeCorrectDir($documentroot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2090,7 +2111,6 @@ class Domains extends ApiCommand implements ResourceEntity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$idna_convert = new IdnaWrapper();
|
|
||||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] updated domain '" . $idna_convert->decode($result['domain']) . "'");
|
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] updated domain '" . $idna_convert->decode($result['domain']) . "'");
|
||||||
$result = $this->apiCall('Domains.get', [
|
$result = $this->apiCall('Domains.get', [
|
||||||
'domainname' => $result['domain']
|
'domainname' => $result['domain']
|
||||||
|
|||||||
@@ -260,10 +260,12 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
|||||||
$_mailerror = false;
|
$_mailerror = false;
|
||||||
$mailerr_msg = "";
|
$mailerr_msg = "";
|
||||||
try {
|
try {
|
||||||
$this->mailer()->setFrom($admin['email'], User::getCorrectUserSalutation($admin));
|
$this->mailer()->setFrom(Settings::Get('panel.adminmail'), User::getCorrectUserSalutation($admin));
|
||||||
|
$this->mailer()->clearReplyTos();
|
||||||
|
$this->mailer()->addReplyTo($admin['email'], User::getCorrectUserSalutation($admin));
|
||||||
$this->mailer()->Subject = $mail_subject;
|
$this->mailer()->Subject = $mail_subject;
|
||||||
$this->mailer()->AltBody = $mail_body;
|
$this->mailer()->AltBody = $mail_body;
|
||||||
$this->mailer()->msgHTML(str_replace("\n", "<br />", $mail_body));
|
$this->mailer()->Body = str_replace("\n", "<br />", $mail_body);
|
||||||
$this->mailer()->addAddress($email_full);
|
$this->mailer()->addAddress($email_full);
|
||||||
$this->mailer()->send();
|
$this->mailer()->send();
|
||||||
} catch (\PHPMailer\PHPMailer\Exception $e) {
|
} catch (\PHPMailer\PHPMailer\Exception $e) {
|
||||||
@@ -290,7 +292,9 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
|||||||
|
|
||||||
$_mailerror = false;
|
$_mailerror = false;
|
||||||
try {
|
try {
|
||||||
$this->mailer()->setFrom($admin['email'], User::getCorrectUserSalutation($admin));
|
$this->mailer()->setFrom(Settings::Get('panel.adminmail'), User::getCorrectUserSalutation($admin));
|
||||||
|
$this->mailer()->clearReplyTos();
|
||||||
|
$this->mailer()->addReplyTo($admin['email'], User::getCorrectUserSalutation($admin));
|
||||||
$this->mailer()->Subject = $mail_subject;
|
$this->mailer()->Subject = $mail_subject;
|
||||||
$this->mailer()->AltBody = $mail_body;
|
$this->mailer()->AltBody = $mail_body;
|
||||||
$this->mailer()->msgHTML(str_replace("\n", "<br />", $mail_body));
|
$this->mailer()->msgHTML(str_replace("\n", "<br />", $mail_body));
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ class Emails extends ApiCommand implements ResourceEntity
|
|||||||
* @param float $spam_tag_level
|
* @param float $spam_tag_level
|
||||||
* optional, score which is required to tag emails as spam, default: 7.0
|
* optional, score which is required to tag emails as spam, default: 7.0
|
||||||
* @param bool $rewrite_subject
|
* @param bool $rewrite_subject
|
||||||
* optional, whether to add ***SPAM*** to the email's subject if applicable, default true
|
* optional, whether to add ***SPAM*** to the email's subject if applicable, default: [antispam.default_spam_rewrite_subject]
|
||||||
* @param float $spam_kill_level
|
* @param float $spam_kill_level
|
||||||
* optional, score which is required to discard emails, default: 14.0
|
* optional, score which is required to discard emails, default: 14.0
|
||||||
* @param boolean $bypass_spam
|
* @param boolean $bypass_spam
|
||||||
* optional, disable spam-filter entirely, default: no
|
* optional, disable spam-filter entirely, default: [antispam.default_bypass_spam]
|
||||||
* @param boolean $policy_greylist
|
* @param boolean $policy_greylist
|
||||||
* optional, enable grey-listing, default: yes
|
* optional, enable grey-listing, default: [antispam.default_policy_greylist]
|
||||||
* @param boolean $iscatchall
|
* @param boolean $iscatchall
|
||||||
* optional, make this address a catchall address, default: no
|
* optional, make this address a catchall address, default: no
|
||||||
* @param int $customerid
|
* @param int $customerid
|
||||||
@@ -87,13 +87,26 @@ class Emails extends ApiCommand implements ResourceEntity
|
|||||||
|
|
||||||
// parameters
|
// parameters
|
||||||
$spam_tag_level = $this->getParam('spam_tag_level', true, '7.0');
|
$spam_tag_level = $this->getParam('spam_tag_level', true, '7.0');
|
||||||
$rewrite_subject = $this->getBoolParam('rewrite_subject', true, 1);
|
|
||||||
$spam_kill_level = $this->getUlParam('spam_kill_level', 'spam_kill_level_ul', true, '14.0');
|
$spam_kill_level = $this->getUlParam('spam_kill_level', 'spam_kill_level_ul', true, '14.0');
|
||||||
$bypass_spam = $this->getBoolParam('bypass_spam', true, 0);
|
|
||||||
$policy_greylist = $this->getBoolParam('policy_greylist', true, 1);
|
|
||||||
$iscatchall = $this->getBoolParam('iscatchall', true, 0);
|
$iscatchall = $this->getBoolParam('iscatchall', true, 0);
|
||||||
$description = $this->getParam('description', true, '');
|
$description = $this->getParam('description', true, '');
|
||||||
|
|
||||||
|
if ((int)Settings::Get('antispam.default_spam_rewrite_subject') <= 2) {
|
||||||
|
$rewrite_subject = $this->getBoolParam('rewrite_subject', true, (int)Settings::Get('antispam.default_spam_rewrite_subject') == 1 ? 1 : 0);
|
||||||
|
} else {
|
||||||
|
$rewrite_subject = (int)Settings::Get('antispam.default_spam_rewrite_subject') == 3 ? 1 : 0;
|
||||||
|
}
|
||||||
|
if ((int)Settings::Get('antispam.default_bypass_spam') <= 2) {
|
||||||
|
$bypass_spam = $this->getBoolParam('bypass_spam', true, (int)Settings::Get('antispam.default_bypass_spam') == 1 ? 1 : 0);
|
||||||
|
} else {
|
||||||
|
$bypass_spam = (int)Settings::Get('antispam.default_bypass_spam') == 3 ? 1 : 0;
|
||||||
|
}
|
||||||
|
if ((int)Settings::Get('antispam.default_policy_greylist') <= 2) {
|
||||||
|
$policy_greylist = $this->getBoolParam('policy_greylist', true, (int)Settings::Get('antispam.default_policy_greylist') == 1 ? 1 : 0);
|
||||||
|
} else {
|
||||||
|
$policy_greylist = (int)Settings::Get('antispam.default_policy_greylist') == 3 ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
// validation
|
// validation
|
||||||
$idna_convert = new IdnaWrapper();
|
$idna_convert = new IdnaWrapper();
|
||||||
if (substr($domain, 0, 4) != 'xn--') {
|
if (substr($domain, 0, 4) != 'xn--') {
|
||||||
@@ -258,13 +271,13 @@ class Emails extends ApiCommand implements ResourceEntity
|
|||||||
* @param float $spam_tag_level
|
* @param float $spam_tag_level
|
||||||
* optional, score which is required to tag emails as spam, default: 7.0
|
* optional, score which is required to tag emails as spam, default: 7.0
|
||||||
* @param bool $rewrite_subject
|
* @param bool $rewrite_subject
|
||||||
* optional, whether to add ***SPAM*** to the email's subject if applicable, default true
|
* optional, whether to add ***SPAM*** to the email's subject if applicable, default: [antispam.default_spam_rewrite_subject]
|
||||||
* @param float $spam_kill_level
|
* @param float $spam_kill_level
|
||||||
* optional, score which is required to discard emails, default: 14.0
|
* optional, score which is required to discard emails, default: 14.0
|
||||||
* @param boolean $bypass_spam
|
* @param boolean $bypass_spam
|
||||||
* optional, disable spam-filter entirely, default: no
|
* optional, disable spam-filter entirely, default: [antispam.default_bypass_spam]
|
||||||
* @param boolean $policy_greylist
|
* @param boolean $policy_greylist
|
||||||
* optional, enable grey-listing, default: yes
|
* optional, enable grey-listing, default: [antispam.default_policy_greylist]
|
||||||
* @param boolean $iscatchall
|
* @param boolean $iscatchall
|
||||||
* optional
|
* optional
|
||||||
* @param string $description
|
* @param string $description
|
||||||
@@ -292,13 +305,26 @@ class Emails extends ApiCommand implements ResourceEntity
|
|||||||
|
|
||||||
// parameters
|
// parameters
|
||||||
$spam_tag_level = $this->getParam('spam_tag_level', true, $result['spam_tag_level']);
|
$spam_tag_level = $this->getParam('spam_tag_level', true, $result['spam_tag_level']);
|
||||||
$rewrite_subject = $this->getBoolParam('rewrite_subject', true, $result['rewrite_subject']);
|
|
||||||
$spam_kill_level = $this->getUlParam('spam_kill_level', 'spam_kill_level_ul', true, $result['spam_kill_level']);
|
$spam_kill_level = $this->getUlParam('spam_kill_level', 'spam_kill_level_ul', true, $result['spam_kill_level']);
|
||||||
$bypass_spam = $this->getBoolParam('bypass_spam', true, $result['bypass_spam']);
|
|
||||||
$policy_greylist = $this->getBoolParam('policy_greylist', true, $result['policy_greylist']);
|
|
||||||
$iscatchall = $this->getBoolParam('iscatchall', true, $result['iscatchall']);
|
$iscatchall = $this->getBoolParam('iscatchall', true, $result['iscatchall']);
|
||||||
$description = $this->getParam('description', true, $result['description']);
|
$description = $this->getParam('description', true, $result['description']);
|
||||||
|
|
||||||
|
if ((int)Settings::Get('antispam.default_spam_rewrite_subject') <= 2) {
|
||||||
|
$rewrite_subject = $this->getBoolParam('rewrite_subject', true, $result['rewrite_subject']);
|
||||||
|
} else {
|
||||||
|
$rewrite_subject = (int)Settings::Get('antispam.default_spam_rewrite_subject') == 3 ? 1 : 0;
|
||||||
|
}
|
||||||
|
if ((int)Settings::Get('antispam.default_bypass_spam') <= 2) {
|
||||||
|
$bypass_spam = $this->getBoolParam('bypass_spam', true, $result['bypass_spam']);
|
||||||
|
} else {
|
||||||
|
$bypass_spam = (int)Settings::Get('antispam.default_bypass_spam') == 3 ? 1 : 0;
|
||||||
|
}
|
||||||
|
if ((int)Settings::Get('antispam.default_policy_greylist') <= 2) {
|
||||||
|
$policy_greylist = $this->getBoolParam('policy_greylist', true, $result['policy_greylist']);
|
||||||
|
} else {
|
||||||
|
$policy_greylist = (int)Settings::Get('antispam.default_policy_greylist') == 3 ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
// if enabling catchall is not allowed by settings, we do not need
|
// if enabling catchall is not allowed by settings, we do not need
|
||||||
// to run update()
|
// to run update()
|
||||||
if ($iscatchall && $result['iscatchall'] == 0 && Settings::Get('catchall.catchall_enabled') != '1') {
|
if ($iscatchall && $result['iscatchall'] == 0 && Settings::Get('catchall.catchall_enabled') != '1') {
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
|||||||
try {
|
try {
|
||||||
$this->mailer()->Subject = $mail_subject;
|
$this->mailer()->Subject = $mail_subject;
|
||||||
$this->mailer()->AltBody = $mail_body;
|
$this->mailer()->AltBody = $mail_body;
|
||||||
$this->mailer()->msgHTML(str_replace("\n", "<br />", $mail_body));
|
$this->mailer()->Body = str_replace("\n", "<br />", $mail_body);
|
||||||
$this->mailer()->addAddress($customer['email'], User::getCorrectUserSalutation($customer));
|
$this->mailer()->addAddress($customer['email'], User::getCorrectUserSalutation($customer));
|
||||||
$this->mailer()->send();
|
$this->mailer()->send();
|
||||||
} catch (\PHPMailer\PHPMailer\Exception $e) {
|
} catch (\PHPMailer\PHPMailer\Exception $e) {
|
||||||
|
|||||||
@@ -113,9 +113,9 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
|||||||
if (strlen($newdb_params['loginname'] . '_' . $databasename) > Database::getSqlUsernameLength()) {
|
if (strlen($newdb_params['loginname'] . '_' . $databasename) > Database::getSqlUsernameLength()) {
|
||||||
throw new Exception("Database name cannot be longer than " . (Database::getSqlUsernameLength() - strlen($newdb_params['loginname'] . '_')) . " characters.", 406);
|
throw new Exception("Database name cannot be longer than " . (Database::getSqlUsernameLength() - strlen($newdb_params['loginname'] . '_')) . " characters.", 406);
|
||||||
}
|
}
|
||||||
$username = $dbm->createDatabase($newdb_params['loginname'] . '_' . $databasename, $password, $dbserver);
|
$username = $dbm->createDatabase($newdb_params['loginname'] . '_' . $databasename, $password, $dbserver, 0, $newdb_params['loginname']);
|
||||||
} else {
|
} else {
|
||||||
$username = $dbm->createDatabase($newdb_params['loginname'], $password, $dbserver, $newdb_params['mysql_lastaccountnumber']);
|
$username = $dbm->createDatabase($newdb_params['loginname'], $password, $dbserver, $newdb_params['mysql_lastaccountnumber'], $newdb_params['loginname']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we've checked against the password in dbm->createDatabase
|
// we've checked against the password in dbm->createDatabase
|
||||||
@@ -184,7 +184,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
|||||||
try {
|
try {
|
||||||
$this->mailer()->Subject = $mail_subject;
|
$this->mailer()->Subject = $mail_subject;
|
||||||
$this->mailer()->AltBody = $mail_body;
|
$this->mailer()->AltBody = $mail_body;
|
||||||
$this->mailer()->msgHTML(str_replace("\n", "<br />", $mail_body));
|
$this->mailer()->Body = str_replace("\n", "<br />", $mail_body);
|
||||||
$this->mailer()->addAddress($userinfo['email'], User::getCorrectUserSalutation($userinfo));
|
$this->mailer()->addAddress($userinfo['email'], User::getCorrectUserSalutation($userinfo));
|
||||||
$this->mailer()->send();
|
$this->mailer()->send();
|
||||||
} catch (\PHPMailer\PHPMailer\Exception $e) {
|
} catch (\PHPMailer\PHPMailer\Exception $e) {
|
||||||
@@ -541,7 +541,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
|||||||
// Begin root-session
|
// Begin root-session
|
||||||
Database::needRoot(true, $result['dbserver'], false);
|
Database::needRoot(true, $result['dbserver'], false);
|
||||||
$dbm = new DbManager($this->logger());
|
$dbm = new DbManager($this->logger());
|
||||||
$dbm->getManager()->deleteDatabase($result['databasename']);
|
$dbm->getManager()->deleteDatabase($result['databasename'], $customer['loginname']);
|
||||||
Database::needRoot(false);
|
Database::needRoot(false);
|
||||||
// End root-session
|
// End root-session
|
||||||
|
|
||||||
|
|||||||
@@ -503,8 +503,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
|||||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get subdomain '" . $result['domain'] . "'");
|
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get subdomain '" . $result['domain'] . "'");
|
||||||
return $this->response($result);
|
return $this->response($result);
|
||||||
}
|
}
|
||||||
$key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'");
|
throw new Exception("Requested subdomain could not be found", 404);
|
||||||
throw new Exception("Subdomain with " . $key . " could not be found", 404);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getHasCertValueForDomain(int $domainid, int $parentdomainid): int
|
private function getHasCertValueForDomain(int $domainid, int $parentdomainid): int
|
||||||
@@ -550,32 +549,33 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
|||||||
*/
|
*/
|
||||||
private function validateDomainDocumentRoot($path = null, $url = null, $customer = null, $completedomain = null, &$_doredirect = false)
|
private function validateDomainDocumentRoot($path = null, $url = null, $customer = null, $completedomain = null, &$_doredirect = false)
|
||||||
{
|
{
|
||||||
// check whether an URL was specified
|
|
||||||
$_doredirect = false;
|
$_doredirect = false;
|
||||||
if (!empty($url) && Validate::validateUrl($url, true)) {
|
$idna = new IdnaWrapper();
|
||||||
$path = $url;
|
|
||||||
|
// url mode: either $url or $path begins with http:// or https://
|
||||||
|
$maybeUrl = !empty($url) ? $url : (preg_match('/^https?\:\/\//', $path) ? $path : '');
|
||||||
|
if ($maybeUrl !== '') {
|
||||||
|
$encoded = $idna->encode($maybeUrl);
|
||||||
|
if (!Validate::validateUrl($encoded, true)) {
|
||||||
|
Response::standardError('invaliddocumentrooturl', '', true);
|
||||||
|
}
|
||||||
$_doredirect = true;
|
$_doredirect = true;
|
||||||
} else {
|
return $encoded;
|
||||||
$path = Validate::validate($path, 'path', '', '', [], true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether path is a real path
|
// path mode: regular directory path
|
||||||
if (!preg_match('/^https?\:\/\//', $path) || !Validate::validateUrl($path, true)) {
|
$path = Validate::validate($path, 'path', Validate::REGEX_DIR, '', [], true);
|
||||||
if (strstr($path, ":") !== false) {
|
|
||||||
Response::standardError('pathmaynotcontaincolon', '', true);
|
// default path if empty and setting active
|
||||||
}
|
if (($path === '' || $path === '/') && Settings::Get('system.documentroot_use_default_value') == 1) {
|
||||||
// If path is empty or '/' and 'Use domain name as default value for DocumentRoot path' is enabled in settings,
|
return FileDir::makeCorrectDir($customer['documentroot'] . '/' . $completedomain, $customer['documentroot']);
|
||||||
// set default path to subdomain or domain name
|
|
||||||
if ((($path == '') || ($path == '/')) && Settings::Get('system.documentroot_use_default_value') == 1) {
|
|
||||||
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $completedomain, $customer['documentroot']);
|
|
||||||
} else {
|
|
||||||
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no it's not, create a redirect
|
|
||||||
$_doredirect = true;
|
|
||||||
}
|
}
|
||||||
return $path;
|
// check if path does not contain a colon
|
||||||
|
if (strpos($path, ':') !== false) {
|
||||||
|
Response::standardError('pathmaynotcontaincolon', '', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ class Response
|
|||||||
|
|
||||||
public static function jsonResponse($data = null, int $response_code = 200)
|
public static function jsonResponse($data = null, int $response_code = 200)
|
||||||
{
|
{
|
||||||
http_response_code($response_code);
|
if (!defined('TRAVIS_CI') || TRAVIS_CI == 0) {
|
||||||
|
http_response_code($response_code);
|
||||||
|
}
|
||||||
|
|
||||||
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
|
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ final class MasterCron extends CliCommand
|
|||||||
Cronjob::inserttask(TaskId::REBUILD_RSPAMD);
|
Cronjob::inserttask(TaskId::REBUILD_RSPAMD);
|
||||||
Cronjob::inserttask(TaskId::CREATE_QUOTA);
|
Cronjob::inserttask(TaskId::CREATE_QUOTA);
|
||||||
Cronjob::inserttask(TaskId::REBUILD_CRON);
|
Cronjob::inserttask(TaskId::REBUILD_CRON);
|
||||||
|
Cronjob::inserttask(TaskId::UPDATE_LE_SERVICES);
|
||||||
$jobs[] = 'tasks';
|
$jobs[] = 'tasks';
|
||||||
}
|
}
|
||||||
define('CRON_IS_FORCED', 1);
|
define('CRON_IS_FORCED', 1);
|
||||||
@@ -214,9 +215,14 @@ final class MasterCron extends CliCommand
|
|||||||
|
|
||||||
if (file_exists($this->lockFile)) {
|
if (file_exists($this->lockFile)) {
|
||||||
$jobinfo = json_decode(file_get_contents($this->lockFile), true);
|
$jobinfo = json_decode(file_get_contents($this->lockFile), true);
|
||||||
$check_pid_return = null;
|
if ($jobinfo === false || !is_array($jobinfo)) {
|
||||||
// get status of process
|
// looks like an invalid lockfile
|
||||||
system("kill -CHLD " . (int)$jobinfo['pid'] . " 1> /dev/null 2> /dev/null", $check_pid_return);
|
$check_pid_return = 1;
|
||||||
|
} else {
|
||||||
|
$check_pid_return = null;
|
||||||
|
// get status of process
|
||||||
|
system("kill -CHLD " . (int)$jobinfo['pid'] . " 1> /dev/null 2> /dev/null", $check_pid_return);
|
||||||
|
}
|
||||||
if ($check_pid_return == 1) {
|
if ($check_pid_return == 1) {
|
||||||
// Process does not seem to run, most likely it has died
|
// Process does not seem to run, most likely it has died
|
||||||
$this->unlockJob();
|
$this->unlockJob();
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ class AcmeSh extends FroxlorCron
|
|||||||
* run the task
|
* run the task
|
||||||
*
|
*
|
||||||
* @param bool $internal
|
* @param bool $internal
|
||||||
* @return number
|
* @return int
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function run(bool $internal = false)
|
public static function run(bool $internal = false)
|
||||||
{
|
{
|
||||||
@@ -85,6 +86,9 @@ class AcmeSh extends FroxlorCron
|
|||||||
if ($issue_froxlor || !empty($issue_domains) || !empty($renew_froxlor) || $renew_domains) {
|
if ($issue_froxlor || !empty($issue_domains) || !empty($renew_froxlor) || $renew_domains) {
|
||||||
// insert task to generate certificates and vhost-configs
|
// insert task to generate certificates and vhost-configs
|
||||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||||
|
if ($renew_froxlor) {
|
||||||
|
Cronjob::inserttask(TaskId::UPDATE_LE_SERVICES);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -217,6 +221,7 @@ class AcmeSh extends FroxlorCron
|
|||||||
* check whether we need to issue a new certificate for froxlor itself
|
* check whether we need to issue a new certificate for froxlor itself
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
private static function issueFroxlorVhost()
|
private static function issueFroxlorVhost()
|
||||||
{
|
{
|
||||||
@@ -228,9 +233,7 @@ 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 && empty($froxlor_ssl['validtodate']))
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,6 +343,7 @@ EOC;
|
|||||||
* check whether we need to renew-check the certificate for froxlor itself
|
* check whether we need to renew-check the certificate for froxlor itself
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
private static function renewFroxlorVhost()
|
private static function renewFroxlorVhost()
|
||||||
{
|
{
|
||||||
@@ -391,6 +395,7 @@ EOC;
|
|||||||
AND dom.`aliasdomain` IS NULL
|
AND dom.`aliasdomain` IS NULL
|
||||||
AND dom.`iswildcarddomain` = 0
|
AND dom.`iswildcarddomain` = 0
|
||||||
AND dom.`email_only` = 0
|
AND dom.`email_only` = 0
|
||||||
|
AND dom.`ssl_redirect` != 2
|
||||||
");
|
");
|
||||||
$renew_certs = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC);
|
$renew_certs = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
if ($renew_certs) {
|
if ($renew_certs) {
|
||||||
@@ -539,6 +544,7 @@ EOC;
|
|||||||
* @param array $domains
|
* @param array $domains
|
||||||
* @param int $domain_id
|
* @param int $domain_id
|
||||||
* @param FroxlorLogger $cronlog
|
* @param FroxlorLogger $cronlog
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
private static function validateDns(array &$domains, $domain_id, &$cronlog)
|
private static function validateDns(array &$domains, $domain_id, &$cronlog)
|
||||||
{
|
{
|
||||||
@@ -619,27 +625,47 @@ EOC;
|
|||||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, "Successful exit-code returned - storing certificate");
|
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, "Successful exit-code returned - storing certificate");
|
||||||
$cert_stored = self::certToDb($certrow, $cronlog, $acme_result);
|
$cert_stored = self::certToDb($certrow, $cronlog, $acme_result);
|
||||||
|
|
||||||
if ($cert_stored
|
if ($cert_stored && $renew_hook) {
|
||||||
&& $renew_hook
|
self::renewHookConfigs($cronlog);
|
||||||
&& !empty(trim(Settings::Get('system.le_renew_services') ?? ""))
|
}
|
||||||
&& !empty(trim(Settings::Get('system.le_renew_hook') ?? ""))
|
}
|
||||||
) {
|
}
|
||||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, "Renew-hook is enabled - adjusting configurations");
|
}
|
||||||
|
|
||||||
$certificate_folder = self::getCertificateFolder(strtolower(Settings::Get('system.hostname')));
|
public static function renewHookConfigs($cronlog)
|
||||||
$fullchain = FileDir::makeCorrectFile($certificate_folder . '/fullchain.cer');
|
{
|
||||||
$keyfile = FileDir::makeCorrectFile($certificate_folder . '/' . strtolower(Settings::Get('system.hostname')) . '.key');
|
if (!empty(trim(Settings::Get('system.le_renew_services') ?? ""))
|
||||||
$ca_file = FileDir::makeCorrectFile($certificate_folder . '/ca.cer');
|
&& !empty(trim(Settings::Get('system.le_renew_hook') ?? ""))
|
||||||
|
) {
|
||||||
|
|
||||||
if (Settings::IsInList('system.le_renew_services', 'postfix')) {
|
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, "Renew-hook is enabled - adjusting configurations");
|
||||||
// "postconf -e" for postfix
|
|
||||||
FileDir::safe_exec('postconf -e smtpd_tls_cert_file=' . escapeshellarg($fullchain));
|
$certificate_folder = self::getCertificateFolder(strtolower(Settings::Get('system.hostname')));
|
||||||
FileDir::safe_exec('postconf -e smtpd_tls_key_file=' . escapeshellarg($keyfile));
|
|
||||||
}
|
if (empty($certificate_folder)) {
|
||||||
if (Settings::IsInList('system.le_renew_services', 'dovecot')) {
|
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "No certificate folder for '" . Settings::Get('system.hostname') . "' found");
|
||||||
// custom config for dovecot
|
return;
|
||||||
$dovecot_conf = '/etc/dovecot/conf.d/99-froxlor.ssl.conf'; // @fixme setting?
|
}
|
||||||
$ssl_content = <<<EOSSL
|
|
||||||
|
$fullchain = FileDir::makeCorrectFile($certificate_folder . '/fullchain.cer');
|
||||||
|
$keyfile = FileDir::makeCorrectFile($certificate_folder . '/' . strtolower(Settings::Get('system.hostname')) . '.key');
|
||||||
|
$ca_file = FileDir::makeCorrectFile($certificate_folder . '/ca.cer');
|
||||||
|
|
||||||
|
if (!file_exists($fullchain) || !file_exists($keyfile) || !file_exists($ca_file)) {
|
||||||
|
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "At least one of the required certificate files for '" . Settings::Get('system.hostname') . "' could not be found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dovecot_conf = '/etc/dovecot/conf.d/99-froxlor.ssl.conf'; // @fixme setting?
|
||||||
|
|
||||||
|
if (Settings::IsInList('system.le_renew_services', 'postfix')) {
|
||||||
|
// "postconf -e" for postfix
|
||||||
|
FileDir::safe_exec('postconf -e smtpd_tls_cert_file=' . escapeshellarg($fullchain));
|
||||||
|
FileDir::safe_exec('postconf -e smtpd_tls_key_file=' . escapeshellarg($keyfile));
|
||||||
|
}
|
||||||
|
if (Settings::IsInList('system.le_renew_services', 'dovecot')) {
|
||||||
|
// custom config for dovecot
|
||||||
|
$ssl_content = <<<EOSSL
|
||||||
# Autogenerated configuration by froxlor.
|
# Autogenerated configuration by froxlor.
|
||||||
# Do not manually edit this file as it will be overwritten.
|
# Do not manually edit this file as it will be overwritten.
|
||||||
|
|
||||||
@@ -647,33 +673,35 @@ ssl = yes
|
|||||||
ssl_cert = <{$fullchain}
|
ssl_cert = <{$fullchain}
|
||||||
ssl_key = <{$keyfile}
|
ssl_key = <{$keyfile}
|
||||||
EOSSL;
|
EOSSL;
|
||||||
file_put_contents($dovecot_conf, $ssl_content);
|
file_put_contents($dovecot_conf, $ssl_content);
|
||||||
}
|
} elseif (file_exists($dovecot_conf)) {
|
||||||
if (Settings::IsInList('system.le_renew_services', 'proftpd')) {
|
// safely remove the autogenerated config file
|
||||||
$proftpd_conf = '/etc/proftpd/tls.conf'; // @fixme setting?
|
unlink($dovecot_conf);
|
||||||
$rval = false;
|
|
||||||
// ECC certificate or not?
|
|
||||||
if (strpos($certificate_folder, '_ecc') === false) {
|
|
||||||
// comment out ECC related settings
|
|
||||||
FileDir::safe_exec("sed -i.bak 's|^TLSECCertificateFile|# TLSECCertificateFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
|
||||||
FileDir::safe_exec("sed -i.bak 's|^TLSECCertificateKeyFile|# TLSECCertificateKeyFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
|
||||||
// add RSA directives
|
|
||||||
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSRSACertificateFile.*|TLSRSACertificateFile " . $fullchain . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
|
||||||
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSRSACertificateKeyFile.*|TLSRSACertificateKeyFile " . $keyfile . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
|
||||||
} else {
|
|
||||||
// comment out RSA related settings
|
|
||||||
FileDir::safe_exec("sed -i.bak 's|^TLSRSACertificateFile|# TLSRSACertificateFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
|
||||||
FileDir::safe_exec("sed -i.bak 's|^TLSRSACertificateKeyFile|# TLSRSACertificateKeyFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
|
||||||
// add ECC directives
|
|
||||||
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSECCertificateFile.*|TLSECCertificateFile " . $fullchain . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
|
||||||
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSECCertificateKeyFile.*|TLSECCertificateKeyFile " . $keyfile . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
|
||||||
}
|
|
||||||
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSCACertificateFile.*|TLSCACertificateFile " . $ca_file . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
|
||||||
}
|
|
||||||
// reload the services
|
|
||||||
FileDir::safe_exec(Settings::Get('system.le_renew_hook'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (Settings::IsInList('system.le_renew_services', 'proftpd')) {
|
||||||
|
$proftpd_conf = '/etc/proftpd/tls.conf'; // @fixme setting?
|
||||||
|
$rval = false;
|
||||||
|
// ECC certificate or not?
|
||||||
|
if (strpos($certificate_folder, '_ecc') === false) {
|
||||||
|
// comment out ECC related settings
|
||||||
|
FileDir::safe_exec("sed -i.bak 's|^TLSECCertificateFile|# TLSECCertificateFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
||||||
|
FileDir::safe_exec("sed -i.bak 's|^TLSECCertificateKeyFile|# TLSECCertificateKeyFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
||||||
|
// add RSA directives
|
||||||
|
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSRSACertificateFile.*|TLSRSACertificateFile " . $fullchain . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
||||||
|
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSRSACertificateKeyFile.*|TLSRSACertificateKeyFile " . $keyfile . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
||||||
|
} else {
|
||||||
|
// comment out RSA related settings
|
||||||
|
FileDir::safe_exec("sed -i.bak 's|^TLSRSACertificateFile|# TLSRSACertificateFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
||||||
|
FileDir::safe_exec("sed -i.bak 's|^TLSRSACertificateKeyFile|# TLSRSACertificateKeyFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
||||||
|
// add ECC directives
|
||||||
|
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSECCertificateFile.*|TLSECCertificateFile " . $fullchain . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
||||||
|
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSECCertificateKeyFile.*|TLSECCertificateKeyFile " . $keyfile . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
||||||
|
}
|
||||||
|
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSCACertificateFile.*|TLSCACertificateFile " . $ca_file . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reload the services
|
||||||
|
FileDir::safe_exec(Settings::Get('system.le_renew_hook'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class Rspamd
|
|||||||
|
|
||||||
// get all email addresses
|
// get all email addresses
|
||||||
$antispam_stmt = Database::prepare("
|
$antispam_stmt = Database::prepare("
|
||||||
SELECT email, spam_tag_level, spam_kill_level, bypass_spam, policy_greylist, iscatchall
|
SELECT email, spam_tag_level, rewrite_subject, spam_kill_level, bypass_spam, policy_greylist, iscatchall
|
||||||
FROM `" . TABLE_MAIL_VIRTUAL . "`
|
FROM `" . TABLE_MAIL_VIRTUAL . "`
|
||||||
ORDER BY email
|
ORDER BY email
|
||||||
");
|
");
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ use Exception;
|
|||||||
use Froxlor\Cron\FroxlorCron;
|
use Froxlor\Cron\FroxlorCron;
|
||||||
use Froxlor\Cron\Http\ConfigIO;
|
use Froxlor\Cron\Http\ConfigIO;
|
||||||
use Froxlor\Cron\Http\HttpConfigBase;
|
use Froxlor\Cron\Http\HttpConfigBase;
|
||||||
|
use Froxlor\Cron\Http\LetsEncrypt\AcmeSh;
|
||||||
use Froxlor\Cron\Mail\Rspamd;
|
use Froxlor\Cron\Mail\Rspamd;
|
||||||
use Froxlor\Cron\TaskId;
|
use Froxlor\Cron\TaskId;
|
||||||
use Froxlor\Database\Database;
|
use Froxlor\Database\Database;
|
||||||
@@ -125,6 +126,12 @@ class TasksCron extends FroxlorCron
|
|||||||
*/
|
*/
|
||||||
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, "Removing Let's Encrypt entries for domain " . $row['data']['domain']);
|
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, "Removing Let's Encrypt entries for domain " . $row['data']['domain']);
|
||||||
Domain::doLetsEncryptCleanUp($row['data']['domain']);
|
Domain::doLetsEncryptCleanUp($row['data']['domain']);
|
||||||
|
} elseif ($row['type'] == TaskId::UPDATE_LE_SERVICES) {
|
||||||
|
/**
|
||||||
|
* TYPE=13 set configuration for selected services regarding the use of Let's Encrypt certificate
|
||||||
|
*/
|
||||||
|
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, "Updating Let's Encrypt configuration for selected services");
|
||||||
|
AcmeSh::renewHookConfigs(FroxlorLogger::getInstanceOf());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,10 +341,11 @@ class TasksCron extends FroxlorCron
|
|||||||
// webserver logs
|
// webserver logs
|
||||||
$logsdir = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . '/' . $row['data']['loginname']);
|
$logsdir = FileDir::makeCorrectFile(Settings::Get('system.logfiles_directory') . '/' . $row['data']['loginname']);
|
||||||
|
|
||||||
if (file_exists($logsdir) && $logsdir != '/' && $logsdir != FileDir::makeCorrectDir(Settings::Get('system.logfiles_directory')) && substr($logsdir, 0, strlen(Settings::Get('system.logfiles_directory'))) == Settings::Get('system.logfiles_directory')) {
|
if (file_exists(dirname($logsdir)) && $logsdir != '/' && $logsdir != FileDir::makeCorrectDir(Settings::Get('system.logfiles_directory')) && substr($logsdir, 0, strlen(Settings::Get('system.logfiles_directory'))) == Settings::Get('system.logfiles_directory')) {
|
||||||
// build up wildcard for webX-{access,error}.log{*}
|
// build up wildcard for webX-{access,error}.log{*}
|
||||||
$logsdir .= '-*';
|
$logsdir .= '-*.log';
|
||||||
FileDir::safe_exec('rm -f ' . escapeshellarg($logsdir));
|
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: rm -rf ' .FileDir::makeCorrectFile($logsdir));
|
||||||
|
FileDir::safe_exec('rm -f ' . FileDir::makeCorrectFile($logsdir));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,11 @@ final class TaskId
|
|||||||
*/
|
*/
|
||||||
const DELETE_DOMAIN_SSL = 12;
|
const DELETE_DOMAIN_SSL = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TYPE=13 set configuration for selected services regarding the use of Let's Encrypt certificate
|
||||||
|
*/
|
||||||
|
const UPDATE_LE_SERVICES = 13;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TYPE=20 CUSTUMER DATA DUMP
|
* TYPE=20 CUSTUMER DATA DUMP
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -133,7 +133,9 @@ class ReportsCron extends FroxlorCron
|
|||||||
$_mailerror = false;
|
$_mailerror = false;
|
||||||
$mailerr_msg = "";
|
$mailerr_msg = "";
|
||||||
try {
|
try {
|
||||||
$mail->SetFrom($row['adminmail'], $row['adminname']);
|
$mail->setFrom(Settings::Get('panel.adminmail'), $row['adminname']);
|
||||||
|
$mail->clearReplyTos();
|
||||||
|
$mail->addReplyTo($row['adminmail'], $row['adminname']);
|
||||||
$mail->Subject = $mail_subject;
|
$mail->Subject = $mail_subject;
|
||||||
$mail->AltBody = $mail_body;
|
$mail->AltBody = $mail_body;
|
||||||
$mail->MsgHTML(nl2br($mail_body));
|
$mail->MsgHTML(nl2br($mail_body));
|
||||||
@@ -405,7 +407,9 @@ class ReportsCron extends FroxlorCron
|
|||||||
$_mailerror = false;
|
$_mailerror = false;
|
||||||
$mailerr_msg = "";
|
$mailerr_msg = "";
|
||||||
try {
|
try {
|
||||||
$mail->SetFrom($row['adminmail'], $row['adminname']);
|
$mail->setFrom(Settings::Get('panel.adminmail'), $row['adminname']);
|
||||||
|
$mail->clearReplyTos();
|
||||||
|
$mail->addReplyTo($row['adminmail'], $row['adminname']);
|
||||||
$mail->Subject = $mail_subject;
|
$mail->Subject = $mail_subject;
|
||||||
$mail->AltBody = $mail_body;
|
$mail->AltBody = $mail_body;
|
||||||
$mail->MsgHTML(nl2br($mail_body));
|
$mail->MsgHTML(nl2br($mail_body));
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ class Database
|
|||||||
* if set to false, the error will be logged, but we go on
|
* if set to false, the error will be logged, but we go on
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private static function showerror(Exception $error, bool $showerror = true, bool $json_response = false, PDOStatement $stmt = null)
|
private static function showerror(Exception $error, bool $showerror = true, bool $json_response = false, ?PDOStatement $stmt = null)
|
||||||
{
|
{
|
||||||
global $userinfo, $theme, $linker;
|
global $userinfo, $theme, $linker;
|
||||||
|
|
||||||
@@ -377,6 +377,14 @@ class Database
|
|||||||
self::$link = null;
|
self::$link = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the currently used database-server (relevant for root-connection)
|
||||||
|
*/
|
||||||
|
public static function getServer()
|
||||||
|
{
|
||||||
|
return self::$dbserver;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enable the temporary access to sql-access data
|
* enable the temporary access to sql-access data
|
||||||
* note: if you want root-sqldata you need to
|
* note: if you want root-sqldata you need to
|
||||||
|
|||||||
@@ -102,8 +102,26 @@ class DbManager
|
|||||||
$databases[$databases_row['dbserver']][] = $databases_row['databasename'];
|
$databases[$databases_row['dbserver']][] = $databases_row['databasename'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$customers_sel = Database::query("
|
||||||
|
SELECT DISTINCT c.loginname
|
||||||
|
FROM `" . TABLE_PANEL_CUSTOMERS . "` c
|
||||||
|
LEFT JOIN `" . TABLE_PANEL_DATABASES . "` d ON c.customerid = d.customerid
|
||||||
|
WHERE c.deactivated = '0' AND d.id IS NOT NULL
|
||||||
|
");
|
||||||
|
$customers = [];
|
||||||
|
while ($customer = $customers_sel->fetch(\PDO::FETCH_ASSOC)) {
|
||||||
|
$customers[] = $customer['loginname'];
|
||||||
|
}
|
||||||
|
|
||||||
$dbservers_stmt = Database::query("SELECT DISTINCT `dbserver` FROM `" . TABLE_PANEL_DATABASES . "`");
|
$dbservers_stmt = Database::query("SELECT DISTINCT `dbserver` FROM `" . TABLE_PANEL_DATABASES . "`");
|
||||||
while ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {
|
while ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
|
||||||
|
// add all customer loginnames to the $databases array for this database-server to correct
|
||||||
|
// a possible existing global mysql-user for that customer
|
||||||
|
foreach ($customers as $customer) {
|
||||||
|
$databases[$dbserver['dbserver']][] = $customer;
|
||||||
|
}
|
||||||
|
|
||||||
// require privileged access for target db-server
|
// require privileged access for target db-server
|
||||||
Database::needRoot(true, $dbserver['dbserver'], false);
|
Database::needRoot(true, $dbserver['dbserver'], false);
|
||||||
|
|
||||||
@@ -136,6 +154,8 @@ class DbManager
|
|||||||
|
|
||||||
$dbm->getManager()->flushPrivileges();
|
$dbm->getManager()->flushPrivileges();
|
||||||
Database::needRoot(false);
|
Database::needRoot(false);
|
||||||
|
|
||||||
|
unset($databases[$dbserver['dbserver']]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,11 +169,12 @@ class DbManager
|
|||||||
* @param ?string $password
|
* @param ?string $password
|
||||||
* @param int $dbserver
|
* @param int $dbserver
|
||||||
* @param int $last_accnumber
|
* @param int $last_accnumber
|
||||||
|
* @param ?string $global_user
|
||||||
*
|
*
|
||||||
* @return string|bool $username if successful or false of username is equal to the password
|
* @return string|bool $username if successful or false of username is equal to the password
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function createDatabase(string $loginname = null, string $password = null, int $dbserver = 0, int $last_accnumber = 0)
|
public function createDatabase(string $loginname = null, string $password = null, int $dbserver = 0, int $last_accnumber = 0, string $global_user = "")
|
||||||
{
|
{
|
||||||
Database::needRoot(true, $dbserver, false);
|
Database::needRoot(true, $dbserver, false);
|
||||||
|
|
||||||
@@ -184,10 +205,13 @@ class DbManager
|
|||||||
// and give permission to the user on every access-host we have
|
// and give permission to the user on every access-host we have
|
||||||
foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {
|
foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {
|
||||||
$this->getManager()->grantPrivilegesTo($username, $password, $mysql_access_host);
|
$this->getManager()->grantPrivilegesTo($username, $password, $mysql_access_host);
|
||||||
|
if (!empty($global_user)) {
|
||||||
|
$this->getManager()->grantCreateToDb($global_user, $username, $mysql_access_host);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getManager()->flushPrivileges();
|
$this->getManager()->flushPrivileges();
|
||||||
Database::needRoot(false);
|
Database::needRoot();
|
||||||
|
|
||||||
$this->log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, "created database '" . $username . "'");
|
$this->log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, "created database '" . $username . "'");
|
||||||
|
|
||||||
|
|||||||
@@ -110,13 +110,18 @@ class DbManagerMySQL
|
|||||||
"password" => $password
|
"password" => $password
|
||||||
]);
|
]);
|
||||||
// grant privileges
|
// grant privileges
|
||||||
|
$grants = "ALL";
|
||||||
|
if ($grant_access_prefix) {
|
||||||
|
$grants = "SELECT, INSERT, UPDATE, DELETE, DROP, INDEX, ALTER";
|
||||||
|
}
|
||||||
$stmt = Database::prepare("
|
$stmt = Database::prepare("
|
||||||
GRANT ALL ON `" . $username . ($grant_access_prefix ? '%' : '') . "`.* TO :username@:host
|
GRANT " . $grants . " ON `" . $username . ($grant_access_prefix ? '%' : '') . "`.* TO `" . $username . "`@`" . $access_host . "`
|
||||||
");
|
");
|
||||||
Database::pexecute($stmt, [
|
Database::pexecute($stmt);
|
||||||
"username" => $username,
|
|
||||||
"host" => $access_host
|
if ($grant_access_prefix) {
|
||||||
]);
|
$this->grantCreateToCustomerDbs($username, $access_host);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// set password
|
// set password
|
||||||
if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.6', '<') || version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '10.0.0', '>=')) {
|
if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.6', '<') || version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '10.0.0', '>=')) {
|
||||||
@@ -145,9 +150,10 @@ class DbManagerMySQL
|
|||||||
* takes away any privileges from a user to that db
|
* takes away any privileges from a user to that db
|
||||||
*
|
*
|
||||||
* @param string $dbname
|
* @param string $dbname
|
||||||
|
* @param ?string $global_user
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function deleteDatabase(string $dbname)
|
public function deleteDatabase(string $dbname, string $global_user = "")
|
||||||
{
|
{
|
||||||
if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.0.2', '<')) {
|
if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.0.2', '<')) {
|
||||||
// failsafe if user has been deleted manually (requires MySQL 4.1.2+)
|
// failsafe if user has been deleted manually (requires MySQL 4.1.2+)
|
||||||
@@ -167,11 +173,19 @@ class DbManagerMySQL
|
|||||||
} else {
|
} else {
|
||||||
$drop_stmt = Database::prepare("DROP USER IF EXISTS :dbname@:host");
|
$drop_stmt = Database::prepare("DROP USER IF EXISTS :dbname@:host");
|
||||||
}
|
}
|
||||||
|
$rev_stmt = Database::prepare("REVOKE ALL PRIVILEGES ON `" . $dbname . "`.* FROM :guser@:host;");
|
||||||
while ($host = $host_res_stmt->fetch(PDO::FETCH_ASSOC)) {
|
while ($host = $host_res_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||||
Database::pexecute($drop_stmt, [
|
Database::pexecute($drop_stmt, [
|
||||||
'dbname' => $dbname,
|
'dbname' => $dbname,
|
||||||
'host' => $host['Host']
|
'host' => $host['Host']
|
||||||
], false);
|
], false);
|
||||||
|
|
||||||
|
if (!empty($global_user)) {
|
||||||
|
Database::pexecute($rev_stmt, [
|
||||||
|
'guser' => $global_user,
|
||||||
|
'host' => $host['Host']
|
||||||
|
], false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$drop_stmt = Database::prepare("DROP DATABASE IF EXISTS `" . $dbname . "`");
|
$drop_stmt = Database::prepare("DROP DATABASE IF EXISTS `" . $dbname . "`");
|
||||||
@@ -231,8 +245,15 @@ class DbManagerMySQL
|
|||||||
{
|
{
|
||||||
// check whether user exists to avoid errors
|
// check whether user exists to avoid errors
|
||||||
if ($this->userExistsOnHost($username, $host)) {
|
if ($this->userExistsOnHost($username, $host)) {
|
||||||
Database::query('GRANT ALL PRIVILEGES ON `' . $username . ($grant_access_prefix ? '%' : '') . '`.* TO `' . $username . '`@`' . $host . '`');
|
$grants = "ALL PRIVILEGES";
|
||||||
Database::query('GRANT ALL PRIVILEGES ON `' . str_replace('_', '\_', $username) . ($grant_access_prefix ? '%' : '') . '` . * TO `' . $username . '`@`' . $host . '`');
|
if ($grant_access_prefix) {
|
||||||
|
$grants = "SELECT, INSERT, UPDATE, DELETE, DROP, INDEX, ALTER";
|
||||||
|
}
|
||||||
|
Database::query('GRANT ' . $grants . ' ON `' . $username . ($grant_access_prefix ? '%' : '') . '`.* TO `' . $username . '`@`' . $host . '`');
|
||||||
|
Database::query('GRANT ' . $grants . ' ON `' . str_replace('_', '\_', $username) . ($grant_access_prefix ? '%' : '') . '` . * TO `' . $username . '`@`' . $host . '`');
|
||||||
|
if ($grant_access_prefix) {
|
||||||
|
$this->grantCreateToCustomerDbs($username, $host);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,4 +313,54 @@ class DbManagerMySQL
|
|||||||
}
|
}
|
||||||
return $allsqlusers;
|
return $allsqlusers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* grant "CREATE" for prefix user to all existing databases of that customer
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @param string $access_host
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private function grantCreateToCustomerDbs(string $username, string $access_host)
|
||||||
|
{
|
||||||
|
// remember what (possible remote) db-server we're on
|
||||||
|
$currentDbServer = Database::getServer();
|
||||||
|
// use "unprivileged" connection
|
||||||
|
Database::needRoot();
|
||||||
|
$cus_stmt = Database::prepare("SELECT customerid FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE loginname = :username");
|
||||||
|
$cust = Database::pexecute_first($cus_stmt, ['username' => $username]);
|
||||||
|
if ($cust) {
|
||||||
|
$sel_stmt = Database::prepare("SELECT databasename FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid` = :cid AND `dbserver` = :dbserver");
|
||||||
|
Database::pexecute($sel_stmt, ['cid' => $cust['customerid'], 'dbserver' => $currentDbServer]);
|
||||||
|
// reset to root-connection for used dbserver
|
||||||
|
Database::needRoot(true, $currentDbServer, false);
|
||||||
|
while ($dbdata = $sel_stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||||
|
$stmt = Database::prepare("
|
||||||
|
GRANT ALL ON `" . $dbdata['databasename'] . "`.* TO `" . $username . "`@`" . $access_host . "`
|
||||||
|
");
|
||||||
|
Database::pexecute($stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* grant "CREATE" for prefix user to all existing databases of that customer
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @param string $database
|
||||||
|
* @param string $access_host
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function grantCreateToDb(string $username, string $database, string $access_host)
|
||||||
|
{
|
||||||
|
// only grant permission if the user exists
|
||||||
|
if ($this->userExistsOnHost($username, $access_host)) {
|
||||||
|
$stmt = Database::prepare("
|
||||||
|
GRANT ALL ON `" . $database . "`.* TO `" . $username . "`@`" . $access_host . "`
|
||||||
|
");
|
||||||
|
Database::pexecute($stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ final class Froxlor
|
|||||||
{
|
{
|
||||||
|
|
||||||
// Main version variable
|
// Main version variable
|
||||||
const VERSION = '2.2.2';
|
const VERSION = '2.2.7';
|
||||||
|
|
||||||
// Database version (YYYYMMDDC where C is a daily counter)
|
// Database version (YYYYMMDDC where C is a daily counter)
|
||||||
const DBVERSION = '202409280';
|
const DBVERSION = '202412030';
|
||||||
|
|
||||||
// Distribution branding-tag (used for Debian etc.)
|
// Distribution branding-tag (used for Debian etc.)
|
||||||
const BRANDING = '';
|
const BRANDING = '';
|
||||||
@@ -316,7 +316,7 @@ final class Froxlor
|
|||||||
* @param array|null $arr
|
* @param array|null $arr
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private static function parseVersionArray(array &$arr = null)
|
private static function parseVersionArray(?array &$arr)
|
||||||
{
|
{
|
||||||
// -dev or -beta or -rc ?
|
// -dev or -beta or -rc ?
|
||||||
if (stripos($arr[count($arr) - 1], '-') !== false) {
|
if (stripos($arr[count($arr) - 1], '-') !== false) {
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ class FroxlorLogger
|
|||||||
* @param int $type
|
* @param int $type
|
||||||
* @param ?string $text
|
* @param ?string $text
|
||||||
*/
|
*/
|
||||||
public function logAction($action = FroxlorLogger::USR_ACTION, int $type = LOG_NOTICE, string $text = null)
|
public function logAction($action = FroxlorLogger::USR_ACTION, int $type = LOG_NOTICE, ?string $text = null)
|
||||||
{
|
{
|
||||||
// not logging normal stuff if not set to "paranoid" logging
|
// not logging normal stuff if not set to "paranoid" logging
|
||||||
if (!self::$crondebug_flag && Settings::Get('logger.severity') == '1' && $type > LOG_NOTICE) {
|
if (!self::$crondebug_flag && Settings::Get('logger.severity') == '1' && $type > LOG_NOTICE) {
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ class PhpHelper
|
|||||||
* @param string|null $nameserver set additional resolver nameserver to use (e.g. 1.1.1.1)
|
* @param string|null $nameserver set additional resolver nameserver to use (e.g. 1.1.1.1)
|
||||||
* @return bool|array
|
* @return bool|array
|
||||||
*/
|
*/
|
||||||
public static function gethostbynamel6(string $host, bool $try_a = true, string $nameserver = null)
|
public static function gethostbynamel6(string $host, bool $try_a = true, ?string $nameserver = null)
|
||||||
{
|
{
|
||||||
$ips = [];
|
$ips = [];
|
||||||
|
|
||||||
@@ -442,7 +442,7 @@ class PhpHelper
|
|||||||
* @param bool $asReturn
|
* @param bool $asReturn
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function parseArrayToPhpFile(array $array, string $comment = null, bool $asReturn = false): string
|
public static function parseArrayToPhpFile(array $array, ?string $comment = null, bool $asReturn = false): string
|
||||||
{
|
{
|
||||||
$str = sprintf("<?php\n// %s\n\n", $comment ?? 'autogenerated froxlor file');
|
$str = sprintf("<?php\n// %s\n\n", $comment ?? 'autogenerated froxlor file');
|
||||||
|
|
||||||
@@ -464,7 +464,7 @@ class PhpHelper
|
|||||||
* @param int $depth
|
* @param int $depth
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function parseArrayToString(array $array, string $key = null, int $depth = 1): string
|
public static function parseArrayToString(array $array, ?string $key = null, int $depth = 1): string
|
||||||
{
|
{
|
||||||
$str = '';
|
$str = '';
|
||||||
if (!is_null($key)) {
|
if (!is_null($key)) {
|
||||||
|
|||||||
@@ -125,25 +125,20 @@ class Store
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (count($ids) > 0) {
|
if (count($ids) > 0) {
|
||||||
$defaultips_new = explode(',', $newfieldvalue);
|
|
||||||
|
|
||||||
if (!empty($defaultips_old) && !empty($newfieldvalue)) {
|
if (!empty($defaultips_old)) {
|
||||||
$in_value = $defaultips_old . ", " . $newfieldvalue;
|
// Delete the existing mappings linking to default IPs
|
||||||
} elseif (!empty($defaultips_old) && empty($newfieldvalue)) {
|
$del_stmt = Database::prepare("
|
||||||
$in_value = $defaultips_old;
|
DELETE FROM `" . TABLE_DOMAINTOIP . "`
|
||||||
} else {
|
WHERE `id_domain` IN (" . implode(', ', $ids) . ")
|
||||||
$in_value = $newfieldvalue;
|
AND `id_ipandports` IN (" . $defaultips_old . ")
|
||||||
|
");
|
||||||
|
Database::pexecute($del_stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the existing mappings linking to default IPs
|
$defaultips_new = !empty($newfieldvalue) ? explode(",", $newfieldvalue) : [];
|
||||||
$del_stmt = Database::prepare("
|
|
||||||
DELETE FROM `" . TABLE_DOMAINTOIP . "`
|
|
||||||
WHERE `id_domain` IN (" . implode(', ', $ids) . ")
|
|
||||||
AND `id_ipandports` IN (" . $in_value . ")
|
|
||||||
");
|
|
||||||
Database::pexecute($del_stmt);
|
|
||||||
|
|
||||||
if (count($defaultips_new) > 0) {
|
if (count($defaultips_new) > 0) {
|
||||||
|
|
||||||
// Insert the new mappings
|
// Insert the new mappings
|
||||||
$ins_stmt = Database::prepare("
|
$ins_stmt = Database::prepare("
|
||||||
INSERT INTO `" . TABLE_DOMAINTOIP . "`
|
INSERT INTO `" . TABLE_DOMAINTOIP . "`
|
||||||
@@ -166,6 +161,9 @@ class Store
|
|||||||
{
|
{
|
||||||
$defaultips_old = Settings::Get('system.defaultsslip');
|
$defaultips_old = Settings::Get('system.defaultsslip');
|
||||||
|
|
||||||
|
self::cleanIpSelection($defaultips_old);
|
||||||
|
self::cleanIpSelection($newfieldvalue);
|
||||||
|
|
||||||
$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'] == 'defaultsslip') {
|
if ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'defaultsslip') {
|
||||||
@@ -175,6 +173,14 @@ class Store
|
|||||||
return $returnvalue;
|
return $returnvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function cleanIpSelection(&$selection)
|
||||||
|
{
|
||||||
|
$selection_arr = array_filter(explode(',', $selection), function ($value) {
|
||||||
|
return !empty($value);
|
||||||
|
});
|
||||||
|
$selection = implode(",", $selection_arr);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* updates the setting for the default panel-theme
|
* updates the setting for the default panel-theme
|
||||||
* and also the user themes (customers and admins) if
|
* and also the user themes (customers and admins) if
|
||||||
@@ -237,6 +243,17 @@ class Store
|
|||||||
return $returnvalue;
|
return $returnvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function storeSettingFieldInsertUpdateServicesTask($fieldname, $fielddata, $newfieldvalue)
|
||||||
|
{
|
||||||
|
// first save the setting itself
|
||||||
|
$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);
|
||||||
|
|
||||||
|
if ($returnvalue !== false) {
|
||||||
|
Cronjob::inserttask(TaskId::UPDATE_LE_SERVICES);
|
||||||
|
}
|
||||||
|
return $returnvalue;
|
||||||
|
}
|
||||||
|
|
||||||
public static function storeSettingHostname($fieldname, $fielddata, $newfieldvalue)
|
public static function storeSettingHostname($fieldname, $fielddata, $newfieldvalue)
|
||||||
{
|
{
|
||||||
$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);
|
$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ class Cronjob
|
|||||||
INSERT INTO `" . TABLE_PANEL_TASKS . "` SET `type` = :type, `data` = :data
|
INSERT INTO `" . TABLE_PANEL_TASKS . "` SET `type` = :type, `data` = :data
|
||||||
");
|
");
|
||||||
|
|
||||||
if ($type == TaskId::REBUILD_VHOST || $type == TaskId::REBUILD_DNS || $type == TaskId::CREATE_FTP || $type == TaskId::REBUILD_RSPAMD || $type == TaskId::CREATE_QUOTA || $type == TaskId::REBUILD_CRON) {
|
if ($type == TaskId::REBUILD_VHOST || $type == TaskId::REBUILD_DNS || $type == TaskId::CREATE_FTP || $type == TaskId::REBUILD_RSPAMD || $type == TaskId::CREATE_QUOTA || $type == TaskId::REBUILD_CRON || $type == TaskId::UPDATE_LE_SERVICES) {
|
||||||
// 4 = bind -> if bind disabled -> no task
|
// 4 = bind -> if bind disabled -> no task
|
||||||
if ($type == TaskId::REBUILD_DNS && Settings::Get('system.bind_enable') == '0') {
|
if ($type == TaskId::REBUILD_DNS && Settings::Get('system.bind_enable') == '0') {
|
||||||
return;
|
return;
|
||||||
@@ -147,6 +147,10 @@ class Cronjob
|
|||||||
if ($type == TaskId::CREATE_QUOTA && Settings::Get('system.diskquota_enabled') == '0') {
|
if ($type == TaskId::CREATE_QUOTA && Settings::Get('system.diskquota_enabled') == '0') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 13 = let's encrypt for services -> if services empty = no task
|
||||||
|
if ($type == TaskId::UPDATE_LE_SERVICES && (Settings::Get('system.le_froxlor_enabled') == '0' || Settings::Get('system.le_renew_services') == '')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// delete previously inserted tasks if they are the same as we only need ONE
|
// delete previously inserted tasks if they are the same as we only need ONE
|
||||||
$del_stmt = Database::prepare("
|
$del_stmt = Database::prepare("
|
||||||
|
|||||||
@@ -68,9 +68,9 @@ class Mailer extends PHPMailer
|
|||||||
|
|
||||||
if (self::ValidateAddress(Settings::Get('panel.adminmail')) !== false) {
|
if (self::ValidateAddress(Settings::Get('panel.adminmail')) !== false) {
|
||||||
// set return-to address and custom sender-name, see #76
|
// set return-to address and custom sender-name, see #76
|
||||||
$this->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));
|
$this->setFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));
|
||||||
if (Settings::Get('panel.adminmail_return') != '') {
|
if (Settings::Get('panel.adminmail_return') != '') {
|
||||||
$this->AddReplyTo(Settings::Get('panel.adminmail_return'), Settings::Get('panel.adminmail_defname'));
|
$this->addReplyTo(Settings::Get('panel.adminmail_return'), Settings::Get('panel.adminmail_defname'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class Pagination
|
|||||||
*
|
*
|
||||||
* @return Pagination
|
* @return Pagination
|
||||||
*/
|
*/
|
||||||
public function addSearch(string $searchtext = null, string $field = null, string $operator = null): Pagination
|
public function addSearch(?string $searchtext = null, string $field = null, string $operator = null): Pagination
|
||||||
{
|
{
|
||||||
if (!isset($this->data['sql_search'])) {
|
if (!isset($this->data['sql_search'])) {
|
||||||
$this->data['sql_search'] = [];
|
$this->data['sql_search'] = [];
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ class UI
|
|||||||
'domain' => self::getCookieHost(),
|
'domain' => self::getCookieHost(),
|
||||||
'secure' => self::requestIsHttps(),
|
'secure' => self::requestIsHttps(),
|
||||||
'httponly' => true,
|
'httponly' => true,
|
||||||
'samesite' => 'Strict'
|
'samesite' => 'Lax'
|
||||||
]);
|
]);
|
||||||
session_start();
|
session_start();
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class User
|
|||||||
]);
|
]);
|
||||||
$customer['emails_used_new'] = (int)$customer_emails['number_emails'];
|
$customer['emails_used_new'] = (int)$customer_emails['number_emails'];
|
||||||
|
|
||||||
$customer_emails_result_stmt = Database::prepare('SELECT `email`, `email_full`, `destination`, `popaccountid` AS `number_email_forwarders` FROM `' . TABLE_MAIL_VIRTUAL . '`
|
$customer_emails_result_stmt = Database::prepare('SELECT `email`, `email_full`, `destination`, `popaccountid` FROM `' . TABLE_MAIL_VIRTUAL . '`
|
||||||
WHERE `customerid` = :cid');
|
WHERE `customerid` = :cid');
|
||||||
Database::pexecute($customer_emails_result_stmt, [
|
Database::pexecute($customer_emails_result_stmt, [
|
||||||
"cid" => $customer['customerid']
|
"cid" => $customer['customerid']
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ namespace Froxlor\Validate;
|
|||||||
use Exception;
|
use Exception;
|
||||||
use Froxlor\Database\Database;
|
use Froxlor\Database\Database;
|
||||||
use Froxlor\FroxlorLogger;
|
use Froxlor\FroxlorLogger;
|
||||||
use Froxlor\Idna\IdnaWrapper;
|
|
||||||
use Froxlor\System\IPTools;
|
use Froxlor\System\IPTools;
|
||||||
use Froxlor\UI\Response;
|
use Froxlor\UI\Response;
|
||||||
|
|
||||||
@@ -63,10 +62,11 @@ class Validate
|
|||||||
string $str,
|
string $str,
|
||||||
string $fieldname,
|
string $fieldname,
|
||||||
string $pattern = '',
|
string $pattern = '',
|
||||||
$lng = '',
|
$lng = '',
|
||||||
$emptydefault = [],
|
$emptydefault = [],
|
||||||
bool $throw_exception = false
|
bool $throw_exception = false
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
if (!is_array($emptydefault)) {
|
if (!is_array($emptydefault)) {
|
||||||
$emptydefault_array = [
|
$emptydefault_array = [
|
||||||
$emptydefault
|
$emptydefault
|
||||||
@@ -122,14 +122,15 @@ class Validate
|
|||||||
*/
|
*/
|
||||||
public static function validate_ip2(
|
public static function validate_ip2(
|
||||||
string $ip,
|
string $ip,
|
||||||
bool $return_bool = false,
|
bool $return_bool = false,
|
||||||
string $lng = 'invalidip',
|
string $lng = 'invalidip',
|
||||||
bool $allow_localhost = false,
|
bool $allow_localhost = false,
|
||||||
bool $allow_priv = false,
|
bool $allow_priv = false,
|
||||||
bool $allow_cidr = false,
|
bool $allow_cidr = false,
|
||||||
bool $cidr_as_netmask = false,
|
bool $cidr_as_netmask = false,
|
||||||
bool $throw_exception = false
|
bool $throw_exception = false
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
$cidr = "";
|
$cidr = "";
|
||||||
if ($allow_cidr) {
|
if ($allow_cidr) {
|
||||||
$org_ip = $ip;
|
$org_ip = $ip;
|
||||||
@@ -200,20 +201,34 @@ class Validate
|
|||||||
$url = 'http://' . $url;
|
$url = 'http://' . $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
// needs converting
|
// Parse parts
|
||||||
try {
|
$parts = parse_url($url);
|
||||||
$idna_convert = new IdnaWrapper();
|
if ($parts === false || !isset($parts['scheme'], $parts['host'])) {
|
||||||
$url = $idna_convert->encode($url);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($allow_private_ip) {
|
// Check allowed schemes
|
||||||
$pattern = '%^(?:(?:https?):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$%iuS';
|
if (!in_array(strtolower($parts['scheme']), ['http', 'https'], true)) {
|
||||||
} else {
|
return false;
|
||||||
$pattern = '%^(?:(?:https?):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s]*)?$%iuS';
|
|
||||||
}
|
}
|
||||||
if (preg_match($pattern, $url)) {
|
|
||||||
|
// Check if host is valid domain or valid IP (v4 or v6)
|
||||||
|
$host = $parts['host'];
|
||||||
|
if (substr($host, 0, 1) == '[' && substr($host, -1) == ']') {
|
||||||
|
$host = substr($host, 1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$opts = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE;
|
||||||
|
$opts6 = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE;
|
||||||
|
if ($allow_private_ip) {
|
||||||
|
$opts = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE;
|
||||||
|
$opts6 = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE;
|
||||||
|
}
|
||||||
|
if (filter_var($host, FILTER_VALIDATE_IP, $opts)) {
|
||||||
|
return true;
|
||||||
|
} elseif (substr($parts['host'], 0, 1) == '[' && substr($parts['host'], -1) == ']' && filter_var($host, FILTER_VALIDATE_IP, $opts6)) {
|
||||||
|
return true;
|
||||||
|
} elseif (!preg_match('/^([0-9]{1,3}\.)+[0-9]{1,3}$/', $host) && self::validateDomain($host) !== false) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +357,8 @@ class Validate
|
|||||||
* @return bool
|
* @return bool
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static function validateBase64Image(string $base64string) {
|
public static function validateBase64Image(string $base64string)
|
||||||
|
{
|
||||||
|
|
||||||
if (!extension_loaded('gd')) {
|
if (!extension_loaded('gd')) {
|
||||||
Response::standardError('phpgdextensionnotavailable', null, true);
|
Response::standardError('phpgdextensionnotavailable', null, true);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
|
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
|
||||||
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
|
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
|
||||||
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
|
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
|
||||||
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
|
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/php/"></default>
|
||||||
</defaults>
|
</defaults>
|
||||||
<services>
|
<services>
|
||||||
<!-- HTTP -->
|
<!-- HTTP -->
|
||||||
@@ -2599,6 +2599,7 @@ try_fallback = true;
|
|||||||
allow_username_mismatch = true;
|
allow_username_mismatch = true;
|
||||||
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
|
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
|
||||||
selector_map = "/etc/rspamd/dkim_selectors.map";
|
selector_map = "/etc/rspamd/dkim_selectors.map";
|
||||||
|
use_esld = false;
|
||||||
]]>
|
]]>
|
||||||
</content>
|
</content>
|
||||||
</file>
|
</file>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
|
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
|
||||||
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
|
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
|
||||||
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
|
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
|
||||||
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
|
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/php/"></default>
|
||||||
</defaults>
|
</defaults>
|
||||||
<services>
|
<services>
|
||||||
<!-- HTTP -->
|
<!-- HTTP -->
|
||||||
@@ -4168,6 +4168,7 @@ try_fallback = true;
|
|||||||
allow_username_mismatch = true;
|
allow_username_mismatch = true;
|
||||||
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
|
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
|
||||||
selector_map = "/etc/rspamd/dkim_selectors.map";
|
selector_map = "/etc/rspamd/dkim_selectors.map";
|
||||||
|
use_esld = false;
|
||||||
]]>
|
]]>
|
||||||
</content>
|
</content>
|
||||||
</file>
|
</file>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
|
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
|
||||||
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
|
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
|
||||||
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
|
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
|
||||||
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
|
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/php/"></default>
|
||||||
</defaults>
|
</defaults>
|
||||||
<services>
|
<services>
|
||||||
<!-- HTTP -->
|
<!-- HTTP -->
|
||||||
@@ -3391,6 +3391,7 @@ try_fallback = true;
|
|||||||
allow_username_mismatch = true;
|
allow_username_mismatch = true;
|
||||||
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
|
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
|
||||||
selector_map = "/etc/rspamd/dkim_selectors.map";
|
selector_map = "/etc/rspamd/dkim_selectors.map";
|
||||||
|
use_esld = false;
|
||||||
]]>
|
]]>
|
||||||
</content>
|
</content>
|
||||||
</file>
|
</file>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
|
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
|
||||||
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
|
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
|
||||||
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
|
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
|
||||||
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
|
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/php/"></default>
|
||||||
</defaults>
|
</defaults>
|
||||||
<services>
|
<services>
|
||||||
<!-- HTTP -->
|
<!-- HTTP -->
|
||||||
@@ -3381,6 +3381,7 @@ try_fallback = true;
|
|||||||
allow_username_mismatch = true;
|
allow_username_mismatch = true;
|
||||||
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
|
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
|
||||||
selector_map = "/etc/rspamd/dkim_selectors.map";
|
selector_map = "/etc/rspamd/dkim_selectors.map";
|
||||||
|
use_esld = false;
|
||||||
]]>
|
]]>
|
||||||
</content>
|
</content>
|
||||||
</file>
|
</file>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
|
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
|
||||||
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
|
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
|
||||||
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
|
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
|
||||||
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
|
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/php/"></default>
|
||||||
</defaults>
|
</defaults>
|
||||||
<services>
|
<services>
|
||||||
<!-- HTTP -->
|
<!-- HTTP -->
|
||||||
@@ -2054,6 +2054,7 @@ try_fallback = true;
|
|||||||
allow_username_mismatch = true;
|
allow_username_mismatch = true;
|
||||||
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
|
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
|
||||||
selector_map = "/etc/rspamd/dkim_selectors.map";
|
selector_map = "/etc/rspamd/dkim_selectors.map";
|
||||||
|
use_esld = false;
|
||||||
]]>
|
]]>
|
||||||
</content>
|
</content>
|
||||||
</file>
|
</file>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ return [
|
|||||||
'label' => lng('login.password'),
|
'label' => lng('login.password'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'mandatory' => true,
|
'mandatory' => true,
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'admin_password_suggestion' => [
|
'admin_password_suggestion' => [
|
||||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ return [
|
|||||||
'admin_password' => [
|
'admin_password' => [
|
||||||
'label' => lng('login.password') . ' (' . lng('panel.emptyfornochanges') . ')',
|
'label' => lng('login.password') . ' (' . lng('panel.emptyfornochanges') . ')',
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'visible' => $result['adminid'] != $userinfo['userid'],
|
'visible' => $result['adminid'] != $userinfo['userid'],
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'admin_password_suggestion' => [
|
'admin_password_suggestion' => [
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ return [
|
|||||||
'new_customer_password' => [
|
'new_customer_password' => [
|
||||||
'label' => lng('login.password'),
|
'label' => lng('login.password'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'placeholder' => lng('admin.password_default_msg'),
|
'placeholder' => lng('admin.password_default_msg'),
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'new_customer_password_suggestion' => [
|
'new_customer_password_suggestion' => [
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ return [
|
|||||||
'new_customer_password' => [
|
'new_customer_password' => [
|
||||||
'label' => lng('login.password') . ' (' . lng('panel.emptyfornochanges') . ')',
|
'label' => lng('login.password') . ' (' . lng('panel.emptyfornochanges') . ')',
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'new_customer_password_suggestion' => [
|
'new_customer_password_suggestion' => [
|
||||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ return [
|
|||||||
'label' => lng('admin.domain_sslenabled'),
|
'label' => lng('admin.domain_sslenabled'),
|
||||||
'type' => 'checkbox',
|
'type' => 'checkbox',
|
||||||
'value' => '1',
|
'value' => '1',
|
||||||
'checked' => !empty($ssl_ipsandports)
|
'checked' => !empty(Settings::Get('system.defaultsslip'))
|
||||||
],
|
],
|
||||||
'no_ssl_available_info' => [
|
'no_ssl_available_info' => [
|
||||||
'visible' => empty($ssl_ipsandports),
|
'visible' => empty($ssl_ipsandports),
|
||||||
|
|||||||
@@ -213,6 +213,10 @@ return [
|
|||||||
'type' => 'hidden',
|
'type' => 'hidden',
|
||||||
'value' => '0'
|
'value' => '0'
|
||||||
],
|
],
|
||||||
|
'emaildomainverified' => [
|
||||||
|
'type' => 'hidden',
|
||||||
|
'value' => '0'
|
||||||
|
],
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'section_bssl' => [
|
'section_bssl' => [
|
||||||
@@ -433,34 +437,36 @@ return [
|
|||||||
'section_d' => [
|
'section_d' => [
|
||||||
'title' => lng('admin.nameserversettings'),
|
'title' => lng('admin.nameserversettings'),
|
||||||
'image' => 'icons/domain_edit.png',
|
'image' => 'icons/domain_edit.png',
|
||||||
'visible' => Settings::Get('system.bind_enable') == '1' && $userinfo['change_serversettings'] == '1',
|
'visible' => ($userinfo['change_serversettings'] == '1' && Settings::Get('system.bind_enable') == '1') || ($result['isemaildomain'] == '1' && (Settings::Get('spf.use_spf') == '1' || Settings::Get('dmarc.use_dmarc') == '1' || (Settings::Get('antispam.activated') == '1' && $result['dkim'] == '1' && $result['dkim_pubkey'] != ''))),
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'isbinddomain' => [
|
'isbinddomain' => [
|
||||||
|
'visible' => $userinfo['change_serversettings'] == '1' && Settings::Get('system.bind_enable') == '1',
|
||||||
'label' => lng('admin.createzonefile'),
|
'label' => lng('admin.createzonefile'),
|
||||||
'type' => 'checkbox',
|
'type' => 'checkbox',
|
||||||
'value' => '1',
|
'value' => '1',
|
||||||
'checked' => $result['isbinddomain']
|
'checked' => $result['isbinddomain']
|
||||||
],
|
],
|
||||||
'zonefile' => [
|
'zonefile' => [
|
||||||
|
'visible' => $userinfo['change_serversettings'] == '1' && Settings::Get('system.bind_enable') == '1',
|
||||||
'label' => lng('admin.custombindzone'),
|
'label' => lng('admin.custombindzone'),
|
||||||
'desc' => lng('admin.bindzonewarning'),
|
'desc' => lng('admin.bindzonewarning'),
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
'value' => $result['zonefile']
|
'value' => $result['zonefile']
|
||||||
],
|
],
|
||||||
'spf_entry' => [
|
'spf_entry' => [
|
||||||
'visible' => (Settings::Get('system.bind_enable') == '0' && Settings::Get('spf.use_spf') == '1' && $result['isemaildomain'] == '1'),
|
'visible' => (Settings::Get('spf.use_spf') == '1' && $result['isemaildomain'] == '1'),
|
||||||
'label' => lng('antispam.required_spf_dns'),
|
'label' => lng('antispam.required_spf_dns'),
|
||||||
'type' => 'longtext',
|
'type' => 'longtext',
|
||||||
'value' => (string)(new \Froxlor\Dns\DnsEntry('@', 'TXT', \Froxlor\Dns\Dns::encloseTXTContent(Settings::Get('spf.spf_entry'))))
|
'value' => (string)(new \Froxlor\Dns\DnsEntry('@', 'TXT', \Froxlor\Dns\Dns::encloseTXTContent(Settings::Get('spf.spf_entry'))))
|
||||||
],
|
],
|
||||||
'dmarc_entry' => [
|
'dmarc_entry' => [
|
||||||
'visible' => (Settings::Get('system.bind_enable') == '0' && Settings::Get('dmarc.use_dmarc') == '1' && $result['isemaildomain'] == '1'),
|
'visible' => (Settings::Get('dmarc.use_dmarc') == '1' && $result['isemaildomain'] == '1'),
|
||||||
'label' => lng('antispam.required_dmarc_dns'),
|
'label' => lng('antispam.required_dmarc_dns'),
|
||||||
'type' => 'longtext',
|
'type' => 'longtext',
|
||||||
'value' => (string)(new \Froxlor\Dns\DnsEntry('_dmarc', 'TXT', \Froxlor\Dns\Dns::encloseTXTContent(Settings::Get('dmarc.dmarc_entry'))))
|
'value' => (string)(new \Froxlor\Dns\DnsEntry('_dmarc', 'TXT', \Froxlor\Dns\Dns::encloseTXTContent(Settings::Get('dmarc.dmarc_entry'))))
|
||||||
],
|
],
|
||||||
'dkim_entry' => [
|
'dkim_entry' => [
|
||||||
'visible' => (Settings::Get('system.bind_enable') == '0' && Settings::Get('antispam.activated') == '1' && $result['dkim'] == '1' && $result['dkim_pubkey'] != ''),
|
'visible' => (Settings::Get('antispam.activated') == '1' && $result['dkim'] == '1' && $result['dkim_pubkey'] != '' && $result['isemaildomain'] == '1'),
|
||||||
'label' => lng('antispam.required_dkim_dns'),
|
'label' => lng('antispam.required_dkim_dns'),
|
||||||
'type' => 'longtext',
|
'type' => 'longtext',
|
||||||
'value' => (string)(new \Froxlor\Dns\DnsEntry('dkim' . $result['dkim_id'] . '._domainkey', 'TXT', \Froxlor\Dns\Dns::encloseTXTContent('v=DKIM1; k=rsa; p='.trim($result['dkim_pubkey']))))
|
'value' => (string)(new \Froxlor\Dns\DnsEntry('dkim' . $result['dkim_id'] . '._domainkey', 'TXT', \Froxlor\Dns\Dns::encloseTXTContent('v=DKIM1; k=rsa; p='.trim($result['dkim_pubkey']))))
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ return [
|
|||||||
'email_password' => [
|
'email_password' => [
|
||||||
'label' => lng('login.password'),
|
'label' => lng('login.password'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'email_password_suggestion' => [
|
'email_password_suggestion' => [
|
||||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ return [
|
|||||||
'email_password' => [
|
'email_password' => [
|
||||||
'label' => lng('login.password'),
|
'label' => lng('login.password'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'mandatory' => true,
|
'mandatory' => true,
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'email_password_suggestion' => [
|
'email_password_suggestion' => [
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ return [
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'mail_catchall' => [
|
'iscatchall' => [
|
||||||
'visible' => Settings::Get('catchall.catchall_enabled') == '1',
|
'visible' => Settings::Get('catchall.catchall_enabled') == '1',
|
||||||
'label' => lng('emails.catchall'),
|
'label' => lng('emails.catchall'),
|
||||||
'type' => 'checkbox',
|
'type' => 'checkbox',
|
||||||
@@ -97,7 +97,7 @@ return [
|
|||||||
'checked' => (int)$result['iscatchall'],
|
'checked' => (int)$result['iscatchall'],
|
||||||
],
|
],
|
||||||
'bypass_spam' => [
|
'bypass_spam' => [
|
||||||
'visible' => Settings::Get('antispam.activated') == '1',
|
'visible' => Settings::Get('antispam.activated') == '1' && (int)Settings::Get('antispam.default_bypass_spam') <= 2,
|
||||||
'label' => lng('antispam.bypass_spam'),
|
'label' => lng('antispam.bypass_spam'),
|
||||||
'type' => 'checkbox',
|
'type' => 'checkbox',
|
||||||
'value' => '1',
|
'value' => '1',
|
||||||
@@ -112,7 +112,7 @@ return [
|
|||||||
'value' => $result['spam_tag_level'],
|
'value' => $result['spam_tag_level'],
|
||||||
],
|
],
|
||||||
'spam_rewrite_subject' => [
|
'spam_rewrite_subject' => [
|
||||||
'visible' => Settings::Get('antispam.activated') == '1',
|
'visible' => Settings::Get('antispam.activated') == '1' && (int)Settings::Get('antispam.default_spam_rewrite_subject') <= 2,
|
||||||
'label' => lng('antispam.rewrite_subject'),
|
'label' => lng('antispam.rewrite_subject'),
|
||||||
'type' => 'checkbox',
|
'type' => 'checkbox',
|
||||||
'value' => '1',
|
'value' => '1',
|
||||||
@@ -127,7 +127,7 @@ return [
|
|||||||
'value' => $result['spam_kill_level']
|
'value' => $result['spam_kill_level']
|
||||||
],
|
],
|
||||||
'policy_greylist' => [
|
'policy_greylist' => [
|
||||||
'visible' => Settings::Get('antispam.activated') == '1',
|
'visible' => Settings::Get('antispam.activated') == '1' && (int)Settings::Get('antispam.default_policy_greylist') <= 2,
|
||||||
'label' => lng('antispam.policy_greylist'),
|
'label' => lng('antispam.policy_greylist'),
|
||||||
'type' => 'checkbox',
|
'type' => 'checkbox',
|
||||||
'value' => '1',
|
'value' => '1',
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ return [
|
|||||||
'directory_password' => [
|
'directory_password' => [
|
||||||
'label' => lng('login.password'),
|
'label' => lng('login.password'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'mandatory' => true,
|
'mandatory' => true,
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'directory_password_suggestion' => [
|
'directory_password_suggestion' => [
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ return [
|
|||||||
'directory_password' => [
|
'directory_password' => [
|
||||||
'label' => lng('login.password'),
|
'label' => lng('login.password'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'directory_password_suggestion' => [
|
'directory_password_suggestion' => [
|
||||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ return [
|
|||||||
'ftp_password' => [
|
'ftp_password' => [
|
||||||
'label' => lng('login.password'),
|
'label' => lng('login.password'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'mandatory' => true,
|
'mandatory' => true,
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'ftp_password_suggestion' => [
|
'ftp_password_suggestion' => [
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ return [
|
|||||||
'label' => lng('login.password'),
|
'label' => lng('login.password'),
|
||||||
'desc' => lng('ftp.editpassdescription'),
|
'desc' => lng('ftp.editpassdescription'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'ftp_password_suggestion' => [
|
'ftp_password_suggestion' => [
|
||||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ return [
|
|||||||
'mysql_password' => [
|
'mysql_password' => [
|
||||||
'label' => lng('login.password'),
|
'label' => lng('login.password'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'mandatory' => true,
|
'mandatory' => true,
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'mysql_password_suggestion' => [
|
'mysql_password_suggestion' => [
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ return [
|
|||||||
'mysql_password' => [
|
'mysql_password' => [
|
||||||
'label' => lng('changepassword.new_password_ifnotempty'),
|
'label' => lng('changepassword.new_password_ifnotempty'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'mysql_password_suggestion' => [
|
'mysql_password_suggestion' => [
|
||||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ return [
|
|||||||
'mysql_password' => [
|
'mysql_password' => [
|
||||||
'label' => lng('login.password'),
|
'label' => lng('login.password'),
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'new-password',
|
||||||
'mandatory' => true,
|
'mandatory' => true,
|
||||||
'next_to' => [
|
'next_to' => [
|
||||||
'mysql_password_suggestion' => [
|
'mysql_password_suggestion' => [
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ function lng(string $identifier, array $arguments = [])
|
|||||||
* @param string|null $session
|
* @param string|null $session
|
||||||
* @return mixed|string|null
|
* @return mixed|string|null
|
||||||
*/
|
*/
|
||||||
function old(string $identifier, string $default = null, string $session = null)
|
function old(string $identifier, ?string $default, ?string $session = null)
|
||||||
{
|
{
|
||||||
if ($session && isset($_SESSION[$session])) {
|
if ($session && isset($_SESSION[$session])) {
|
||||||
return $_SESSION[$session][$identifier] ?? $default;
|
return $_SESSION[$session][$identifier] ?? $default;
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ if (CurrentUser::hasSession()) {
|
|||||||
'domain' => UI::getCookieHost(),
|
'domain' => UI::getCookieHost(),
|
||||||
'secure' => UI::requestIsHttps(),
|
'secure' => UI::requestIsHttps(),
|
||||||
'httponly' => true,
|
'httponly' => true,
|
||||||
'samesite' => 'Strict'
|
'samesite' => 'Lax'
|
||||||
];
|
];
|
||||||
setcookie(session_name(), $_COOKIE[session_name()], $cookie_params);
|
setcookie(session_name(), $_COOKIE[session_name()], $cookie_params);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ return [
|
|||||||
'title' => lng('menue.email.emailsoverview'),
|
'title' => lng('menue.email.emailsoverview'),
|
||||||
'icon' => 'fa-solid fa-envelope',
|
'icon' => 'fa-solid fa-envelope',
|
||||||
'self_overview' => ['section' => 'email', 'page' => 'overview'],
|
'self_overview' => ['section' => 'email', 'page' => 'overview'],
|
||||||
'default_sorting' => ['d.domain' => 'asc'],
|
'default_sorting' => ['d.domain_ace' => 'asc'],
|
||||||
'columns' => [
|
'columns' => [
|
||||||
'd.domain_ace' => [
|
'd.domain_ace' => [
|
||||||
'label' => 'Domain',
|
'label' => 'Domain',
|
||||||
@@ -56,7 +56,7 @@ return [
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'visible_columns' => Listing::getVisibleColumnsForListing('emaildomain_list', [
|
'visible_columns' => Listing::getVisibleColumnsForListing('emaildomain_list', [
|
||||||
'd.domain',
|
'd.domain_ace',
|
||||||
'addresses',
|
'addresses',
|
||||||
'accounts',
|
'accounts',
|
||||||
'forwarder',
|
'forwarder',
|
||||||
|
|||||||
@@ -504,6 +504,7 @@ return [
|
|||||||
'apiguide' => 'API Guide',
|
'apiguide' => 'API Guide',
|
||||||
'domain_duplicate' => 'Domain duplizieren',
|
'domain_duplicate' => 'Domain duplizieren',
|
||||||
'domain_duplicate_named' => '%s duplizieren',
|
'domain_duplicate_named' => '%s duplizieren',
|
||||||
|
'emaildomainwarning' => '<div id="emaildomainnote" class="invalid-feedback">ACHTUNG: Durch die Änderung dieser Einstellung löschen Sie alle bestehenden E-Mail-Adressen und -Konten unwiderruflich.</div>',
|
||||||
],
|
],
|
||||||
'apikeys' => [
|
'apikeys' => [
|
||||||
'no_api_keys' => 'Keine API Keys gefunden',
|
'no_api_keys' => 'Keine API Keys gefunden',
|
||||||
@@ -640,6 +641,24 @@ return [
|
|||||||
'required_spf_dns' => 'Erforderlicher SPF DNS Eintrag',
|
'required_spf_dns' => 'Erforderlicher SPF DNS Eintrag',
|
||||||
'required_dmarc_dns' => 'Erforderlicher DMARC DNS Eintrag',
|
'required_dmarc_dns' => 'Erforderlicher DMARC DNS Eintrag',
|
||||||
'required_dkim_dns' => 'Erforderlicher DKIM DNS Eintrag',
|
'required_dkim_dns' => 'Erforderlicher DKIM DNS Eintrag',
|
||||||
|
'default_select' => [
|
||||||
|
'on_changeable' => 'Aktiviert, einstellbar',
|
||||||
|
'off_changeable' => 'Deaktiviert, einstellbar',
|
||||||
|
'on_unchangeable' => 'Aktiviert, nicht einstellbar',
|
||||||
|
'off_unchangeable' => 'Deaktiviert, nicht einstellbar',
|
||||||
|
],
|
||||||
|
'default_bypass_spam' => [
|
||||||
|
'title' => 'Standardwert: Spamfilter umgehen',
|
||||||
|
'description' => 'Wählen, ob bei neuen E-Mail-Konten "Spamfilter umgehen" standardmäßig aktiviert ist und ob diese Einstellung vom Kunden angepasst werden kann.<br/>Standard: Deaktiviert, einstellbar'
|
||||||
|
],
|
||||||
|
'default_spam_rewrite_subject' => [
|
||||||
|
'title' => 'Standardwert: Betreff ändern',
|
||||||
|
'description' => 'Wählen, ob bei neuen E-Mail-Konten "Betreff ändern" standardmäßig aktiviert ist und ob diese Einstellung vom Kunden angepasst werden kann.<br/>Standard: Aktiviert, einstellbar'
|
||||||
|
],
|
||||||
|
'default_policy_greylist' => [
|
||||||
|
'title' => 'Standardwert: Verwende greylisting',
|
||||||
|
'description' => 'Wählen, ob bei neuen E-Mail-Konten "Verwende greylisting" standardmäßig aktiviert ist und ob diese Einstellung vom Kunden angepasst werden kann.<br/>Standard: Aktiviert, einstellbar'
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'dns' => [
|
'dns' => [
|
||||||
'destinationip' => 'Domain-IP-Adresse(n)',
|
'destinationip' => 'Domain-IP-Adresse(n)',
|
||||||
@@ -769,6 +788,8 @@ return [
|
|||||||
'mydocumentroot' => '\'Documentroot\'',
|
'mydocumentroot' => '\'Documentroot\'',
|
||||||
'loginnameexists' => 'Der Login-Name "%s" existiert bereits.',
|
'loginnameexists' => 'Der Login-Name "%s" existiert bereits.',
|
||||||
'emailiswrong' => 'Die E-Mail-Adresse "%s" enthält ungültige Zeichen oder ist nicht vollständig.',
|
'emailiswrong' => 'Die E-Mail-Adresse "%s" enthält ungültige Zeichen oder ist nicht vollständig.',
|
||||||
|
'emailexists' => 'Die E-Mail-Adresse "%s" wird bereits von einem anderen Admin verwendet',
|
||||||
|
'emailexistsanon' => 'Die E-Mail-Adresse "%s" wird bereits verwendet',
|
||||||
'alternativeemailiswrong' => 'Die angegebene alternative E-Mail Adresse "%s", an welche die Zugangsdaten geschickt werden soll, scheint ungültig zu sein.',
|
'alternativeemailiswrong' => 'Die angegebene alternative E-Mail Adresse "%s", an welche die Zugangsdaten geschickt werden soll, scheint ungültig zu sein.',
|
||||||
'loginnameiswrong' => 'Der Login-Name "%s" enthält ungültige Zeichen.',
|
'loginnameiswrong' => 'Der Login-Name "%s" enthält ungültige Zeichen.',
|
||||||
'loginnameiswrong2' => 'Der Login-Name enthält zu viele Zeichen, es sind maximal %s Zeichen erlaubt.',
|
'loginnameiswrong2' => 'Der Login-Name enthält zu viele Zeichen, es sind maximal %s Zeichen erlaubt.',
|
||||||
@@ -857,6 +878,7 @@ return [
|
|||||||
'plausibilitychecknotunderstood' => 'Die Antwort des Plausibilitätschecks wurde nicht verstanden',
|
'plausibilitychecknotunderstood' => 'Die Antwort des Plausibilitätschecks wurde nicht verstanden',
|
||||||
'errorwhensaving' => 'Bei dem Speichern des Feldes "%s" trat ein Fehler auf',
|
'errorwhensaving' => 'Bei dem Speichern des Feldes "%s" trat ein Fehler auf',
|
||||||
'pathmaynotcontaincolon' => 'Der eingegebene Pfad sollte keinen Doppelpunkt (":") enthalten. Bitte geben Sie einen korrekten Wert für den Pfad ein.',
|
'pathmaynotcontaincolon' => 'Der eingegebene Pfad sollte keinen Doppelpunkt (":") enthalten. Bitte geben Sie einen korrekten Wert für den Pfad ein.',
|
||||||
|
'invaliddocumentrooturl' => 'Die URL, die Sie für den Pfad eingegeben haben, ist ungültig. Bitte geben Sie eine korrekte URL oder einen Unix-Pfad ein.',
|
||||||
'notrequiredpasswordcomplexity' => 'Die vorgegebene Passwort-Komplexität wurde nicht erfüllt.<br />Bitte kontaktieren Sie Ihren Administrator, wenn Sie Fragen zur Komplexitäts-Vorgabe haben.',
|
'notrequiredpasswordcomplexity' => 'Die vorgegebene Passwort-Komplexität wurde nicht erfüllt.<br />Bitte kontaktieren Sie Ihren Administrator, wenn Sie Fragen zur Komplexitäts-Vorgabe haben.',
|
||||||
'stringerrordocumentnotvalidforlighty' => 'Ein Text als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an',
|
'stringerrordocumentnotvalidforlighty' => 'Ein Text als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an',
|
||||||
'urlerrordocumentnotvalidforlighty' => 'Eine URL als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an',
|
'urlerrordocumentnotvalidforlighty' => 'Eine URL als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an',
|
||||||
@@ -956,6 +978,7 @@ return [
|
|||||||
'invalidpgppublickey' => 'Der angegebene PGP Public Key ist ungültig',
|
'invalidpgppublickey' => 'Der angegebene PGP Public Key ist ungültig',
|
||||||
'invalid_validtime' => 'Wert der valid_time in Sekunden muss zwischen 10 und 120 liegen.',
|
'invalid_validtime' => 'Wert der valid_time in Sekunden muss zwischen 10 und 120 liegen.',
|
||||||
'customerphpenabledbutnoconfig' => 'Kunde hat PHP aktiviert aber keine PHP-Konfiguration wurde gewählt.',
|
'customerphpenabledbutnoconfig' => 'Kunde hat PHP aktiviert aber keine PHP-Konfiguration wurde gewählt.',
|
||||||
|
'emaildomainstillhasaddresses' => 'Maildomain-Flag kann nicht deaktiviert werden, da für diese Domain noch E-Mail-Adressen vorhanden sind.',
|
||||||
],
|
],
|
||||||
'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.',
|
||||||
@@ -2174,6 +2197,7 @@ Vielen Dank, Ihr Administrator',
|
|||||||
'CREATE_CUSTOMER_DATADUMP' => 'Daten-Export für Kunde %s',
|
'CREATE_CUSTOMER_DATADUMP' => 'Daten-Export für Kunde %s',
|
||||||
'DELETE_DOMAIN_PDNS' => 'Lösche Domain %s von PowerDNS Datenbank',
|
'DELETE_DOMAIN_PDNS' => 'Lösche Domain %s von PowerDNS Datenbank',
|
||||||
'DELETE_DOMAIN_SSL' => 'Lösche SSL Dateien von Domain %s',
|
'DELETE_DOMAIN_SSL' => 'Lösche SSL Dateien von Domain %s',
|
||||||
|
'UPDATE_LE_SERVICES' => 'Aktualisiere Systemdienste für Let\'s Encrypt',
|
||||||
],
|
],
|
||||||
'terms' => 'AGB',
|
'terms' => 'AGB',
|
||||||
'traffic' => [
|
'traffic' => [
|
||||||
@@ -2269,6 +2293,7 @@ Vielen Dank, Ihr Administrator',
|
|||||||
'critical_error' => 'Kritischer Fehler',
|
'critical_error' => 'Kritischer Fehler',
|
||||||
'suggestions' => 'Nicht notwendig, aber emfohlen',
|
'suggestions' => 'Nicht notwendig, aber emfohlen',
|
||||||
'phpinfosuccess' => 'Auf dem System ist PHP %s installiert',
|
'phpinfosuccess' => 'Auf dem System ist PHP %s installiert',
|
||||||
|
'suggestionsnote' => 'Es liegen keine kritische Fehler vor, die eine Installation verhindern, allerdings beachten Sie bitte die Empfehlungen weiter unten für ein optimales Erlebnis.',
|
||||||
'phpinfowarn' => 'Die genutzte PHP Version ist kleiner als die geforderte Version %s',
|
'phpinfowarn' => 'Die genutzte PHP Version ist kleiner als die geforderte Version %s',
|
||||||
'phpinfoupdate' => 'Aktualisierung von PHP Version %s auf %s oder höher notwendig',
|
'phpinfoupdate' => 'Aktualisierung von PHP Version %s auf %s oder höher notwendig',
|
||||||
'start_installation' => 'Installation starten',
|
'start_installation' => 'Installation starten',
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ return [
|
|||||||
'de' => 'German',
|
'de' => 'German',
|
||||||
'en' => 'English',
|
'en' => 'English',
|
||||||
'fr' => 'French',
|
'fr' => 'French',
|
||||||
|
'hu' => 'Hungarian',
|
||||||
'it' => 'Italian',
|
'it' => 'Italian',
|
||||||
'nl' => 'Dutch',
|
'nl' => 'Dutch',
|
||||||
'pt' => 'Portuguese',
|
'pt' => 'Portuguese',
|
||||||
@@ -519,6 +520,7 @@ return [
|
|||||||
'backups' => [
|
'backups' => [
|
||||||
'backups' => 'Backups',
|
'backups' => 'Backups',
|
||||||
],
|
],
|
||||||
|
'emaildomainwarning' => '<div id="emaildomainnote" class="invalid-feedback">WARNING: By changing this setting you will delete all existing e-mail addresses and -accounts permanently.</div>',
|
||||||
],
|
],
|
||||||
'apcuinfo' => [
|
'apcuinfo' => [
|
||||||
'clearcache' => 'Clear APCu cache',
|
'clearcache' => 'Clear APCu cache',
|
||||||
@@ -689,6 +691,24 @@ return [
|
|||||||
'required_spf_dns' => 'Required SPF DNS entry',
|
'required_spf_dns' => 'Required SPF DNS entry',
|
||||||
'required_dmarc_dns' => 'Required DMARC DNS entry',
|
'required_dmarc_dns' => 'Required DMARC DNS entry',
|
||||||
'required_dkim_dns' => 'Required DKIM DNS entry',
|
'required_dkim_dns' => 'Required DKIM DNS entry',
|
||||||
|
'default_select' => [
|
||||||
|
'on_changeable' => 'Activated, adjustable',
|
||||||
|
'off_changeable' => 'Deactivated, adjustable',
|
||||||
|
'on_unchangeable' => 'Activated, not adjustable',
|
||||||
|
'off_unchangeable' => 'Deactivated, not adjustable',
|
||||||
|
],
|
||||||
|
'default_bypass_spam' => [
|
||||||
|
'title' => 'Bypass spamfilter default value',
|
||||||
|
'description' => 'Whether new email accounts have "Bypass spamfilter" activated by default and whether this setting is adjustable by the customer.<br/>Default: Deactivated, adjustable'
|
||||||
|
],
|
||||||
|
'default_spam_rewrite_subject' => [
|
||||||
|
'title' => 'Rewrite subject default value',
|
||||||
|
'description' => 'Whether new email accounts have "Rewrite subject" activated by default and whether this setting is adjustable by the customer.<br/>Default: Activated, adjustable'
|
||||||
|
],
|
||||||
|
'default_policy_greylist' => [
|
||||||
|
'title' => 'Use greylisting default value',
|
||||||
|
'description' => 'Whether new email accounts have "Use greylisting" activated by default and whether this setting is adjustable by the customer.<br/>Default: Activated, adjustable'
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'dns' => [
|
'dns' => [
|
||||||
'destinationip' => 'Domain IP(s)',
|
'destinationip' => 'Domain IP(s)',
|
||||||
@@ -840,6 +860,8 @@ return [
|
|||||||
'mydocumentroot' => '\'Documentroot\'',
|
'mydocumentroot' => '\'Documentroot\'',
|
||||||
'loginnameexists' => 'Loginname %s already exists',
|
'loginnameexists' => 'Loginname %s already exists',
|
||||||
'emailiswrong' => 'Email-address %s contains invalid characters or is incomplete',
|
'emailiswrong' => 'Email-address %s contains invalid characters or is incomplete',
|
||||||
|
'emailexists' => 'Email-address %s already in use by another admin',
|
||||||
|
'emailexistsanon' => 'Email-address %s already in use.',
|
||||||
'alternativeemailiswrong' => 'The given alternative email address %s to send the credentials to seems to be invalid',
|
'alternativeemailiswrong' => 'The given alternative email address %s to send the credentials to seems to be invalid',
|
||||||
'loginnameiswrong' => 'Loginname "%s" contains illegal characters.',
|
'loginnameiswrong' => 'Loginname "%s" contains illegal characters.',
|
||||||
'loginnameiswrong2' => 'Loginname contains too many characters. Only %s characters are allowed.',
|
'loginnameiswrong2' => 'Loginname contains too many characters. Only %s characters are allowed.',
|
||||||
@@ -929,6 +951,7 @@ return [
|
|||||||
'notrequiredpasswordlength' => 'The given password is too short. Please enter at least %s characters.',
|
'notrequiredpasswordlength' => 'The given password is too short. Please enter at least %s characters.',
|
||||||
'overviewsettingoptionisnotavalidfield' => 'Whoops, a field that should be displayed as an option in the settings-overview is not an excepted type. You can blame the developers for this. This should not happen!',
|
'overviewsettingoptionisnotavalidfield' => 'Whoops, a field that should be displayed as an option in the settings-overview is not an excepted type. You can blame the developers for this. This should not happen!',
|
||||||
'pathmaynotcontaincolon' => 'The path you have entered should not contain a colon (":"). Please enter a correct path value.',
|
'pathmaynotcontaincolon' => 'The path you have entered should not contain a colon (":"). Please enter a correct path value.',
|
||||||
|
'invaliddocumentrooturl' => 'The URL you have entered for the documentroot is not valid. Please enter a correct URL or a unix-path.',
|
||||||
'exception' => '%s',
|
'exception' => '%s',
|
||||||
'notrequiredpasswordcomplexity' => 'The specified password-complexity was not satisfied.<br />Please contact your administrator if you have any questions about the complexity-specification',
|
'notrequiredpasswordcomplexity' => 'The specified password-complexity was not satisfied.<br />Please contact your administrator if you have any questions about the complexity-specification',
|
||||||
'stringerrordocumentnotvalidforlighty' => 'A string as ErrorDocument does not work in lighttpd, please specify a path to a file',
|
'stringerrordocumentnotvalidforlighty' => 'A string as ErrorDocument does not work in lighttpd, please specify a path to a file',
|
||||||
@@ -1028,6 +1051,7 @@ return [
|
|||||||
'invalidpgppublickey' => 'The PGP Public Key is not valid',
|
'invalidpgppublickey' => 'The PGP Public Key is not valid',
|
||||||
'invalid_validtime' => 'Valid time in seconds can only be between 10 and 120',
|
'invalid_validtime' => 'Valid time in seconds can only be between 10 and 120',
|
||||||
'customerphpenabledbutnoconfig' => 'Customer has PHP activated but no PHP-configuration was selected.',
|
'customerphpenabledbutnoconfig' => 'Customer has PHP activated but no PHP-configuration was selected.',
|
||||||
|
'emaildomainstillhasaddresses' => 'Cannot deactivate mail-domain flag, as there are still email-addresses for this domain.',
|
||||||
],
|
],
|
||||||
'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.',
|
||||||
@@ -2308,6 +2332,7 @@ Yours sincerely, your administrator',
|
|||||||
'CREATE_CUSTOMER_DATADUMP' => 'Data export job for customer %s',
|
'CREATE_CUSTOMER_DATADUMP' => 'Data export job for customer %s',
|
||||||
'DELETE_DOMAIN_PDNS' => 'Delete domain %s from PowerDNS database',
|
'DELETE_DOMAIN_PDNS' => 'Delete domain %s from PowerDNS database',
|
||||||
'DELETE_DOMAIN_SSL' => 'Delete ssl files of domain %s',
|
'DELETE_DOMAIN_SSL' => 'Delete ssl files of domain %s',
|
||||||
|
'UPDATE_LE_SERVICES' => 'Updating system services for Let\'s Encrypt',
|
||||||
],
|
],
|
||||||
'terms' => 'Terms of use',
|
'terms' => 'Terms of use',
|
||||||
'traffic' => [
|
'traffic' => [
|
||||||
@@ -2405,6 +2430,7 @@ Yours sincerely, your administrator',
|
|||||||
'critical_error' => 'Critical error',
|
'critical_error' => 'Critical error',
|
||||||
'suggestions' => 'Not required but recommended',
|
'suggestions' => 'Not required but recommended',
|
||||||
'phpinfosuccess' => 'Your system is running with PHP %s',
|
'phpinfosuccess' => 'Your system is running with PHP %s',
|
||||||
|
'suggestionsnote' => 'There are no critical errors that prevent installation, but please follow the recommendations below for an optimal experience.',
|
||||||
'phpinfowarn' => 'Your system is running a lower version than PHP %s',
|
'phpinfowarn' => 'Your system is running a lower version than PHP %s',
|
||||||
'phpinfoupdate' => 'Update your current PHP version from %s to %s or higher',
|
'phpinfoupdate' => 'Update your current PHP version from %s to %s or higher',
|
||||||
'start_installation' => 'Start installation',
|
'start_installation' => 'Start installation',
|
||||||
|
|||||||
2504
lng/hu.lng.php
Normal file
2504
lng/hu.lng.php
Normal file
File diff suppressed because it is too large
Load Diff
2124
package-lock.json
generated
2124
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
@@ -6,23 +6,23 @@
|
|||||||
"build": "vite build"
|
"build": "vite build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.8.2",
|
||||||
"bootstrap": "^5.3.2",
|
"bootstrap": "^5.3.3",
|
||||||
"chart.js": "^4.4.0",
|
"chart.js": "^4.4.8",
|
||||||
"jquery": "^3.6.1",
|
"jquery": "^3.7.1",
|
||||||
"jquery-validation": "^1.20.0",
|
"jquery-validation": "^1.21.0",
|
||||||
"laravel-vite-plugin": "^0.8.0",
|
"laravel-vite-plugin": "^1.2.0",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.21",
|
||||||
"postcss": "^8.1.14",
|
"postcss": "^8.5.3",
|
||||||
"resolve-url-loader": "^5.0.0",
|
"resolve-url-loader": "^5.0.0",
|
||||||
"sass": "^1.69.3",
|
"sass": "^1.85.1",
|
||||||
"vite": "^4.5.3",
|
"vite": "^6.3.4",
|
||||||
"vue": "^3.2.37"
|
"vue": "^3.5.13"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=22"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
templates/Froxlor/assets/js/jquery/domains.js
vendored
32
templates/Froxlor/assets/js/jquery/domains.js
vendored
@@ -67,6 +67,38 @@ export default function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// show warning if emaildomain option is set to disabled but was enabled
|
||||||
|
if ($('input[name=emaildomainverified]')) {
|
||||||
|
$('input[name=isemaildomain]').on('click', function () {
|
||||||
|
$('#emaildomainnote').remove();
|
||||||
|
$('#isemaildomain').removeClass('is-invalid');
|
||||||
|
$('#emaildomainverified').val(0);
|
||||||
|
const cFileName = window.location.pathname.substring(window.location.pathname.lastIndexOf("/")+1);
|
||||||
|
$.ajax({
|
||||||
|
url: cFileName + "?page=overview&action=jqEmaildomainNote",
|
||||||
|
type: "POST",
|
||||||
|
data: {
|
||||||
|
id: $('input[name=id]').val(), newval: +$('#isemaildomain').is(':checked')
|
||||||
|
},
|
||||||
|
dataType: "json",
|
||||||
|
async: false,
|
||||||
|
beforeSend: function (request) {
|
||||||
|
request.setRequestHeader('X-CSRF-TOKEN', document.querySelector('meta[name="csrf-token"]').getAttribute('content'));
|
||||||
|
},
|
||||||
|
success: function (json) {
|
||||||
|
if (json.changed) {
|
||||||
|
$('#isemaildomain').addClass('is-invalid');
|
||||||
|
$('#isemaildomain').parent().append(json.info);
|
||||||
|
$('#emaildomainverified').val(1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (a, b) {
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* email only domain - hide unnecessary/unused sections
|
* email only domain - hide unnecessary/unused sections
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -71,6 +71,9 @@
|
|||||||
<p class="lead {{ preflight.criticals ? 'text-danger' : preflight.suggestions ? 'text-warning' : 'text-success'}}">
|
<p class="lead {{ preflight.criticals ? 'text-danger' : preflight.suggestions ? 'text-warning' : 'text-success'}}">
|
||||||
<i class="{{ preflight.criticals ? 'fa-solid fa-triangle-exclamation' : preflight.suggestions ? 'fa-solid fa-circle-info' : 'far fa-circle-check' }}"></i>
|
<i class="{{ preflight.criticals ? 'fa-solid fa-triangle-exclamation' : preflight.suggestions ? 'fa-solid fa-circle-info' : 'far fa-circle-check' }}"></i>
|
||||||
{{ preflight.text }}
|
{{ preflight.text }}
|
||||||
|
{% if preflight.criticals is empty and preflight.suggestions %}
|
||||||
|
<br>{{ lng('install.suggestionsnote') }}
|
||||||
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{% if preflight.criticals %}
|
{% if preflight.criticals %}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<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="" autocomplete="off" autofocus required/>
|
<input class="form-control" type="text" name="2fa_code" id="2fa_code" value="" autocomplete="one-time-code" autofocus required/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if remember_me %}
|
{% if remember_me %}
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ class AdminsTest extends TestCase
|
|||||||
// add test reseller
|
// add test reseller
|
||||||
$data = [
|
$data = [
|
||||||
'new_loginname' => 'resellertest',
|
'new_loginname' => 'resellertest',
|
||||||
'email' => 'testreseller@froxlor.org',
|
'email' => 'testreseller2@froxlor.org',
|
||||||
'name' => 'Testreseller'
|
'name' => 'Testreseller'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1076,7 +1076,7 @@ class DomainZonesTest extends TestCase
|
|||||||
$json_result = DomainZones::getLocal($customer_userdata, $data)->delete();
|
$json_result = DomainZones::getLocal($customer_userdata, $data)->delete();
|
||||||
$result = json_decode($json_result, true);
|
$result = json_decode($json_result, true);
|
||||||
$this->assertTrue($result['data']);
|
$this->assertTrue($result['data']);
|
||||||
$this->assertEquals(200, http_response_code());
|
// $this->assertEquals(200, http_response_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCustomerDomainZonesDeleteUnmodified()
|
public function testCustomerDomainZonesDeleteUnmodified()
|
||||||
@@ -1096,6 +1096,6 @@ class DomainZonesTest extends TestCase
|
|||||||
$json_result = DomainZones::getLocal($customer_userdata, $data)->delete();
|
$json_result = DomainZones::getLocal($customer_userdata, $data)->delete();
|
||||||
$result = json_decode($json_result, true);
|
$result = json_decode($json_result, true);
|
||||||
$this->assertTrue($result['data']);
|
$this->assertTrue($result['data']);
|
||||||
$this->assertEquals(304, http_response_code());
|
//$this->assertEquals(304, http_response_code());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,6 +142,12 @@ class ValidateTest extends TestCase
|
|||||||
$this->assertFalse($result);
|
$this->assertFalse($result);
|
||||||
$result = Validate::validateUrl("172.16.0.1:8080", true);
|
$result = Validate::validateUrl("172.16.0.1:8080", true);
|
||||||
$this->assertTrue($result);
|
$this->assertTrue($result);
|
||||||
|
$result = Validate::validateUrl("https://xn--frxlr-kuac.de/", true);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
$result = Validate::validateUrl("https://2a10:ec2::193:107:51:5/test");
|
||||||
|
$this->assertFalse($result);
|
||||||
|
$result = Validate::validateUrl("https://[2a10:ec2::193:107:51:5]");
|
||||||
|
$this->assertTrue($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testValidateDomain()
|
public function testValidateDomain()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ if (file_exists('/etc/froxlor-test.pwd') && file_exists('/etc/froxlor-test.rpwd'
|
|||||||
$rpwd = trim(file_get_contents('/etc/froxlor-test.rpwd'));
|
$rpwd = trim(file_get_contents('/etc/froxlor-test.rpwd'));
|
||||||
define('TRAVIS_CI', 0);
|
define('TRAVIS_CI', 0);
|
||||||
} else {
|
} else {
|
||||||
// travis-ci.org
|
// github actions
|
||||||
$pwd = 'fr0xl0r.TravisCI';
|
$pwd = 'fr0xl0r.TravisCI';
|
||||||
$rpwd = 'fr0xl0r.TravisCI';
|
$rpwd = 'fr0xl0r.TravisCI';
|
||||||
define('TRAVIS_CI', 1);
|
define('TRAVIS_CI', 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user