Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10555bff76 | ||
|
|
37aa7af4da | ||
|
|
4b75369597 | ||
|
|
9d0e463906 | ||
|
|
a7198f58ce | ||
|
|
47be4b2847 | ||
|
|
b0fae4bd14 | ||
|
|
4711a41436 | ||
|
|
faa71ceaef | ||
|
|
2d30394150 | ||
|
|
99c1182af8 | ||
|
|
d9abe58dd2 | ||
|
|
23034b8ad2 | ||
|
|
1cae5638d3 | ||
|
|
ce9a5f97a3 | ||
|
|
c38b90deef | ||
|
|
13daa7d6fa | ||
|
|
b0e43d332d | ||
|
|
75c8754fb4 | ||
|
|
e0fa64f897 | ||
|
|
ed72fd1766 | ||
|
|
826ae36647 | ||
|
|
9ddf24539e | ||
|
|
3940c1429d | ||
|
|
c236d9eaab | ||
|
|
688994e40c | ||
|
|
9facaee809 | ||
|
|
a7dd5f4685 | ||
|
|
da810ea953 | ||
|
|
51b6e067e8 | ||
|
|
34cf6698bc | ||
|
|
4642160724 | ||
|
|
78a259ef3b | ||
|
|
68cf4ab69a | ||
|
|
d5661d492d | ||
|
|
6900898ae1 | ||
|
|
d90fb7fa68 | ||
|
|
4ea8629fcc | ||
|
|
9d4ff8698d | ||
|
|
b164038846 | ||
|
|
5c46960734 | ||
|
|
a7f4f0c737 | ||
|
|
b64dd501dd | ||
|
|
1679675aa1 | ||
|
|
640466f301 | ||
|
|
9c9771a371 | ||
|
|
1922b3ce65 | ||
|
|
83e819908a | ||
|
|
0924aa644b | ||
|
|
7711ce1d66 | ||
|
|
7dae63e586 | ||
|
|
1bcaa45492 | ||
|
|
66cb114f0d | ||
|
|
1c5d60dcfd | ||
|
|
b6da6356fc | ||
|
|
c09670cc45 | ||
|
|
464f5b7bed | ||
|
|
c799235c24 | ||
|
|
a2860e70a5 | ||
|
|
95a96d46a6 | ||
|
|
81f3dbda31 | ||
|
|
4eb4191843 | ||
|
|
ca433d8a61 | ||
|
|
8f4dfe1514 | ||
|
|
ee42f5168e | ||
|
|
fc8ca57f8c | ||
|
|
7e4bba2d55 | ||
|
|
7e635f9be4 | ||
|
|
e9406a20f2 | ||
|
|
de7729cec8 | ||
|
|
d60e48849b | ||
|
|
908df5a7bb | ||
|
|
c1952afb94 | ||
|
|
7a22e8f4dd | ||
|
|
3ac0da2cdd | ||
|
|
eb816c4cc6 | ||
|
|
64d8bf4fba | ||
|
|
ae6ee95973 | ||
|
|
e9051dc30a | ||
|
|
b6c7c53c3a | ||
|
|
f36bc61fc7 | ||
|
|
c56e0b9dac | ||
|
|
1deb08bf75 | ||
|
|
b30d7a8252 | ||
|
|
b03e11c18d | ||
|
|
bf7d22a794 | ||
|
|
fb57a8a3b5 | ||
|
|
0d625797b0 | ||
|
|
6777fbf229 | ||
|
|
23f1f79eff | ||
|
|
a5af104d53 | ||
|
|
38d94698ce | ||
|
|
5ba28ef599 | ||
|
|
a3486cc5b3 | ||
|
|
5ab322ab1d | ||
|
|
4f26bdd535 | ||
|
|
88f76e4355 | ||
|
|
a464d8cb19 | ||
|
|
0f596dce8b | ||
|
|
60270b20b3 | ||
|
|
4003a8d2b6 | ||
|
|
89843d6f37 | ||
|
|
256a52a5da | ||
|
|
c9b2bfe53c | ||
|
|
98cb36327e | ||
|
|
7d23e4882d | ||
|
|
1cc3a1d066 | ||
|
|
de0f7d2f01 | ||
|
|
aa48ffca2b | ||
|
|
802168cb5b | ||
|
|
6ace2e9f3d | ||
|
|
0bff360d22 | ||
|
|
e300acf109 | ||
|
|
14d8e12cdc | ||
|
|
d29411dba6 | ||
|
|
464663877c | ||
|
|
c3f769d48b | ||
|
|
f97536ed02 | ||
|
|
7686effc8c | ||
|
|
ee8385467b | ||
|
|
0a51d97684 | ||
|
|
67fc762eef | ||
|
|
8378795f5d | ||
|
|
98e6f1df4a | ||
|
|
674e35e5c5 | ||
|
|
b24ca44e6f | ||
|
|
e0f7fcd2ef | ||
|
|
c5bece64ce | ||
|
|
0034681412 | ||
|
|
bd5b99dc1c | ||
|
|
2feb802094 | ||
|
|
7b08a71c59 | ||
|
|
2a84e9c120 | ||
|
|
d854e8e991 | ||
|
|
0a363910d6 | ||
|
|
b23d5cd909 | ||
|
|
3b753aa69d | ||
|
|
492cd288bc | ||
|
|
47938c5082 | ||
|
|
97c4c9a366 | ||
|
|
d090e48544 | ||
|
|
314e4407a0 | ||
|
|
ed50e03957 | ||
|
|
dff7530cc5 | ||
|
|
19423c9644 | ||
|
|
42b3f1e59d | ||
|
|
1b77632fa8 | ||
|
|
867b7b1390 | ||
|
|
4c6ebde58c | ||
|
|
1e013d9e9a | ||
|
|
c56bc651b9 | ||
|
|
6cbdf45a7c | ||
|
|
715667e227 | ||
|
|
41de161555 | ||
|
|
1f1ea370c0 | ||
|
|
090cfc26f2 | ||
|
|
529890b5d2 | ||
|
|
d4a6ab146d | ||
|
|
e3f02879cf | ||
|
|
b52d6df777 | ||
|
|
9e671100ae | ||
|
|
7e801ea502 | ||
|
|
b68522f7d5 | ||
|
|
86852942e0 | ||
|
|
ea88d53e39 | ||
|
|
61f6a474e4 | ||
|
|
ec05c84f4d | ||
|
|
9e13c077e9 | ||
|
|
da8d315e77 | ||
|
|
82af9af1e1 | ||
|
|
cb67e3ae63 | ||
|
|
82d15c4dc2 | ||
|
|
6d048e2cee | ||
|
|
87bd80eea1 | ||
|
|
80e442e396 | ||
|
|
489ad375bd | ||
|
|
c420196e73 | ||
|
|
cc6d8d5f8b | ||
|
|
24f47bc58b | ||
|
|
c769c074e0 | ||
|
|
2ecb8eb034 | ||
|
|
6827c100c3 | ||
|
|
c402acd1bd | ||
|
|
c4ec2509fa | ||
|
|
0f382586ce | ||
|
|
9c2f12ecb1 |
@@ -1,4 +1,4 @@
|
||||
name: build-docs
|
||||
name: build-documentation
|
||||
|
||||
on:
|
||||
release:
|
||||
@@ -11,4 +11,4 @@ jobs:
|
||||
- env:
|
||||
GITHUB_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh workflow run --repo Froxlor/Documentation build-docs -f ref=${{github.ref_name}}
|
||||
gh workflow run --repo Froxlor/Documentation build-and-deploy.yml -f type=tags ref=${{github.ref_name}}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@ install/update.log
|
||||
install/*.json
|
||||
lib/userdata.inc.php
|
||||
lib/userdata.inc.php.bak
|
||||
lib/config.inc.php
|
||||
logs/*
|
||||
!logs/index.html
|
||||
.buildpath
|
||||
|
||||
@@ -57,7 +57,7 @@ May be found in [COPYING](COPYING)
|
||||
### 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)
|
||||
|
||||
### Debian / Ubutnu repository
|
||||
### Debian / Ubuntu repository
|
||||
|
||||
[HowTo](https://docs.froxlor.org/latest/general/installation/apt-package.html)
|
||||
|
||||
|
||||
@@ -269,7 +269,8 @@ return [
|
||||
'traffic' => lng('menue.traffic.traffic'),
|
||||
'traffic.http' => lng('menue.traffic.traffic') . " / HTTP",
|
||||
'traffic.ftp' => lng('menue.traffic.traffic') . " / FTP",
|
||||
'traffic.mail' => lng('menue.traffic.traffic') . " / Mail"
|
||||
'traffic.mail' => lng('menue.traffic.traffic') . " / Mail",
|
||||
'misc.documentation' => lng('admin.documentation'),
|
||||
],
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
|
||||
@@ -138,6 +138,26 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_req_limit_per_interval' => [
|
||||
'label' => lng('serversettings.req_limit_per_interval'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'req_limit_per_interval',
|
||||
'type' => 'number',
|
||||
'min' => 30,
|
||||
'default' => 60,
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_req_limit_interval' => [
|
||||
'label' => lng('serversettings.req_limit_interval'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'req_limit_interval',
|
||||
'type' => 'number',
|
||||
'min' => 5,
|
||||
'default' => 60,
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'customer_accountprefix' => [
|
||||
'label' => lng('serversettings.accountprefix'),
|
||||
'settinggroup' => 'customer',
|
||||
|
||||
@@ -109,7 +109,19 @@ return [
|
||||
'default' => false,
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'update_channel' => [
|
||||
'api_customer_default' => [
|
||||
'label' => lng('serversettings.api_customer_default'),
|
||||
'settinggroup' => 'api',
|
||||
'varname' => 'customer_default',
|
||||
'type' => 'select',
|
||||
'default' => 1,
|
||||
'select_var' => [
|
||||
1 => lng('panel.yes'),
|
||||
0 => lng('panel.no')
|
||||
],
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'system_update_channel' => [
|
||||
'label' => lng('serversettings.update_channel'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'update_channel',
|
||||
@@ -122,7 +134,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_validatedomain' => [
|
||||
'system_validate_domain' => [
|
||||
'label' => lng('serversettings.validate_domain'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'validate_domain',
|
||||
@@ -307,7 +319,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'hide_incompatible_settings' => [
|
||||
'system_hide_incompatible_settings' => [
|
||||
'label' => lng('serversettings.hide_incompatible_settings'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'hide_incompatible_settings',
|
||||
|
||||
@@ -53,7 +53,7 @@ return [
|
||||
'string_regexp' => '/^(([a-z0-9\-\._]+, ?)*[a-z0-9\-\._]+)?$/i',
|
||||
'string_emptyallowed' => true,
|
||||
'default' => '',
|
||||
'save_method' => 'storeSettingField',
|
||||
'save_method' => 'storeSettingClearCertificates',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
/**
|
||||
@@ -154,7 +154,7 @@ return [
|
||||
/**
|
||||
* FCGID
|
||||
*/
|
||||
'system_mod_fcgid_enabled_ownvhost' => [
|
||||
'system_mod_fcgid_ownvhost' => [
|
||||
'label' => lng('serversettings.mod_fcgid_ownvhost'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'mod_fcgid_ownvhost',
|
||||
@@ -224,7 +224,7 @@ return [
|
||||
/**
|
||||
* php-fpm
|
||||
*/
|
||||
'system_phpfpm_enabled_ownvhost' => [
|
||||
'phpfpm_enabled_ownvhost' => [
|
||||
'label' => lng('phpfpm.ownvhost'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'enabled_ownvhost',
|
||||
@@ -237,7 +237,7 @@ return [
|
||||
]),
|
||||
'requires_reconf' => ['system:php-fpm']
|
||||
],
|
||||
'system_phpfpm_httpuser' => [
|
||||
'phpfpm_vhost_httpuser' => [
|
||||
'label' => lng('phpfpm.vhost_httpuser'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'vhost_httpuser',
|
||||
@@ -250,7 +250,7 @@ return [
|
||||
]),
|
||||
'requires_reconf' => ['system:php-fpm']
|
||||
],
|
||||
'system_phpfpm_httpgroup' => [
|
||||
'phpfpm_vhost_httpgroup' => [
|
||||
'label' => lng('phpfpm.vhost_httpgroup'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'vhost_httpgroup',
|
||||
@@ -263,7 +263,7 @@ return [
|
||||
]),
|
||||
'requires_reconf' => ['system:php-fpm']
|
||||
],
|
||||
'system_phpfpm_defaultini_ownvhost' => [
|
||||
'phpfpm_vhost_defaultini' => [
|
||||
'label' => lng('serversettings.mod_fcgid.defaultini_ownvhost'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'vhost_defaultini',
|
||||
|
||||
@@ -60,7 +60,7 @@ return [
|
||||
'apache2'
|
||||
]
|
||||
],
|
||||
'system_apache_itksupport' => [
|
||||
'system_apacheitksupport' => [
|
||||
'label' => lng('serversettings.apache_itksupport'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'apacheitksupport',
|
||||
@@ -229,7 +229,7 @@ return [
|
||||
'nginx'
|
||||
]
|
||||
],
|
||||
'system_customersslpath' => [
|
||||
'system_customer_ssl_path' => [
|
||||
'label' => lng('serversettings.customerssl_directory'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'customer_ssl_path',
|
||||
@@ -287,7 +287,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_apache_globaldiropt' => [
|
||||
'system_apacheglobaldiropt' => [
|
||||
'label' => lng('serversettings.apache_globaldiropt'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'apacheglobaldiropt',
|
||||
|
||||
@@ -32,7 +32,7 @@ return [
|
||||
'title' => lng('admin.sslsettings'),
|
||||
'icon' => 'fa-solid fa-shield',
|
||||
'fields' => [
|
||||
'system_ssl_enabled' => [
|
||||
'system_use_ssl' => [
|
||||
'label' => lng('serversettings.ssl.use_ssl'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'use_ssl',
|
||||
@@ -180,7 +180,9 @@ return [
|
||||
'letsencrypt' => 'Let\'s Encrypt (Live)',
|
||||
'buypass_test' => 'Buypass (Test / Staging)',
|
||||
'buypass' => 'Buypass (Live)',
|
||||
'zerossl' => 'ZeroSSL (Live)'
|
||||
'zerossl' => 'ZeroSSL (Live)',
|
||||
'google' => 'Google (Live)',
|
||||
'google_test' => 'Google (Test / Staging)',
|
||||
],
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
@@ -239,6 +241,16 @@ return [
|
||||
'type' => 'checkbox',
|
||||
'default' => true,
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'system_le_domain_dnscheck_resolver' => [
|
||||
'label' => lng('serversettings.le_domain_dnscheck_resolver'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'le_domain_dnscheck_resolver',
|
||||
'type' => 'text',
|
||||
'string_regexp' => '/^(([0-9]+ [a-z0-9\-\._]+, ?)*[0-9]+ [a-z0-9\-\._]+)?$/i',
|
||||
'string_emptyallowed' => true,
|
||||
'default' => '',
|
||||
'save_method' => 'storeSettingField'
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -33,7 +33,7 @@ return [
|
||||
'lighttpd'
|
||||
],
|
||||
'fields' => [
|
||||
'system_mod_fcgid_enabled' => [
|
||||
'system_mod_fcgid' => [
|
||||
'label' => lng('serversettings.mod_fcgid'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'mod_fcgid',
|
||||
|
||||
@@ -31,7 +31,7 @@ return [
|
||||
'title' => lng('admin.phpfpm_settings'),
|
||||
'icon' => 'fa-brands fa-php',
|
||||
'fields' => [
|
||||
'system_phpfpm_enabled' => [
|
||||
'phpfpm_enabled' => [
|
||||
'label' => lng('serversettings.phpfpm'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'enabled',
|
||||
@@ -45,7 +45,7 @@ return [
|
||||
'overview_option' => true,
|
||||
'requires_reconf' => ['http', 'system:php-fpm']
|
||||
],
|
||||
'system_phpfpm_defaultini' => [
|
||||
'phpfpm_defaultini' => [
|
||||
'label' => lng('serversettings.mod_fcgid.defaultini'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'defaultini',
|
||||
@@ -57,7 +57,7 @@ return [
|
||||
],
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'system_phpfpm_aliasconfigdir' => [
|
||||
'phpfpm_aliasconfigdir' => [
|
||||
'label' => lng('serversettings.phpfpm_settings.aliasconfigdir'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'aliasconfigdir',
|
||||
@@ -67,7 +67,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_phpfpm_tmpdir' => [
|
||||
'phpfpm_tmpdir' => [
|
||||
'label' => lng('serversettings.mod_fcgid.tmpdir'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'tmpdir',
|
||||
@@ -76,7 +76,7 @@ return [
|
||||
'default' => '/var/customers/tmp/',
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'system_phpfpm_peardir' => [
|
||||
'phpfpm_peardir' => [
|
||||
'label' => lng('serversettings.mod_fcgid.peardir'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'peardir',
|
||||
@@ -88,7 +88,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_phpfpm_envpath' => [
|
||||
'phpfpm_envpath' => [
|
||||
'label' => lng('serversettings.phpfpm_settings.envpath'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'envpath',
|
||||
@@ -100,7 +100,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_phpfpm_fastcgi_ipcdir' => [
|
||||
'phpfpm_fastcgi_ipcdir' => [
|
||||
'label' => lng('serversettings.phpfpm_settings.ipcdir'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'fastcgi_ipcdir',
|
||||
@@ -110,7 +110,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_phpfpm_use_mod_proxy' => [
|
||||
'phpfpm_use_mod_proxy' => [
|
||||
'label' => lng('phpfpm.use_mod_proxy'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'use_mod_proxy',
|
||||
@@ -119,7 +119,7 @@ return [
|
||||
'visible' => Settings::Get('system.apache24'),
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'system_phpfpm_ini_flags' => [
|
||||
'phpfpm_ini_flags' => [
|
||||
'label' => lng('phpfpm.ini_flags'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'ini_flags',
|
||||
@@ -128,7 +128,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_phpfpm_ini_values' => [
|
||||
'phpfpm_ini_values' => [
|
||||
'label' => lng('phpfpm.ini_values'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'ini_values',
|
||||
@@ -137,7 +137,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_phpfpm_ini_admin_flags' => [
|
||||
'phpfpm_ini_admin_flags' => [
|
||||
'label' => lng('phpfpm.ini_admin_flags'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'ini_admin_flags',
|
||||
@@ -146,7 +146,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'system_phpfpm_ini_admin_values' => [
|
||||
'phpfpm_ini_admin_values' => [
|
||||
'label' => lng('phpfpm.ini_admin_values'),
|
||||
'settinggroup' => 'phpfpm',
|
||||
'varname' => 'ini_admin_values',
|
||||
|
||||
@@ -29,7 +29,7 @@ return [
|
||||
'title' => lng('admin.perl_settings'),
|
||||
'icon' => 'fa-solid fa-code',
|
||||
'fields' => [
|
||||
'perl_path' => [
|
||||
'system_perl_path' => [
|
||||
'label' => lng('serversettings.perl_path'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'perl_path',
|
||||
@@ -40,7 +40,7 @@ return [
|
||||
'lighttpd'
|
||||
]
|
||||
],
|
||||
'system_perl_suexecworkaround' => [
|
||||
'perl_suexecworkaround' => [
|
||||
'label' => lng('serversettings.perl.suexecworkaround'),
|
||||
'settinggroup' => 'perl',
|
||||
'varname' => 'suexecworkaround',
|
||||
@@ -51,7 +51,7 @@ return [
|
||||
'apache2'
|
||||
]
|
||||
],
|
||||
'system_perl_suexeccgipath' => [
|
||||
'perl_suexecpath' => [
|
||||
'label' => lng('serversettings.perl.suexeccgipath'),
|
||||
'settinggroup' => 'perl',
|
||||
'varname' => 'suexecpath',
|
||||
@@ -63,7 +63,7 @@ return [
|
||||
'apache2'
|
||||
]
|
||||
],
|
||||
'perl_server' => [
|
||||
'serversettings_perl_server' => [
|
||||
'label' => lng('serversettings.perl_server'),
|
||||
'settinggroup' => 'serversettings',
|
||||
'varname' => 'perl_server',
|
||||
|
||||
@@ -98,7 +98,7 @@ return [
|
||||
'default' => 100,
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'system_catchall_enabled' => [
|
||||
'catchall_catchall_enabled' => [
|
||||
'label' => lng('serversettings.catchall_enabled'),
|
||||
'settinggroup' => 'catchall',
|
||||
'varname' => 'catchall_enabled',
|
||||
|
||||
@@ -29,7 +29,7 @@ return [
|
||||
'title' => lng('admin.ftpserversettings'),
|
||||
'icon' => 'fa-solid fa-arrow-right-arrow-left',
|
||||
'fields' => [
|
||||
'ftpserver' => [
|
||||
'system_ftpserver' => [
|
||||
'label' => lng('admin.ftpserver'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'ftpserver',
|
||||
|
||||
@@ -31,7 +31,7 @@ return [
|
||||
'title' => lng('admin.nameserversettings'),
|
||||
'icon' => 'fa-solid fa-globe',
|
||||
'fields' => [
|
||||
'nameserver_enable' => [
|
||||
'system_bind_enable' => [
|
||||
'label' => lng('serversettings.bindenable'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'bind_enable',
|
||||
|
||||
@@ -31,7 +31,7 @@ return [
|
||||
'title' => lng('admin.dkimsettings'),
|
||||
'icon' => 'fa-solid fa-fingerprint',
|
||||
'fields' => [
|
||||
'dkim_enabled' => [
|
||||
'dkim_use_dkim' => [
|
||||
'label' => lng('dkim.use_dkim'),
|
||||
'settinggroup' => 'dkim',
|
||||
'varname' => 'use_dkim',
|
||||
@@ -40,7 +40,7 @@ return [
|
||||
'save_method' => 'storeSettingFieldInsertBindTask',
|
||||
'overview_option' => true
|
||||
],
|
||||
'dkim_prefix' => [
|
||||
'dkim_dkim_prefix' => [
|
||||
'label' => lng('dkim.dkim_prefix'),
|
||||
'settinggroup' => 'dkim',
|
||||
'varname' => 'dkim_prefix',
|
||||
@@ -59,7 +59,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'dkim_domains' => [
|
||||
'dkim_dkim_domains' => [
|
||||
'label' => lng('dkim.dkim_domains'),
|
||||
'settinggroup' => 'dkim',
|
||||
'varname' => 'dkim_domains',
|
||||
@@ -68,7 +68,7 @@ return [
|
||||
'default' => 'domains',
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'dkim_dkimkeys' => [
|
||||
'dkim_dkim_dkimkeys' => [
|
||||
'label' => lng('dkim.dkim_dkimkeys'),
|
||||
'settinggroup' => 'dkim',
|
||||
'varname' => 'dkim_dkimkeys',
|
||||
@@ -77,7 +77,7 @@ return [
|
||||
'default' => 'dkim-keys.conf',
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'dkim_algorithm' => [
|
||||
'dkim_dkim_algorithm' => [
|
||||
'label' => lng('dkim.dkim_algorithm'),
|
||||
'settinggroup' => 'dkim',
|
||||
'varname' => 'dkim_algorithm',
|
||||
@@ -92,7 +92,7 @@ return [
|
||||
'save_method' => 'storeSettingFieldInsertBindTask',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'dkim_servicetype' => [
|
||||
'dkim_dkim_servicetype' => [
|
||||
'label' => lng('dkim.dkim_servicetype'),
|
||||
'settinggroup' => 'dkim',
|
||||
'varname' => 'dkim_servicetype',
|
||||
@@ -105,7 +105,7 @@ return [
|
||||
'save_method' => 'storeSettingFieldInsertBindTask',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'dkim_keylength' => [
|
||||
'dkim_dkim_keylength' => [
|
||||
'label' => [
|
||||
'title' => lng('dkim.dkim_keylength.title'),
|
||||
'description' => lng('dkim.dkim_keylength.description', [Settings::Get('dkim.dkim_prefix')])
|
||||
@@ -120,7 +120,7 @@ return [
|
||||
],
|
||||
'save_method' => 'storeSettingFieldInsertBindTask'
|
||||
],
|
||||
'dkim_notes' => [
|
||||
'dkim_dkim_notes' => [
|
||||
'label' => lng('dkim.dkim_notes'),
|
||||
'settinggroup' => 'dkim',
|
||||
'varname' => 'dkim_notes',
|
||||
@@ -130,7 +130,7 @@ return [
|
||||
'save_method' => 'storeSettingFieldInsertBindTask',
|
||||
'advanced_mode' => true
|
||||
],
|
||||
'dkimrestart_command' => [
|
||||
'dkim_dkimrestart_command' => [
|
||||
'label' => lng('dkim.dkimrestart_command'),
|
||||
'settinggroup' => 'dkim',
|
||||
'varname' => 'dkimrestart_command',
|
||||
|
||||
@@ -29,7 +29,7 @@ return [
|
||||
'title' => lng('admin.spfsettings'),
|
||||
'icon' => 'fa-solid fa-clipboard-check',
|
||||
'fields' => [
|
||||
'use_spf' => [
|
||||
'spf_use_spf' => [
|
||||
'label' => lng('spf.use_spf'),
|
||||
'settinggroup' => 'spf',
|
||||
'varname' => 'use_spf',
|
||||
@@ -38,7 +38,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'overview_option' => true
|
||||
],
|
||||
'spf_entry' => [
|
||||
'spf_spf_entry' => [
|
||||
'label' => lng('spf.spf_entry'),
|
||||
'settinggroup' => 'spf',
|
||||
'varname' => 'spf_entry',
|
||||
|
||||
@@ -30,7 +30,7 @@ return [
|
||||
'icon' => 'fa-solid fa-sliders',
|
||||
'advanced_mode' => true,
|
||||
'fields' => [
|
||||
'diskquota_enabled' => [
|
||||
'system_diskquota_enabled' => [
|
||||
'label' => lng('serversettings.diskquota_enabled'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'diskquota_enabled',
|
||||
@@ -39,7 +39,7 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'overview_option' => true
|
||||
],
|
||||
'diskquota_repquota_path' => [
|
||||
'system_diskquota_repquota_path' => [
|
||||
'label' => lng('serversettings.diskquota_repquota_path.description'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'diskquota_repquota_path',
|
||||
@@ -47,7 +47,7 @@ return [
|
||||
'default' => '/usr/sbin/repquota',
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'diskquota_quotatool_path' => [
|
||||
'system_diskquota_quotatool_path' => [
|
||||
'label' => lng('serversettings.diskquota_quotatool_path.description'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'diskquota_quotatool_path',
|
||||
@@ -55,7 +55,7 @@ return [
|
||||
'default' => '/usr/bin/quotatool',
|
||||
'save_method' => 'storeSettingField'
|
||||
],
|
||||
'diskquota_customer_partition' => [
|
||||
'system_diskquota_customer_partition' => [
|
||||
'label' => lng('serversettings.diskquota_customer_partition.description'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'diskquota_customer_partition',
|
||||
|
||||
@@ -77,6 +77,7 @@ if (($page == 'admins' || $page == 'overview') && $userinfo['change_serversettin
|
||||
$result['switched_user'] = CurrentUser::getData();
|
||||
$result['adminsession'] = 1;
|
||||
$result['userid'] = $result['adminid'];
|
||||
session_regenerate_id(true);
|
||||
CurrentUser::setData($result);
|
||||
|
||||
$log->logAction(
|
||||
|
||||
@@ -28,7 +28,7 @@ require __DIR__ . '/lib/init.php';
|
||||
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\Http\HttpClient;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Install\AutoUpdate;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
@@ -132,7 +132,7 @@ elseif ($page == 'getdownload') {
|
||||
elseif ($page == 'extract') {
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
||||
$toExtract = isset($_POST['archive']) ? $_POST['archive'] : null;
|
||||
$localArchive = Froxlor::getInstallDir() . '/updates/' . $toExtract;
|
||||
$localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
|
||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Extracting " . $localArchive . " to " . Froxlor::getInstallDir());
|
||||
$result = AutoUpdate::extractZip($localArchive);
|
||||
if ($result > 0) {
|
||||
@@ -146,7 +146,7 @@ elseif ($page == 'extract') {
|
||||
Response::redirectTo('admin_updates.php');
|
||||
} else {
|
||||
$toExtract = isset($_GET['archive']) ? $_GET['archive'] : null;
|
||||
$localArchive = Froxlor::getInstallDir() . '/updates/' . $toExtract;
|
||||
$localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
|
||||
}
|
||||
|
||||
if (!file_exists($localArchive)) {
|
||||
|
||||
@@ -33,6 +33,7 @@ use Froxlor\Settings;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
use Froxlor\UI\Request;
|
||||
use Froxlor\UI\Response;
|
||||
use Froxlor\Validate\Validate;
|
||||
|
||||
if ($userinfo['change_serversettings'] == '1') {
|
||||
if ($action == 'setconfigured') {
|
||||
@@ -91,13 +92,29 @@ if ($userinfo['change_serversettings'] == '1') {
|
||||
}
|
||||
|
||||
if ($distribution != "" && isset($_POST['finish'])) {
|
||||
$valid_keys = ['http', 'dns', 'smtp', 'mail', 'ftp', 'system', 'distro'];
|
||||
unset($_POST['finish']);
|
||||
unset($_POST['csrf_token']);
|
||||
$params = $_POST;
|
||||
$params['distro'] = $distribution;
|
||||
$params['system'] = [];
|
||||
foreach ($_POST['system'] as $sysdaemon) {
|
||||
$params['system'][] = $sysdaemon;
|
||||
}
|
||||
// validate params
|
||||
foreach ($params as $key => $value) {
|
||||
if (!in_array($key, $valid_keys)) {
|
||||
unset($params[$key]);
|
||||
continue;
|
||||
}
|
||||
if (!is_array($value)) {
|
||||
$params[$key] = Validate::validate($value, $key);
|
||||
} else {
|
||||
foreach ($value as $subkey => $subvalue) {
|
||||
$params[$key][$subkey] = Validate::validate($subvalue, $key.'.'.$subkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
$params_content = json_encode($params);
|
||||
$params_filename = FileDir::makeCorrectFile(Froxlor::getInstallDir() . 'install/' . Froxlor::genSessionId() . '.json');
|
||||
file_put_contents($params_filename, $params_content);
|
||||
@@ -121,8 +138,6 @@ if ($userinfo['change_serversettings'] == '1') {
|
||||
'distribution' => $distribution
|
||||
]);
|
||||
} else {
|
||||
// @fixme check set distribution from settings
|
||||
|
||||
$cfg_formfield = [
|
||||
'config' => [
|
||||
'title' => lng('admin.configfiles.serverconfiguration'),
|
||||
|
||||
@@ -93,6 +93,7 @@ if (($page == 'customers' || $page == 'overview') && $userinfo['customers'] != '
|
||||
$result['switched_user'] = CurrentUser::getData();
|
||||
$result['adminsession'] = 0;
|
||||
$result['userid'] = $result['customerid'];
|
||||
session_regenerate_id(true);
|
||||
CurrentUser::setData($result);
|
||||
|
||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "switched user and is now '" . $destination_user . "'");
|
||||
|
||||
@@ -282,6 +282,12 @@ if ($page == 'domains' || $page == 'overview') {
|
||||
}
|
||||
}
|
||||
|
||||
$openbasedir = [
|
||||
0 => lng('domain.docroot'),
|
||||
1 => lng('domain.homedir'),
|
||||
2 => lng('domain.docparent')
|
||||
];
|
||||
|
||||
// create serveralias options
|
||||
$serveraliasoptions = [
|
||||
0 => lng('domains.serveraliasoption_wildcard'),
|
||||
@@ -545,6 +551,12 @@ if ($page == 'domains' || $page == 'overview') {
|
||||
$result['temporary_ssl_redirect'] = $result['ssl_redirect'];
|
||||
$result['ssl_redirect'] = ($result['ssl_redirect'] == 0 ? 0 : 1);
|
||||
|
||||
$openbasedir = [
|
||||
0 => lng('domain.docroot'),
|
||||
1 => lng('domain.homedir'),
|
||||
2 => lng('domain.docparent')
|
||||
];
|
||||
|
||||
$serveraliasoptions = [
|
||||
0 => lng('domains.serveraliasoption_wildcard'),
|
||||
1 => lng('domains.serveraliasoption_www'),
|
||||
|
||||
@@ -53,6 +53,7 @@ if ($action == 'logout') {
|
||||
if (is_array(CurrentUser::getField('switched_user'))) {
|
||||
$result = CurrentUser::getData();
|
||||
$result = $result['switched_user'];
|
||||
session_regenerate_id(true);
|
||||
CurrentUser::setData($result);
|
||||
$target = (isset($_GET['target']) ? $_GET['target'] : 'index');
|
||||
$redirect = "admin_" . $target . ".php";
|
||||
|
||||
@@ -43,12 +43,6 @@ use PHPMailer\PHPMailer\PHPMailer;
|
||||
const AREA = 'admin';
|
||||
require __DIR__ . '/lib/init.php';
|
||||
|
||||
// get sql-root access data
|
||||
Database::needRoot(true);
|
||||
Database::needSqlData();
|
||||
$sql_root = Database::getSqlData();
|
||||
Database::needRoot(false);
|
||||
|
||||
if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
|
||||
$settings_data = PhpHelper::loadConfigArrayDir('./actions/admin/settings/');
|
||||
Settings::loadSettingsInto($settings_data);
|
||||
|
||||
@@ -253,6 +253,9 @@ if ($action == '') {
|
||||
if (isset($_POST['prepare']) && $_POST['prepare'] == 'prepare') {
|
||||
// email templates
|
||||
$language = htmlentities(Validate::validate($_POST['language'], 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect'));
|
||||
if (!array_key_exists($language, $languages)) {
|
||||
Response::standardError('templatelanguageinvalid');
|
||||
}
|
||||
$template = Validate::validate($_POST['template'], 'template');
|
||||
|
||||
$result_stmt = Database::prepare("
|
||||
@@ -288,6 +291,9 @@ if ($action == '') {
|
||||
} elseif (isset($_POST['send']) && $_POST['send'] == 'send' && !isset($_POST['filesend'])) {
|
||||
// email templates
|
||||
$language = htmlentities(Validate::validate($_POST['language'], 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect'));
|
||||
if (!array_key_exists($language, $languages)) {
|
||||
Response::standardError('templatelanguageinvalid');
|
||||
}
|
||||
$template = Validate::validate($_POST['template'], 'template');
|
||||
$subject = Validate::validate($_POST['subject'], 'subject', '/^[^\r\n\0]+$/', 'nosubjectcreate');
|
||||
$mailbody = Validate::validate($_POST['mailbody'], 'mailbody', '/^[^\0]+$/', 'nomailbodycreate');
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Froxlor\Cli\ConfigDiff;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Froxlor\Cli\RunApiCommand;
|
||||
use Froxlor\Cli\ConfigServices;
|
||||
@@ -35,6 +36,7 @@ use Froxlor\Cli\UpdateCommand;
|
||||
use Froxlor\Cli\InstallCommand;
|
||||
use Froxlor\Cli\MasterCron;
|
||||
use Froxlor\Cli\UserCommand;
|
||||
use Froxlor\Cli\ValidateAcmeWebroot;
|
||||
use Froxlor\Froxlor;
|
||||
|
||||
// validate correct php version
|
||||
@@ -59,4 +61,6 @@ $application->add(new UpdateCommand());
|
||||
$application->add(new InstallCommand());
|
||||
$application->add(new MasterCron());
|
||||
$application->add(new UserCommand());
|
||||
$application->add(new ValidateAcmeWebroot());
|
||||
$application->add(new ConfigDiff());
|
||||
$application->run();
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-gmp": "*",
|
||||
"ext-gd": "*",
|
||||
"phpmailer/phpmailer": "~6.0",
|
||||
"monolog/monolog": "^1.24",
|
||||
"robthree/twofactorauth": "^1.6",
|
||||
@@ -52,7 +53,8 @@
|
||||
"voku/anti-xss": "^4.1",
|
||||
"twig/twig": "^3.3",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"symfony/console": "^5.4"
|
||||
"symfony/console": "^5.4",
|
||||
"pear/net_dns2": "^1.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9",
|
||||
|
||||
836
composer.lock
generated
836
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -59,7 +59,6 @@ if ($page == 'overview' || $page == 'domains') {
|
||||
$domain_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.domains.php';
|
||||
$collection = (new Collection(SubDomains::class, $userinfo))
|
||||
->withPagination($domain_list_data['domain_list']['columns'], $domain_list_data['domain_list']['default_sorting']);
|
||||
$parentDomainCollection = (new Collection(SubDomains::class, $userinfo, ['sql_search' => ['d.parentdomainid' => 0]]));
|
||||
} catch (Exception $e) {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
|
||||
@@ -26,9 +26,10 @@
|
||||
const AREA = 'customer';
|
||||
require __DIR__ . '/lib/init.php';
|
||||
|
||||
use Froxlor\Api\Commands\EmailAccounts as EmailAccounts;
|
||||
use Froxlor\Api\Commands\EmailForwarders as EmailForwarders;
|
||||
use Froxlor\Api\Commands\Emails as Emails;
|
||||
use Froxlor\Api\Commands\EmailAccounts;
|
||||
use Froxlor\Api\Commands\EmailForwarders;
|
||||
use Froxlor\Api\Commands\Emails;
|
||||
use Froxlor\Api\Commands\EmailDomains;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\PhpHelper;
|
||||
@@ -50,13 +51,50 @@ if (Settings::IsInList('panel.customer_hide_options', 'email') || $userinfo['ema
|
||||
$id = (int)Request::any('id');
|
||||
|
||||
if ($page == 'overview' || $page == 'emails') {
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT COUNT(DISTINCT `domainid`) as maildomains FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid`= :cid
|
||||
");
|
||||
$domain_count = Database::pexecute_first($result_stmt, [
|
||||
"cid" => $userinfo['customerid']
|
||||
]);
|
||||
if ($domain_count['maildomains'] && $domain_count['maildomains'] > 1) {
|
||||
try {
|
||||
$emaildomain_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.emails_overview.php';
|
||||
$collection = (new Collection(EmailDomains::class, $userinfo))
|
||||
->withPagination($emaildomain_list_data['emaildomain_list']['columns'],
|
||||
$emaildomain_list_data['emaildomain_list']['default_sorting']);
|
||||
} catch (Exception $e) {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
|
||||
UI::view('user/table.html.twig', [
|
||||
'listing' => Listing::format($collection, $emaildomain_list_data, 'emaildomain_list'),
|
||||
'actions_links' => CurrentUser::canAddResource('emails') ? [
|
||||
[
|
||||
'href' => $linker->getLink(['section' => 'email', 'page' => 'email_domain', 'action' => 'add']),
|
||||
'label' => lng('emails.emails_add')
|
||||
]
|
||||
] : null,
|
||||
]);
|
||||
} else {
|
||||
// only emails for one domain -> show email address listing directly
|
||||
$page = 'email_domain';
|
||||
}
|
||||
}
|
||||
if ($page == 'email_domain') {
|
||||
$email_domainid = Request::any('domainid', 0);
|
||||
if ($action == '') {
|
||||
$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed customer_email::emails");
|
||||
|
||||
$sql_search = [];
|
||||
if ($email_domainid > 0) {
|
||||
$sql_search = ['sql_search' => ['m.domainid' => ['op' => '=', 'value' => $email_domainid]]];
|
||||
}
|
||||
try {
|
||||
$email_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.emails.php';
|
||||
$collection = (new Collection(Emails::class, $userinfo))
|
||||
->withPagination($email_list_data['email_list']['columns'], $email_list_data['email_list']['default_sorting']);
|
||||
$collection = (new Collection(Emails::class, $userinfo, $sql_search))
|
||||
->withPagination($email_list_data['email_list']['columns'],
|
||||
$email_list_data['email_list']['default_sorting']);
|
||||
} catch (Exception $e) {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
@@ -71,13 +109,22 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
]);
|
||||
$emaildomains_count = $result2['emaildomains'];
|
||||
|
||||
$actions_links = false;
|
||||
$actions_links = [];
|
||||
if ($email_domainid > 0) {
|
||||
$actions_links[] = [
|
||||
'class' => 'btn-outline-primary',
|
||||
'href' => $linker->getLink([
|
||||
'section' => 'email',
|
||||
'page' => 'emails',
|
||||
]),
|
||||
'label' => lng('emails.back_to_overview'),
|
||||
'icon' => 'fa-solid fa-reply'
|
||||
];
|
||||
}
|
||||
if (CurrentUser::canAddResource('emails')) {
|
||||
$actions_links = [
|
||||
[
|
||||
'href' => $linker->getLink(['section' => 'email', 'page' => $page, 'action' => 'add']),
|
||||
'label' => lng('emails.emails_add')
|
||||
]
|
||||
$actions_links[] = [
|
||||
'href' => $linker->getLink(['section' => 'email', 'page' => 'email_domain', 'action' => 'add', 'domainid' => $email_domainid]),
|
||||
'label' => lng('emails.emails_add')
|
||||
];
|
||||
}
|
||||
|
||||
@@ -145,7 +192,11 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
"cid" => $userinfo['customerid']
|
||||
]);
|
||||
$domains = [];
|
||||
$selected_domain = "";
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
if ($email_domainid == $row['id']) {
|
||||
$selected_domain = $row['domain'];
|
||||
}
|
||||
$domains[$row['domain']] = $idna_convert->decode($row['domain']);
|
||||
}
|
||||
|
||||
@@ -244,11 +295,13 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
}
|
||||
Response::redirectTo($filename, [
|
||||
'page' => $page,
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
'id' => $id,
|
||||
]);
|
||||
}
|
||||
} elseif ($page == 'accounts') {
|
||||
$email_domainid = Request::any('domainid', 0);
|
||||
if ($action == 'add' && $id != 0) {
|
||||
if ($userinfo['email_accounts'] == '-1' || ($userinfo['email_accounts_used'] < $userinfo['email_accounts'])) {
|
||||
try {
|
||||
@@ -267,7 +320,8 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
Response::redirectTo($filename, [
|
||||
'page' => 'emails',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -292,7 +346,8 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
'class' => 'btn-secondary',
|
||||
'href' => $linker->getLink([
|
||||
'section' => 'email',
|
||||
'page' => 'emails',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
]),
|
||||
@@ -301,7 +356,11 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
],
|
||||
[
|
||||
'class' => 'btn-secondary',
|
||||
'href' => $linker->getLink(['section' => 'email', 'page' => 'emails']),
|
||||
'href' => $linker->getLink([
|
||||
'section' => 'email',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid
|
||||
]),
|
||||
'label' => lng('menue.email.emails'),
|
||||
'icon' => 'fa-solid fa-envelope'
|
||||
]
|
||||
@@ -332,7 +391,8 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
Response::redirectTo($filename, [
|
||||
'page' => 'emails',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -350,7 +410,8 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
'class' => 'btn-secondary',
|
||||
'href' => $linker->getLink([
|
||||
'section' => 'email',
|
||||
'page' => 'emails',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
]),
|
||||
@@ -359,7 +420,11 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
],
|
||||
[
|
||||
'class' => 'btn-secondary',
|
||||
'href' => $linker->getLink(['section' => 'email', 'page' => 'emails']),
|
||||
'href' => $linker->getLink([
|
||||
'section' => 'email',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid
|
||||
]),
|
||||
'label' => lng('menue.email.emails'),
|
||||
'icon' => 'fa-solid fa-envelope'
|
||||
]
|
||||
@@ -385,7 +450,8 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
Response::redirectTo($filename, [
|
||||
'page' => 'emails',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -403,7 +469,8 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
'class' => 'btn-secondary',
|
||||
'href' => $linker->getLink([
|
||||
'section' => 'email',
|
||||
'page' => 'emails',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
]),
|
||||
@@ -412,7 +479,11 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
],
|
||||
[
|
||||
'class' => 'btn-secondary',
|
||||
'href' => $linker->getLink(['section' => 'email', 'page' => 'emails']),
|
||||
'href' => $linker->getLink([
|
||||
'section' => 'email',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid
|
||||
]),
|
||||
'label' => lng('menue.email.emails'),
|
||||
'icon' => 'fa-solid fa-envelope'
|
||||
]
|
||||
@@ -438,7 +509,8 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
Response::redirectTo($filename, [
|
||||
'page' => 'emails',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -446,12 +518,14 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
HTML::askYesNoWithCheckbox('email_reallydelete_account', 'admin_customer_alsoremovemail', $filename, [
|
||||
'id' => $id,
|
||||
'page' => $page,
|
||||
'domainid' => $email_domainid,
|
||||
'action' => $action
|
||||
], $idna_convert->decode($result['email_full']));
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($page == 'forwarders') {
|
||||
$email_domainid = Request::any('domainid', 0);
|
||||
if ($action == 'add' && $id != 0) {
|
||||
if ($userinfo['email_forwarders_used'] < $userinfo['email_forwarders'] || $userinfo['email_forwarders'] == '-1') {
|
||||
try {
|
||||
@@ -471,7 +545,8 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
Response::redirectTo($filename, [
|
||||
'page' => 'emails',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -489,7 +564,8 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
'class' => 'btn-secondary',
|
||||
'href' => $linker->getLink([
|
||||
'section' => 'email',
|
||||
'page' => 'emails',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
]),
|
||||
@@ -498,7 +574,11 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
],
|
||||
[
|
||||
'class' => 'btn-secondary',
|
||||
'href' => $linker->getLink(['section' => 'email', 'page' => 'emails']),
|
||||
'href' => $linker->getLink([
|
||||
'section' => 'email',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid
|
||||
]),
|
||||
'label' => lng('menue.email.emails'),
|
||||
'icon' => 'fa-solid fa-envelope'
|
||||
]
|
||||
@@ -540,7 +620,8 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
Response::redirectTo($filename, [
|
||||
'page' => 'emails',
|
||||
'page' => 'email_domain',
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -549,6 +630,7 @@ if ($page == 'overview' || $page == 'emails') {
|
||||
'id' => $id,
|
||||
'forwarderid' => $forwarderid,
|
||||
'page' => $page,
|
||||
'domainid' => $email_domainid,
|
||||
'action' => $action
|
||||
], $idna_convert->decode($result['email_full']) . ' -> ' . $idna_convert->decode($forwarder));
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ use Froxlor\UI\Response;
|
||||
use Froxlor\CurrentUser;
|
||||
|
||||
// redirect if this customer page is hidden via settings
|
||||
if (Settings::IsInList('panel.customer_hide_options', 'ftp') || $userinfo['ftps'] == 0) {
|
||||
if (Settings::IsInList('panel.customer_hide_options', 'ftp')) {
|
||||
Response::redirectTo('customer_index.php');
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ if ($page == 'overview' || $page == 'accounts') {
|
||||
if (Settings::Get('customer.ftpatdomain') == '1') {
|
||||
$domainlist = [];
|
||||
$result_domains_stmt = Database::prepare("SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "`
|
||||
WHERE `customerid`= :customerid");
|
||||
WHERE `customerid`= :customerid ORDER BY `domain` ASC");
|
||||
Database::pexecute($result_domains_stmt, [
|
||||
"customerid" => $userinfo['customerid']
|
||||
]);
|
||||
@@ -127,7 +127,6 @@ if ($page == 'overview' || $page == 'accounts') {
|
||||
while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$domainlist[$row_domain['domain']] = $idna_convert->decode($row_domain['domain']);
|
||||
}
|
||||
sort($domainlist);
|
||||
}
|
||||
|
||||
if (Settings::Get('system.allow_customer_shell') == '1') {
|
||||
|
||||
@@ -52,6 +52,7 @@ if ($action == 'logout') {
|
||||
if (is_array(CurrentUser::getField('switched_user'))) {
|
||||
$result = CurrentUser::getData();
|
||||
$result = $result['switched_user'];
|
||||
session_regenerate_id(true);
|
||||
CurrentUser::setData($result);
|
||||
$target = (isset($_GET['target']) ? $_GET['target'] : 'index');
|
||||
$redirect = "admin_" . $target . ".php";
|
||||
@@ -115,10 +116,14 @@ if ($page == 'overview') {
|
||||
|
||||
if ($usages) {
|
||||
$userinfo['diskspace_bytes_used'] = $usages['webspace'] * 1024;
|
||||
$userinfo['mailspace_used'] = $usages['mail'] * 1024;
|
||||
$userinfo['dbspace_used'] = $usages['mysql'] * 1024;
|
||||
$userinfo['total_bytes_used'] = ($usages['webspace'] + $usages['mail'] + $usages['mysql']) * 1024;
|
||||
} else {
|
||||
$userinfo['diskspace_bytes_used'] = 0;
|
||||
$userinfo['total_bytes_used'] = 0;
|
||||
$userinfo['mailspace_used'] = 0;
|
||||
$userinfo['dbspace_used'] = 0;
|
||||
}
|
||||
|
||||
UI::twig()->addGlobal('userinfo', $userinfo);
|
||||
|
||||
@@ -92,7 +92,7 @@ if ($page == 'overview' || $page == 'mysqls') {
|
||||
$result = json_decode($json_result, true)['data'];
|
||||
|
||||
if (isset($result['databasename']) && $result['databasename'] != '') {
|
||||
Database::needRoot(true, $result['dbserver']);
|
||||
Database::needRoot(true, $result['dbserver'], false);
|
||||
Database::needSqlData();
|
||||
$sql_root = Database::getSqlData();
|
||||
Database::needRoot(false);
|
||||
|
||||
@@ -104,7 +104,7 @@ if ($action == 'add_record' && !empty($_POST)) {
|
||||
try {
|
||||
$dns_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/tablelisting.dns.php';
|
||||
$collection = (new Collection(DomainZones::class, $userinfo, ['id' => $domain_id]))
|
||||
->withPagination($dns_list_data['dns_list']['columns'], $dns_list_data['dns_list']['default_sorting']);
|
||||
->withPagination($dns_list_data['dns_list']['columns'], $dns_list_data['dns_list']['default_sorting'], ['domain_id='.$domain_id]);
|
||||
} catch (Exception $e) {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ use Froxlor\Froxlor;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
use Froxlor\UI\Request;
|
||||
use Froxlor\UI\Response;
|
||||
use Froxlor\Database\Database;
|
||||
|
||||
// This file is being included in admin_domains and customer_domains
|
||||
// and therefore does not need to require lib/init.php
|
||||
|
||||
101
index.php
101
index.php
@@ -40,7 +40,6 @@ use Froxlor\UI\Panel\UI;
|
||||
use Froxlor\UI\Response;
|
||||
use Froxlor\User;
|
||||
use Froxlor\Validate\Validate;
|
||||
use Froxlor\Language;
|
||||
|
||||
if ($action == '') {
|
||||
$action = 'login';
|
||||
@@ -53,9 +52,15 @@ if ($action == '2fa_entercode') {
|
||||
Response::redirectTo('index.php');
|
||||
exit();
|
||||
}
|
||||
$smessage = isset($_GET['showmessage']) ? (int)$_GET['showmessage'] : 0;
|
||||
$message = "";
|
||||
if ($smessage > 0) {
|
||||
$message = lng('error.2fa_wrongcode');
|
||||
}
|
||||
// show template to enter code
|
||||
UI::view('login/enter2fa.html.twig', [
|
||||
'pagetitle' => lng('login.2fa')
|
||||
'pagetitle' => lng('login.2fa'),
|
||||
'message' => $message
|
||||
]);
|
||||
} elseif ($action == '2fa_verify') {
|
||||
// verify code from 2fa code-enter form
|
||||
@@ -68,25 +73,25 @@ if ($action == '2fa_entercode') {
|
||||
// verify entered code
|
||||
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
|
||||
$result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3));
|
||||
// get user-data
|
||||
$table = $_SESSION['uidtable_2fa'];
|
||||
$field = $_SESSION['uidfield_2fa'];
|
||||
$uid = $_SESSION['uid_2fa'];
|
||||
$isadmin = $_SESSION['unfo_2fa'];
|
||||
// either the code is valid when using authenticator-app, or we will select userdata by id and entered code
|
||||
// which is temporarily stored for the customer when using email-2fa
|
||||
if ($result) {
|
||||
// get user-data
|
||||
$table = $_SESSION['uidtable_2fa'];
|
||||
$field = $_SESSION['uidfield_2fa'];
|
||||
$uid = $_SESSION['uid_2fa'];
|
||||
$isadmin = $_SESSION['unfo_2fa'];
|
||||
$sel_param = [
|
||||
'uid' => $uid
|
||||
];
|
||||
if ($_SESSION['secret_2fa'] == 'email') {
|
||||
// verify code by selecting user by id and the temp. stored code,
|
||||
// so only if it's the correct code, we get the user-data
|
||||
$sel_stmt = Database::prepare("SELECT * FROM $table WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
|
||||
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
|
||||
$sel_param['code'] = $code;
|
||||
} else {
|
||||
// Authenticator-verification has already happened at this point, so just get the user-data
|
||||
$sel_stmt = Database::prepare("SELECT * FROM $table WHERE `" . $field . "` = :uid");
|
||||
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid");
|
||||
}
|
||||
$userinfo = Database::pexecute_first($sel_stmt, $sel_param);
|
||||
// whoops, no (valid) user? Start again
|
||||
@@ -108,19 +113,54 @@ if ($action == '2fa_entercode') {
|
||||
|
||||
// when using email-2fa, remove the one-time-code
|
||||
if ($userinfo['type_2fa'] == '1') {
|
||||
$del_stmt = Database::prepare("UPDATE $table SET `data_2fa` = '' WHERE `" . $field . "` = :uid");
|
||||
$del_stmt = Database::prepare("UPDATE " . $table . " SET `data_2fa` = '' WHERE `" . $field . "` = :uid");
|
||||
$userinfo = Database::pexecute_first($del_stmt, [
|
||||
'uid' => $uid
|
||||
]);
|
||||
}
|
||||
exit();
|
||||
}
|
||||
// wrong 2fa code - treat like "wrong password"
|
||||
$stmt = Database::prepare("
|
||||
UPDATE " . $table . "
|
||||
SET `lastlogin_fail`= :lastlogin_fail, `loginfail_count`=`loginfail_count`+1
|
||||
WHERE `" . $field . "`= :uid
|
||||
");
|
||||
Database::pexecute($stmt, [
|
||||
"lastlogin_fail" => time(),
|
||||
"uid" => $uid
|
||||
]);
|
||||
|
||||
// get data for processing further
|
||||
$stmt = Database::prepare("
|
||||
SELECT `loginname`, `loginfail_count`, `lastlogin_fail` FROM " . $table . "
|
||||
WHERE `" . $field . "`= :uid
|
||||
");
|
||||
$fail_user = Database::pexecute_first($stmt, [
|
||||
"uid" => $uid
|
||||
]);
|
||||
|
||||
if ($fail_user['loginfail_count'] >= Settings::Get('login.maxloginattempts') && $fail_user['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))) {
|
||||
// Log failed login
|
||||
$rstlog = FroxlorLogger::getInstanceOf([
|
||||
'loginname' => $_SERVER['REMOTE_ADDR']
|
||||
]);
|
||||
$rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, "User '" . $fail_user['loginname'] . "' entered wrong 2fa code too often.");
|
||||
unset($fail_user);
|
||||
Response::redirectTo('index.php', [
|
||||
'showmessage' => '3'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
unset($fail_user);
|
||||
// back to form
|
||||
Response::redirectTo('index.php', [
|
||||
'showmessage' => '2'
|
||||
'action' => '2fa_entercode',
|
||||
'showmessage' => '1'
|
||||
]);
|
||||
exit();
|
||||
} elseif ($action == 'login') {
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
||||
if (!empty($_POST)) {
|
||||
$loginname = Validate::validate($_POST['loginname'], 'loginname');
|
||||
$password = Validate::validate($_POST['password'], 'password');
|
||||
|
||||
@@ -390,13 +430,18 @@ if ($action == '2fa_entercode') {
|
||||
}
|
||||
$lastqrystr = "";
|
||||
if (isset($_REQUEST['qrystr']) && $_REQUEST['qrystr'] != "") {
|
||||
$lastqrystr = htmlspecialchars($_REQUEST['qrystr'], ENT_QUOTES);
|
||||
$lastqrystr = urlencode($_REQUEST['qrystr']);
|
||||
}
|
||||
|
||||
if (!empty($lastscript)) {
|
||||
$_SESSION['lastscript'] = $lastscript;
|
||||
}
|
||||
if (!empty($lastqrystr)) {
|
||||
$_SESSION['lastqrystr'] = $lastqrystr;
|
||||
}
|
||||
|
||||
UI::view('login/login.html.twig', [
|
||||
'pagetitle' => 'Login',
|
||||
'lastscript' => $lastscript,
|
||||
'lastqrystr' => $lastqrystr,
|
||||
'upd_in_progress' => $update_in_progress,
|
||||
'message' => $message,
|
||||
'successmsg' => $successmessage
|
||||
@@ -408,7 +453,7 @@ if ($action == 'forgotpwd') {
|
||||
$adminchecked = false;
|
||||
$message = '';
|
||||
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
||||
if (!empty($_POST)) {
|
||||
$loginname = Validate::validate($_POST['loginname'], 'loginname');
|
||||
$email = Validate::validateEmail($_POST['loginemail']);
|
||||
$result_stmt = Database::prepare("SELECT `adminid`, `customerid`, `customernumber`, `firstname`, `name`, `company`, `email`, `loginname`, `def_language`, `deactivated` FROM `" . TABLE_PANEL_CUSTOMERS . "`
|
||||
@@ -592,7 +637,7 @@ if ($action == 'forgotpwd') {
|
||||
|
||||
UI::view('login/fpwd.html.twig', [
|
||||
'pagetitle' => lng('login.presend'),
|
||||
'action' => $action,
|
||||
'formaction' => 'index.php?action=' . $action,
|
||||
'message' => $message,
|
||||
]);
|
||||
}
|
||||
@@ -615,7 +660,7 @@ if ($action == 'resetpwd') {
|
||||
$check = substr($activationcode, 40, 10);
|
||||
|
||||
if (substr(md5($third . $timestamp), 0, 10) == $check && $timestamp >= time() - 86400) {
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
||||
if (!empty($_POST)) {
|
||||
$stmt = Database::prepare("SELECT `userid`, `admin` FROM `" . TABLE_PANEL_ACTIVATION . "`
|
||||
WHERE `activationcode` = :activationcode");
|
||||
$result = Database::pexecute_first($stmt, [
|
||||
@@ -692,6 +737,7 @@ if ($action == 'resetpwd') {
|
||||
function finishLogin($userinfo)
|
||||
{
|
||||
if (isset($userinfo['userid']) && $userinfo['userid'] != '') {
|
||||
session_regenerate_id(true);
|
||||
CurrentUser::setData($userinfo);
|
||||
|
||||
$language = $userinfo['def_language'] ?? Settings::Get('panel.standardlanguage');
|
||||
@@ -705,29 +751,34 @@ function finishLogin($userinfo)
|
||||
}
|
||||
|
||||
$qryparams = [];
|
||||
if (isset($_POST['qrystr']) && $_POST['qrystr'] != "") {
|
||||
parse_str(urldecode($_POST['qrystr']), $qryparams);
|
||||
if (!empty($_SESSION['lastqrystr'])) {
|
||||
parse_str(urldecode($_SESSION['lastqrystr']), $qryparams);
|
||||
unset($_SESSION['lastqrystr']);
|
||||
}
|
||||
|
||||
if ($userinfo['adminsession'] == '1') {
|
||||
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
|
||||
Response::redirectTo('admin_updates.php?page=overview');
|
||||
} else {
|
||||
if (isset($_POST['script']) && $_POST['script'] != "") {
|
||||
if (preg_match("/customer\_/", $_POST['script']) === 1) {
|
||||
if (!empty($_SESSION['lastscript'])) {
|
||||
$lastscript = $_SESSION['lastscript'];
|
||||
unset($_SESSION['lastscript']);
|
||||
if (preg_match("/customer\_/", $lastscript) === 1) {
|
||||
Response::redirectTo('admin_customers.php', [
|
||||
"page" => "customers"
|
||||
]);
|
||||
} else {
|
||||
Response::redirectTo($_POST['script'], $qryparams);
|
||||
Response::redirectTo($lastscript, $qryparams);
|
||||
}
|
||||
} else {
|
||||
Response::redirectTo('admin_index.php', $qryparams);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isset($_POST['script']) && $_POST['script'] != "") {
|
||||
Response::redirectTo($_POST['script'], $qryparams);
|
||||
if (!empty($_SESSION['lastscript'])) {
|
||||
$lastscript = $_SESSION['lastscript'];
|
||||
unset($_SESSION['lastscript']);
|
||||
Response::redirectTo($lastscript, $qryparams);
|
||||
} else {
|
||||
Response::redirectTo('customer_index.php', $qryparams);
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ CREATE TABLE `panel_customers` (
|
||||
`allowed_mysqlserver` text NOT NULL,
|
||||
PRIMARY KEY (`customerid`),
|
||||
UNIQUE KEY `loginname` (`loginname`)
|
||||
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `panel_databases`;
|
||||
@@ -642,7 +642,7 @@ opcache.validate_timestamps'),
|
||||
('system', 'leprivatekey', 'unset'),
|
||||
('system', 'lepublickey', 'unset'),
|
||||
('system', 'letsencryptca', 'letsencrypt'),
|
||||
('system', 'letsencryptchallengepath', '/var/www/froxlor'),
|
||||
('system', 'letsencryptchallengepath', '/var/www/html/froxlor'),
|
||||
('system', 'letsencryptkeysize', '4096'),
|
||||
('system', 'letsencryptreuseold', 0),
|
||||
('system', 'leenabled', '0'),
|
||||
@@ -670,6 +670,7 @@ opcache.validate_timestamps'),
|
||||
('system', 'leaccount', ''),
|
||||
('system', 'nssextrausers', '1'),
|
||||
('system', 'le_domain_dnscheck', '1'),
|
||||
('system', 'le_domain_dnscheck_resolver', '1.1.1.1'),
|
||||
('system', 'ssl_protocols', 'TLSv1.2'),
|
||||
('system', 'tlsv13_cipher_list', ''),
|
||||
('system', 'honorcipherorder', '0'),
|
||||
@@ -696,9 +697,12 @@ opcache.validate_timestamps'),
|
||||
('system', 'distribution', ''),
|
||||
('system', 'update_channel', 'stable'),
|
||||
('system', 'updatecheck_data', ''),
|
||||
('system', 'update_notify_last', '2.0.4'),
|
||||
('system', 'update_notify_last', '2.0.23'),
|
||||
('system', 'traffictool', 'goaccess'),
|
||||
('system', 'req_limit_per_interval', 60),
|
||||
('system', 'req_limit_interval', 60),
|
||||
('api', 'enabled', '0'),
|
||||
('api', 'customer_default', '1'),
|
||||
('2fa', 'enabled', '1'),
|
||||
('panel', 'decimal_places', '4'),
|
||||
('panel', 'adminmail', 'admin@SERVERNAME'),
|
||||
@@ -740,8 +744,8 @@ opcache.validate_timestamps'),
|
||||
('panel', 'logo_overridetheme', '0'),
|
||||
('panel', 'logo_overridecustom', '0'),
|
||||
('panel', 'settings_mode', '0'),
|
||||
('panel', 'version', '2.0.4'),
|
||||
('panel', 'db_version', '202212060');
|
||||
('panel', 'version', '2.0.23'),
|
||||
('panel', 'db_version', '202304260');
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `panel_tasks`;
|
||||
@@ -981,7 +985,9 @@ CREATE TABLE IF NOT EXISTS `domain_ssl_settings` (
|
||||
`ssl_cert_chainfile` mediumtext,
|
||||
`ssl_csr_file` mediumtext,
|
||||
`ssl_fullchain_file` mediumtext,
|
||||
`expirationdate` datetime DEFAULT NULL,
|
||||
`validfromdate` datetime DEFAULT NULL,
|
||||
`validtodate` datetime DEFAULT NULL,
|
||||
`issuer` varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY (`domainid`)
|
||||
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
||||
*/
|
||||
|
||||
use Froxlor\Http\RateLimiter;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
use Froxlor\Install\Install;
|
||||
|
||||
@@ -62,6 +63,7 @@ require dirname(__DIR__) . '/lib/tables.inc.php';
|
||||
// init twig
|
||||
UI::initTwig(true);
|
||||
UI::sendHeaders();
|
||||
RateLimiter::run(true);
|
||||
|
||||
$installer = new Install();
|
||||
$installer->handle();
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
||||
*/
|
||||
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\Install\Update;
|
||||
use Froxlor\Settings;
|
||||
|
||||
if (!defined('_CRON_UPDATE')) {
|
||||
if (!defined('AREA') || (defined('AREA') && AREA != 'admin') || !isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) {
|
||||
@@ -38,10 +38,9 @@ if (!defined('_CRON_UPDATE')) {
|
||||
|
||||
// last 0.10.x release
|
||||
if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
|
||||
$update_to = '2.0.0-beta1';
|
||||
|
||||
Update::showUpdateStep("Updating from 0.10.38.3 to ".$update_to, false);
|
||||
Update::showUpdateStep("Updating from 0.10.38.3 to " . $update_to, false);
|
||||
|
||||
Update::showUpdateStep("Removing unused table");
|
||||
Database::query("DROP TABLE IF EXISTS `panel_sessions`;");
|
||||
@@ -66,8 +65,8 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
KEY customerid (customerid)
|
||||
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;";
|
||||
Database::query($sql);
|
||||
Database::query("SET SESSION innodb_strict_mode=OFF;");
|
||||
// new customer allowed_mysqlserver field
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ROW_FORMAT=DYNAMIC;");
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` CHANGE COLUMN `customernumber` `customernumber` varchar(100) NOT NULL default '';");
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` CHANGE COLUMN `allowed_phpconfigs` `allowed_phpconfigs` text NOT NULL;");
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `allowed_mysqlserver` text NOT NULL;");
|
||||
@@ -83,7 +82,7 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP COLUMN `domains_see_all`;");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Update::showUpdateStep("Checking for multiple mysql-servers to allow acccess to customers for existing databases");
|
||||
Update::showUpdateStep("Checking for multiple mysql-servers to allow access to customers for existing databases");
|
||||
$dbservers_stmt = Database::query("
|
||||
SELECT `customerid`,
|
||||
GROUP_CONCAT(DISTINCT `dbserver` SEPARATOR ',') as allowed_mysqlserver
|
||||
@@ -94,7 +93,8 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
while ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
if (isset($dbserver['allowed_mysqlserver']) && !empty($dbserver['allowed_mysqlserver'])) {
|
||||
$allowed_mysqlserver = json_encode(explode(",", $dbserver['allowed_mysqlserver']));
|
||||
Database::pexecute($upd_stmt, ['allowed_mysql_server' => $allowed_mysqlserver, 'customerid' => $dbserver['customerid']]);
|
||||
Database::pexecute($upd_stmt,
|
||||
['allowed_mysql_server' => $allowed_mysqlserver, 'customerid' => $dbserver['customerid']]);
|
||||
}
|
||||
}
|
||||
Update::lastStepStatus(0);
|
||||
@@ -141,14 +141,15 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
// none of the files existed
|
||||
Update::lastStepStatus(0);
|
||||
} else {
|
||||
Update::lastStepStatus(1, 'manual commands needed', 'Please run the following commands manually:<br><pre>' . $del_list . '</pre>');
|
||||
Update::lastStepStatus(1, 'manual commands needed',
|
||||
'Please run the following commands manually:<br><pre>' . $del_list . '</pre>');
|
||||
}
|
||||
}
|
||||
|
||||
Update::showUpdateStep("Adding new settings");
|
||||
$panel_settings_mode = isset($_POST['panel_settings_mode']) ? (int) $_POST['panel_settings_mode'] : 0;
|
||||
$panel_settings_mode = isset($_POST['panel_settings_mode']) ? (int)$_POST['panel_settings_mode'] : 0;
|
||||
Settings::AddNew("panel.settings_mode", $panel_settings_mode);
|
||||
$system_distribution = isset($_POST['system_distribution']) ? $_POST['system_distribution'] : '';
|
||||
$system_distribution = isset($_POST['system_distribution']) ? $_POST['system_distribution'] : 'bullseye';
|
||||
Settings::AddNew("system.distribution", $system_distribution);
|
||||
Settings::AddNew("system.update_channel", 'stable');
|
||||
Settings::AddNew("system.updatecheck_data", '');
|
||||
@@ -183,17 +184,16 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Update::showUpdateStep("Updating email account password-hashes");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password` = REPLACE(`password`, '$1$', '{MD5-CRYPT}$1$') WHERE SUBSTRING(`password`, 1, 3) = '$1$'");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password` = REPLACE(`password`, '$5$', '{SHA256-CRYPT}$5$') WHERE SUBSTRING(`password`, 1, 3) = '$5$'");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password` = REPLACE(`password`, '$6$', '{SHA512-CRYPT}$6$') WHERE SUBSTRING(`password`, 1, 3) = '$6$'");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password` = REPLACE(`password`, '$2y$', '{BLF-CRYPT}$2y$') WHERE SUBSTRING(`password`, 1, 4) = '$2y$'");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password_enc` = REPLACE(`password_enc`, '$1$', '{MD5-CRYPT}$1$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$1$'");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password_enc` = REPLACE(`password_enc`, '$5$', '{SHA256-CRYPT}$5$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$5$'");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password_enc` = REPLACE(`password_enc`, '$6$', '{SHA512-CRYPT}$6$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$6$'");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password_enc` = REPLACE(`password_enc`, '$2y$', '{BLF-CRYPT}$2y$') WHERE SUBSTRING(`password_enc`, 1, 4) = '$2y$'");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToVersion($update_to);
|
||||
}
|
||||
|
||||
if (Froxlor::isDatabaseVersion('202112310')) {
|
||||
|
||||
Update::showUpdateStep("Adjusting traffic tool settings");
|
||||
$traffic_tool = Settings::Get('system.awstats_enabled') == 1 ? 'awstats' : 'webalizer';
|
||||
Settings::AddNew("system.traffictool", $traffic_tool);
|
||||
@@ -204,20 +204,31 @@ if (Froxlor::isDatabaseVersion('202112310')) {
|
||||
}
|
||||
|
||||
if (Froxlor::isDatabaseVersion('202211030')) {
|
||||
|
||||
Update::showUpdateStep("Creating backward compatibility for cronjob");
|
||||
$complete_filedir = Froxlor::getInstallDir() . '/scripts';
|
||||
mkdir($complete_filedir, 0750, true);
|
||||
$newCronBin = Froxlor::getInstallDir().'/bin/froxlor-cli';
|
||||
$compCron = <<<EOF
|
||||
$disabled = explode(',', ini_get('disable_functions'));
|
||||
$exec_allowed = !in_array('exec', $disabled);
|
||||
// check whether old files could be deleted in previous updates and if not,
|
||||
// user should run cron to regenerate cron.d-file manually as he will run
|
||||
// the other commands manually only after the update so this file would be deleted too
|
||||
if ($exec_allowed) {
|
||||
$complete_filedir = Froxlor::getInstallDir() . '/scripts';
|
||||
mkdir($complete_filedir, 0750, true);
|
||||
$newCronBin = Froxlor::getInstallDir() . '/bin/froxlor-cli';
|
||||
$compCron = <<<EOF
|
||||
<?php
|
||||
chmod('$newCronBin', 0755);
|
||||
// re-create cron.d configuration file
|
||||
exec('$newCronBin froxlor:cron -r 99');
|
||||
exit;
|
||||
EOF;
|
||||
file_put_contents($complete_filedir.'/froxlor_master_cronjob.php', $compCron);
|
||||
Update::lastStepStatus(0);
|
||||
file_put_contents($complete_filedir . '/froxlor_master_cronjob.php', $compCron);
|
||||
Update::lastStepStatus(0);
|
||||
} else {
|
||||
$cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL;
|
||||
$cron_run_cmd .= FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -r 99';
|
||||
Update::lastStepStatus(1, 'manual commands needed',
|
||||
'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>');
|
||||
}
|
||||
|
||||
Froxlor::updateToDbVersion('202212060');
|
||||
}
|
||||
@@ -257,19 +268,247 @@ if (Froxlor::isFroxlorVersion('2.0.3')) {
|
||||
|
||||
$complete_filedir = Froxlor::getInstallDir() . '/scripts';
|
||||
// check if compat. cronjob still exists (most likely didn't run successfully b/c of error from former 2.0 release)
|
||||
if (@file_exists($complete_filedir.'/froxlor_master_cronjob.php')) {
|
||||
if (@file_exists($complete_filedir . '/froxlor_master_cronjob.php')) {
|
||||
Update::showUpdateStep("Adjusting backward compatibility for cronjob");
|
||||
$newCronBin = Froxlor::getInstallDir() . '/bin/froxlor-cli';
|
||||
$compCron = <<<EOF
|
||||
$disabled = explode(',', ini_get('disable_functions'));
|
||||
$exec_allowed = !in_array('exec', $disabled);
|
||||
if ($exec_allowed) {
|
||||
$newCronBin = Froxlor::getInstallDir() . '/bin/froxlor-cli';
|
||||
$compCron = <<<EOF
|
||||
<?php
|
||||
chmod('$newCronBin', 0755);
|
||||
// re-create cron.d configuration file
|
||||
exec('$newCronBin froxlor:cron -r 99');
|
||||
exit;
|
||||
EOF;
|
||||
file_put_contents($complete_filedir . '/froxlor_master_cronjob.php', $compCron);
|
||||
file_put_contents($complete_filedir . '/froxlor_master_cronjob.php', $compCron);
|
||||
Update::lastStepStatus(0);
|
||||
} else {
|
||||
$cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL;
|
||||
$cron_run_cmd .= FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -r 99';
|
||||
Update::lastStepStatus(1, 'manual commands needed',
|
||||
'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>');
|
||||
}
|
||||
}
|
||||
Froxlor::updateToVersion('2.0.4');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.4')) {
|
||||
Update::showUpdateStep("Updating from 2.0.4 to 2.0.5", false);
|
||||
Froxlor::updateToVersion('2.0.5');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.5')) {
|
||||
Update::showUpdateStep("Updating from 2.0.5 to 2.0.6", false);
|
||||
|
||||
Update::showUpdateStep("Updating possible missing email account password-hashes");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password_enc` = REPLACE(`password_enc`, '$1$', '{MD5-CRYPT}$1$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$1$'");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password_enc` = REPLACE(`password_enc`, '$5$', '{SHA256-CRYPT}$5$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$5$'");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password_enc` = REPLACE(`password_enc`, '$6$', '{SHA512-CRYPT}$6$') WHERE SUBSTRING(`password_enc`, 1, 3) = '$6$'");
|
||||
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password_enc` = REPLACE(`password_enc`, '$2y$', '{BLF-CRYPT}$2y$') WHERE SUBSTRING(`password_enc`, 1, 4) = '$2y$'");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToVersion('2.0.6');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.6')) {
|
||||
Update::showUpdateStep("Updating from 2.0.6 to 2.0.7", false);
|
||||
|
||||
Update::showUpdateStep("Correcting allowed_mysqlserver for customers");
|
||||
Database::query("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `allowed_mysqlserver` = '[0]' WHERE `allowed_mysqlserver` = ''");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToVersion('2.0.7');
|
||||
}
|
||||
|
||||
if (Froxlor::isDatabaseVersion('202212060')) {
|
||||
Update::showUpdateStep("Validating acme.sh challenge path");
|
||||
$acmesh_challenge_dir = Settings::Get('system.letsencryptchallengepath');
|
||||
$system_letsencryptchallengepath_upd = isset($_POST['system_letsencryptchallengepath_upd']) ? $_POST['system_letsencryptchallengepath_upd'] : $acmesh_challenge_dir;
|
||||
if ($acmesh_challenge_dir != $system_letsencryptchallengepath_upd) {
|
||||
Settings::Set('system.letsencryptchallengepath', $system_letsencryptchallengepath_upd);
|
||||
if ((int)Settings::Get('system.leenabled') == 1) {
|
||||
// create JSON string for --apply
|
||||
$dist = Settings::Get('system.distribution');
|
||||
$webserver = Settings::Get('system.webserver');
|
||||
if ($webserver == 'apache2') {
|
||||
$webserver = 'apache22';
|
||||
if (Settings::Get('system.apache24')) {
|
||||
$webserver = 'apache24';
|
||||
}
|
||||
}
|
||||
$apply_json = '{"http":"' . $webserver . '","dns":"x","smtp":"x","mail":"x","ftp":"x","distro":"' . $dist . '","system":[]}';
|
||||
Update::lastStepStatus(1, 'manual commands needed',
|
||||
"Please reconfigure webserver service using <pre>bin/froxlor-cli froxlor:config-services --apply='" . $apply_json . "'</pre>" .
|
||||
'<br>or adjust the path manually in <pre>' . Settings::Get('system.letsencryptacmeconf') . '</pre>' .
|
||||
'<br><br>In case you already have certificates issued, run the following command to validate and correct the webroot used for renewal:<br>' .
|
||||
'<pre>bin/froxlor-cli froxlor:validate-acme-webroot</pre><br>'
|
||||
);
|
||||
} else {
|
||||
Update::lastStepStatus(0);
|
||||
}
|
||||
} else {
|
||||
Update::lastStepStatus(0);
|
||||
}
|
||||
|
||||
Froxlor::updateToVersion('2.0.4');
|
||||
Froxlor::updateToDbVersion('202301120');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.7')) {
|
||||
Update::showUpdateStep("Updating from 2.0.7 to 2.0.8", false);
|
||||
|
||||
// adjust file-logging to be set to froxlor/logs/
|
||||
$logtypes = explode(',', Settings::Get('logger.logtypes'));
|
||||
if (in_array('file', $logtypes)) {
|
||||
Update::showUpdateStep("Adjusting froxlor logfile for system-logging to be stored in logs/froxlor.log");
|
||||
Settings::Set('logger.logfile', 'froxlor.log');
|
||||
Update::lastStepStatus(0);
|
||||
}
|
||||
|
||||
Froxlor::updateToVersion('2.0.8');
|
||||
}
|
||||
|
||||
if (Froxlor::isDatabaseVersion('202301120')) {
|
||||
Update::showUpdateStep("Adding new setting for DNS resolver when using Let's Encrypt");
|
||||
$system_le_domain_dnscheck_resolver = isset($_POST['system_le_domain_dnscheck_resolver']) ? $_POST['system_le_domain_dnscheck_resolver'] : '1.1.1.1';
|
||||
Settings::AddNew("system.le_domain_dnscheck_resolver", $system_le_domain_dnscheck_resolver);
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToDbVersion('202301180');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.8')) {
|
||||
Update::showUpdateStep("Updating from 2.0.8 to 2.0.9", false);
|
||||
Froxlor::updateToVersion('2.0.9');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.9')) {
|
||||
Update::showUpdateStep("Updating from 2.0.9 to 2.0.10", false);
|
||||
Froxlor::updateToVersion('2.0.10');
|
||||
}
|
||||
|
||||
if (Froxlor::isDatabaseVersion('202301180')) {
|
||||
Update::showUpdateStep("Adding new setting for 'Allow API access' default value for new customers");
|
||||
Settings::AddNew("api.customer_default", "1");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToDbVersion('202302030');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.10')) {
|
||||
Update::showUpdateStep("Updating from 2.0.10 to 2.0.11", false);
|
||||
Froxlor::updateToVersion('2.0.11');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.11')) {
|
||||
Update::showUpdateStep("Updating from 2.0.11 to 2.0.12", false);
|
||||
Froxlor::updateToVersion('2.0.12');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.12')) {
|
||||
Update::showUpdateStep("Updating from 2.0.12 to 2.0.13", false);
|
||||
Froxlor::updateToVersion('2.0.13');
|
||||
}
|
||||
|
||||
if (Froxlor::isDatabaseVersion('202302030')) {
|
||||
Update::showUpdateStep("Correcting language mapping of templates created pre 2.0.x");
|
||||
// languages from 0.10.x
|
||||
$language_mapping_comp = [
|
||||
'de' => 'Deutsch',
|
||||
'en' => 'English',
|
||||
'fr' => 'Français',
|
||||
'pt' => 'Português',
|
||||
'it' => 'Italiano',
|
||||
'nl' => 'Nederlands',
|
||||
'se' => 'Svenska',
|
||||
'cz' => 'Česká republika'
|
||||
];
|
||||
$upd_tpl_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET `language` = :iso WHERE `language` = :lng");
|
||||
foreach ($language_mapping_comp as $iso => $lang) {
|
||||
Database::pexecute($upd_tpl_stmt, ['iso' => $iso, 'lng' => $lang]);
|
||||
}
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Update::showUpdateStep("Enhancing ssl data table");
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` CHANGE `expirationdate` `validtodate` datetime DEFAULT NULL;");
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `validfromdate` datetime DEFAULT NULL AFTER `ssl_fullchain_file`;");
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `issuer` varchar(255) NOT NULL default '' AFTER `validtodate`;");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Update::showUpdateStep("Filling new ssl data fields with existing certificate data");
|
||||
$crt_upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SET `validfromdate` = :validfromdate, `issuer` = :issuer WHERE `id` = :id");
|
||||
$crt_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`");
|
||||
Database::pexecute($crt_stmt);
|
||||
while ($cert = $crt_stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$cert_content = openssl_x509_parse($cert['ssl_cert_file']);
|
||||
if (is_array($cert_content)) {
|
||||
$validfromdate = empty($cert_content['validFrom_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validFrom_time_t']);
|
||||
$issuer = $cert_content['issuer']['O'] ?? "";
|
||||
Database::pexecute($crt_upd_stmt, ['validfromdate' => $validfromdate, 'issuer' => $issuer, 'id' => $cert['id']]);
|
||||
}
|
||||
}
|
||||
// clear possible user customized columns
|
||||
Database::query("DELETE FROM `" . TABLE_PANEL_USERCOLUMNS . "` WHERE `section` = 'sslcertificates_list'");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToDbVersion('202303150');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.13')) {
|
||||
Update::showUpdateStep("Updating from 2.0.13 to 2.0.14", false);
|
||||
Froxlor::updateToVersion('2.0.14');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.14')) {
|
||||
Update::showUpdateStep("Updating from 2.0.14 to 2.0.15", false);
|
||||
Froxlor::updateToVersion('2.0.15');
|
||||
}
|
||||
|
||||
if (Froxlor::isDatabaseVersion('202303150')) {
|
||||
Update::showUpdateStep("Adding new request rate limit settings");
|
||||
Settings::AddNew("system.req_limit_per_interval", "60");
|
||||
Settings::AddNew("system.req_limit_interval", "60");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToDbVersion('202304260');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.15')) {
|
||||
Update::showUpdateStep("Updating from 2.0.15 to 2.0.16", false);
|
||||
Froxlor::updateToVersion('2.0.16');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.16')) {
|
||||
Update::showUpdateStep("Updating from 2.0.16 to 2.0.17", false);
|
||||
Froxlor::updateToVersion('2.0.17');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.17')) {
|
||||
Update::showUpdateStep("Updating from 2.0.17 to 2.0.18", false);
|
||||
Froxlor::updateToVersion('2.0.18');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.18')) {
|
||||
Update::showUpdateStep("Updating from 2.0.18 to 2.0.19", false);
|
||||
Froxlor::updateToVersion('2.0.19');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.19')) {
|
||||
Update::showUpdateStep("Updating from 2.0.19 to 2.0.20", false);
|
||||
Froxlor::updateToVersion('2.0.20');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.20')) {
|
||||
Update::showUpdateStep("Updating from 2.0.20 to 2.0.21", false);
|
||||
Froxlor::updateToVersion('2.0.21');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.21')) {
|
||||
Update::showUpdateStep("Updating from 2.0.21 to 2.0.22", false);
|
||||
Froxlor::updateToVersion('2.0.22');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.22')) {
|
||||
Update::showUpdateStep("Updating from 2.0.22 to 2.0.23", false);
|
||||
Froxlor::updateToVersion('2.0.23');
|
||||
}
|
||||
|
||||
@@ -34,9 +34,14 @@ $return = [];
|
||||
if (Update::versionInUpdate($current_db_version, '202004140')) {
|
||||
$has_preconfig = true;
|
||||
$description = 'Froxlor can now optionally validate the dns entries of domains that request Lets Encrypt certificates to reduce dns-related problems (e.g. freshly registered domain or updated a-record).';
|
||||
$return['system_le_domain_dnscheck_note'] = ['type' => 'infotext', 'value' => $description];
|
||||
$question = '<strong>Validate DNS of domains when using Lets Encrypt ';
|
||||
$return['system_le_domain_dnscheck'] = ['type' => 'checkbox', 'value' => 1, 'checked' => 1, 'label' => $question];
|
||||
$return['system_le_domain_dnscheck'] = [
|
||||
'type' => 'checkbox',
|
||||
'value' => 1,
|
||||
'checked' => 1,
|
||||
'label' => $question,
|
||||
'prior_infotext' => $description
|
||||
];
|
||||
}
|
||||
|
||||
$preconfig['fields'] = $return;
|
||||
|
||||
@@ -27,6 +27,7 @@ use Froxlor\Froxlor;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Config\ConfigParser;
|
||||
use Froxlor\Install\Update;
|
||||
use Froxlor\Settings;
|
||||
|
||||
$preconfig = [
|
||||
'title' => '2.x updates',
|
||||
@@ -36,7 +37,6 @@ $return = [];
|
||||
|
||||
if (Update::versionInUpdate($current_version, '2.0.0-beta1')) {
|
||||
$description = 'We have rearranged the settings and split them into basic and advanced categories. This makes it easier for users who do not need all the detailed or very specific settings and options and gives a better overview of the basic/mostly used settings.';
|
||||
$return['panel_settings_mode_note'] = ['type' => 'infotext', 'value' => $description];
|
||||
$question = '<strong>Chose settings mode (you can change that at any time)</strong>';
|
||||
$return['panel_settings_mode'] = [
|
||||
'type' => 'select',
|
||||
@@ -45,16 +45,16 @@ if (Update::versionInUpdate($current_version, '2.0.0-beta1')) {
|
||||
1 => 'Advanced'
|
||||
],
|
||||
'selected' => 1,
|
||||
'label' => $question
|
||||
'label' => $question,
|
||||
'prior_infotext' => $description
|
||||
];
|
||||
|
||||
$description = 'The configuration page now can preselect a distribution, please select your current distribution';
|
||||
$return['system_distribution_note'] = ['type' => 'infotext', 'value' => $description];
|
||||
$question = '<strong>Select distribution</strong>';
|
||||
$config_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/lib/configfiles/');
|
||||
// show list of available distro's
|
||||
$distros = glob($config_dir . '*.xml');
|
||||
$distributions_select[''] = '-';
|
||||
// selection is required $distributions_select[''] = '-';
|
||||
// read in all the distros
|
||||
foreach ($distros as $_distribution) {
|
||||
// get configparser object
|
||||
@@ -68,9 +68,44 @@ if (Update::versionInUpdate($current_version, '2.0.0-beta1')) {
|
||||
'type' => 'select',
|
||||
'select_var' => $distributions_select,
|
||||
'selected' => '',
|
||||
'label' => $question
|
||||
'label' => $question,
|
||||
'prior_infotext' => $description
|
||||
];
|
||||
}
|
||||
|
||||
if (Update::versionInUpdate($current_db_version, '202301120')) {
|
||||
$acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/");
|
||||
$recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), "/");
|
||||
if ((int) Settings::Get('system.leenabled') == 1 && $acmesh_challenge_dir != $recommended) {
|
||||
$has_preconfig = true;
|
||||
$description = 'ACME challenge docroot from settings differs from the current installation directory.';
|
||||
$question = '<strong>Validate Let\'s Encrypt challenge path (recommended value: ' . $recommended . ')</strong>';
|
||||
$return['system_letsencryptchallengepath_upd'] = [
|
||||
'type' => 'text',
|
||||
'value' => $recommended,
|
||||
'placeholder' => $acmesh_challenge_dir,
|
||||
'label' => $question,
|
||||
'prior_infotext' => $description,
|
||||
'mandatory' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (Update::versionInUpdate($current_db_version, '202301180')) {
|
||||
if ((int) Settings::Get('system.leenabled') == 1) {
|
||||
$has_preconfig = true;
|
||||
$description = 'Froxlor now supports to set an external DNS resolver for the Let\'s Encrypt pre-check.';
|
||||
$question = '<strong>Specify a DNS resolver IP (recommended value: 1.1.1.1 or similar)</strong>';
|
||||
$return['system_le_domain_dnscheck_resolver'] = [
|
||||
'type' => 'text',
|
||||
'pattern' => '^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$|^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$|^\s*$',
|
||||
'value' => '1.1.1.1',
|
||||
'placeholder' => '1.1.1.1',
|
||||
'label' => $question,
|
||||
'prior_infotext' => $description,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$preconfig['fields'] = $return;
|
||||
return $preconfig;
|
||||
|
||||
@@ -162,7 +162,7 @@ class Ajax
|
||||
$content = preg_replace("/[\r\n]+/", " ", strip_tags($item->description));
|
||||
$content = substr($content, 0, 150) . "...";
|
||||
|
||||
$items .= UI::twig()->render($this->theme . '/user/newsfeeditem.html.twig', [
|
||||
$items .= UI::twig()->render(UI::validateThemeTemplate('/user/newsfeeditem.html.twig', $this->theme), [
|
||||
'link' => $link,
|
||||
'title' => $title,
|
||||
'date' => $date,
|
||||
@@ -201,7 +201,7 @@ class Ajax
|
||||
$result['last_update_check'] = $uc_data['ts'];
|
||||
$result['channel'] = Settings::Get('system.update_channel');
|
||||
|
||||
$result_rendered = UI::twig()->render($this->theme . '/misc/version_top.html.twig', $result);
|
||||
$result_rendered = UI::twig()->render(UI::validateThemeTemplate('/misc/version_top.html.twig', $this->theme), $result);
|
||||
return $this->jsonResponse($result_rendered);
|
||||
} catch (Exception $e) {
|
||||
// don't display anything if just not allowed due to permissions
|
||||
@@ -237,11 +237,11 @@ class Ajax
|
||||
private function updateTablelisting()
|
||||
{
|
||||
$columns = [];
|
||||
foreach ((Request::any('columns') ?? []) as $value) {
|
||||
foreach ((Request::post('columns') ?? []) as $value) {
|
||||
$columns[] = $value;
|
||||
}
|
||||
if (!empty($columns)) {
|
||||
Listing::storeColumnListingForUser([Request::any('listing') => $columns]);
|
||||
$columns = Listing::storeColumnListingForUser([Request::get('listing') => $columns]);
|
||||
return $this->jsonResponse($columns);
|
||||
}
|
||||
return $this->errorResponse('At least one column must be selected', 406);
|
||||
@@ -249,7 +249,7 @@ class Ajax
|
||||
|
||||
private function resetTablelisting()
|
||||
{
|
||||
Listing::deleteColumnListingForUser([Request::any('listing') => []]);
|
||||
Listing::deleteColumnListingForUser([Request::get('listing') => []]);
|
||||
return $this->jsonResponse([]);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Froxlor\Ajax;
|
||||
use Froxlor\Api\Commands\Admins;
|
||||
use Froxlor\Api\Commands\Customers;
|
||||
use Froxlor\Api\Commands\Domains;
|
||||
use Froxlor\Api\Commands\EmailDomains;
|
||||
use Froxlor\Api\Commands\Emails;
|
||||
use Froxlor\Api\Commands\FpmDaemons;
|
||||
use Froxlor\Api\Commands\Ftps;
|
||||
@@ -267,7 +268,20 @@ class GlobalSearch
|
||||
'result_format' => [
|
||||
'title' => ['self', 'getFieldFromResult'],
|
||||
'title_args' => 'email',
|
||||
'href' => 'customer_email.php?page=emails&searchfield=m.email&searchtext='
|
||||
'href' => 'customer_email.php?page=email_domain&domainid={domainid}&searchfield=m.email&searchtext='
|
||||
]
|
||||
],
|
||||
// email-domains
|
||||
'email_domains' => [
|
||||
'class' => EmailDomains::class,
|
||||
'searchfields' => [
|
||||
'd.domain',
|
||||
],
|
||||
'result_key' => 'domain',
|
||||
'result_format' => [
|
||||
'title' => ['self', 'getFieldFromResult'],
|
||||
'title_args' => 'domain',
|
||||
'href' => 'customer_email.php?page=emails&searchfield=d.domain&searchtext='
|
||||
]
|
||||
],
|
||||
// databases
|
||||
@@ -326,6 +340,14 @@ class GlobalSearch
|
||||
if (!isset($result[$entity])) {
|
||||
$result[$entity] = [];
|
||||
}
|
||||
// replacer from result in href
|
||||
$href_replacer = [];
|
||||
if (preg_match_all('/\{([a-z]+)\}/', $edata['result_format']['href'], $href_replacer) !== false) {
|
||||
foreach ($href_replacer[1] as $href_field) {
|
||||
$href_field_value = self::getFieldFromResult($cresult, $href_field);
|
||||
$edata['result_format']['href'] = str_replace('{'.$href_field.'}', $href_field_value, $edata['result_format']['href']);
|
||||
}
|
||||
}
|
||||
$result[$entity][] = [
|
||||
'title' => call_user_func($edata['result_format']['title'], $cresult, ($edata['result_format']['title_args'] ?? null)),
|
||||
'href' => $edata['result_format']['href'] . $cresult[$edata['result_key']]
|
||||
@@ -335,7 +357,7 @@ class GlobalSearch
|
||||
}
|
||||
} // foreach entity
|
||||
|
||||
} // foreach splitted search-term
|
||||
} // foreach split search-term
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
namespace Froxlor\Api;
|
||||
|
||||
use Exception;
|
||||
use Froxlor\Http\RateLimiter;
|
||||
use Froxlor\Settings;
|
||||
use voku\helper\AntiXSS;
|
||||
|
||||
@@ -52,6 +53,8 @@ class Api
|
||||
if (Settings::Get('api.enabled') != 1) {
|
||||
throw new Exception('API is not enabled. Please contact the administrator if you think this is wrong.', 400);
|
||||
}
|
||||
|
||||
RateLimiter::run();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,6 +120,6 @@ class Api
|
||||
|
||||
private function stripcslashesDeep($value)
|
||||
{
|
||||
return is_array($value) ? array_map([$this, 'stripcslashesDeep'], $value) : stripcslashes($value);
|
||||
return is_array($value) ? array_map([$this, 'stripcslashesDeep'], $value) : (!empty($value) ? stripcslashes($value) : $value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +39,12 @@ abstract class ApiParameter
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $params
|
||||
* @param array|null $params
|
||||
* optional, array of parameters (var=>value) for the command
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($params = null)
|
||||
public function __construct(array $params = null)
|
||||
{
|
||||
if (!is_null($params)) {
|
||||
$params = $this->trimArray($params);
|
||||
@@ -57,7 +57,7 @@ abstract class ApiParameter
|
||||
*
|
||||
* @param array $input
|
||||
*
|
||||
* @return array
|
||||
* @return string|array
|
||||
*/
|
||||
private function trimArray($input)
|
||||
{
|
||||
@@ -79,9 +79,9 @@ abstract class ApiParameter
|
||||
/**
|
||||
* get specific parameter which also has and unlimited-field
|
||||
*
|
||||
* @param string $param
|
||||
* @param string|null $param
|
||||
* parameter to get out of the request-parameter list
|
||||
* @param string $ul_field
|
||||
* @param string|null $ul_field
|
||||
* parameter to get out of the request-parameter list
|
||||
* @param bool $optional
|
||||
* default: false
|
||||
@@ -91,7 +91,7 @@ abstract class ApiParameter
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getUlParam($param = null, $ul_field = null, $optional = false, $default = 0)
|
||||
protected function getUlParam(string $param = null, string $ul_field = null, bool $optional = false, $default = 0)
|
||||
{
|
||||
$param_value = (int)$this->getParam($param, $optional, $default);
|
||||
$ul_field_value = $this->getBoolParam($ul_field, true, 0);
|
||||
@@ -102,11 +102,11 @@ abstract class ApiParameter
|
||||
}
|
||||
|
||||
/**
|
||||
* get specific parameter from the parameterlist;
|
||||
* get specific parameter from the parameter list;
|
||||
* check for existence and != empty if needed.
|
||||
* Maybe more in the future
|
||||
*
|
||||
* @param string $param
|
||||
* @param string|null $param
|
||||
* parameter to get out of the request-parameter list
|
||||
* @param bool $optional
|
||||
* default: false
|
||||
@@ -116,7 +116,7 @@ abstract class ApiParameter
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getParam($param = null, $optional = false, $default = '')
|
||||
protected function getParam(string $param = null, bool $optional = false, $default = '')
|
||||
{
|
||||
// does it exist?
|
||||
if (!isset($this->cmd_params[$param])) {
|
||||
@@ -128,7 +128,7 @@ abstract class ApiParameter
|
||||
return $default;
|
||||
}
|
||||
// is it empty? - test really on string, as value 0 is being seen as empty by php
|
||||
if ($this->cmd_params[$param] === "") {
|
||||
if (!is_array($this->cmd_params[$param]) && trim($this->cmd_params[$param]) === "") {
|
||||
if ($optional === false) {
|
||||
// get module + function for better error-messages
|
||||
$inmod = $this->getModFunctionString();
|
||||
@@ -142,7 +142,7 @@ abstract class ApiParameter
|
||||
|
||||
/**
|
||||
* returns "module::function()" for better error-messages (missing parameter etc.)
|
||||
* makes debugging a whole lot more comfortable
|
||||
* makes debugging a lot more comfortable
|
||||
*
|
||||
* @param int $level
|
||||
* depth of backtrace, default 2
|
||||
@@ -152,7 +152,7 @@ abstract class ApiParameter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getModFunctionString($level = 1, $max_level = 5, $trace = null)
|
||||
private function getModFunctionString(int $level = 1, int $max_level = 5, $trace = null)
|
||||
{
|
||||
// which class called us
|
||||
$_class = get_called_class();
|
||||
@@ -174,7 +174,7 @@ abstract class ApiParameter
|
||||
/**
|
||||
* getParam wrapper for boolean parameter
|
||||
*
|
||||
* @param string $param
|
||||
* @param string|null $param
|
||||
* parameter to get out of the request-parameter list
|
||||
* @param bool $optional
|
||||
* default: false
|
||||
@@ -183,7 +183,7 @@ abstract class ApiParameter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getBoolParam($param = null, $optional = false, $default = false)
|
||||
protected function getBoolParam(string $param = null, bool $optional = false, $default = false)
|
||||
{
|
||||
$_default = '0';
|
||||
if ($default) {
|
||||
|
||||
@@ -95,7 +95,7 @@ class Admins extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list admins");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list admins");
|
||||
$query_fields = [];
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT *
|
||||
@@ -407,7 +407,7 @@ class Admins extends ApiCommand implements ResourceEntity
|
||||
];
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get admin '" . $result['loginname'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get admin '" . $result['loginname'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'");
|
||||
@@ -584,6 +584,18 @@ class Admins extends ApiCommand implements ResourceEntity
|
||||
$theme = Settings::Get('panel.default_theme');
|
||||
}
|
||||
|
||||
if (empty(trim($name))) {
|
||||
Response::standardError([
|
||||
'stringisempty',
|
||||
'admin.name'
|
||||
], '', true);
|
||||
}
|
||||
if (empty(trim($email))) {
|
||||
Response::standardError([
|
||||
'stringisempty',
|
||||
'admin.email'
|
||||
], '', true);
|
||||
}
|
||||
if (!Validate::validateEmail($email)) {
|
||||
Response::standardError('emailiswrong', $email, true);
|
||||
} else {
|
||||
@@ -705,7 +717,7 @@ class Admins extends ApiCommand implements ResourceEntity
|
||||
WHERE `adminid` = :adminid
|
||||
");
|
||||
Database::pexecute($upd_stmt, $upd_data, true, true);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] edited admin '" . $result['loginname'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] edited admin '" . $result['loginname'] . "'");
|
||||
|
||||
// get all admin-data for return-array
|
||||
$result = $this->apiCall('Admins.get', [
|
||||
|
||||
@@ -97,7 +97,7 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
if (!$has_cert) {
|
||||
$this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added ssl-certificate for '" . $domain['domain'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ssl-certificate for '" . $domain['domain'] . "'");
|
||||
$result = $this->apiCall('Certificates.get', [
|
||||
'id' => $domain['id']
|
||||
]);
|
||||
@@ -127,7 +127,9 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
|
||||
$do_verify = true;
|
||||
$expirationdate = null;
|
||||
$validtodate = null;
|
||||
$validtodate = null;
|
||||
$issuer = "";
|
||||
// no cert-file given -> forget everything
|
||||
if ($ssl_cert_file == '') {
|
||||
$ssl_key_file = '';
|
||||
@@ -168,7 +170,10 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
} else {
|
||||
Response::standardError('sslcertificateinvalidcert', '', true);
|
||||
}
|
||||
$expirationdate = empty($cert_content['validTo_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validTo_time_t']);
|
||||
// get data from certificate to store in the table
|
||||
$validfromdate = empty($cert_content['validFrom_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validFrom_time_t']);
|
||||
$validtodate = empty($cert_content['validTo_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validTo_time_t']);
|
||||
$issuer = $cert_content['issuer']['O'] ?? "";
|
||||
}
|
||||
|
||||
// Add/Update database entry
|
||||
@@ -183,7 +188,9 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
`ssl_key_file` = :ssl_key_file,
|
||||
`ssl_ca_file` = :ssl_ca_file,
|
||||
`ssl_cert_chainfile` = :ssl_cert_chainfile,
|
||||
`expirationdate` = :expirationdate
|
||||
`validfromdate` = :validfromdate,
|
||||
`validtodate` = :validtodate,
|
||||
`issuer` = :issuer
|
||||
" . $qrywhere . " `domainid`= :domainid
|
||||
");
|
||||
$params = [
|
||||
@@ -191,7 +198,9 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
"ssl_key_file" => $ssl_key_file,
|
||||
"ssl_ca_file" => $ssl_ca_file,
|
||||
"ssl_cert_chainfile" => $ssl_cert_chainfile,
|
||||
"expirationdate" => $expirationdate,
|
||||
"validfromdate" => $validfromdate,
|
||||
"validtodate" => $validtodate,
|
||||
"issuer" => $issuer,
|
||||
"domainid" => $domainid
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
@@ -239,7 +248,7 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
$ssl_ca_file = $this->getParam('ssl_ca_file', true, '');
|
||||
$ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, '');
|
||||
$this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, false);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ssl-certificate for '" . $domain['domain'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ssl-certificate for '" . $domain['domain'] . "'");
|
||||
$result = $this->apiCall('Certificates.get', [
|
||||
'id' => $domain['id']
|
||||
]);
|
||||
@@ -299,27 +308,23 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
|
||||
// Set data from certificate
|
||||
$cert['isvalid'] = false;
|
||||
$cert['san'] = null;
|
||||
$cert_data = openssl_x509_parse($cert['ssl_cert_file']);
|
||||
if ($cert_data) {
|
||||
$cert['validfromdate'] = date('Y-m-d H:i:s', $cert_data['validFrom_time_t']);
|
||||
$cert['validtodate'] = date('Y-m-d H:i:s', $cert_data['validTo_time_t']);
|
||||
$cert['isvalid'] = (bool)$cert_data['validTo_time_t'] > time();
|
||||
$cert['issuer'] = $cert_data['issuer']['O'] ?? null;
|
||||
}
|
||||
|
||||
// Set subject alt names from certificate
|
||||
$cert['san'] = null;
|
||||
if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) {
|
||||
$SANs = explode(",", $cert_data['extensions']['subjectAltName']);
|
||||
$SANs = array_map('trim', $SANs);
|
||||
foreach ($SANs as $san) {
|
||||
$san = str_replace("DNS:", "", $san);
|
||||
if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) {
|
||||
$cert['san'][] = $san;
|
||||
// Set subject alt names from certificate
|
||||
if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) {
|
||||
$SANs = explode(",", $cert_data['extensions']['subjectAltName']);
|
||||
$SANs = array_map('trim', $SANs);
|
||||
foreach ($SANs as $san) {
|
||||
$san = str_replace("DNS:", "", $san);
|
||||
if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) {
|
||||
$cert['san'][] = $san;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = $cert;
|
||||
}
|
||||
return $this->response([
|
||||
@@ -465,7 +470,7 @@ class Certificates extends ApiCommand implements ResourceEntity
|
||||
if ($chk['letsencrypt'] == '1') {
|
||||
Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $chk['domain']);
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] removed ssl-certificate for '" . $chk['domain'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] removed ssl-certificate for '" . $chk['domain'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
throw new Exception("Unable to determine SSL certificate. Maybe no access?", 406);
|
||||
|
||||
@@ -32,6 +32,7 @@ use Froxlor\Cron\TaskId;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\System\Cronjob;
|
||||
use Froxlor\UI\Response;
|
||||
use Froxlor\Validate\Validate;
|
||||
use PDO;
|
||||
|
||||
@@ -41,6 +42,14 @@ use PDO;
|
||||
class Cronjobs extends ApiCommand implements ResourceEntity
|
||||
{
|
||||
|
||||
private array $allowed_intervals = [
|
||||
'MINUTE',
|
||||
'HOUR',
|
||||
'DAY',
|
||||
'WEEK',
|
||||
'MONTH'
|
||||
];
|
||||
|
||||
/**
|
||||
* You cannot add new cronjobs yet.
|
||||
*/
|
||||
@@ -118,6 +127,10 @@ class Cronjobs extends ApiCommand implements ResourceEntity
|
||||
$interval_value = Validate::validate($interval_value, 'interval_value', '/^([0-9]+)$/Di', 'stringisempty', [], true);
|
||||
$interval_interval = Validate::validate($interval_interval, 'interval_interval', '', '', [], true);
|
||||
|
||||
if (!in_array(strtoupper($interval_interval), $this->allowed_intervals)) {
|
||||
Response::standardError('invalidcronjobintervalvalue', implode(", ", $this->allowed_intervals), true);
|
||||
}
|
||||
|
||||
// put together interval value
|
||||
$interval = $interval_value . ' ' . strtoupper($interval_interval);
|
||||
|
||||
@@ -134,7 +147,7 @@ class Cronjobs extends ApiCommand implements ResourceEntity
|
||||
|
||||
// insert task to re-generate the cron.d-file
|
||||
Cronjob::inserttask(TaskId::REBUILD_CRON);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] cronjob with description '" . $result['module'] . '/' . $result['cronfile'] . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] cronjob with description '" . $result['module'] . '/' . $result['cronfile'] . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
$result = $this->apiCall('Cronjobs.get', [
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -164,7 +177,7 @@ class Cronjobs extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list cronjobs");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list cronjobs");
|
||||
$query_fields = [];
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT `c`.* FROM `" . TABLE_PANEL_CRONRUNS . "` `c` " . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());
|
||||
|
||||
@@ -194,7 +194,7 @@ class CustomerBackups extends ApiCommand implements ResourceEntity
|
||||
$result[] = $entry;
|
||||
}
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list customer-backups");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list customer-backups");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
|
||||
@@ -298,7 +298,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
||||
$fax = $this->getParam('fax', true, '');
|
||||
$customernumber = $this->getParam('customernumber', true, '');
|
||||
$def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage'));
|
||||
$api_allowed = $this->getBoolParam('api_allowed', true, Settings::Get('api.enabled'));
|
||||
$api_allowed = $this->getBoolParam('api_allowed', true, (Settings::Get('api.enabled') && Settings::Get('api.customer_default')));
|
||||
$gender = (int)$this->getParam('gender', true, 0);
|
||||
$custom_notes = $this->getParam('custom_notes', true, '');
|
||||
$custom_notes_show = $this->getBoolParam('custom_notes_show', true, 0);
|
||||
@@ -895,7 +895,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
||||
$result['dbspace_used'] = 0;
|
||||
}
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get customer '" . $result['loginname'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get customer '" . $result['loginname'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'");
|
||||
@@ -1327,7 +1327,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
||||
'vu' => $valid_until
|
||||
], true, true);
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
}
|
||||
|
||||
@@ -1538,7 +1538,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
||||
Database::query($admin_update_query);
|
||||
}
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited user '" . $result['loginname'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited user '" . $result['loginname'] . "'");
|
||||
|
||||
/*
|
||||
* move customer to another admin/reseller; #1166
|
||||
@@ -1911,7 +1911,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
||||
// now, recalculate the resource-usage for the old and the new admin
|
||||
User::updateCounters(false);
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'");
|
||||
|
||||
$result = $this->apiCall('Customers.get', [
|
||||
'id' => $c_result['customerid']
|
||||
|
||||
@@ -144,7 +144,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
$id = Database::lastInsertId();
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added directory-option for '" . $userpath . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added directory-option for '" . $userpath . "'");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
|
||||
$result = $this->apiCall('DirOptions.get', [
|
||||
@@ -157,16 +157,15 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
* this functions validates a given value as ErrorDocument
|
||||
* refs #267
|
||||
*
|
||||
* @param
|
||||
* string error-document-string
|
||||
* @param string $errdoc
|
||||
* @param bool $throw_exception
|
||||
*
|
||||
* @return string error-document-string
|
||||
*
|
||||
*/
|
||||
private function correctErrorDocument($errdoc = null, $throw_exception = false)
|
||||
private function correctErrorDocument(string $errdoc, $throw_exception = false)
|
||||
{
|
||||
if ($errdoc !== null && $errdoc != '') {
|
||||
if (trim($errdoc) != '') {
|
||||
// not a URL
|
||||
if ((strtoupper(substr($errdoc, 0, 5)) != 'HTTP:' && strtoupper(substr($errdoc, 0, 6)) != 'HTTPS:') || !Validate::validateUrl($errdoc)) {
|
||||
// a file
|
||||
@@ -176,14 +175,14 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
if (!substr($errdoc, 0, 1) == '/') {
|
||||
$errdoc = '/' . $errdoc;
|
||||
}
|
||||
} else {
|
||||
} elseif (preg_match('/^"([^\r\n\t\f\0"]+)"$/', $errdoc)) {
|
||||
// a string (check for ending ")
|
||||
// string won't work for lighty
|
||||
if (Settings::Get('system.webserver') == 'lighttpd') {
|
||||
Response::standardError('stringerrordocumentnotvalidforlighty', '', $throw_exception);
|
||||
} elseif (substr($errdoc, -1) != '"') {
|
||||
$errdoc .= '"';
|
||||
}
|
||||
} else {
|
||||
Response::standardError('invaliderrordocumentvalue', '', $throw_exception);
|
||||
}
|
||||
} else {
|
||||
if (Settings::Get('system.webserver') == 'lighttpd') {
|
||||
@@ -191,7 +190,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
}
|
||||
}
|
||||
return $errdoc;
|
||||
return trim($errdoc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,7 +247,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
$params['id'] = $id;
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get directory options for '" . $result['path'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get directory options for '" . $result['path'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = "id #" . $id;
|
||||
@@ -332,7 +331,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
"id" => $id
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited directory options for '" . str_replace($customer['documentroot'], '/', $result['path']) . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited directory options for '" . str_replace($customer['documentroot'], '/', $result['path']) . "'");
|
||||
}
|
||||
|
||||
$result = $this->apiCall('DirOptions.get', [
|
||||
@@ -380,7 +379,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list directory-options");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list directory-options");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
@@ -479,7 +478,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
||||
"customerid" => $customer_data['customerid'],
|
||||
"id" => $id
|
||||
], true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted directory-option for '" . str_replace($customer_data['documentroot'], '/', $result['path']) . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] deleted directory-option for '" . str_replace($customer_data['documentroot'], '/', $result['path']) . "'");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
return $this->response($result);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,8 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path);
|
||||
$username = Validate::validate($username, 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\-_]+\$?$/', '', [], true);
|
||||
$authname = Validate::validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/', '', [], true);
|
||||
Validate::validate($password, 'password', '', '', [], true);
|
||||
$password = Validate::validate($password, 'password', '', '', [], true);
|
||||
$password = Crypt::validatePassword($password, true);
|
||||
|
||||
// check for duplicate usernames for the path
|
||||
$username_path_check_stmt = Database::prepare("
|
||||
@@ -128,7 +129,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
$id = Database::lastInsertId();
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added directory-protection for '" . $username . " (" . $path . ")'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added directory-protection for '" . $username . " (" . $path . ")'");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
|
||||
$result = $this->apiCall('DirProtections.get', [
|
||||
@@ -195,7 +196,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
$params['idun'] = ($id <= 0 ? $username : $id);
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get directory protection for '" . $result['path'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get directory protection for '" . $result['path'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "username '" . $username . "'");
|
||||
@@ -244,7 +245,8 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
|
||||
// validation
|
||||
$authname = Validate::validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/', '', [], true);
|
||||
Validate::validate($password, 'password', '', '', [], true);
|
||||
$password = Validate::validate($password, 'password', '', '', [], true);
|
||||
$password = Crypt::validatePassword($password, true);
|
||||
|
||||
$upd_query = "";
|
||||
$upd_params = [
|
||||
@@ -277,7 +279,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
}
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated directory-protection '" . $result['username'] . " (" . $result['path'] . ")'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated directory-protection '" . $result['username'] . " (" . $result['path'] . ")'");
|
||||
$result = $this->apiCall('DirProtections.get', [
|
||||
'id' => $result['id']
|
||||
]);
|
||||
@@ -323,7 +325,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list directory-protections");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list directory-protections");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
@@ -411,7 +413,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
||||
"id" => $id
|
||||
]);
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
return $this->response($result);
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@ class DomainZones extends ApiCommand implements ResourceEntity
|
||||
$zone = Dns::createDomainZone($id);
|
||||
$zonefile = (string)$zone;
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get dns-zone for '" . $result['domain'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get dns-zone for '" . $result['domain'] . "'");
|
||||
return $this->response(explode("\n", $zonefile));
|
||||
}
|
||||
|
||||
|
||||
@@ -225,6 +225,8 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
* optional, whether php is enabled for this domain, default 0 (false)
|
||||
* @param bool $openbasedir
|
||||
* optional, whether to activate openbasedir restriction for this domain, default 0 (false)
|
||||
* @param int $openbasedir_path
|
||||
* optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot
|
||||
* @param int $phpsettingid
|
||||
* optional, specify php-configuration that is being used by id, default 1 (system-default)
|
||||
* @param int $mod_fcgid_starter
|
||||
@@ -312,6 +314,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$documentroot = $this->getParam('documentroot', true, '');
|
||||
$phpenabled = $this->getBoolParam('phpenabled', true, 0);
|
||||
$openbasedir = $this->getBoolParam('openbasedir', true, 0);
|
||||
$openbasedir_path = $this->getParam('openbasedir_path', true, 0);
|
||||
$phpsettingid = $this->getParam('phpsettingid', true, 1);
|
||||
$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, -1);
|
||||
$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, -1);
|
||||
@@ -404,20 +407,25 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$documentroot = $_documentroot;
|
||||
}
|
||||
|
||||
$registration_date = Validate::validate($registration_date, 'registration_date', Validate::REGEX_YYYY_MM_DD, '', [
|
||||
'0000-00-00',
|
||||
'0',
|
||||
''
|
||||
], true);
|
||||
if (!is_null($registration_date)) {
|
||||
$registration_date = Validate::validate($registration_date, 'registration_date',
|
||||
Validate::REGEX_YYYY_MM_DD, '', [
|
||||
'0000-00-00',
|
||||
'0',
|
||||
''
|
||||
], true);
|
||||
}
|
||||
if ($registration_date == '0000-00-00' || empty($registration_date)) {
|
||||
$registration_date = null;
|
||||
}
|
||||
|
||||
$termination_date = Validate::validate($termination_date, 'termination_date', Validate::REGEX_YYYY_MM_DD, '', [
|
||||
'0000-00-00',
|
||||
'0',
|
||||
''
|
||||
], true);
|
||||
if (!is_null($termination_date)) {
|
||||
$termination_date = Validate::validate($termination_date, 'termination_date',
|
||||
Validate::REGEX_YYYY_MM_DD, '', [
|
||||
'0000-00-00',
|
||||
'0',
|
||||
''
|
||||
], true);
|
||||
}
|
||||
if ($termination_date == '0000-00-00' || empty($termination_date)) {
|
||||
$termination_date = null;
|
||||
}
|
||||
@@ -525,6 +533,10 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$mod_fcgid_maxrequests = '-1';
|
||||
}
|
||||
|
||||
if ($openbasedir_path > 2 && $openbasedir_path < 0) {
|
||||
$openbasedir_path = 0;
|
||||
}
|
||||
|
||||
// check non-ssl IP
|
||||
$ipandports = $this->validateIpAddresses($p_ipandports);
|
||||
// check ssl IP
|
||||
@@ -559,7 +571,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
|
||||
// validate dns if lets encrypt is enabled to check whether we can use it at all
|
||||
if ($letsencrypt == '1' && Settings::Get('system.le_domain_dnscheck') == '1') {
|
||||
$domain_ips = PhpHelper::gethostbynamel6($domain);
|
||||
$domain_ips = PhpHelper::gethostbynamel6($domain, true, Settings::Get('system.le_domain_dnscheck_resolver'));
|
||||
$selected_ips = $this->getIpsFromIdArray($ssl_ipandports);
|
||||
if ($domain_ips == false || count(array_intersect($selected_ips, $domain_ips)) <= 0) {
|
||||
Response::standardError('invaliddnsforletsencrypt', '', true);
|
||||
@@ -696,6 +708,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
'caneditdomain' => $caneditdomain,
|
||||
'phpenabled' => $phpenabled,
|
||||
'openbasedir' => $openbasedir,
|
||||
'openbasedir_path' => $openbasedir_path,
|
||||
'speciallogfile' => $speciallogfile,
|
||||
'specialsettings' => $specialsettings,
|
||||
'ssl_specialsettings' => $ssl_specialsettings,
|
||||
@@ -749,6 +762,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
`caneditdomain` = :caneditdomain,
|
||||
`phpenabled` = :phpenabled,
|
||||
`openbasedir` = :openbasedir,
|
||||
`openbasedir_path` = :openbasedir_path,
|
||||
`speciallogfile` = :speciallogfile,
|
||||
`specialsettings` = :specialsettings,
|
||||
`ssl_specialsettings` = :ssl_specialsettings,
|
||||
@@ -884,7 +898,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$result['ipsandports'] = $this->getIpsForDomain($result['id']);
|
||||
}
|
||||
$result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get domain '" . $result['domain'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get domain '" . $result['domain'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'");
|
||||
@@ -1096,6 +1110,8 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
* from setting system.apply_phpconfigs_default
|
||||
* @param bool $openbasedir
|
||||
* optional, whether to activate openbasedir restriction for this domain, default 0 (false)
|
||||
* @param int $openbasedir_path
|
||||
* optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot
|
||||
* @param int $phpsettingid
|
||||
* optional, specify php-configuration that is being used by id, default 1 (system-default)
|
||||
* @param int $mod_fcgid_starter
|
||||
@@ -1193,6 +1209,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$phpenabled = $this->getBoolParam('phpenabled', true, $result['phpenabled']);
|
||||
$phpfs = $this->getBoolParam('phpsettingsforsubdomains', true, Settings::Get('system.apply_phpconfigs_default'));
|
||||
$openbasedir = $this->getBoolParam('openbasedir', true, $result['openbasedir']);
|
||||
$openbasedir_path = $this->getParam('openbasedir_path', true, $result['openbasedir_path']);
|
||||
$phpsettingid = $this->getParam('phpsettingid', true, $result['phpsettingid']);
|
||||
$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, $result['mod_fcgid_starter']);
|
||||
$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, $result['mod_fcgid_maxrequests']);
|
||||
@@ -1322,19 +1339,25 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$adminid = $result['adminid'];
|
||||
}
|
||||
|
||||
$registration_date = Validate::validate($registration_date, 'registration_date', Validate::REGEX_YYYY_MM_DD, '', [
|
||||
'0000-00-00',
|
||||
'0',
|
||||
''
|
||||
], true);
|
||||
if (!is_null($registration_date)) {
|
||||
$registration_date = Validate::validate($registration_date, 'registration_date',
|
||||
Validate::REGEX_YYYY_MM_DD, '', [
|
||||
'0000-00-00',
|
||||
'0',
|
||||
''
|
||||
], true);
|
||||
}
|
||||
if ($registration_date == '0000-00-00' || empty($registration_date)) {
|
||||
$registration_date = null;
|
||||
}
|
||||
$termination_date = Validate::validate($termination_date, 'termination_date', Validate::REGEX_YYYY_MM_DD, '', [
|
||||
'0000-00-00',
|
||||
'0',
|
||||
''
|
||||
], true);
|
||||
if (!is_null($termination_date)) {
|
||||
$termination_date = Validate::validate($termination_date, 'termination_date',
|
||||
Validate::REGEX_YYYY_MM_DD, '', [
|
||||
'0000-00-00',
|
||||
'0',
|
||||
''
|
||||
], true);
|
||||
}
|
||||
if ($termination_date == '0000-00-00' || empty($termination_date)) {
|
||||
$termination_date = null;
|
||||
}
|
||||
@@ -1478,6 +1501,11 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests'];
|
||||
}
|
||||
|
||||
// check changes of openbasedir-path variable
|
||||
if ($openbasedir_path > 2 && $openbasedir_path < 0) {
|
||||
$openbasedir_path = 0;
|
||||
}
|
||||
|
||||
// check non-ssl IP
|
||||
$ipandports = $this->validateIpAddresses($p_ipandports, false, $result['id']);
|
||||
// check ssl IP
|
||||
@@ -1523,7 +1551,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
|
||||
// validate dns if lets encrypt is enabled to check whether we can use it at all
|
||||
if ($letsencrypt == '1' && Settings::Get('system.le_domain_dnscheck') == '1') {
|
||||
$domain_ips = PhpHelper::gethostbynamel6($result['domain']);
|
||||
$domain_ips = PhpHelper::gethostbynamel6($result['domain'], true, Settings::Get('system.le_domain_dnscheck_resolver'));
|
||||
$selected_ips = $this->getIpsFromIdArray($ssl_ipandports);
|
||||
if ($domain_ips == false || count(array_intersect($selected_ips, $domain_ips)) <= 0) {
|
||||
Response::standardError('invaliddnsforletsencrypt', '', true);
|
||||
@@ -1623,7 +1651,31 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';
|
||||
$iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0';
|
||||
|
||||
if ($documentroot != $result['documentroot'] || $ssl_redirect != $result['ssl_redirect'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $phpenabled != $result['phpenabled'] || $openbasedir != $result['openbasedir'] || $phpsettingid != $result['phpsettingid'] || $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || $ssl_specialsettings != $result['ssl_specialsettings'] || $notryfiles != $result['notryfiles'] || $writeaccesslog != $result['writeaccesslog'] || $writeerrorlog != $result['writeerrorlog'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt'] || $http2 != $result['http2'] || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload'] || $ocsp_stapling != $result['ocsp_stapling']) {
|
||||
if ($documentroot != $result['documentroot']
|
||||
|| $ssl_redirect != $result['ssl_redirect']
|
||||
|| $wwwserveralias != $result['wwwserveralias']
|
||||
|| $iswildcarddomain != $result['iswildcarddomain']
|
||||
|| $phpenabled != $result['phpenabled']
|
||||
|| $openbasedir != $result['openbasedir']
|
||||
|| $phpsettingid != $result['phpsettingid']
|
||||
|| $mod_fcgid_starter != $result['mod_fcgid_starter']
|
||||
|| $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests']
|
||||
|| $specialsettings != $result['specialsettings']
|
||||
|| $ssl_specialsettings != $result['ssl_specialsettings']
|
||||
|| $notryfiles != $result['notryfiles']
|
||||
|| $writeaccesslog != $result['writeaccesslog']
|
||||
|| $writeerrorlog != $result['writeerrorlog']
|
||||
|| $aliasdomain != $result['aliasdomain']
|
||||
|| $issubof != $result['ismainbutsubto']
|
||||
|| $email_only != $result['email_only']
|
||||
|| ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1')
|
||||
|| $letsencrypt != $result['letsencrypt']
|
||||
|| $http2 != $result['http2']
|
||||
|| $hsts_maxage != $result['hsts']
|
||||
|| $hsts_sub != $result['hsts_sub']
|
||||
|| $hsts_preload != $result['hsts_preload']
|
||||
|| $ocsp_stapling != $result['ocsp_stapling']
|
||||
) {
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
}
|
||||
|
||||
@@ -1749,7 +1801,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
Database::pexecute($upd_stmt, [
|
||||
'id' => $id
|
||||
], true, true);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] removed specialsettings on all subdomains of domain #" . $id);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] removed specialsettings on all subdomains of domain #" . $id);
|
||||
}
|
||||
|
||||
$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';
|
||||
@@ -1771,7 +1823,8 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$update_data['wwwserveralias'] = $wwwserveralias;
|
||||
$update_data['iswildcarddomain'] = $iswildcarddomain;
|
||||
$update_data['phpenabled'] = $phpenabled;
|
||||
$update_data['openbasedir'] = $openbasedir;
|
||||
$update_data['openbasedir'] = $openbasedir;;
|
||||
$update_data['openbasedir_path'] = $openbasedir_path;
|
||||
$update_data['speciallogfile'] = $speciallogfile;
|
||||
$update_data['phpsettingid'] = $phpsettingid;
|
||||
$update_data['mod_fcgid_starter'] = $mod_fcgid_starter;
|
||||
@@ -1819,6 +1872,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
`iswildcarddomain` = :iswildcarddomain,
|
||||
`phpenabled` = :phpenabled,
|
||||
`openbasedir` = :openbasedir,
|
||||
`openbasedir_path` = :openbasedir_path,
|
||||
`speciallogfile` = :speciallogfile,
|
||||
`phpsettingid` = :phpsettingid,
|
||||
`mod_fcgid_starter` = :mod_fcgid_starter,
|
||||
@@ -1854,6 +1908,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
$_update_data['adminid'] = $adminid;
|
||||
$_update_data['phpenabled'] = $phpenabled;
|
||||
$_update_data['openbasedir'] = $openbasedir;
|
||||
$_update_data['openbasedir_path'] = $openbasedir_path;
|
||||
$_update_data['mod_fcgid_starter'] = $mod_fcgid_starter;
|
||||
$_update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests;
|
||||
$_update_data['notryfiles'] = $notryfiles;
|
||||
@@ -1887,6 +1942,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
`adminid` = :adminid,
|
||||
`phpenabled` = :phpenabled,
|
||||
`openbasedir` = :openbasedir,
|
||||
`openbasedir_path` = :openbasedir_path,
|
||||
`mod_fcgid_starter` = :mod_fcgid_starter,
|
||||
`mod_fcgid_maxrequests` = :mod_fcgid_maxrequests,
|
||||
`notryfiles` = :notryfiles,
|
||||
@@ -1903,6 +1959,18 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
");
|
||||
Database::pexecute($_update_stmt, $_update_data, true, true);
|
||||
|
||||
// get current ip<>domain entries
|
||||
$ip_sel_stmt = Database::prepare("
|
||||
SELECT id_ipandports FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id
|
||||
");
|
||||
Database::pexecute($ip_sel_stmt, [
|
||||
'id' => $id
|
||||
], true, true);
|
||||
$current_ips = [];
|
||||
while ($cIP = $ip_sel_stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$current_ips[] = $cIP['id_ipandports'];
|
||||
}
|
||||
|
||||
// Cleanup domain <-> ip mapping
|
||||
$del_stmt = Database::prepare("
|
||||
DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id
|
||||
@@ -1930,6 +1998,12 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
}
|
||||
|
||||
// check ip changes
|
||||
$all_new_ips = array_merge($ipandports, $ssl_ipandports);
|
||||
if (count(array_diff($current_ips, $all_new_ips)) != 0 || count(array_diff($all_new_ips, $current_ips)) != 0) {
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
}
|
||||
|
||||
// Cleanup domain <-> ip mapping for subdomains
|
||||
$domainidsresult_stmt = Database::prepare("
|
||||
SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :id
|
||||
@@ -1974,12 +2048,11 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) {
|
||||
// or when wwwserveralias or letsencrypt was changed
|
||||
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());
|
||||
if ((int)$aliasdomain === 0) {
|
||||
// in case the wwwserveralias is set on a main domain, $aliasdomain is 0
|
||||
// --> the call just above to triggerLetsEncryptCSRForAliasDestinationDomain
|
||||
// is a noop...let's repeat it with the domain id of the main domain
|
||||
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($id, $this->logger());
|
||||
} else {
|
||||
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2138,7 +2211,9 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
'domainid' => $id
|
||||
], true, true);
|
||||
|
||||
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger());
|
||||
if ((int)$result['aliasdomain'] !== 0) {
|
||||
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger());
|
||||
}
|
||||
|
||||
// remove domains DNS from powerDNS if used, #581
|
||||
Cronjob::inserttask(TaskId::DELETE_DOMAIN_PDNS, $result['domain']);
|
||||
@@ -2146,7 +2221,7 @@ class Domains extends ApiCommand implements ResourceEntity
|
||||
// remove domain from acme.sh / lets encrypt if used
|
||||
Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $result['domain']);
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] deleted domain/subdomains (#" . $result['id'] . ")");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] deleted domain/subdomains (#" . $result['id'] . ")");
|
||||
User::updateCounters();
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
// Using nameserver, insert a task which rebuilds the server config
|
||||
|
||||
@@ -63,7 +63,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
* @param string $alternative_email
|
||||
* optional email address to send account information to, default is the account that is being created
|
||||
* @param int $email_quota
|
||||
* optional quota if enabled in MB, default 0
|
||||
* optional quota if enabled in MB, default setting: system.mail_quota
|
||||
* @param bool $sendinfomail
|
||||
* optional, sends the welcome message to the new account (needed for creation, without the user won't
|
||||
* be able to login before any mail is received), default 1 (true)
|
||||
@@ -85,7 +85,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
$emailaddr = $this->getParam('emailaddr', $ea_optional, '');
|
||||
$email_password = $this->getParam('email_password');
|
||||
$alternative_email = $this->getParam('alternative_email', true, '');
|
||||
$quota = $this->getParam('email_quota', true, 0);
|
||||
$quota = $this->getParam('email_quota', true, Settings::Get('system.mail_quota') ?? 0);
|
||||
$sendinfomail = $this->getBoolParam('sendinfomail', true, 1);
|
||||
|
||||
// validation
|
||||
@@ -99,6 +99,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
Response::standardError('notallowedtouseaccounts', '', true);
|
||||
}
|
||||
|
||||
if (!empty($emailaddr)) {
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$emailaddr = $idna_convert->encode($emailaddr);
|
||||
}
|
||||
|
||||
// get email address
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'id' => $id,
|
||||
@@ -306,7 +311,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email account for '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email account for '" . $result['email_full'] . "'");
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
]);
|
||||
@@ -357,6 +362,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
$ea_optional = $id > 0;
|
||||
$emailaddr = $this->getParam('emailaddr', $ea_optional, '');
|
||||
|
||||
if (!empty($emailaddr)) {
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$emailaddr = $idna_convert->encode($emailaddr);
|
||||
}
|
||||
|
||||
// validation
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'id' => $id,
|
||||
@@ -450,7 +460,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
Admins::increaseUsage($customer['adminid'], 'email_quota_used', '', ($quota - $result['quota']));
|
||||
}
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated email account '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated email account '" . $result['email_full'] . "'");
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
]);
|
||||
@@ -556,7 +566,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
|
||||
Customers::decreaseUsage($customer['customerid'], 'email_accounts_used');
|
||||
Customers::decreaseUsage($customer['customerid'], 'email_quota_used', '', $quota);
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email account for '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted email account for '" . $result['email_full'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
}
|
||||
|
||||
188
lib/Froxlor/Api/Commands/EmailDomains.php
Normal file
188
lib/Froxlor/Api/Commands/EmailDomains.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Froxlor project.
|
||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can also view it online at
|
||||
* https://files.froxlor.org/misc/COPYING.txt
|
||||
*
|
||||
* @copyright the authors
|
||||
* @author Froxlor team <team@froxlor.org>
|
||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
||||
*/
|
||||
|
||||
namespace Froxlor\Api\Commands;
|
||||
|
||||
use Exception;
|
||||
use Froxlor\Api\ApiCommand;
|
||||
use Froxlor\Api\ResourceEntity;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\Settings;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* @since 2.0
|
||||
*/
|
||||
class EmailDomains extends ApiCommand implements ResourceEntity
|
||||
{
|
||||
/**
|
||||
* list all domains with email addresses connected to it.
|
||||
* If called from an admin, list all domains with email addresses
|
||||
* connected to it from all customers you are allowed to view, or
|
||||
* specify id or loginname for one specific customer
|
||||
*
|
||||
* @param int $customerid
|
||||
* optional, admin-only, select email addresses of a specific customer by id
|
||||
* @param string $loginname
|
||||
* optional, admin-only, select email addresses of a specific customer by loginname
|
||||
* @param array $sql_search
|
||||
* optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),
|
||||
* LIKE is used if left empty and 'value' => searchvalue
|
||||
* @param int $sql_limit
|
||||
* optional specify number of results to be returned
|
||||
* @param int $sql_offset
|
||||
* optional specify offset for resultset
|
||||
* @param array $sql_orderby
|
||||
* optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more
|
||||
* fields
|
||||
*
|
||||
* @access admin, customer
|
||||
* @return string json-encoded array count|list
|
||||
* @throws Exception
|
||||
*/
|
||||
public function listing()
|
||||
{
|
||||
$customer_ids = $this->getAllowedCustomerIds('email');
|
||||
$result = [];
|
||||
$query_fields = [];
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT DISTINCT d.domain, e.domainid,
|
||||
COUNT(e.email) as addresses,
|
||||
IFNULL(SUM(CASE WHEN e.popaccountid > 0 THEN 1 ELSE 0 END), 0) as accounts,
|
||||
IFNULL(SUM(
|
||||
CASE
|
||||
WHEN LENGTH(REPLACE(e.destination, CONCAT(e.email_full, ' '), '')) - LENGTH(REPLACE(REPLACE(e.destination, CONCAT(e.email_full, ' '), ''), ' ', '')) > 0
|
||||
THEN LENGTH(REPLACE(e.destination, CONCAT(e.email_full, ' '), '')) - LENGTH(REPLACE(REPLACE(e.destination, CONCAT(e.email_full, ' '), ''), ' ', ''))
|
||||
WHEN e.destination <> e.email_full THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
), 0) as forwarder
|
||||
FROM `" . TABLE_MAIL_VIRTUAL . "` e
|
||||
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON d.id = e.domainid
|
||||
WHERE e.customerid IN (" . implode(", ", $customer_ids) . ") AND d.domain IS NOT NULL " .
|
||||
$this->getSearchWhere($query_fields,
|
||||
true) . " GROUP BY e.domainid " . $this->getOrderBy() . $this->getLimit());
|
||||
Database::pexecute($result_stmt, $query_fields, true, true);
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO,
|
||||
"[API] list email-domains");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the total number of accessible domains with email addresses connected to
|
||||
*
|
||||
* @param int $customerid
|
||||
* optional, admin-only, select email addresses of a specific customer by id
|
||||
* @param string $loginname
|
||||
* optional, admin-only, select email addresses of a specific customer by loginname
|
||||
*
|
||||
* @access admin, customer
|
||||
* @return string json-encoded response message
|
||||
* @throws Exception
|
||||
*/
|
||||
public function listingCount()
|
||||
{
|
||||
$customer_ids = $this->getAllowedCustomerIds('email');
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT COUNT(DISTINCT d.domain) as num_emaildomains
|
||||
FROM `" . TABLE_MAIL_VIRTUAL . "` e
|
||||
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON d.id = e.domainid
|
||||
WHERE e.customerid IN (" . implode(", ", $customer_ids) . ") AND d.domain IS NOT NULL
|
||||
");
|
||||
$result = Database::pexecute_first($result_stmt, null, true, true);
|
||||
if ($result) {
|
||||
return $this->response($result['num_emaildomains']);
|
||||
}
|
||||
return $this->response(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* You cannot directly access email-domains
|
||||
*
|
||||
* @access admin, customer
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {
|
||||
throw new Exception("You cannot access this resource", 405);
|
||||
}
|
||||
throw new Exception('You cannot directly access this resource.', 303);
|
||||
}
|
||||
|
||||
/**
|
||||
* You cannot directly add email-domains
|
||||
*
|
||||
* @access admin, customer
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {
|
||||
throw new Exception("You cannot access this resource", 405);
|
||||
}
|
||||
throw new Exception('You cannot directly add this resource.', 303);
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle catchall flag of given email address either by id or email-address
|
||||
*
|
||||
* @access admin, customer
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {
|
||||
throw new Exception("You cannot access this resource", 405);
|
||||
}
|
||||
throw new Exception('You cannot directly update this resource.', 303);
|
||||
}
|
||||
|
||||
/**
|
||||
* You cannot directly delete email-domains
|
||||
*
|
||||
* @access admin, customer
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {
|
||||
throw new Exception("You cannot access this resource", 405);
|
||||
}
|
||||
throw new Exception('You cannot directly delete this resource.', 303);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -77,6 +77,11 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$destination = $idna_convert->encode($destination);
|
||||
|
||||
if (!empty($emailaddr)) {
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$emailaddr = $idna_convert->encode($emailaddr);
|
||||
}
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'id' => $id,
|
||||
'emailaddr' => $emailaddr
|
||||
@@ -116,7 +121,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
|
||||
// update customer usage
|
||||
Customers::increaseUsage($customer['customerid'], 'email_forwarders_used');
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email forwarder for '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email forwarder for '" . $result['email_full'] . "'");
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
@@ -293,7 +298,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
|
||||
// update customer usage
|
||||
Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used');
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email forwarder for '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] deleted email forwarder for '" . $result['email_full'] . "'");
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
|
||||
@@ -159,7 +159,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
// update customer usage
|
||||
Customers::increaseUsage($customer['customerid'], 'emails_used');
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email address '" . $email_full . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email address '" . $email_full . "'");
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $email_full
|
||||
@@ -195,11 +195,11 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
FROM `" . TABLE_MAIL_VIRTUAL . "` v
|
||||
LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id`
|
||||
WHERE v.`customerid` IN (" . implode(", ", $customer_ids) . ")
|
||||
AND (v.`id`= :idea OR (v.`email` = :idea OR v.`email_full` = :idea))
|
||||
");
|
||||
AND " . (is_numeric($params['idea']) ? "v.`id`= :idea" : "(v.`email` = :idea OR v.`email_full` = :idea)")
|
||||
);
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get email address '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get email address '" . $result['email_full'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "emailaddr '" . $emailaddr . "'");
|
||||
@@ -294,7 +294,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
"id" => $id
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'");
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
@@ -340,7 +340,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list email-addresses");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list email-addresses");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
@@ -445,7 +445,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
], true, true);
|
||||
Customers::decreaseUsage($customer['customerid'], 'emails_used');
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email address '" . $result['email_full'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted email address '" . $result['email_full'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list fpm-daemons");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list fpm-daemons");
|
||||
$query_fields = [];
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "`" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());
|
||||
@@ -258,7 +258,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
|
||||
$id = Database::lastInsertId();
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
|
||||
$result = $this->apiCall('FpmDaemons.get', [
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -384,7 +384,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
|
||||
Database::pexecute($upd_stmt, $upd_data, true, true);
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
$result = $this->apiCall('FpmDaemons.get', [
|
||||
'id' => $id
|
||||
]);
|
||||
@@ -433,7 +433,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
|
||||
], true, true);
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
throw new Exception("Not allowed to execute given command.", 403);
|
||||
|
||||
@@ -72,7 +72,7 @@ class Froxlor extends ApiCommand
|
||||
|
||||
if (empty($uc_data) || empty($response) || $uc_data['ts'] + self::UPDATE_CHECK_INTERVAL < time() || $uc_data['channel'] != Settings::Get('system.update_channel') || $force_ucheck) {
|
||||
// log our actions
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] checking for updates");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] checking for updates");
|
||||
|
||||
// check for new version
|
||||
$aucheck = AutoUpdate::checkVersion();
|
||||
@@ -142,7 +142,7 @@ class Froxlor extends ApiCommand
|
||||
{
|
||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
|
||||
$json_str = $this->getParam('json_str');
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "User " . $this->getUserDetail('loginname') . " imported settings");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "User " . $this->getUserDetail('loginname') . " imported settings");
|
||||
try {
|
||||
SImExporter::import($json_str);
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
|
||||
@@ -257,7 +257,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
Customers::increaseUsage($customer['customerid'], 'ftp_lastaccountnumber');
|
||||
}
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added ftp-account '" . $username . " (" . $path . ")'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ftp-account '" . $username . " (" . $path . ")'");
|
||||
Cronjob::inserttask(TaskId::CREATE_FTP);
|
||||
|
||||
if ($sendinfomail == 1) {
|
||||
@@ -302,7 +302,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
|
||||
$this->mailer()->clearAddresses();
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] added ftp-user '" . $username . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ftp-user '" . $username . "'");
|
||||
|
||||
$result = $this->apiCall('Ftps.get', [
|
||||
'username' => $username
|
||||
@@ -367,7 +367,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
$params['idun'] = ($id <= 0 ? $username : $id);
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get ftp-user '" . $result['username'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get ftp-user '" . $result['username'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "username '" . $username . "'");
|
||||
@@ -453,7 +453,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
"id" => $id,
|
||||
"password" => $cryptPassword
|
||||
], true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ftp-account password for '" . $result['username'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ftp-account password for '" . $result['username'] . "'");
|
||||
}
|
||||
|
||||
// path update?
|
||||
@@ -471,7 +471,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
"customerid" => $customer['customerid'],
|
||||
"id" => $id
|
||||
], true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ftp-account homdir for '" . $result['username'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ftp-account homdir for '" . $result['username'] . "'");
|
||||
}
|
||||
}
|
||||
// it's the task for "new ftp" but that will
|
||||
@@ -533,7 +533,7 @@ class Ftps extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list ftp-users");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list ftp-users");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
|
||||
@@ -61,7 +61,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list hosting-plans");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list hosting-plans");
|
||||
$query_fields = [];
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT p.*, a.loginname as adminname
|
||||
@@ -200,8 +200,8 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
$value_arr['logviewenabled'] = $this->getBoolParam('logviewenabled', true, 0);
|
||||
|
||||
// validation
|
||||
$name = Validate::validate(trim($name), 'name', '', '', [], true);
|
||||
$description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_DESC_TEXT);
|
||||
$name = Validate::validate(trim($name), 'name', Validate::REGEX_DESC_TEXT, '', [], true);
|
||||
$description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_CONF_TEXT);
|
||||
|
||||
if (Settings::Get('system.mail_quota_enabled') != '1') {
|
||||
$value_arr['email_quota'] = -1;
|
||||
@@ -227,7 +227,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
'valuearr' => json_encode($value_arr)
|
||||
];
|
||||
Database::pexecute($ins_stmt, $ins_data, true, true);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] added hosting-plan '" . $name . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] added hosting-plan '" . $name . "'");
|
||||
$result = $this->apiCall('HostingPlans.get', [
|
||||
'planname' => $name
|
||||
]);
|
||||
@@ -264,7 +264,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get hosting-plan '" . $result['name'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get hosting-plan '" . $result['name'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "planname '" . $planname . "'");
|
||||
@@ -382,8 +382,8 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
$value_arr['logviewenabled'] = $this->getBoolParam('logviewenabled', true, $result['logviewenabled']);
|
||||
|
||||
// validation
|
||||
$name = Validate::validate(trim($name), 'name', '', '', [], true);
|
||||
$description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_DESC_TEXT);
|
||||
$name = Validate::validate(trim($name), 'name', Validate::REGEX_DESC_TEXT, '', [], true);
|
||||
$description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_CONF_TEXT);
|
||||
|
||||
if (Settings::Get('system.mail_quota_enabled') != '1') {
|
||||
$value_arr['email_quota'] = -1;
|
||||
@@ -414,7 +414,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
|
||||
'id' => $id
|
||||
];
|
||||
Database::pexecute($upd_stmt, $update_data, true, true);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] updated hosting-plan '" . $result['name'] . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] updated hosting-plan '" . $result['name'] . "'");
|
||||
return $this->response($update_data);
|
||||
}
|
||||
throw new Exception("Not allowed to execute given command.", 403);
|
||||
|
||||
@@ -65,7 +65,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || !empty($this->getUserDetail('ip')))) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list ips and ports");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list ips and ports");
|
||||
$ip_where = "";
|
||||
$append_where = false;
|
||||
if (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) {
|
||||
@@ -175,9 +175,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
|
||||
$docroot = Validate::validate($this->getParam('docroot', true, ''), 'docroot', Validate::REGEX_DIR, '', [], true);
|
||||
|
||||
if ((int)Settings::Get('system.use_ssl') == 1) {
|
||||
$ssl = !empty($this->getBoolParam('ssl', true, 0)) ? intval($this->getBoolParam('ssl', true, 0)) : 0;
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $ssl, ''), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $ssl, ''), 'ssl_key_file', '', '', [], true);
|
||||
$ssl = (bool)$this->getBoolParam('ssl', true, 0);
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', !$ssl, ''), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', !$ssl, ''), 'ssl_key_file', '', '', [], true);
|
||||
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, ''), 'ssl_ca_file', '', '', [], true);
|
||||
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, ''), 'ssl_cert_chainfile', '', '', [], true);
|
||||
$sslss = $this->getParam('ssl_specialsettings', true, '');
|
||||
@@ -335,7 +335,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
|
||||
'id' => $id
|
||||
], true, true);
|
||||
if ($result) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get ip " . $result['ip'] . " " . $result['port']);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get ip " . $result['ip'] . " " . $result['port']);
|
||||
return $this->response($result);
|
||||
}
|
||||
throw new Exception("IP/port with id #" . $id . " could not be found", 404);
|
||||
@@ -414,9 +414,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
|
||||
$docroot = Validate::validate($this->getParam('docroot', true, $result['docroot']), 'docroot', Validate::REGEX_DIR, '', [], true);
|
||||
|
||||
if ((int)Settings::Get('system.use_ssl') == 1) {
|
||||
$ssl = $this->getBoolParam('ssl', true, $result['ssl']);
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $ssl, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $ssl, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);
|
||||
$ssl = (bool)$this->getBoolParam('ssl', true, $result['ssl']);
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', !$ssl, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', !$ssl, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);
|
||||
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, $result['ssl_ca_file']), 'ssl_ca_file', '', '', [], true);
|
||||
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', [], true);
|
||||
$sslss = $this->getParam('ssl_specialsettings', true, $result['ssl_specialsettings']);
|
||||
|
||||
@@ -26,14 +26,15 @@
|
||||
namespace Froxlor\Api\Commands;
|
||||
|
||||
use Exception;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\PhpHelper;
|
||||
use Froxlor\Api\ApiCommand;
|
||||
use Froxlor\Api\ResourceEntity;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\PhpHelper;
|
||||
use Froxlor\Validate\Validate;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
{
|
||||
@@ -73,8 +74,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
* optional, test connection with given credentials, default is true (yes)
|
||||
*
|
||||
* @access admin
|
||||
* @throws Exception
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
@@ -112,7 +113,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
);
|
||||
if (!empty($mysql_ca)) {
|
||||
$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca;
|
||||
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool) $mysql_verifycert;
|
||||
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_verifycert;
|
||||
}
|
||||
|
||||
$dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";";
|
||||
@@ -167,6 +168,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
$this->addDatabaseFromCustomerAllowedList($newdbserver);
|
||||
}
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] added new database server '" . $description . "' (" . $mysql_host . ")");
|
||||
|
||||
return $this->response(['dbserver' => $newdbserver]);
|
||||
}
|
||||
|
||||
@@ -179,16 +182,16 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
* optional the number of the mysql server (either id or dbserver must be set)
|
||||
*
|
||||
* @access admin
|
||||
* @throws Exception
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->validateAccess();
|
||||
|
||||
$id = (int) $this->getParam('id', true, -1);
|
||||
$id = (int)$this->getParam('id', true, -1);
|
||||
$dn_optional = $id >= 0;
|
||||
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = $id >= 0 ? $id : $dbserver;
|
||||
|
||||
if ($dbserver == 0) {
|
||||
@@ -212,8 +215,12 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
// when removing, remove from list of allowed_mysqlservers from any customers
|
||||
$this->removeDatabaseFromCustomerAllowedList($dbserver);
|
||||
|
||||
$description = $sql_root[$dbserver]['caption'] ?? "unknown";
|
||||
$mysql_host = $sql_root[$dbserver]['host'] ?? "unknown";
|
||||
unset($sql_root[$dbserver]);
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] removed database server '" . $description . "' (" . $mysql_host . ")");
|
||||
|
||||
$this->generateNewUserData($sql, $sql_root);
|
||||
return $this->response(['true']);
|
||||
}
|
||||
@@ -287,14 +294,14 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
* optional the number of the mysql server (either id or dbserver must be set)
|
||||
*
|
||||
* @access admin, customer
|
||||
* @throws Exception
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$id = (int) $this->getParam('id', true, -1);
|
||||
$id = (int)$this->getParam('id', true, -1);
|
||||
$dn_optional = $id >= 0;
|
||||
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = $id >= 0 ? $id : $dbserver;
|
||||
|
||||
$sql_root = [];
|
||||
@@ -317,6 +324,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
|
||||
unset($sql_root[$dbserver]['password']);
|
||||
$sql_root[$dbserver]['id'] = $dbserver;
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get database-server '" . $sql_root[$dbserver]['caption'] . "'");
|
||||
return $this->response($sql_root[$dbserver]);
|
||||
}
|
||||
|
||||
@@ -347,16 +355,16 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
* optional, test connection with given credentials, default is true (yes)
|
||||
*
|
||||
* @access admin
|
||||
* @throws Exception
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$this->validateAccess();
|
||||
|
||||
$id = (int) $this->getParam('id', true, -1);
|
||||
$id = (int)$this->getParam('id', true, -1);
|
||||
$dn_optional = $id >= 0;
|
||||
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
|
||||
$dbserver = $id >= 0 ? $id : $dbserver;
|
||||
|
||||
$sql_root = [];
|
||||
@@ -417,7 +425,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
);
|
||||
if (!empty($mysql_ca)) {
|
||||
$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca;
|
||||
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool) $mysql_verifycert;
|
||||
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_verifycert;
|
||||
}
|
||||
|
||||
$dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";";
|
||||
@@ -448,6 +456,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
$this->addDatabaseFromCustomerAllowedList($dbserver);
|
||||
}
|
||||
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] edited database server '" . $description . "' (" . $mysql_host . ")");
|
||||
|
||||
return $this->response(['true']);
|
||||
}
|
||||
|
||||
@@ -472,7 +482,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
WHERE `dbserver` = :dbserver
|
||||
");
|
||||
$result = Database::pexecute_first($result_stmt, ['dbserver' => $dbserver], true, true);
|
||||
return (int) $result['num_dbs'];
|
||||
return (int)$result['num_dbs'];
|
||||
} else {
|
||||
$dbserver = $this->getParam('mysql_server');
|
||||
$customer_ids = $this->getAllowedCustomerIds();
|
||||
@@ -516,7 +526,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
|
||||
`allowed_mysqlserver` = :am WHERE `customerid` = :cid
|
||||
");
|
||||
while ($customer = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$allowed_mysqls = json_decode(($customer['allowed_mysqlserver'] ?? '[]'), true);
|
||||
$allowed_mysqls = json_decode(($customer['allowed_mysqlserver'] ?: '[]'), true);
|
||||
if (!in_array($dbserver, $allowed_mysqls)) {
|
||||
$allowed_mysqls[] = $dbserver;
|
||||
$allowed_mysqls = json_encode($allowed_mysqls);
|
||||
|
||||
@@ -73,12 +73,12 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
$password = $this->getParam('mysql_password');
|
||||
|
||||
// parameters
|
||||
$dbserver = $this->getParam('mysql_server', true, 0);
|
||||
$databasedescription = $this->getParam('description', true, '');
|
||||
$databasename = $this->getParam('custom_suffix', true, '');
|
||||
$sendinfomail = $this->getBoolParam('sendinfomail', true, 0);
|
||||
// get needed customer info to reduce the mysql-usage-counter by one
|
||||
$customer = $this->getCustomerData('mysqls');
|
||||
$dbserver = $this->getParam('mysql_server', true, $this->getDefaultMySqlServer($customer));
|
||||
|
||||
// validation
|
||||
$password = Validate::validate($password, 'password', '', '', [], true);
|
||||
@@ -90,7 +90,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
|
||||
// validate whether the dbserver exists
|
||||
$dbserver = Validate::validate($dbserver, html_entity_decode(lng('mysql.mysql_server')), '/^[0-9]+$/', '', 0, true);
|
||||
Database::needRoot(true, $dbserver);
|
||||
Database::needRoot(true, $dbserver, false);
|
||||
Database::needSqlData();
|
||||
$sql_root = Database::getSqlData();
|
||||
Database::needRoot(false);
|
||||
@@ -150,7 +150,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
$pma = Settings::Get('panel.phpmyadmin_url');
|
||||
}
|
||||
|
||||
Database::needRoot(true, $dbserver);
|
||||
Database::needRoot(true, $dbserver, false);
|
||||
Database::needSqlData();
|
||||
$sql_root = Database::getSqlData();
|
||||
Database::needRoot(false);
|
||||
@@ -199,7 +199,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
|
||||
$this->mailer()->clearAddresses();
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] added mysql-database '" . $username . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added mysql-database '" . $username . "'");
|
||||
|
||||
$result = $this->apiCall('Mysqls.get', [
|
||||
'dbname' => $username,
|
||||
@@ -287,7 +287,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
||||
if ($result) {
|
||||
Database::needRoot(true, $result['dbserver']);
|
||||
Database::needRoot(true, $result['dbserver'], false);
|
||||
$mbdata_stmt = Database::prepare("
|
||||
SELECT SUM(data_length + index_length) as MB FROM information_schema.TABLES
|
||||
WHERE table_schema = :table_schema
|
||||
@@ -299,7 +299,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
$mbdata = $mbdata_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
Database::needRoot(false);
|
||||
$result['size'] = $mbdata['MB'] ?? 0;
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get database '" . $result['databasename'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get database '" . $result['databasename'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "dbname '" . $dbname . "'");
|
||||
@@ -364,7 +364,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
|
||||
// Begin root-session
|
||||
Database::needRoot(true, $result['dbserver']);
|
||||
Database::needRoot(true, $result['dbserver'], false);
|
||||
$dbmgr = new DbManager($this->logger());
|
||||
foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {
|
||||
$dbmgr->getManager()->grantPrivilegesTo($result['databasename'], $password, $mysql_access_host, false, true);
|
||||
@@ -388,7 +388,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] updated mysql-database '" . $result['databasename'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated mysql-database '" . $result['databasename'] . "'");
|
||||
$result = $this->apiCall('Mysqls.get', [
|
||||
'dbname' => $result['databasename']
|
||||
]);
|
||||
@@ -449,7 +449,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
'dbserver' => $_dbserver['dbserver']
|
||||
], $query_fields), true, true);
|
||||
// Begin root-session
|
||||
Database::needRoot(true, $_dbserver['dbserver']);
|
||||
Database::needRoot(true, $_dbserver['dbserver'], false);
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$mbdata_stmt = Database::prepare("
|
||||
SELECT SUM(data_length + index_length) as MB FROM information_schema.TABLES
|
||||
@@ -536,7 +536,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
$id = $result['id'];
|
||||
|
||||
// Begin root-session
|
||||
Database::needRoot(true, $result['dbserver']);
|
||||
Database::needRoot(true, $result['dbserver'], false);
|
||||
$dbm = new DbManager($this->logger());
|
||||
$dbm->getManager()->deleteDatabase($result['databasename']);
|
||||
Database::needRoot(false);
|
||||
@@ -558,4 +558,13 @@ class Mysqls extends ApiCommand implements ResourceEntity
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted database '" . $result['databasename'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
|
||||
private function getDefaultMySqlServer(array $customer) {
|
||||
$allowed_mysqlservers = json_decode($customer['allowed_mysqlserver'] ?? '[]', true);
|
||||
asort($allowed_mysqlservers, SORT_NUMERIC);
|
||||
if (count($allowed_mysqlservers) == 1 && $allowed_mysqlservers[0] != 0) {
|
||||
return (int) $allowed_mysqlservers[0];
|
||||
}
|
||||
return (int) array_shift($allowed_mysqlservers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
|
||||
public function listing()
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list php-configs");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list php-configs");
|
||||
|
||||
$with_subdomains = $this->getBoolParam('with_subdomains', true, false);
|
||||
$query_fields = [];
|
||||
@@ -392,7 +392,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
|
||||
$ins_data['id'] = Database::lastInsertId();
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] php setting with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
|
||||
|
||||
$result = $this->apiCall('PhpSettings.get', [
|
||||
'id' => $ins_data['id']
|
||||
@@ -629,7 +629,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
|
||||
Database::pexecute($upd_stmt, $upd_data, true, true);
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] php setting with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
|
||||
|
||||
$result = $this->apiCall('PhpSettings.get', [
|
||||
'id' => $id
|
||||
@@ -686,7 +686,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
|
||||
], true, true);
|
||||
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
|
||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] php setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
throw new Exception("Not allowed to execute given command.", 403);
|
||||
|
||||
@@ -62,7 +62,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
* optional, overwrites path value with an URL to generate a redirect, alternatively use the path
|
||||
* parameter also for URLs
|
||||
* @param int $openbasedir_path
|
||||
* optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot
|
||||
* optional, either 0 for domains-docroot [default], 1 for customers-homedir or 2 for parent-directory of domains-docroot
|
||||
* @param int $phpsettingid
|
||||
* optional, php-settings-id, if empty the $domain value is used
|
||||
* @param int $redirectcode
|
||||
@@ -104,7 +104,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
$aliasdomain = $this->getParam('alias', true, 0);
|
||||
$path = $this->getParam('path', true, '');
|
||||
$url = $this->getParam('url', true, '');
|
||||
$openbasedir_path = $this->getParam('openbasedir_path', true, 1);
|
||||
$openbasedir_path = $this->getParam('openbasedir_path', true, 0);
|
||||
$phpsettingid = $this->getParam('phpsettingid', true, 0);
|
||||
$redirectcode = $this->getParam('redirectcode', true, Settings::Get('customredirect.default'));
|
||||
$isemaildomain = $this->getParam('isemaildomain', true, 0);
|
||||
@@ -262,7 +262,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
// validate dns if lets encrypt is enabled to check whether we can use it at all
|
||||
if ($letsencrypt == '1' && Settings::Get('system.le_domain_dnscheck') == '1') {
|
||||
$our_ips = Domain::getIpsOfDomain($domain_check['id']);
|
||||
$domain_ips = PhpHelper::gethostbynamel6($completedomain);
|
||||
$domain_ips = PhpHelper::gethostbynamel6($completedomain, true, Settings::Get('system.le_domain_dnscheck_resolver'));
|
||||
if ($domain_ips == false || count(array_intersect($our_ips, $domain_ips)) <= 0) {
|
||||
Response::standardError('invaliddnsforletsencrypt', '', true);
|
||||
}
|
||||
@@ -486,7 +486,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
$result['ipsandports'] = $this->getIpsForDomain($result['id']);
|
||||
}
|
||||
$result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get subdomain '" . $result['domain'] . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get subdomain '" . $result['domain'] . "'");
|
||||
return $this->response($result);
|
||||
}
|
||||
$key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'");
|
||||
@@ -701,10 +701,12 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
$wwwserveralias = ($selectserveralias == '1') ? '1' : '0';
|
||||
|
||||
// if allowed, check for 'is email domain'-flag
|
||||
if ($result['parentdomainid'] != '0' && ($result['subcanemaildomain'] == '1' || $result['subcanemaildomain'] == '2') && $isemaildomain != $result['isemaildomain']) {
|
||||
$isemaildomain = intval($isemaildomain);
|
||||
} elseif ($result['parentdomainid'] != '0') {
|
||||
$isemaildomain = $result['subcanemaildomain'] == '3' ? 1 : 0;
|
||||
if ($isemaildomain != $result['isemaildomain']) {
|
||||
if ($result['parentdomainid'] != '0' && ($result['subcanemaildomain'] == '1' || $result['subcanemaildomain'] == '2')) {
|
||||
$isemaildomain = intval($isemaildomain);
|
||||
} elseif ($result['parentdomainid'] != '0') {
|
||||
$isemaildomain = $result['subcanemaildomain'] == '3' ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// check changes of openbasedir-path variable
|
||||
@@ -736,7 +738,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
// validate dns if lets encrypt is enabled to check whether we can use it at all
|
||||
if ($result['letsencrypt'] != $letsencrypt && $letsencrypt == '1' && Settings::Get('system.le_domain_dnscheck') == '1') {
|
||||
$our_ips = Domain::getIpsOfDomain($result['parentdomainid']);
|
||||
$domain_ips = PhpHelper::gethostbynamel6($result['domain']);
|
||||
$domain_ips = PhpHelper::gethostbynamel6($result['domain'], true, Settings::Get('system.le_domain_dnscheck_resolver'));
|
||||
if ($domain_ips == false || count(array_intersect($our_ips, $domain_ips)) <= 0) {
|
||||
Response::standardError('invaliddnsforletsencrypt', '', true);
|
||||
}
|
||||
@@ -854,7 +856,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
Cronjob::inserttask(TaskId::REBUILD_DNS);
|
||||
$idna_convert = new IdnaWrapper();
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited domain '" . $idna_convert->decode($result['domain']) . "'");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited domain '" . $idna_convert->decode($result['domain']) . "'");
|
||||
}
|
||||
$result = $this->apiCall('SubDomains.get', [
|
||||
'id' => $id
|
||||
@@ -1131,7 +1133,9 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
}
|
||||
}
|
||||
|
||||
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger());
|
||||
if ((int)$result['aliasdomain'] !== 0) {
|
||||
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger());
|
||||
}
|
||||
|
||||
// delete domain from table
|
||||
$stmt = Database::prepare("
|
||||
|
||||
@@ -92,7 +92,7 @@ class SysLog extends ApiCommand implements ResourceEntity
|
||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list log-entries");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list log-entries");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
|
||||
@@ -166,7 +166,7 @@ class Traffic extends ApiCommand implements ResourceEntity
|
||||
$row['mail'] *= 1024;
|
||||
$result[] = $row;
|
||||
}
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list traffic");
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list traffic");
|
||||
return $this->response([
|
||||
'count' => count($result),
|
||||
'list' => $result
|
||||
|
||||
@@ -37,7 +37,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
class CliCommand extends Command
|
||||
{
|
||||
|
||||
protected function validateRequirements(InputInterface $input, OutputInterface $output): int
|
||||
protected function validateRequirements(InputInterface $input, OutputInterface $output, bool $ignore_has_updates = false): int
|
||||
{
|
||||
if (!file_exists(Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {
|
||||
$output->writeln("<error>Could not find froxlor's userdata.inc.php file. You should use this script only with an installed froxlor system.</>");
|
||||
@@ -51,7 +51,7 @@ class CliCommand extends Command
|
||||
$output->writeln("<error>" . $e->getMessage() . "</>");
|
||||
return self::INVALID;
|
||||
}
|
||||
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
|
||||
if (!$ignore_has_updates && (Froxlor::hasUpdates() || Froxlor::hasDbUpdates())) {
|
||||
if ((int)Settings::Get('system.cron_allowautoupdate') == 1) {
|
||||
return $this->runUpdate($output);
|
||||
} else {
|
||||
@@ -122,7 +122,7 @@ class CliCommand extends Command
|
||||
include_once Froxlor::getInstallDir() . '/lib/tables.inc.php';
|
||||
define('_CRON_UPDATE', 1);
|
||||
ob_start([
|
||||
'this',
|
||||
$this,
|
||||
'cleanUpdateOutput'
|
||||
]);
|
||||
include_once Froxlor::getInstallDir() . '/install/updatesql.php';
|
||||
|
||||
178
lib/Froxlor/Cli/ConfigDiff.php
Normal file
178
lib/Froxlor/Cli/ConfigDiff.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Froxlor project.
|
||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can also view it online at
|
||||
* https://files.froxlor.org/misc/COPYING.txt
|
||||
*
|
||||
* @copyright the authors
|
||||
* @author Froxlor team <team@froxlor.org>
|
||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
||||
*/
|
||||
|
||||
namespace Froxlor\Cli;
|
||||
|
||||
use Froxlor\Config\ConfigParser;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Froxlor;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class ConfigDiff extends CliCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setName('froxlor:config-diff')
|
||||
->setDescription('Shows differences in config templates between OS versions')
|
||||
->addArgument('from', InputArgument::OPTIONAL, 'OS version to compare against')
|
||||
->addArgument('to', InputArgument::OPTIONAL, 'OS version to compare from')
|
||||
->addOption('list', 'l', InputOption::VALUE_NONE, 'List all possible OS versions')
|
||||
->addOption('diff-params', '', InputOption::VALUE_REQUIRED, 'Additional parameters for `diff`, e.g. --diff-params="--color=always"');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
require Froxlor::getInstallDir() . '/lib/functions.php';
|
||||
|
||||
$parsers = $versions = [];
|
||||
foreach (glob(Froxlor::getInstallDir() . '/lib/configfiles/*.xml') as $config) {
|
||||
$name = str_replace(".xml", "", strtolower(basename($config)));
|
||||
$parser = new ConfigParser($config);
|
||||
$versions[$name] = $parser->getCompleteDistroName();
|
||||
$parsers[$name] = $parser;
|
||||
}
|
||||
asort($versions);
|
||||
|
||||
if ($input->getOption('list') === true) {
|
||||
$output->writeln('The following OS version templates are available:');
|
||||
foreach ($versions as $k => $v) {
|
||||
$output->writeln(str_pad($k, 20) . $v);
|
||||
}
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
if (!$input->hasArgument('from') || !array_key_exists($input->getArgument('from'), $versions)) {
|
||||
$output->writeln('<error>Missing or invalid "from" argument.</error>');
|
||||
$output->writeln('Available versions: ' . implode(', ', array_keys($versions)));
|
||||
return self::INVALID;
|
||||
}
|
||||
|
||||
if (!$input->hasArgument('to') || !array_key_exists($input->getArgument('to'), $versions)) {
|
||||
$output->writeln('<error>Missing or invalid "to" argument.</error>');
|
||||
$output->writeln('Available versions: ' . implode(', ', array_keys($versions)));
|
||||
return self::INVALID;
|
||||
}
|
||||
|
||||
// Make sure diff is installed
|
||||
$check_diff_installed = FileDir::safe_exec('which diff');
|
||||
if (count($check_diff_installed) === 0) {
|
||||
$output->writeln('<error>Unable to find "diff" installation on your system.</error>');
|
||||
return self::INVALID;
|
||||
}
|
||||
|
||||
$parser_from = $parsers[$input->getArgument('from')];
|
||||
$parser_to = $parsers[$input->getArgument('to')];
|
||||
$tmp_from = tempnam(sys_get_temp_dir(), 'froxlor_config_diff_from');
|
||||
$tmp_to = tempnam(sys_get_temp_dir(), 'froxlor_config_diff_to');
|
||||
$files = [];
|
||||
$titles_by_key = [];
|
||||
|
||||
// Aggregate content for each config file
|
||||
foreach ([[$parser_from, 'from'], [$parser_to, 'to']] as $todo) {
|
||||
foreach ($todo[0]->getServices() as $service_type => $service) {
|
||||
foreach ($service->getDaemons() as $daemon_name => $daemon) {
|
||||
foreach ($daemon->getConfig() as $instruction) {
|
||||
if ($instruction['type'] !== 'file') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($instruction['subcommands'])) {
|
||||
foreach ($instruction['subcommands'] as $subinstruction) {
|
||||
if ($subinstruction['type'] !== 'file') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$content = $subinstruction['content'];
|
||||
}
|
||||
} else {
|
||||
$content = $instruction['content'];
|
||||
}
|
||||
|
||||
if (!isset($content)) {
|
||||
throw new \Exception("Cannot find content for {$instruction['name']}");
|
||||
}
|
||||
|
||||
$key = "{$service_type}_{$daemon_name}_{$instruction['name']}";
|
||||
$titles_by_key[$key] = "{$service->title} : {$daemon->title} : {$instruction['name']}";
|
||||
if (!isset($files[$key])) {
|
||||
$files[$key] = ['from' => '', 'to' => ''];
|
||||
}
|
||||
$files[$key][$todo[1]] = $this->filterContent($content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort($files);
|
||||
|
||||
$diff_params = '';
|
||||
if ($input->hasOption('diff-params') && trim($input->getOption('diff-params')) !== '') {
|
||||
$diff_params = trim($input->getOption('diff-params'));
|
||||
}
|
||||
|
||||
// Run diff on each file and output, if anything changed
|
||||
foreach ($files as $file_key => $content) {
|
||||
file_put_contents($tmp_from, $content['from']);
|
||||
file_put_contents($tmp_to, $content['to']);
|
||||
$diff_output = FileDir::safe_exec("{$check_diff_installed[0]} {$diff_params} {$tmp_from} {$tmp_to}");
|
||||
|
||||
if (count($diff_output) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$output->writeln('<info># ' . $titles_by_key[$file_key] . '</info>');
|
||||
$output->writeln(implode("\n", $diff_output) . "\n");
|
||||
unset($diff_output);
|
||||
}
|
||||
|
||||
// Remove tmp files again
|
||||
unlink($tmp_from);
|
||||
unlink($tmp_to);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function filterContent(string $content): string
|
||||
{
|
||||
$new_content = '';
|
||||
|
||||
foreach (explode("\n", $content) as $n) {
|
||||
$n = trim($n);
|
||||
if (!$n) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($n, '#')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$new_content .= $n . "\n";
|
||||
}
|
||||
|
||||
return $new_content;
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ final class InstallCommand extends Command
|
||||
$_SERVER['SERVER_NAME'] = $host[0] ?? '';
|
||||
$ips = [];
|
||||
exec('hostname -I', $ips);
|
||||
$ips = explode(" ", $ips[0]);
|
||||
$ips = explode(" ", $ips[0] ?? "");
|
||||
// ipv4 address?
|
||||
$_SERVER['SERVER_ADDR'] = filter_var($ips[0] ?? "", FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? ($ips[0] ?? '') : '';
|
||||
if (empty($_SERVER['SERVER_ADDR'])) {
|
||||
@@ -246,7 +246,10 @@ final class InstallCommand extends Command
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->io->error($e->getMessage());
|
||||
return $this->showStep($step, $extended, $decoded_input);
|
||||
if ($this->io->confirm('Retry?', empty($decoded_input))) {
|
||||
return $this->showStep($step, $extended, $decoded_input);
|
||||
}
|
||||
return self::FAILURE;
|
||||
}
|
||||
if ($step == 3) {
|
||||
// do actual install with data from $this->formfielddata
|
||||
@@ -297,7 +300,7 @@ final class InstallCommand extends Command
|
||||
$json_output = [];
|
||||
foreach ($fields['install']['sections'] as $section => $section_fields) {
|
||||
foreach ($section_fields['fields'] as $name => $field) {
|
||||
if ($name == 'system' || $name == 'manual_config') {
|
||||
if ($name == 'system' || $name == 'manual_config' || $name == 'target_servername') {
|
||||
continue;
|
||||
}
|
||||
if ($field['type'] == 'text' || $field['type'] == 'email') {
|
||||
|
||||
@@ -62,6 +62,11 @@ final class MasterCron extends CliCommand
|
||||
$result = self::SUCCESS;
|
||||
$result = $this->validateRequirements($input, $output);
|
||||
|
||||
if ($result != self::SUCCESS) {
|
||||
// requirements failed, exit
|
||||
return $result;
|
||||
}
|
||||
|
||||
$jobs = $input->getArgument('job');
|
||||
|
||||
// handle force option
|
||||
@@ -111,8 +116,8 @@ final class MasterCron extends CliCommand
|
||||
]);
|
||||
$this->cronLog->setCronDebugFlag(defined('CRON_DEBUG_FLAG'));
|
||||
|
||||
// check whether there are actual tasks to perform by 'tasks'-cron so
|
||||
// we dont regenerate files unnecessarily
|
||||
// check whether there are actual tasks to perform by 'tasks'-cron, so
|
||||
// we don't regenerate files unnecessarily
|
||||
$tasks_cnt_stmt = Database::query("SELECT COUNT(*) as jobcnt FROM `panel_tasks`");
|
||||
$tasks_cnt = $tasks_cnt_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
|
||||
165
lib/Froxlor/Cli/ValidateAcmeWebroot.php
Normal file
165
lib/Froxlor/Cli/ValidateAcmeWebroot.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Froxlor project.
|
||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can also view it online at
|
||||
* https://files.froxlor.org/misc/COPYING.txt
|
||||
*
|
||||
* @copyright the authors
|
||||
* @author Froxlor team <team@froxlor.org>
|
||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
||||
*/
|
||||
|
||||
namespace Froxlor\Cli;
|
||||
|
||||
use Froxlor\Cron\TaskId;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\System\Cronjob;
|
||||
use PDO;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
final class ValidateAcmeWebroot extends CliCommand
|
||||
{
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('froxlor:validate-acme-webroot');
|
||||
$this->setDescription('Validates the Le_Webroot value is correct for froxlor managed domains with Let\'s Encrypt certificate.');
|
||||
$this->addOption('yes-to-all', 'A', InputOption::VALUE_NONE, 'Do not ask for confirmation, update files if necessary');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$result = self::SUCCESS;
|
||||
|
||||
$result = $this->validateRequirements($input, $output, true);
|
||||
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
if ((int) Settings::Get('system.leenabled') == 0) {
|
||||
$io->info("Let's Encrypt not activated in froxlor settings.");
|
||||
$result = self::INVALID;
|
||||
}
|
||||
|
||||
if ($result == self::SUCCESS) {
|
||||
$yestoall = $input->getOption('yes-to-all') !== false;
|
||||
$helper = $this->getHelper('question');
|
||||
$count_changes = 0;
|
||||
// get all Let's Encrypt enabled domains
|
||||
$sel_stmt = Database::prepare("SELECT id, domain FROM panel_domains WHERE `letsencrypt` = '1' AND aliasdomain IS NULL ORDER BY id ASC");
|
||||
Database::pexecute($sel_stmt);
|
||||
$domains = $sel_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
// check for froxlor-vhost
|
||||
if (Settings::Get('system.le_froxlor_enabled') == '1') {
|
||||
$domains[] = [
|
||||
'id' => 0,
|
||||
'domain' => Settings::Get('system.hostname')
|
||||
];
|
||||
}
|
||||
$upd_stmt = Database::prepare("UPDATE domain_ssl_settings SET `validtodate`=NULL WHERE `domainid` = :did");
|
||||
$acmesh_dir = dirname(Settings::Get('system.acmeshpath'));
|
||||
$acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/");
|
||||
$recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), "/");
|
||||
|
||||
if ($acmesh_challenge_dir != $recommended) {
|
||||
$io->warning([
|
||||
"ACME challenge docroot from settings differs from the current installation directory.",
|
||||
"Settings: '" . $acmesh_challenge_dir . "'",
|
||||
"Default/recommended value: '" . $recommended . "'",
|
||||
]);
|
||||
$question = new ConfirmationQuestion('Fix ACME challenge docroot setting? [yes] ', true, '/^(y|j)/i');
|
||||
if ($yestoall || $helper->ask($input, $output, $question)) {
|
||||
Settings::Set('system.letsencryptchallengepath', $recommended);
|
||||
$former_value = $acmesh_challenge_dir;
|
||||
$acmesh_challenge_dir = $recommended;
|
||||
// need to update the corresponding acme-alias config-file
|
||||
$acme_alias_file = Settings::Get('system.letsencryptacmeconf');
|
||||
$sed_params = "s@".$former_value."@" . $acmesh_challenge_dir . "@";
|
||||
FileDir::safe_exec('sed -i -e "' . $sed_params . '" ' . escapeshellarg($acme_alias_file));
|
||||
$count_changes++;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($domains as $domain_arr) {
|
||||
$domain = $domain_arr['domain'];
|
||||
$acme_domain_conf = FileDir::makeCorrectFile($acmesh_dir . '/' . $domain . '/' . $domain . '.conf');
|
||||
if (file_exists($acme_domain_conf)) {
|
||||
$io->text("Getting info from " . $acme_domain_conf);
|
||||
$conf_content = file_get_contents($acme_domain_conf);
|
||||
} else {
|
||||
$acme_domain_conf = FileDir::makeCorrectFile($acmesh_dir . '/' . $domain . '_ecc/' . $domain . '.conf');
|
||||
if (file_exists($acme_domain_conf)) {
|
||||
$io->text("Getting info from " . $acme_domain_conf);
|
||||
$conf_content = file_get_contents($acme_domain_conf);
|
||||
} else {
|
||||
$io->info("No domain configuration file found in '" . $acmesh_dir . "'");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!empty($conf_content)) {
|
||||
$lines = explode("\n", $conf_content);
|
||||
foreach ($lines as $line) {
|
||||
$val_key = explode("=", $line);
|
||||
if ($val_key[0] == 'Le_Webroot') {
|
||||
$domain_webroot = trim(trim($val_key[1], "'"), '"');
|
||||
if ($domain_webroot != $acmesh_challenge_dir) {
|
||||
$io->warning("Domain '" . $domain . "' has old/wrong Le_Webroot setting: '" . $domain_webroot . ' <> ' . $acmesh_challenge_dir . "'");
|
||||
$question = new ConfirmationQuestion('Fix Le_Webroot? [yes] ', true, '/^(y|j)/i');
|
||||
if ($yestoall || $helper->ask($input, $output, $question)) {
|
||||
$sed_params = "s@Le_Webroot=.*@Le_Webroot='" . $acmesh_challenge_dir . "'@";
|
||||
FileDir::safe_exec('sed -i -e "' . $sed_params . '" ' . escapeshellarg($acme_domain_conf));
|
||||
Database::pexecute($upd_stmt, ['did' => $domain_arr['id']]);
|
||||
$io->success("Correction of Le_Webroot successful");
|
||||
$count_changes++;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
$io->info("Domain '" . $domain . "' Le_Webroot value is correct");
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($count_changes > 0) {
|
||||
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
|
||||
$io->info("Changes detected but froxlor has been updated. Inserting task to rebuild vhosts after update.");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
} else {
|
||||
$question = new ConfirmationQuestion('Changes detected. Force cronjob to refresh certificates? [yes] ', true, '/^(y|j)/i');
|
||||
if ($yestoall || $helper->ask($input, $output, $question)) {
|
||||
passthru(FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -f -d');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$io->success("No changes necessary.");
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -148,7 +148,7 @@ class ConfigDisplay
|
||||
if ($lasttype != '' && $lasttype != $_action['type']) {
|
||||
$commands = trim($commands);
|
||||
$numbrows = count(explode("\n", $commands));
|
||||
$configpage .= UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [
|
||||
$configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [
|
||||
'commands' => $commands,
|
||||
'numbrows' => $numbrows
|
||||
]);
|
||||
@@ -182,7 +182,7 @@ class ConfigDisplay
|
||||
$commands = trim($commands_pre);
|
||||
if ($commands != "") {
|
||||
$numbrows = count(explode("\n", $commands));
|
||||
$commands_pre = UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [
|
||||
$commands_pre = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [
|
||||
'commands' => $commands,
|
||||
'numbrows' => $numbrows
|
||||
]);
|
||||
@@ -190,12 +190,12 @@ class ConfigDisplay
|
||||
$commands = trim($commands_post);
|
||||
if ($commands != "") {
|
||||
$numbrows = count(explode("\n", $commands));
|
||||
$commands_post = UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [
|
||||
$commands_post = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [
|
||||
'commands' => $commands,
|
||||
'numbrows' => $numbrows
|
||||
]);
|
||||
}
|
||||
$configpage .= UI::twig()->render(self::$theme . '/settings/conf/fileblock.html.twig', [
|
||||
$configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/fileblock.html.twig', self::$theme), [
|
||||
'realname' => $realname,
|
||||
'commands_pre' => $commands_pre,
|
||||
'commands_file' => $commands_file,
|
||||
@@ -210,7 +210,7 @@ class ConfigDisplay
|
||||
$commands = trim($commands);
|
||||
if ($commands != '') {
|
||||
$numbrows = count(explode("\n", $commands));
|
||||
$configpage .= UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [
|
||||
$configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [
|
||||
'commands' => $commands,
|
||||
'numbrows' => $numbrows
|
||||
]);
|
||||
@@ -233,7 +233,7 @@ class ConfigDisplay
|
||||
$file_content = htmlspecialchars($file_content);
|
||||
$numbrows = count(explode("\n", $file_content));
|
||||
//eval("\$files=\"" . \Froxlor\UI\Template::getTemplate("configfiles/configfiles_file") . "\";");
|
||||
$files = UI::twig()->render(self::$theme . '/settings/conf/file.html.twig', [
|
||||
$files = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/file.html.twig', self::$theme), [
|
||||
'distro_editor' => self::$editor,
|
||||
'realname' => $realname,
|
||||
'numbrows' => $numbrows,
|
||||
|
||||
@@ -613,7 +613,10 @@ class Apache extends HttpConfigBase
|
||||
// Apply header
|
||||
$this->virtualhosts_data[$vhosts_filename] = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n";
|
||||
|
||||
if ($domain['deactivated'] != '1' || Settings::Get('system.deactivateddocroot') != '') {
|
||||
$ddr = Settings::Get('system.deactivateddocroot');
|
||||
if (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && empty($ddr)) {
|
||||
$this->virtualhosts_data[$vhosts_filename] .= '# Customer/domain deactivated and a docroot for deactivated users hasn\'t been set.' . "\n";
|
||||
} else {
|
||||
// Create vhost without ssl
|
||||
$this->virtualhosts_data[$vhosts_filename] .= $this->getVhostContent($domain, false);
|
||||
|
||||
@@ -623,8 +626,6 @@ class Apache extends HttpConfigBase
|
||||
$this->virtualhosts_data[$vhosts_filename_ssl] = '# Domain ID: ' . $domain['id'] . ' (SSL) - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n";
|
||||
$this->virtualhosts_data[$vhosts_filename_ssl] .= $this->getVhostContent($domain, true);
|
||||
}
|
||||
} else {
|
||||
$this->virtualhosts_data[$vhosts_filename] .= '# Customer deactivated and a docroot for deactivated users hasn\'t been set.' . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -840,29 +841,34 @@ class Apache extends HttpConfigBase
|
||||
$domain['documentroot'] = trim($domain['documentroot']);
|
||||
|
||||
if (preg_match('/^https?\:\/\//', $domain['documentroot'])) {
|
||||
$corrected_docroot = $domain['documentroot'];
|
||||
$possible_deactivated_webroot = $this->getWebroot($domain);
|
||||
if ($this->deactivated == false) {
|
||||
$corrected_docroot = $domain['documentroot'];
|
||||
|
||||
// Get domain's redirect code
|
||||
$code = Domain::getDomainRedirectCode($domain['id']);
|
||||
$modrew_red = '';
|
||||
if ($code != '') {
|
||||
$modrew_red = ' [R=' . $code . ';L,NE]';
|
||||
}
|
||||
// Get domain's redirect code
|
||||
$code = Domain::getDomainRedirectCode($domain['id']);
|
||||
$modrew_red = '';
|
||||
if ($code != '') {
|
||||
$modrew_red = ' [R=' . $code . ';L,NE]';
|
||||
}
|
||||
|
||||
// redirect everything, not only root-directory, #541
|
||||
$vhost_content .= ' <IfModule mod_rewrite.c>' . "\n";
|
||||
$vhost_content .= ' RewriteEngine On' . "\n";
|
||||
if (!$ssl_vhost) {
|
||||
$vhost_content .= ' RewriteCond %{HTTPS} off' . "\n";
|
||||
// redirect everything, not only root-directory, #541
|
||||
$vhost_content .= ' <IfModule mod_rewrite.c>' . "\n";
|
||||
$vhost_content .= ' RewriteEngine On' . "\n";
|
||||
if (!$ssl_vhost) {
|
||||
$vhost_content .= ' RewriteCond %{HTTPS} off' . "\n";
|
||||
}
|
||||
if ($domain['letsencrypt'] == '1') {
|
||||
$vhost_content .= ' RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge' . "\n";
|
||||
}
|
||||
$vhost_content .= ' RewriteRule ^/(.*) ' . $corrected_docroot . '$1' . $modrew_red . "\n";
|
||||
$vhost_content .= ' </IfModule>' . "\n";
|
||||
$vhost_content .= ' <IfModule !mod_rewrite.c>' . "\n";
|
||||
$vhost_content .= ' Redirect ' . $code . ' / ' . $domain['documentroot_norewrite'] . "\n";
|
||||
$vhost_content .= ' </IfModule>' . "\n";
|
||||
} elseif (Settings::Get('system.deactivateddocroot') != '') {
|
||||
$vhost_content .= $possible_deactivated_webroot;
|
||||
}
|
||||
if ($domain['letsencrypt'] == '1') {
|
||||
$vhost_content .= ' RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge' . "\n";
|
||||
}
|
||||
$vhost_content .= ' RewriteRule ^/(.*) ' . $corrected_docroot . '$1' . $modrew_red . "\n";
|
||||
$vhost_content .= ' </IfModule>' . "\n";
|
||||
$vhost_content .= ' <IfModule !mod_rewrite.c>' . "\n";
|
||||
$vhost_content .= ' Redirect ' . $code . ' / ' . $domain['documentroot_norewrite'] . "\n";
|
||||
$vhost_content .= ' </IfModule>' . "\n";
|
||||
} else {
|
||||
FileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true);
|
||||
$vhost_content .= $this->getWebroot($domain);
|
||||
@@ -952,8 +958,8 @@ class Apache extends HttpConfigBase
|
||||
$domain['customerroot'] = FileDir::makeCorrectDir($domain['customerroot']);
|
||||
$domain['documentroot'] = FileDir::makeCorrectDir($domain['documentroot']);
|
||||
|
||||
if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') {
|
||||
$webroot_text .= ' # Using docroot for deactivated users...' . "\n";
|
||||
if (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && Settings::Get('system.deactivateddocroot') != '') {
|
||||
$webroot_text .= ' # Using docroot for deactivated users/domains...' . "\n";
|
||||
$webroot_text .= ' DocumentRoot "' . rtrim(FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')), "/") . "\"\n";
|
||||
$webroot_text .= ' <Directory "' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . '">' . "\n";
|
||||
// >=apache-2.4 enabled?
|
||||
@@ -1034,6 +1040,10 @@ class Apache extends HttpConfigBase
|
||||
|
||||
$statTool = Settings::Get('system.traffictool');
|
||||
$statDomain = "";
|
||||
if ($statTool == 'awstats') {
|
||||
// awstats generates for each domain regardless of speciallogfile
|
||||
$statDomain = "/" . $domain['domain'];
|
||||
}
|
||||
if ($domain['speciallogfile'] == '1') {
|
||||
$statDomain = "/" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']);
|
||||
}
|
||||
|
||||
@@ -126,6 +126,9 @@ class ApacheFcgi extends Apache
|
||||
|
||||
// mod_proxy stuff for apache-2.4
|
||||
if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') {
|
||||
|
||||
$php_options_text .= ' <Directory "' . FileDir::makeCorrectDir($domain['documentroot']) . '">' . "\n";
|
||||
|
||||
$filesmatch = $phpconfig['fpm_settings']['limit_extensions'];
|
||||
$extensions = explode(" ", $filesmatch);
|
||||
$filesmatch = "";
|
||||
@@ -141,23 +144,19 @@ class ApacheFcgi extends Apache
|
||||
$php_options_text .= ' </FilesMatch>' . "\n";
|
||||
|
||||
$mypath_dir = new Directory($domain['documentroot']);
|
||||
|
||||
// only create the require all granted if there is not active directory-protection
|
||||
// only create the "require all granted" directive if there is no active directory-protection
|
||||
// for this path, as this would be the first require and therefore grant all access
|
||||
if ($mypath_dir->isUserProtected() == false) {
|
||||
$php_options_text .= ' <Directory "' . FileDir::makeCorrectDir($domain['documentroot']) . '">' . "\n";
|
||||
if ($phpconfig['pass_authorizationheader'] == '1') {
|
||||
$php_options_text .= ' CGIPassAuth On' . "\n";
|
||||
}
|
||||
$php_options_text .= ' Require all granted' . "\n";
|
||||
$php_options_text .= ' AllowOverride All' . "\n";
|
||||
$php_options_text .= ' </Directory>' . "\n";
|
||||
} elseif ($phpconfig['pass_authorizationheader'] == '1') {
|
||||
// allow Pass of Authorization header
|
||||
$php_options_text .= ' <Directory "' . FileDir::makeCorrectDir($domain['documentroot']) . '">' . "\n";
|
||||
$php_options_text .= ' CGIPassAuth On' . "\n";
|
||||
$php_options_text .= ' </Directory>' . "\n";
|
||||
}
|
||||
$php_options_text .= ' </Directory>' . "\n";
|
||||
} else {
|
||||
$addheader = "";
|
||||
if ($phpconfig['pass_authorizationheader'] == '1') {
|
||||
@@ -196,6 +195,9 @@ class ApacheFcgi extends Apache
|
||||
}
|
||||
} else {
|
||||
$php_options_text .= ' FcgidIdleTimeout ' . Settings::Get('system.mod_fcgid_idle_timeout') . "\n";
|
||||
if ($phpconfig['pass_authorizationheader'] == '1') {
|
||||
$php_options_text .= ' FcgidPassHeader Authorization' . "\n";
|
||||
}
|
||||
if ((int)Settings::Get('system.mod_fcgid_wrapper') == 0) {
|
||||
$php_options_text .= ' SuexecUserGroup "' . $domain['loginname'] . '" "' . $domain['loginname'] . '"' . "\n";
|
||||
$php_options_text .= ' ScriptAlias /php/ ' . $php->getInterface()->getConfigDir() . "\n";
|
||||
|
||||
@@ -179,7 +179,7 @@ class HttpConfigBase
|
||||
$froxlor_ssl_settings_stmt = Database::prepare("
|
||||
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
|
||||
WHERE `domainid` = '0' AND
|
||||
(`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `expirationdate` IS NULL)
|
||||
(`validtodate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `validtodate` IS NULL)
|
||||
");
|
||||
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
|
||||
if ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) {
|
||||
|
||||
@@ -46,7 +46,9 @@ class AcmeSh extends FroxlorCron
|
||||
'letsencrypt_test' => "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||
'buypass' => "https://api.buypass.com/acme/directory",
|
||||
'buypass_test' => "https://api.test4.buypass.no/acme/directory",
|
||||
'zerossl' => "https://acme.zerossl.com/v2/DV90"
|
||||
'zerossl' => "https://acme.zerossl.com/v2/DV90",
|
||||
'google' => "https://dv.acme-v02.api.pki.goog/directory",
|
||||
'google_test' => "https://dv.acme-v02.test-api.pki.goog/directory",
|
||||
];
|
||||
public static $no_inserttask = false;
|
||||
private static $apiserver = "";
|
||||
@@ -112,7 +114,9 @@ class AcmeSh extends FroxlorCron
|
||||
`ssl_cert_chainfile` = :chain,
|
||||
`ssl_csr_file` = :csr,
|
||||
`ssl_fullchain_file` = :fullchain,
|
||||
`expirationdate` = :expirationdate
|
||||
`validfromdate` = :validfromdate,
|
||||
`validtodate` = :validtodate,
|
||||
`issuer` = :issuer
|
||||
");
|
||||
|
||||
// prepare domain update sql
|
||||
@@ -134,7 +138,9 @@ class AcmeSh extends FroxlorCron
|
||||
'lepublickey' => Settings::Get('system.lepublickey'),
|
||||
'leregistered' => Settings::Get('system.leregistered'),
|
||||
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
|
||||
'expirationdate' => null,
|
||||
'validfromdate' => null,
|
||||
'validtodate' => null,
|
||||
'issuer' => "",
|
||||
'ssl_cert_file' => null,
|
||||
'ssl_key_file' => null,
|
||||
'ssl_ca_file' => null,
|
||||
@@ -169,7 +175,9 @@ class AcmeSh extends FroxlorCron
|
||||
'lepublickey' => Settings::Get('system.lepublickey'),
|
||||
'leregistered' => Settings::Get('system.leregistered'),
|
||||
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
|
||||
'expirationdate' => is_array($renew_froxlor) ? $renew_froxlor['expirationdate'] : date('Y-m-d H:i:s', 0),
|
||||
'validfromdate' => is_array($renew_froxlor) ? $renew_froxlor['validfromdate'] : date('Y-m-d H:i:s', 0),
|
||||
'validtodate' => is_array($renew_froxlor) ? $renew_froxlor['validtodate'] : date('Y-m-d H:i:s', 0),
|
||||
'issuer' => is_array($renew_froxlor) ? $renew_froxlor['issuer'] : "",
|
||||
'ssl_cert_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_cert_file'] : null,
|
||||
'ssl_key_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_key_file'] : null,
|
||||
'ssl_ca_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_ca_file'] : null,
|
||||
@@ -185,7 +193,7 @@ class AcmeSh extends FroxlorCron
|
||||
'loginname' => $domain['loginname'],
|
||||
'adminsession' => 0
|
||||
]);
|
||||
if (defined('CRON_IS_FORCED') || self::checkFsFilesAreNewer($domain['domain'], $domain['expirationdate'])) {
|
||||
if (defined('CRON_IS_FORCED') || self::checkFsFilesAreNewer($domain['domain'], $domain['validtodate'])) {
|
||||
self::certToDb($domain, $cronlog, []);
|
||||
$changedetected = 1;
|
||||
}
|
||||
@@ -219,16 +227,21 @@ class AcmeSh extends FroxlorCron
|
||||
");
|
||||
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
|
||||
// also check for possible existing certificate
|
||||
if (!$froxlor_ssl && !self::checkFsFilesAreNewer(Settings::Get('system.hostname'), date('Y-m-d H:i:s'))) {
|
||||
if (($froxlor_ssl && empty($froxlor_ssl['validtodate']))
|
||||
|| (!$froxlor_ssl && !self::checkFsFilesAreNewer(Settings::Get('system.hostname'), date('Y-m-d H:i:s')))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function checkFsFilesAreNewer($domain, $cert_date = 0)
|
||||
private static function checkFsFilesAreNewer($domain, $cert_date = 0): bool
|
||||
{
|
||||
$certificate_folder = self::getWorkingDirFromEnv(strtolower($domain));
|
||||
$certificate_folder = self::getCertificateFolder(strtolower($domain));
|
||||
if (empty($certificate_folder)) {
|
||||
return false;
|
||||
}
|
||||
$ssl_file = FileDir::makeCorrectFile($certificate_folder . '/' . strtolower($domain) . '.cer');
|
||||
|
||||
if (is_dir($certificate_folder) && file_exists($ssl_file) && is_readable($ssl_file)) {
|
||||
@@ -240,9 +253,13 @@ class AcmeSh extends FroxlorCron
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getWorkingDirFromEnv($domain = "", $forced_noecc = false)
|
||||
public static function getWorkingDirFromEnv($domain = "", $forced_ecc = false): string
|
||||
{
|
||||
if (Settings::Get('system.leecc') > 0 && !$forced_noecc) {
|
||||
// first try without _ecc either if it's enabled currently or not as
|
||||
// it might have been at some point so there is a chance we have certificates
|
||||
// with and without _ecc - the method getCertificateFolder() will check both
|
||||
// possibilities
|
||||
if ($forced_ecc) {
|
||||
$domain .= "_ecc";
|
||||
}
|
||||
$env_file = FileDir::makeCorrectFile(dirname(self::getAcmeSh()) . '/acme.sh.env');
|
||||
@@ -252,7 +269,7 @@ class AcmeSh extends FroxlorCron
|
||||
cut -d'"' -f2
|
||||
EOC;
|
||||
exec('grep "LE_WORKING_DIR" ' . escapeshellarg($env_file) . ' | ' . $cut, $output);
|
||||
if (is_array($output) && !empty($output) && isset($output[0]) && !empty($output[0])) {
|
||||
if (is_array($output) && !empty($output) && !empty($output[0])) {
|
||||
return FileDir::makeCorrectDir($output[0] . "/" . $domain);
|
||||
}
|
||||
}
|
||||
@@ -277,7 +294,9 @@ EOC;
|
||||
SELECT
|
||||
domssl.`id`,
|
||||
domssl.`domainid`,
|
||||
domssl.`expirationdate`,
|
||||
domssl.`validfromdate`,
|
||||
domssl.`validtodate`,
|
||||
domssl.`issuer`,
|
||||
domssl.`ssl_cert_file`,
|
||||
domssl.`ssl_key_file`,
|
||||
domssl.`ssl_ca_file`,
|
||||
@@ -304,7 +323,7 @@ EOC;
|
||||
AND dom.`letsencrypt` = 1
|
||||
AND dom.`aliasdomain` IS NULL
|
||||
AND dom.`iswildcarddomain` = 0
|
||||
AND domssl.`expirationdate` IS NULL
|
||||
AND domssl.`validtodate` IS NULL
|
||||
");
|
||||
$customer_ssl = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if ($customer_ssl) {
|
||||
@@ -328,7 +347,7 @@ EOC;
|
||||
");
|
||||
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
|
||||
// also check for possible existing certificate
|
||||
if ($froxlor_ssl && self::checkFsFilesAreNewer(Settings::Get('system.hostname'), $froxlor_ssl['expirationdate'])) {
|
||||
if ($froxlor_ssl && self::checkFsFilesAreNewer(Settings::Get('system.hostname'), $froxlor_ssl['validtodate'])) {
|
||||
return $froxlor_ssl;
|
||||
}
|
||||
}
|
||||
@@ -344,7 +363,9 @@ EOC;
|
||||
SELECT
|
||||
domssl.`id`,
|
||||
domssl.`domainid`,
|
||||
domssl.`expirationdate`,
|
||||
domssl.`validfromdate`,
|
||||
domssl.`validtodate`,
|
||||
domssl.`issuer`,
|
||||
domssl.`ssl_cert_file`,
|
||||
domssl.`ssl_key_file`,
|
||||
dom.`domain`,
|
||||
@@ -368,7 +389,7 @@ EOC;
|
||||
if ($renew_certs) {
|
||||
if ($check) {
|
||||
foreach ($renew_certs as $cert) {
|
||||
if (self::checkFsFilesAreNewer($cert['domain'], $cert['expirationdate'])) {
|
||||
if (self::checkFsFilesAreNewer($cert['domain'], $cert['validtodate'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -451,7 +472,7 @@ EOC;
|
||||
// Only issue let's encrypt certificate if no broken ssl_redirect is enabled
|
||||
if ($certrow['ssl_redirect'] != 2) {
|
||||
$do_force = false;
|
||||
if (!empty($certrow['ssl_cert_file']) && empty($certrow['expirationdate'])) {
|
||||
if (!empty($certrow['ssl_cert_file']) && empty($certrow['validtodate'])) {
|
||||
// domain changed (SAN or similar)
|
||||
$do_force = true;
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']);
|
||||
@@ -519,7 +540,7 @@ EOC;
|
||||
foreach ($loop_domains as $idx => $domain) {
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Validating DNS of " . $domain);
|
||||
// ips according to NS
|
||||
$domain_ips = PhpHelper::gethostbynamel6($domain);
|
||||
$domain_ips = PhpHelper::gethostbynamel6($domain, true, Settings::Get('system.le_domain_dnscheck_resolver'));
|
||||
if ($domain_ips == false || count(array_intersect($our_ips, $domain_ips)) <= 0) {
|
||||
// no common ips...
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Skipping Let's Encrypt generation for " . $domain . " due to no system known IP address via DNS check");
|
||||
@@ -555,7 +576,7 @@ EOC;
|
||||
if (Settings::Get('system.letsencryptreuseold') != '1') {
|
||||
$acmesh_cmd .= " --always-force-new-domain-key";
|
||||
}
|
||||
if (Settings::Get('system.letsencryptca') == 'letsencrypt_test') {
|
||||
if (substr(Settings::Get('system.letsencryptca'), -5) == '_test') {
|
||||
$acmesh_cmd .= " --staging";
|
||||
}
|
||||
if ($force) {
|
||||
@@ -592,7 +613,9 @@ EOC;
|
||||
'chain' => $return['chain'],
|
||||
'csr' => $return['csr'],
|
||||
'fullchain' => $return['fullchain'],
|
||||
'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t'])
|
||||
'validfromdate' => date('Y-m-d H:i:s', $newcert['validFrom_time_t']),
|
||||
'validtodate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']),
|
||||
'issuer' => $newcert['issuer']['O'] ?? ""
|
||||
]);
|
||||
|
||||
if ($certrow['ssl_redirect'] == 3) {
|
||||
@@ -619,35 +642,21 @@ EOC;
|
||||
*/
|
||||
private static function readCertificateToVar($domain, &$return, &$cronlog)
|
||||
{
|
||||
$certificate_folder = self::getWorkingDirFromEnv($domain);
|
||||
$certificate_folder_noecc = null;
|
||||
if (Settings::Get('system.leecc') > 0) {
|
||||
$certificate_folder_noecc = self::getWorkingDirFromEnv($domain, true);
|
||||
}
|
||||
$certificate_folder = FileDir::makeCorrectDir($certificate_folder);
|
||||
$certificate_folder = self::getCertificateFolder($domain);
|
||||
|
||||
if (is_dir($certificate_folder) || is_dir($certificate_folder_noecc)) {
|
||||
foreach (
|
||||
[
|
||||
'crt' => $domain . '.cer',
|
||||
'key' => $domain . '.key',
|
||||
'chain' => 'ca.cer',
|
||||
'fullchain' => 'fullchain.cer',
|
||||
'csr' => $domain . '.csr'
|
||||
] as $index => $sslfile
|
||||
) {
|
||||
if (!empty($certificate_folder)) {
|
||||
$certificate_files = [
|
||||
'crt' => $domain . '.cer',
|
||||
'key' => $domain . '.key',
|
||||
'chain' => 'ca.cer',
|
||||
'fullchain' => 'fullchain.cer',
|
||||
'csr' => $domain . '.csr'
|
||||
];
|
||||
foreach ($certificate_files as $index => $sslfile) {
|
||||
$ssl_file = FileDir::makeCorrectFile($certificate_folder . '/' . $sslfile);
|
||||
if (file_exists($ssl_file)) {
|
||||
$return[$index] = file_get_contents($ssl_file);
|
||||
} else {
|
||||
if (!empty($certificate_folder_noecc)) {
|
||||
$ssl_file_fb = FileDir::makeCorrectFile($certificate_folder_noecc . '/' . $sslfile);
|
||||
if (file_exists($ssl_file_fb)) {
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "ECC certificates activated but found only non-ecc file");
|
||||
$return[$index] = file_get_contents($ssl_file_fb);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find file '" . $sslfile . "' in '" . $certificate_folder . "'");
|
||||
$return[$index] = null;
|
||||
}
|
||||
@@ -656,4 +665,18 @@ EOC;
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find certificate-folder '" . $certificate_folder . "'");
|
||||
}
|
||||
}
|
||||
|
||||
private static function getCertificateFolder(string $domain): string
|
||||
{
|
||||
$certificate_folder = self::getWorkingDirFromEnv(strtolower($domain));
|
||||
if (file_exists($certificate_folder)) {
|
||||
return $certificate_folder;
|
||||
}
|
||||
$certificate_folder_ecc = self::getWorkingDirFromEnv($domain, true);
|
||||
if (file_exists($certificate_folder_ecc)) {
|
||||
return $certificate_folder_ecc;
|
||||
}
|
||||
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find certificate-folder for domain '" . $domain . "'");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,15 +414,20 @@ class Lighttpd extends HttpConfigBase
|
||||
$domain['documentroot'] = trim($domain['documentroot']);
|
||||
|
||||
if (preg_match('/^https?\:\/\//', $domain['documentroot'])) {
|
||||
$uri = $domain['documentroot'];
|
||||
$possible_deactivated_webroot = $this->getWebroot($domain);
|
||||
if ($this->deactivated == false) {
|
||||
$uri = $domain['documentroot'];
|
||||
|
||||
// Get domain's redirect code
|
||||
$code = Domain::getDomainRedirectCode($domain['id']);
|
||||
// Get domain's redirect code
|
||||
$code = Domain::getDomainRedirectCode($domain['id']);
|
||||
|
||||
$vhost_content .= ' url.redirect-code = ' . $code . "\n";
|
||||
$vhost_content .= ' url.redirect = (' . "\n";
|
||||
$vhost_content .= ' "^/(.*)$" => "' . $uri . '$1"' . "\n";
|
||||
$vhost_content .= ' )' . "\n";
|
||||
$vhost_content .= ' url.redirect-code = ' . $code . "\n";
|
||||
$vhost_content .= ' url.redirect = (' . "\n";
|
||||
$vhost_content .= ' "^/(.*)$" => "' . $uri . '$1"' . "\n";
|
||||
$vhost_content .= ' )' . "\n";
|
||||
} elseif (Settings::Get('system.deactivateddocroot') != '') {
|
||||
$vhost_content .= $possible_deactivated_webroot;
|
||||
}
|
||||
} else {
|
||||
FileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true);
|
||||
|
||||
@@ -562,12 +567,12 @@ class Lighttpd extends HttpConfigBase
|
||||
return $servernames_text;
|
||||
}
|
||||
|
||||
protected function getWebroot($domain, $ssl)
|
||||
protected function getWebroot($domain, bool $ssl = false)
|
||||
{
|
||||
$webroot_text = '';
|
||||
|
||||
if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') {
|
||||
$webroot_text .= ' # Using docroot for deactivated users...' . "\n";
|
||||
if (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && Settings::Get('system.deactivateddocroot') != '') {
|
||||
$webroot_text .= ' # Using docroot for deactivated users/domains...' . "\n";
|
||||
$webroot_text .= ' server.document-root = "' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n";
|
||||
$this->deactivated = true;
|
||||
} else {
|
||||
@@ -719,6 +724,10 @@ class Lighttpd extends HttpConfigBase
|
||||
|
||||
$statTool = Settings::Get('system.traffictool');
|
||||
$statDomain = "";
|
||||
if ($statTool == 'awstats') {
|
||||
// awstats generates for each domain regardless of speciallogfile
|
||||
$statDomain = "/" . $domain['domain'];
|
||||
}
|
||||
if ($domain['speciallogfile'] == '1') {
|
||||
$statDomain = "/" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']);
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ class Nginx extends HttpConfigBase
|
||||
$this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n";
|
||||
|
||||
// protect bin/
|
||||
$this->nginx_data[$vhost_filename] .= "\t" . 'location ~ ' . rtrim($relpath, "/") . '/(bin|cache|logs|tests|vendor) {' . "\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t" . 'location ~ ^' . rtrim($relpath, "/") . '/(bin|cache|logs|tests|vendor) {' . "\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t" . ' deny all;' . "\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n";
|
||||
}
|
||||
@@ -493,10 +493,10 @@ class Nginx extends HttpConfigBase
|
||||
return '';
|
||||
}
|
||||
|
||||
// check whether the customer is deactivated and NO docroot for deactivated users has been set#
|
||||
// check whether the customer/domain is deactivated and NO docroot for deactivated users has been set#
|
||||
$ddr = Settings::Get('system.deactivateddocroot');
|
||||
if ($domain['deactivated'] == '1' && empty($ddr)) {
|
||||
return '# Customer deactivated and a docroot for deactivated users hasn\'t been set.' . "\n";
|
||||
if (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && empty($ddr)) {
|
||||
return '# Customer deactivated and a docroot for deactivated users/domains hasn\'t been set.' . "\n";
|
||||
}
|
||||
|
||||
$vhost_content = '';
|
||||
@@ -596,22 +596,27 @@ class Nginx extends HttpConfigBase
|
||||
|
||||
// if the documentroot is an URL we just redirect
|
||||
if (preg_match('/^https?\:\/\//', $domain['documentroot'])) {
|
||||
$uri = $domain['documentroot'];
|
||||
if (substr($uri, -1) == '/') {
|
||||
$uri = substr($uri, 0, -1);
|
||||
$possible_deactivated_webroot = $this->getWebroot($domain);
|
||||
if ($this->deactivated == false) {
|
||||
$uri = $domain['documentroot'];
|
||||
if (substr($uri, -1) == '/') {
|
||||
$uri = substr($uri, 0, -1);
|
||||
}
|
||||
|
||||
// Get domain's redirect code
|
||||
$code = Domain::getDomainRedirectCode($domain['id']);
|
||||
|
||||
$vhost_content .= "\t" . 'location / {' . "\n";
|
||||
$vhost_content .= "\t\t" . 'return ' . $code . ' ' . $uri . '$request_uri;' . "\n";
|
||||
$vhost_content .= "\t" . '}' . "\n";
|
||||
} elseif (Settings::Get('system.deactivateddocroot') != '') {
|
||||
$vhost_content .= $possible_deactivated_webroot;
|
||||
}
|
||||
|
||||
// Get domain's redirect code
|
||||
$code = Domain::getDomainRedirectCode($domain['id']);
|
||||
|
||||
$vhost_content .= "\t" . 'location / {' . "\n";
|
||||
$vhost_content .= "\t\t" . 'return ' . $code . ' ' . $uri . '$request_uri;' . "\n";
|
||||
$vhost_content .= "\t" . '}' . "\n";
|
||||
} else {
|
||||
FileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true);
|
||||
|
||||
$vhost_content .= $this->getLogFiles($domain);
|
||||
$vhost_content .= $this->getWebroot($domain, $ssl_vhost);
|
||||
$vhost_content .= $this->getWebroot($domain);
|
||||
|
||||
if ($this->deactivated == false) {
|
||||
$vhost_content = $this->mergeVhostCustom($vhost_content, $this->createPathOptions($domain)) . "\n";
|
||||
@@ -770,12 +775,12 @@ class Nginx extends HttpConfigBase
|
||||
return $logfiles_text;
|
||||
}
|
||||
|
||||
protected function getWebroot($domain, $ssl)
|
||||
protected function getWebroot($domain)
|
||||
{
|
||||
$webroot_text = '';
|
||||
|
||||
if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') {
|
||||
$webroot_text .= "\t" . '# Using docroot for deactivated users...' . "\n";
|
||||
if (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1' ) && Settings::Get('system.deactivateddocroot') != '') {
|
||||
$webroot_text .= "\t" . '# Using docroot for deactivated users/domains...' . "\n";
|
||||
$webroot_text .= "\t" . 'root ' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . ';' . "\n";
|
||||
$this->deactivated = true;
|
||||
} else {
|
||||
@@ -878,13 +883,7 @@ class Nginx extends HttpConfigBase
|
||||
// remove comments
|
||||
$vhost = implode("\n", preg_replace('/^(\s+)?#(.*)$/', '', explode("\n", $vhost)));
|
||||
// Break blocks into lines
|
||||
$vhost = str_replace([
|
||||
"{",
|
||||
"}"
|
||||
], [
|
||||
" {\n",
|
||||
"\n}"
|
||||
], $vhost);
|
||||
$vhost = preg_replace("/^(\s+)location(.+)\{(.+)\}$/misU", "location $2 {\n $3 \n}", $vhost);
|
||||
// Break into array items
|
||||
$vhost = explode("\n", preg_replace('/[ \t]+/', ' ', trim(preg_replace('/\t+/', '', $vhost))));
|
||||
// Remove empty lines
|
||||
@@ -1035,6 +1034,11 @@ class Nginx extends HttpConfigBase
|
||||
$path_options .= "\t\t" . 'auth_basic_user_file ' . FileDir::makeCorrectFile($single['usrf']) . ';' . "\n";
|
||||
if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {
|
||||
$path_options .= "\t\t" . 'index index.php index.html index.htm;' . "\n";
|
||||
if ($domain['notryfiles'] != 1) {
|
||||
$path_options .= "\t\t" . 'location ~ ^(.+?\.php)(/.*)?$ {' . "\n";
|
||||
$path_options .= "\t\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n";
|
||||
$path_options .= "\t\t" . '}' . "\n\n";
|
||||
}
|
||||
} else {
|
||||
$path_options .= "\t\t" . 'index index.html index.htm;' . "\n";
|
||||
}
|
||||
@@ -1120,6 +1124,10 @@ class Nginx extends HttpConfigBase
|
||||
|
||||
$statTool = Settings::Get('system.traffictool');
|
||||
$statDomain = "";
|
||||
if ($statTool == 'awstats') {
|
||||
// awstats generates for each domain regardless of speciallogfile
|
||||
$statDomain = "/" . $domain['domain'];
|
||||
}
|
||||
if ($domain['speciallogfile'] == '1') {
|
||||
$statDomain = "/" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']);
|
||||
}
|
||||
@@ -1157,7 +1165,6 @@ class Nginx extends HttpConfigBase
|
||||
$phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n";
|
||||
$phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n";
|
||||
$phpopts .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n";
|
||||
$phpopts .= "\t\ttry_files \$fastcgi_script_name =404;\n";
|
||||
$phpopts .= "\t\tfastcgi_pass " . Settings::Get('system.nginx_php_backend') . ";\n";
|
||||
$phpopts .= "\t\tfastcgi_index index.php;\n";
|
||||
if ($domain['ssl'] == '1' && $ssl_vhost) {
|
||||
|
||||
@@ -342,8 +342,17 @@ pm.max_children = 1
|
||||
public function getSocketFile($createifnotexists = true)
|
||||
{
|
||||
$socketdir = FileDir::makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir'));
|
||||
// add fpm-config-id to filename so it's unique for the fpm-daemon and doesn't interfere with running configs when reuilding
|
||||
$socket = strtolower(FileDir::makeCorrectFile($socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['domain'] . '-php-fpm.socket'));
|
||||
// add fpm-config-id to filename, so it's unique for the fpm-daemon and doesn't interfere with running configs when reuilding
|
||||
$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['domain'] . '-php-fpm.socket';
|
||||
if (strlen($socket_filename) > 100) {
|
||||
// respect the unix socket-length limitation
|
||||
$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['id'] . '-php-fpm.socket';
|
||||
if (strlen($socket_filename) > 100) {
|
||||
// even a long loginname it seems
|
||||
$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['guid'] . '-' . $this->domain['id'] . '-php-fpm.socket';
|
||||
}
|
||||
}
|
||||
$socket = strtolower(FileDir::makeCorrectFile($socket_filename));
|
||||
|
||||
if (!is_dir($socketdir) && $createifnotexists) {
|
||||
FileDir::safe_exec('mkdir -p ' . escapeshellarg($socketdir));
|
||||
|
||||
@@ -43,7 +43,7 @@ class WebserverBase
|
||||
{
|
||||
$query = "SELECT `d`.*, `pd`.`domain` AS `parentdomain`, `c`.`loginname`,
|
||||
`d`.`phpsettingid`, `c`.`adminid`, `c`.`guid`, `c`.`email`,
|
||||
`c`.`documentroot` AS `customerroot`, `c`.`deactivated`,
|
||||
`c`.`documentroot` AS `customerroot`, `c`.`deactivated` as `customer_deactivated`,
|
||||
`c`.`phpenabled` AS `phpenabled_customer`,
|
||||
`d`.`phpenabled` AS `phpenabled_vhost`
|
||||
FROM `" . TABLE_PANEL_DOMAINS . "` `d`
|
||||
|
||||
@@ -146,28 +146,37 @@ class BackupCron extends FroxlorCron
|
||||
FileDir::safe_exec('mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir($tmpdir . '/mysql')));
|
||||
|
||||
// get all customer database-names
|
||||
$sel_stmt = Database::prepare("SELECT `databasename` FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid` = :cid");
|
||||
$sel_stmt = Database::prepare("SELECT `databasename`, `dbserver` FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid` = :cid ORDER BY `dbserver`");
|
||||
Database::pexecute($sel_stmt, [
|
||||
'cid' => $data['customerid']
|
||||
]);
|
||||
|
||||
Database::needRoot(true);
|
||||
Database::needSqlData();
|
||||
$sql_root = Database::getSqlData();
|
||||
Database::needRoot(false);
|
||||
|
||||
$mysqlcnf_file = tempnam("/tmp", "frx");
|
||||
$mysqlcnf = "[mysqldump]\npassword=".$sql_root['passwd']."\n";
|
||||
file_put_contents($mysqlcnf_file, $mysqlcnf);
|
||||
|
||||
$has_dbs = false;
|
||||
$current_dbserver = null;
|
||||
while ($row = $sel_stmt->fetch()) {
|
||||
// Get sql_root data for the specific database-server the database resides on
|
||||
if ($current_dbserver != $row['dbserver']) {
|
||||
Database::needRoot(true, $row['dbserver']);
|
||||
Database::needSqlData();
|
||||
$sql_root = Database::getSqlData();
|
||||
Database::needRoot(false);
|
||||
// create temporary mysql-defaults file for the connection-credentials/details
|
||||
$mysqlcnf_file = tempnam("/tmp", "frx");
|
||||
$mysqlcnf = "[mysqldump]\npassword=" . $sql_root['passwd'] . "\nhost=" . $sql_root['host'] . "\n";
|
||||
if (!empty($sql_root['port'])) {
|
||||
$mysqlcnf .= "port=" . $sql_root['port'] . "\n";
|
||||
} elseif (!empty($sql_root['socket'])) {
|
||||
$mysqlcnf .= "socket=" . $sql_root['socket'] . "\n";
|
||||
}
|
||||
file_put_contents($mysqlcnf_file, $mysqlcnf);
|
||||
}
|
||||
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> mysqldump -u ' . escapeshellarg($sql_root['user']) . ' -pXXXXX ' . $row['databasename'] . ' > ' . FileDir::makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'));
|
||||
$bool_false = false;
|
||||
FileDir::safe_exec('mysqldump --defaults-file=' . escapeshellarg($mysqlcnf_file) .' -u ' . escapeshellarg($sql_root['user']) . ' ' . $row['databasename'] . ' > ' . FileDir::makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'), $bool_false, [
|
||||
FileDir::safe_exec('mysqldump --defaults-file=' . escapeshellarg($mysqlcnf_file) . ' -u ' . escapeshellarg($sql_root['user']) . ' ' . $row['databasename'] . ' > ' . FileDir::makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'), $bool_false, [
|
||||
'>'
|
||||
]);
|
||||
$has_dbs = true;
|
||||
$current_dbserver = $row['dbserver'];
|
||||
}
|
||||
|
||||
if ($has_dbs) {
|
||||
|
||||
@@ -33,8 +33,6 @@ namespace Froxlor\Cron\Traffic;
|
||||
use Exception;
|
||||
use Froxlor\Cron\FroxlorCron;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\PhpHelper;
|
||||
use Froxlor\Settings;
|
||||
@@ -99,8 +97,8 @@ class ReportsCron extends FroxlorCron
|
||||
'COMPANY' => $rep_userinfo['company'],
|
||||
'USERNAME' => $rep_userinfo['loginname'],
|
||||
'CUSTOMER_NO' => $rep_userinfo['customernumber'],
|
||||
'TRAFFIC' => PhpHelper::sizeReadable($row['traffic'], null, 'bi'),
|
||||
'TRAFFICUSED' => PhpHelper::sizeReadable($row['traffic_used'], null, 'bi'),
|
||||
'TRAFFIC' => PhpHelper::sizeReadable((int)$row['traffic'], null, 'bi'),
|
||||
'TRAFFICUSED' => PhpHelper::sizeReadable((int)$row['traffic_used'], null, 'bi'),
|
||||
'USAGE_PERCENT' => round(($row['traffic_used'] * 100) / $row['traffic'], 2),
|
||||
'MAX_PERCENT' => Settings::Get('system.report_trafficmax')
|
||||
];
|
||||
@@ -182,8 +180,8 @@ class ReportsCron extends FroxlorCron
|
||||
if (isset($row['traffic']) && $row['traffic'] > 0 && (($row['traffic_used_total'] * 100) / ($row['traffic'])) >= (int)Settings::Get('system.report_trafficmax')) {
|
||||
$replace_arr = [
|
||||
'NAME' => $row['name'],
|
||||
'TRAFFIC' => PhpHelper::sizeReadable($row['traffic'], null, 'bi'),
|
||||
'TRAFFICUSED' => PhpHelper::sizeReadable($row['traffic_used_total'], null, 'bi'),
|
||||
'TRAFFIC' => PhpHelper::sizeReadable((int)$row['traffic'], null, 'bi'),
|
||||
'TRAFFICUSED' => PhpHelper::sizeReadable((int)$row['traffic_used_total'], null, 'bi'),
|
||||
'USAGE_PERCENT' => round(($row['traffic_used_total'] * 100) / $row['traffic'], 2),
|
||||
'MAX_PERCENT' => Settings::Get('system.report_trafficmax')
|
||||
];
|
||||
@@ -265,10 +263,10 @@ class ReportsCron extends FroxlorCron
|
||||
|
||||
while ($customer = $customers_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$customer['traffic'] *= 1024;
|
||||
$t = $customer['traffic_used_total'] * 1024;
|
||||
$t = (int) $customer['traffic_used_total'] * 1024;
|
||||
if ($customer['traffic'] > 0) {
|
||||
$p = (($t * 100) / $customer['traffic']);
|
||||
$tg = $customer['traffic'];
|
||||
$tg = (int) $customer['traffic'];
|
||||
$str = sprintf('%s ( %00.1f %% )', PhpHelper::sizeReadable($t, null, 'bi'), $p);
|
||||
$mail_body .= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%s', PhpHelper::sizeReadable($tg, null, 'bi')) . "\n";
|
||||
} elseif ($customer['traffic'] == 0) {
|
||||
@@ -282,10 +280,10 @@ class ReportsCron extends FroxlorCron
|
||||
|
||||
$mail_body .= '---------------------------------------------------------------' . "\n";
|
||||
|
||||
$t = $row['traffic_used_total'];
|
||||
$t = (int) $row['traffic_used_total'];
|
||||
if ($row['traffic'] > 0) {
|
||||
$p = (($t * 100) / $row['traffic']);
|
||||
$tg = $row['traffic'];
|
||||
$tg = (int) $row['traffic'];
|
||||
$str = sprintf('%s ( %00.1f %% )', PhpHelper::sizeReadable($t, null, 'bi'), $p);
|
||||
$mail_body .= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%s', PhpHelper::sizeReadable($tg, null, 'bi')) . "\n";
|
||||
} elseif ($row['traffic'] == 0) {
|
||||
@@ -369,8 +367,8 @@ class ReportsCron extends FroxlorCron
|
||||
'COMPANY' => $rep_userinfo['company'],
|
||||
'USERNAME' => $rep_userinfo['loginname'],
|
||||
'CUSTOMER_NO' => $rep_userinfo['customernumber'],
|
||||
'DISKAVAILABLE' => PhpHelper::sizeReadable($row['diskspace'], null, 'bi'),
|
||||
'DISKUSED' => PhpHelper::sizeReadable($row['diskspace_used'], null, 'bi'),
|
||||
'DISKAVAILABLE' => PhpHelper::sizeReadable((int)$row['diskspace'], null, 'bi'),
|
||||
'DISKUSED' => PhpHelper::sizeReadable((int)$row['diskspace_used'], null, 'bi'),
|
||||
'USAGE_PERCENT' => round(($row['diskspace_used'] * 100) / $row['diskspace'], 2),
|
||||
'MAX_PERCENT' => Settings::Get('system.report_webmax')
|
||||
];
|
||||
@@ -443,8 +441,8 @@ class ReportsCron extends FroxlorCron
|
||||
if (isset($row['diskspace']) && $row['diskspace_used'] != null && $row['diskspace_used'] > 0 && (($row['diskspace_used'] * 100) / $row['diskspace']) >= (int)Settings::Get('system.report_webmax')) {
|
||||
$replace_arr = [
|
||||
'NAME' => $row['name'],
|
||||
'DISKAVAILABLE' => PhpHelper::sizeReadable($row['diskspace'], null, 'bi'),
|
||||
'DISKUSED' => PhpHelper::sizeReadable($row['diskspace_used'], null, 'bi'),
|
||||
'DISKAVAILABLE' => PhpHelper::sizeReadable((int)$row['diskspace'], null, 'bi'),
|
||||
'DISKUSED' => PhpHelper::sizeReadable((int)$row['diskspace_used'], null, 'bi'),
|
||||
'USAGE_PERCENT' => ($row['diskspace_used'] * 100) / $row['diskspace'],
|
||||
'MAX_PERCENT' => Settings::Get('system.report_webmax')
|
||||
];
|
||||
|
||||
@@ -78,7 +78,7 @@ class TrafficCron extends FroxlorCron
|
||||
// Fork failed
|
||||
return 1;
|
||||
}
|
||||
} else if (!defined('CRON_NOFORK_FLAG')) {
|
||||
} elseif (!defined('CRON_NOFORK_FLAG')) {
|
||||
if (extension_loaded('pcntl')) {
|
||||
$msg = "PHP compiled with pcntl but pcntl_fork function is not available.";
|
||||
} else {
|
||||
@@ -127,7 +127,7 @@ class TrafficCron extends FroxlorCron
|
||||
|
||||
while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
if ($last_dbserver != $row_database['dbserver']) {
|
||||
Database::needRoot(true, $row_database['dbserver']);
|
||||
Database::needRoot(true, $row_database['dbserver'], true);
|
||||
$last_dbserver = $row_database['dbserver'];
|
||||
|
||||
$databases_list = [];
|
||||
@@ -406,7 +406,7 @@ class TrafficCron extends FroxlorCron
|
||||
} else {
|
||||
// Use the old fashioned way with "du"
|
||||
if (file_exists($row['documentroot']) && is_dir($row['documentroot'])) {
|
||||
$back = FileDir::safe_exec('du -sk ' . escapeshellarg($row['documentroot']) . '');
|
||||
$back = FileDir::safe_exec('du -sk ' . escapeshellarg($row['documentroot']));
|
||||
foreach ($back as $backrow) {
|
||||
$webspaceusage = explode(' ', $backrow);
|
||||
}
|
||||
@@ -426,7 +426,7 @@ class TrafficCron extends FroxlorCron
|
||||
|
||||
$maildir = FileDir::makeCorrectDir(Settings::Get('system.vmail_homedir') . $row['loginname']);
|
||||
if (file_exists($maildir) && is_dir($maildir)) {
|
||||
$back = FileDir::safe_exec('du -sk ' . escapeshellarg($maildir) . '');
|
||||
$back = FileDir::safe_exec('du -sk ' . escapeshellarg($maildir));
|
||||
foreach ($back as $backrow) {
|
||||
$emailusage = explode(' ', $backrow);
|
||||
}
|
||||
@@ -627,7 +627,7 @@ class TrafficCron extends FroxlorCron
|
||||
* @param string $caption Caption for webalizer output
|
||||
* @param array $monthyear_arr
|
||||
* @param int $current_stamp
|
||||
*
|
||||
*
|
||||
* @return int Used traffic
|
||||
*/
|
||||
private static function callGoaccessGetTraffic($customerid, $logfile, $outputdir, $caption, array $monthyear_arr = [], int $current_stamp = 0)
|
||||
@@ -705,7 +705,7 @@ class TrafficCron extends FroxlorCron
|
||||
* @param string $outputdir Place where stats should be build
|
||||
* @param string $caption Caption for webalizer output
|
||||
* @param array $usersdomainlist
|
||||
*
|
||||
*
|
||||
* @return float Used traffic
|
||||
*/
|
||||
private static function callWebalizerGetTraffic($logfile, $outputdir, $caption, array $usersdomainlist = [])
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
namespace Froxlor;
|
||||
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\UI\Collection;
|
||||
use Froxlor\Api\Commands\Customers;
|
||||
use Froxlor\Api\Commands\SubDomains;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\UI\Collection;
|
||||
|
||||
/**
|
||||
* Class to manage the current user / session
|
||||
@@ -63,9 +63,10 @@ class CurrentUser
|
||||
/**
|
||||
* re-read in the user data if a valid session exists
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function reReadUserData()
|
||||
public static function reReadUserData(): bool
|
||||
{
|
||||
$table = self::isAdmin() ? TABLE_PANEL_ADMINS : TABLE_PANEL_CUSTOMERS;
|
||||
$userinfo_stmt = Database::prepare("
|
||||
@@ -75,7 +76,7 @@ class CurrentUser
|
||||
"loginname" => self::getField('loginname')
|
||||
]);
|
||||
if ($userinfo) {
|
||||
// dont just set the data, we need to merge with current data
|
||||
// don't just set the data, we need to merge with current data
|
||||
// array_merge is a right-reduction - value existing in getData() will be overwritten with $userinfo,
|
||||
// other than the union-operator (+) which would keep the values already existing from getData()
|
||||
$newuserinfo = array_merge(self::getData(), $userinfo);
|
||||
@@ -107,7 +108,7 @@ class CurrentUser
|
||||
*/
|
||||
public static function getField(string $index)
|
||||
{
|
||||
return isset($_SESSION['userinfo'][$index]) ? $_SESSION['userinfo'][$index] : "";
|
||||
return $_SESSION['userinfo'][$index] ?? "";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,6 +131,11 @@ class CurrentUser
|
||||
$_SESSION['userinfo'] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $resource
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function canAddResource(string $resource): bool
|
||||
{
|
||||
$addition = true;
|
||||
@@ -145,14 +151,15 @@ class CurrentUser
|
||||
]);
|
||||
$addition = $result['emaildomains'] != 0;
|
||||
} elseif ($resource == 'subdomains') {
|
||||
$parentDomainCollection = (new Collection(SubDomains::class, $_SESSION['userinfo'], ['sql_search' => ['d.parentdomainid' => 0]]));
|
||||
$addition = $parentDomainCollection != 0;
|
||||
$parentDomainCollection = (new Collection(SubDomains::class, $_SESSION['userinfo'],
|
||||
['sql_search' => ['d.parentdomainid' => 0]]));
|
||||
$addition = $parentDomainCollection->count() != 0;
|
||||
} elseif ($resource == 'domains') {
|
||||
$customerCollection = (new Collection(Customers::class, $_SESSION['userinfo']));
|
||||
$addition = $customerCollection != 0;
|
||||
}
|
||||
|
||||
return ($_SESSION['userinfo'][$resource.'_used'] < $_SESSION['userinfo'][$resource] || $_SESSION['userinfo'][$resource] == '-1') && $addition;
|
||||
return ($_SESSION['userinfo'][$resource . '_used'] < $_SESSION['userinfo'][$resource] || $_SESSION['userinfo'][$resource] == '-1') && $addition;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,15 @@ use PDO;
|
||||
class Customer
|
||||
{
|
||||
|
||||
public static function getCustomerDetail($customerid, $varname)
|
||||
/**
|
||||
* Get value of a a specific field from a given customer
|
||||
*
|
||||
* @param int $customerid
|
||||
* @param string $varname
|
||||
* @return false|mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getCustomerDetail(int $customerid, string $varname)
|
||||
{
|
||||
$customer_stmt = Database::prepare("
|
||||
SELECT `" . $varname . "` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :customerid
|
||||
@@ -42,20 +50,19 @@ class Customer
|
||||
|
||||
if (isset($customer[$varname])) {
|
||||
return $customer[$varname];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the loginname of a customer by given uid
|
||||
*
|
||||
* @param int $uid
|
||||
* uid of customer
|
||||
* @param int $uid uid of customer
|
||||
*
|
||||
* @return string customers loginname
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getLoginNameByUid($uid = null)
|
||||
public static function getLoginNameByUid(int $uid)
|
||||
{
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `guid` = :guid
|
||||
@@ -64,7 +71,7 @@ class Customer
|
||||
'guid' => $uid
|
||||
]);
|
||||
|
||||
if (is_array($result) && isset($result['loginname'])) {
|
||||
if ($result && isset($result['loginname'])) {
|
||||
return $result['loginname'];
|
||||
}
|
||||
return false;
|
||||
@@ -76,23 +83,22 @@ class Customer
|
||||
* returns true or false whether perl is
|
||||
* enabled for the given customer
|
||||
*
|
||||
* @param
|
||||
* int customer-id
|
||||
* @param int $cid customer-id
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function customerHasPerlEnabled($cid = 0)
|
||||
public static function customerHasPerlEnabled(int $cid = 0)
|
||||
{
|
||||
if ($cid > 0) {
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT `perlenabled` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :cid");
|
||||
Database::pexecute($result_stmt, [
|
||||
$result = Database::pexecute_first($result_stmt, [
|
||||
'cid' => $cid
|
||||
]);
|
||||
$result = $result_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (is_array($result) && isset($result['perlenabled'])) {
|
||||
return $result['perlenabled'] == '1';
|
||||
if ($result && isset($result['perlenabled'])) {
|
||||
return (bool)$result['perlenabled'];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Froxlor\Database;
|
||||
use Exception;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\PhpHelper;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
use PDO;
|
||||
@@ -60,39 +61,43 @@ class Database
|
||||
/**
|
||||
* indicator whether to use root-connection or not
|
||||
*/
|
||||
private static $needroot = false;
|
||||
private static bool $needroot = false;
|
||||
|
||||
/**
|
||||
* indicator which database-server we're on (not really used)
|
||||
*/
|
||||
private static $dbserver = 0;
|
||||
private static int $dbserver = 0;
|
||||
|
||||
/**
|
||||
* used database-name
|
||||
*/
|
||||
private static $dbname = null;
|
||||
private static ?string $dbname = null;
|
||||
|
||||
/**
|
||||
* sql-access data
|
||||
*/
|
||||
private static $needsqldata = false;
|
||||
private static bool $needsqldata = false;
|
||||
|
||||
private static $sqldata = null;
|
||||
|
||||
private static bool $need_dbname = true;
|
||||
|
||||
/**
|
||||
* Wrapper for PDOStatement::execute so we can catch the PDOException
|
||||
* Wrapper for PDOStatement::execute, so we can catch the PDOException
|
||||
* and display the error nicely on the panel - also fetches the
|
||||
* result from the statement and returns the resulting array
|
||||
*
|
||||
* @param PDOStatement $stmt
|
||||
* @param array $params
|
||||
* @param array|null $params
|
||||
* (optional)
|
||||
* @param bool $showerror
|
||||
* suppress errordisplay (default true)
|
||||
* suppress error display (default true)
|
||||
* @param bool $json_response
|
||||
*
|
||||
* @return array
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function pexecute_first(&$stmt, $params = null, $showerror = true, $json_response = false)
|
||||
public static function pexecute_first(PDOStatement &$stmt, $params = null, bool $showerror = true, bool $json_response = false)
|
||||
{
|
||||
self::pexecute($stmt, $params, $showerror, $json_response);
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
@@ -103,12 +108,15 @@ class Database
|
||||
* and display the error nicely on the panel
|
||||
*
|
||||
* @param PDOStatement $stmt
|
||||
* @param array $params
|
||||
* @param array|null $params
|
||||
* (optional)
|
||||
* @param bool $showerror
|
||||
* suppress errordisplay (default true)
|
||||
* suppress error display (default true)
|
||||
* @param bool $json_response
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function pexecute(&$stmt, $params = null, $showerror = true, $json_response = false)
|
||||
public static function pexecute(PDOStatement &$stmt, $params = null, bool $showerror = true, bool $json_response = false)
|
||||
{
|
||||
try {
|
||||
$stmt->execute($params);
|
||||
@@ -122,9 +130,10 @@ class Database
|
||||
*
|
||||
* @param PDOException $error
|
||||
* @param bool $showerror
|
||||
* 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
|
||||
*/
|
||||
private static function showerror($error, $showerror = true, $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;
|
||||
|
||||
@@ -135,22 +144,30 @@ class Database
|
||||
require Froxlor::getInstallDir() . "/lib/userdata.inc.php";
|
||||
|
||||
// le format
|
||||
if (isset($sql['root_user']) && isset($sql['root_password']) && !is_array($sql_root)) {
|
||||
if (isset($sql['root_user']) && isset($sql['root_password']) && empty($sql_root)) {
|
||||
$sql_root = [
|
||||
0 => [
|
||||
'caption' => 'Default',
|
||||
'host' => $sql['host'],
|
||||
'socket' => (isset($sql['socket']) ? $sql['socket'] : null),
|
||||
'socket' => ($sql['socket'] ?? null),
|
||||
'user' => $sql['root_user'],
|
||||
'password' => $sql['root_password']
|
||||
]
|
||||
];
|
||||
unset($sql['root_user']);
|
||||
unset($sql['root_password']);
|
||||
// write new layout so this won't happen again
|
||||
self::generateNewUserData($sql, $sql_root);
|
||||
// re-read file
|
||||
require Froxlor::getInstallDir() . "/lib/userdata.inc.php";
|
||||
}
|
||||
|
||||
$substitutions = [
|
||||
$sql['password'] => 'DB_UNPRIV_PWD',
|
||||
$sql_root[0]['password'] => 'DB_ROOT_PWD'
|
||||
];
|
||||
foreach ($sql_root as $sql_root_data) {
|
||||
$substitutions[$sql_root_data['password']] = 'DB_ROOT_PWD';
|
||||
}
|
||||
|
||||
// hide username/password in messages
|
||||
$error_message = $error->getMessage();
|
||||
@@ -243,7 +260,7 @@ class Database
|
||||
* @param int $minLength
|
||||
* @return string
|
||||
*/
|
||||
private static function substitute($content, array $substitutions, $minLength = 6)
|
||||
private static function substitute(string $content, array $substitutions, int $minLength = 6): string
|
||||
{
|
||||
$replacements = [];
|
||||
|
||||
@@ -251,9 +268,7 @@ class Database
|
||||
$replacements += self::createShiftedSubstitutions($search, $replace, $minLength);
|
||||
}
|
||||
|
||||
$content = str_replace(array_keys($replacements), array_values($replacements), $content);
|
||||
|
||||
return $content;
|
||||
return str_replace(array_keys($replacements), array_values($replacements), $content);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,7 +288,7 @@ class Database
|
||||
* @param int $minLength
|
||||
* @return array
|
||||
*/
|
||||
private static function createShiftedSubstitutions($search, $replace, $minLength)
|
||||
private static function createShiftedSubstitutions(string $search, string $replace, int $minLength): array
|
||||
{
|
||||
$substitutions = [];
|
||||
$length = strlen($search);
|
||||
@@ -292,8 +307,9 @@ class Database
|
||||
*
|
||||
* @param int $length
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function genUniqueToken(int $length = 16)
|
||||
private static function genUniqueToken(int $length = 16): string
|
||||
{
|
||||
if (intval($length) <= 8) {
|
||||
$length = 16;
|
||||
@@ -316,7 +332,7 @@ class Database
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function num_rows()
|
||||
public static function num_rows(): int
|
||||
{
|
||||
return Database::query("SELECT FOUND_ROWS()")->fetchColumn();
|
||||
}
|
||||
@@ -326,7 +342,7 @@ class Database
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getDbName()
|
||||
public static function getDbName(): ?string
|
||||
{
|
||||
return self::$dbname;
|
||||
}
|
||||
@@ -338,15 +354,16 @@ class Database
|
||||
* the 'normal' database-connection
|
||||
*
|
||||
* @param bool $needroot
|
||||
* @param int $dbserver
|
||||
* optional
|
||||
* @param int $dbserver optional
|
||||
* @param bool $need_db
|
||||
*/
|
||||
public static function needRoot($needroot = false, $dbserver = 0)
|
||||
public static function needRoot(bool $needroot = false, int $dbserver = 0, bool $need_db = true)
|
||||
{
|
||||
// force re-connecting to the db with corresponding user
|
||||
// and set the $dbserver (mostly to 0 = default)
|
||||
self::setServer($dbserver);
|
||||
self::$needroot = $needroot;
|
||||
self::$need_dbname = $need_db;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,7 +371,7 @@ class Database
|
||||
*
|
||||
* @param int $dbserver
|
||||
*/
|
||||
private static function setServer($dbserver = 0)
|
||||
private static function setServer(int $dbserver = 0)
|
||||
{
|
||||
self::$dbserver = $dbserver;
|
||||
self::$link = null;
|
||||
@@ -385,17 +402,16 @@ class Database
|
||||
* function that will be called on every static call
|
||||
* which connects to the database if necessary
|
||||
*
|
||||
* @param bool $root
|
||||
*
|
||||
* @return object
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function getDB()
|
||||
{
|
||||
if (!extension_loaded('pdo') || in_array("mysql", PDO::getAvailableDrivers()) == false) {
|
||||
if (!extension_loaded('pdo') || !in_array("mysql", PDO::getAvailableDrivers())) {
|
||||
self::showerror(new Exception("The php PDO extension or PDO-MySQL driver is not available"));
|
||||
}
|
||||
|
||||
// do we got a connection already?
|
||||
// do we have a connection already?
|
||||
if (self::$link) {
|
||||
// return it
|
||||
return self::$link;
|
||||
@@ -405,18 +421,22 @@ class Database
|
||||
require Froxlor::getInstallDir() . "/lib/userdata.inc.php";
|
||||
|
||||
// le format
|
||||
if (self::$needroot == true && isset($sql['root_user']) && isset($sql['root_password']) && (!isset($sql_root) || !is_array($sql_root))) {
|
||||
if (isset($sql['root_user']) && isset($sql['root_password']) && (!isset($sql_root) || !is_array($sql_root))) {
|
||||
$sql_root = [
|
||||
0 => [
|
||||
'caption' => 'Default',
|
||||
'host' => $sql['host'],
|
||||
'socket' => (isset($sql['socket']) ? $sql['socket'] : null),
|
||||
'socket' => ($sql['socket'] ?? null),
|
||||
'user' => $sql['root_user'],
|
||||
'password' => $sql['root_password']
|
||||
]
|
||||
];
|
||||
unset($sql['root_user']);
|
||||
unset($sql['root_password']);
|
||||
// write new layout so this won't happen again
|
||||
self::generateNewUserData($sql, $sql_root);
|
||||
// re-read file
|
||||
require Froxlor::getInstallDir() . "/lib/userdata.inc.php";
|
||||
}
|
||||
|
||||
// either root or unprivileged user
|
||||
@@ -425,8 +445,8 @@ class Database
|
||||
$user = $sql_root[self::$dbserver]['user'];
|
||||
$password = $sql_root[self::$dbserver]['password'];
|
||||
$host = $sql_root[self::$dbserver]['host'];
|
||||
$socket = isset($sql_root[self::$dbserver]['socket']) ? $sql_root[self::$dbserver]['socket'] : null;
|
||||
$port = isset($sql_root[self::$dbserver]['port']) ? $sql_root[self::$dbserver]['port'] : '3306';
|
||||
$socket = $sql_root[self::$dbserver]['socket'] ?? null;
|
||||
$port = $sql_root[self::$dbserver]['port'] ?? '3306';
|
||||
$sslCAFile = $sql_root[self::$dbserver]['ssl']['caFile'] ?? "";
|
||||
$sslVerifyServerCertificate = $sql_root[self::$dbserver]['ssl']['verifyServerCertificate'] ?? false;
|
||||
} else {
|
||||
@@ -434,8 +454,8 @@ class Database
|
||||
$user = $sql["user"];
|
||||
$password = $sql["password"];
|
||||
$host = $sql["host"];
|
||||
$socket = isset($sql['socket']) ? $sql['socket'] : null;
|
||||
$port = isset($sql['port']) ? $sql['port'] : '3306';
|
||||
$socket = $sql['socket'] ?? null;
|
||||
$port = $sql['port'] ?? '3306';
|
||||
$sslCAFile = $sql['ssl']['caFile'] ?? "";
|
||||
$sslVerifyServerCertificate = $sql['ssl']['verifyServerCertificate'] ?? false;
|
||||
}
|
||||
@@ -465,10 +485,11 @@ class Database
|
||||
'ATTR_ERRMODE' => 'ERRMODE_EXCEPTION'
|
||||
];
|
||||
|
||||
$dbconf["dsn"] = [
|
||||
'dbname' => $sql["db"],
|
||||
'charset' => 'utf8'
|
||||
];
|
||||
$dbconf["dsn"] = ['charset' => 'utf8'];
|
||||
|
||||
if (self::$need_dbname) {
|
||||
$dbconf["dsn"]['dbname'] = $sql["db"];
|
||||
}
|
||||
|
||||
if ($socket != null) {
|
||||
$dbconf["dsn"]['unix_socket'] = FileDir::makeCorrectFile($socket);
|
||||
@@ -539,14 +560,14 @@ class Database
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getSqlUsernameLength()
|
||||
public static function getSqlUsernameLength(): int
|
||||
{
|
||||
// MariaDB supports up to 80 characters but only 64 for databases and as we use the loginname also for
|
||||
// MariaDB supports up to 80 characters but only 64 for databases and as we use the login-name also for
|
||||
// database names, we set the limit to 64 here
|
||||
if (strpos(strtolower(Database::getAttribute(\PDO::ATTR_SERVER_VERSION)), "mariadb") !== false) {
|
||||
$mysql_max = 64;
|
||||
} else {
|
||||
// MySQL user names can be up to 32 characters long (16 characters before MySQL 5.7.8).
|
||||
// MySQL user-names can be up to 32 characters long (16 characters before MySQL 5.7.8).
|
||||
$mysql_max = 32;
|
||||
if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.8', '<')) {
|
||||
$mysql_max = 16;
|
||||
@@ -556,15 +577,16 @@ class Database
|
||||
}
|
||||
|
||||
/**
|
||||
* let's us interact with the PDO-Object by using static
|
||||
* Lets us interact with the PDO-Object by using static
|
||||
* call like "Database::function()"
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $args
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function __callStatic($name, $args)
|
||||
public static function __callStatic(string $name, $args)
|
||||
{
|
||||
$callback = [
|
||||
self::getDB(),
|
||||
@@ -578,4 +600,22 @@ class Database
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* write new userdata.inc.php file
|
||||
*/
|
||||
private static function generateNewUserData(array $sql, array $sql_root)
|
||||
{
|
||||
$content = PhpHelper::parseArrayToPhpFile(
|
||||
['sql' => $sql, 'sql_root' => $sql_root],
|
||||
'automatically generated userdata.inc.php for froxlor'
|
||||
);
|
||||
chmod(Froxlor::getInstallDir() . "/lib/userdata.inc.php", 0700);
|
||||
file_put_contents(Froxlor::getInstallDir() . "/lib/userdata.inc.php", $content);
|
||||
chmod(Froxlor::getInstallDir() . "/lib/userdata.inc.php", 0400);
|
||||
clearstatcache();
|
||||
if (function_exists('opcache_invalidate')) {
|
||||
@opcache_invalidate(Froxlor::getInstallDir() . "/lib/userdata.inc.php", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,15 @@ class DbManager
|
||||
$this->manager = new DbManagerMySQL($this->log);
|
||||
}
|
||||
|
||||
public static function correctMysqlUsers($mysql_access_host_array)
|
||||
/**
|
||||
* function called when the mysql-access-host setting changes
|
||||
*
|
||||
* @param array $mysql_access_host_array
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function correctMysqlUsers(array $mysql_access_host_array)
|
||||
{
|
||||
// get all databases for all dbservers
|
||||
$databases = [];
|
||||
@@ -96,12 +104,12 @@ class DbManager
|
||||
$dbservers_stmt = Database::query("SELECT DISTINCT `dbserver` FROM `" . TABLE_PANEL_DATABASES . "`");
|
||||
while ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
// require privileged access for target db-server
|
||||
Database::needRoot(true, $dbserver['dbserver']);
|
||||
Database::needRoot(true, $dbserver['dbserver'], false);
|
||||
|
||||
$dbm = new DbManager(FroxlorLogger::getInstanceOf());
|
||||
$users = $dbm->getManager()->getAllSqlUsers(false);
|
||||
|
||||
foreach ($databases[$dbserver] as $username) {
|
||||
foreach ($databases[$dbserver['dbserver']] as $username) {
|
||||
if (isset($users[$username]) && is_array($users[$username]) && isset($users[$username]['hosts']) && is_array($users[$username]['hosts'])) {
|
||||
|
||||
$password = [
|
||||
@@ -136,15 +144,16 @@ class DbManager
|
||||
* DB-name and user-name are being generated and
|
||||
* the password for the user will be set
|
||||
*
|
||||
* @param string $loginname
|
||||
* @param string $password
|
||||
* @param ?string $loginname
|
||||
* @param ?string $password
|
||||
* @param int $dbserver
|
||||
* @param int $last_accnumber
|
||||
*
|
||||
* @return string|bool $username if successful or false of username is equal to the password
|
||||
*/
|
||||
public function createDatabase($loginname = null, $password = null, int $dbserver = 0, $last_accnumber = 0)
|
||||
public function createDatabase(string $loginname = null, string $password = null, int $dbserver = 0, int $last_accnumber = 0)
|
||||
{
|
||||
Database::needRoot(true, $dbserver);
|
||||
Database::needRoot(true, $dbserver, false);
|
||||
|
||||
// check whether we shall create a random username
|
||||
if (strtoupper(Settings::Get('customer.mysqlprefix')) == 'RANDOM') {
|
||||
@@ -169,18 +178,17 @@ class DbManager
|
||||
|
||||
// now create the database itself
|
||||
$this->getManager()->createDatabase($username);
|
||||
$this->log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, "created database '" . $username . "'");
|
||||
|
||||
// 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) {
|
||||
$this->getManager()->grantPrivilegesTo($username, $password, $mysql_access_host);
|
||||
$this->log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "grant all privileges for '" . $username . "'@'" . $mysql_access_host . "'");
|
||||
}
|
||||
|
||||
$this->getManager()->flushPrivileges();
|
||||
|
||||
Database::needRoot(false);
|
||||
|
||||
$this->log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, "created database '" . $username . "'");
|
||||
|
||||
return $username;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,21 +85,22 @@ class IntegrityCheck
|
||||
/**
|
||||
* check whether the froxlor database and its tables are in utf-8 character-set
|
||||
*
|
||||
* @param bool $fix
|
||||
* fix db charset/collation if not utf8
|
||||
* @param bool $fix fix db charset/collation if not utf8
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function databaseCharset($fix = false)
|
||||
public function databaseCharset(bool $fix = false): bool
|
||||
{
|
||||
// get characterset
|
||||
// get character-set
|
||||
$cs_stmt = Database::prepare('SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = :dbname');
|
||||
$resp = Database::pexecute_first($cs_stmt, [
|
||||
'dbname' => Database::getDbName()
|
||||
]);
|
||||
$charset = isset($resp['default_character_set_name']) ? $resp['default_character_set_name'] : null;
|
||||
$charset = $resp['default_character_set_name'] ?? null;
|
||||
if (!empty($charset) && substr(strtolower($charset), 0, 4) != 'utf8') {
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "database charset seems to be different from UTF-8, integrity-check can fix that");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
|
||||
"database charset seems to be different from UTF-8, integrity-check can fix that");
|
||||
if ($fix) {
|
||||
// fix database
|
||||
Database::query('ALTER DATABASE `' . Database::getDbName() . '` CHARACTER SET utf8 COLLATE utf8_general_ci');
|
||||
@@ -109,7 +110,8 @@ class IntegrityCheck
|
||||
$table = $row[0];
|
||||
Database::query('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;');
|
||||
}
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "database charset was different from UTF-8, integrity-check fixed that");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
|
||||
"database charset was different from UTF-8, integrity-check fixed that");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -124,10 +126,12 @@ class IntegrityCheck
|
||||
/**
|
||||
* Check the integrity of the domain to ip/port - association
|
||||
*
|
||||
* @param bool $fix
|
||||
* Fix everything found directly
|
||||
* @param bool $fix fix everything found directly
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function domainIpTable($fix = false)
|
||||
public function domainIpTable(bool $fix = false): bool
|
||||
{
|
||||
$ips = [];
|
||||
$domains = [];
|
||||
@@ -184,9 +188,11 @@ class IntegrityCheck
|
||||
'domainid' => $row['id_domain'],
|
||||
'ipandportid' => $row['id_ipandports']
|
||||
]);
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found an ip/port-id in domain <> ip table which does not exist, integrity check fixed this");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
|
||||
"found an ip/port-id in domain <> ip table which does not exist, integrity check fixed this");
|
||||
} else {
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found an ip/port-id in domain <> ip table which does not exist, integrity check can fix this");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
|
||||
"found an ip/port-id in domain <> ip table which does not exist, integrity check can fix this");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -196,9 +202,11 @@ class IntegrityCheck
|
||||
'domainid' => $row['id_domain'],
|
||||
'ipandportid' => $row['id_ipandports']
|
||||
]);
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a domain-id in domain <> ip table which does not exist, integrity check fixed this");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
|
||||
"found a domain-id in domain <> ip table which does not exist, integrity check fixed this");
|
||||
} else {
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a domain-id in domain <> ip table which does not exist, integrity check can fix this");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
|
||||
"found a domain-id in domain <> ip table which does not exist, integrity check can fix this");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -216,9 +224,11 @@ class IntegrityCheck
|
||||
'ipandportid' => $defaultip
|
||||
]);
|
||||
}
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a domain-id with no entry in domain <> ip table, integrity check fixed this");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
|
||||
"found a domain-id with no entry in domain <> ip table, integrity check fixed this");
|
||||
} else {
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a domain-id with no entry in domain <> ip table, integrity check can fix this");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
|
||||
"found a domain-id with no entry in domain <> ip table, integrity check can fix this");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -226,18 +236,19 @@ class IntegrityCheck
|
||||
|
||||
if ($fix) {
|
||||
return $this->domainIpTable();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all subdomains have ssl-redirect = 0 if domain has no ssl-port
|
||||
*
|
||||
* @param bool $fix
|
||||
* Fix everything found directly
|
||||
* @param bool $fix fix everything found directly
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function subdomainSslRedirect($fix = false)
|
||||
public function subdomainSslRedirect(bool $fix = false): bool
|
||||
{
|
||||
$ips = [];
|
||||
$parentdomains = [];
|
||||
@@ -300,28 +311,31 @@ class IntegrityCheck
|
||||
Database::pexecute($upd_stmt, [
|
||||
'domainid' => $id
|
||||
]);
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check fixed this");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
|
||||
"found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check fixed this");
|
||||
} else {
|
||||
// It's just the check, let the function fail
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check can fix this");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
|
||||
"found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check can fix this");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fix) {
|
||||
return $this->subdomainSslRedirect();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all subdomain have letsencrypt = 0 if domain has no ssl-port
|
||||
*
|
||||
* @param bool $fix
|
||||
* Fix everything found directly
|
||||
* @param bool $fix fix everything found directly
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function subdomainLetsencrypt($fix = false)
|
||||
public function subdomainLetsencrypt(bool $fix = false): bool
|
||||
{
|
||||
$ips = [];
|
||||
$parentdomains = [];
|
||||
@@ -384,31 +398,32 @@ class IntegrityCheck
|
||||
Database::pexecute($upd_stmt, [
|
||||
'domainid' => $id
|
||||
]);
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check fixed this");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
|
||||
"found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check fixed this");
|
||||
} else {
|
||||
// It's just the check, let the function fail
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check can fix this");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
|
||||
"found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check can fix this");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fix) {
|
||||
return $this->subdomainLetsencrypt();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether the webserveruser is in
|
||||
* the customers groups when fcgid / php-fpm is used
|
||||
*
|
||||
* @param bool $fix
|
||||
* fix member/groups
|
||||
* @param bool $fix fix member/groups
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function webserverGroupMemberForFcgidPhpFpm($fix = false)
|
||||
public function webserverGroupMemberForFcgidPhpFpm(bool $fix = false): bool
|
||||
{
|
||||
if (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0) {
|
||||
return true;
|
||||
@@ -423,7 +438,8 @@ class IntegrityCheck
|
||||
]);
|
||||
|
||||
if ($cwg_stmt->rowCount() > 0) {
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers are missing the webserver-user as group-member, integrity-check can fix that");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
|
||||
"Customers are missing the webserver-user as group-member, integrity-check can fix that");
|
||||
if ($fix) {
|
||||
// prepare update statement
|
||||
$upd_stmt = Database::prepare("
|
||||
@@ -438,7 +454,8 @@ class IntegrityCheck
|
||||
$upd_data['id'] = $cwg_row['id'];
|
||||
Database::pexecute($upd_stmt, $upd_data);
|
||||
}
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers were missing the webserver-user as group-member, integrity-check fixed that");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
|
||||
"Customers were missing the webserver-user as group-member, integrity-check fixed that");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -455,12 +472,12 @@ class IntegrityCheck
|
||||
* the customers groups when fcgid / php-fpm and
|
||||
* fcgid/fpm in froxlor vhost is used
|
||||
*
|
||||
* @param bool $fix
|
||||
* fix member/groups
|
||||
* @param bool $fix fix member/groups
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function froxlorLocalGroupMemberForFcgidPhpFpm($fix = false)
|
||||
public function froxlorLocalGroupMemberForFcgidPhpFpm(bool $fix = false): bool
|
||||
{
|
||||
if (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0) {
|
||||
return true;
|
||||
@@ -491,7 +508,8 @@ class IntegrityCheck
|
||||
]);
|
||||
|
||||
if ($cwg_stmt->rowCount() > 0) {
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers are missing the local froxlor-user as group-member, integrity-check can fix that");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
|
||||
"Customers are missing the local froxlor-user as group-member, integrity-check can fix that");
|
||||
if ($fix) {
|
||||
// prepare update statement
|
||||
$upd_stmt = Database::prepare("
|
||||
@@ -506,7 +524,8 @@ class IntegrityCheck
|
||||
$upd_data['id'] = $cwg_row['id'];
|
||||
Database::pexecute($upd_stmt, $upd_data);
|
||||
}
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers were missing the local froxlor-user as group-member, integrity-check fixed that");
|
||||
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
|
||||
"Customers were missing the local froxlor-user as group-member, integrity-check fixed that");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class DbManagerMySQL
|
||||
/**
|
||||
* main constructor
|
||||
*
|
||||
* @param FroxlorLogger $log
|
||||
* @param FroxlorLogger|null $log
|
||||
*/
|
||||
public function __construct(&$log = null)
|
||||
{
|
||||
@@ -58,9 +58,9 @@ class DbManagerMySQL
|
||||
/**
|
||||
* creates a database
|
||||
*
|
||||
* @param string $dbname
|
||||
* @param string|null $dbname
|
||||
*/
|
||||
public function createDatabase($dbname = null)
|
||||
public function createDatabase(string $dbname = null)
|
||||
{
|
||||
Database::query("CREATE DATABASE `" . $dbname . "`");
|
||||
}
|
||||
@@ -71,13 +71,14 @@ class DbManagerMySQL
|
||||
*
|
||||
* @param string $username
|
||||
* @param string|array $password
|
||||
* @param string $access_host
|
||||
* @param ?string $access_host
|
||||
* @param bool $p_encrypted
|
||||
* optional, whether the password is encrypted or not, default false
|
||||
* @param bool $update
|
||||
* optional, whether to update the password only (not create user)
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function grantPrivilegesTo($username = null, $password = null, $access_host = null, $p_encrypted = false, $update = false)
|
||||
public function grantPrivilegesTo(string $username, $password, string $access_host = null, bool $p_encrypted = false, bool $update = false)
|
||||
{
|
||||
$pwd_plugin = 'mysql_native_password';
|
||||
if (is_array($password) && count($password) == 2) {
|
||||
@@ -141,8 +142,9 @@ class DbManagerMySQL
|
||||
* takes away any privileges from a user to that db
|
||||
*
|
||||
* @param string $dbname
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function deleteDatabase($dbname = null)
|
||||
public function deleteDatabase(string $dbname)
|
||||
{
|
||||
if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.0.2', '<')) {
|
||||
// failsafe if user has been deleted manually (requires MySQL 4.1.2+)
|
||||
@@ -178,8 +180,9 @@ class DbManagerMySQL
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $host
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function deleteUser($username = null, $host = null)
|
||||
public function deleteUser(string $username, string $host)
|
||||
{
|
||||
if (Database::getAttribute(PDO::ATTR_SERVER_VERSION) < '5.0.2') {
|
||||
// Revoke privileges (only required for MySQL 4.1.2 - 5.0.1)
|
||||
@@ -203,9 +206,9 @@ class DbManagerMySQL
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $host
|
||||
* (unused in mysql)
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function disableUser($username = null, $host = null)
|
||||
public function disableUser(string $username, string $host)
|
||||
{
|
||||
$stmt = Database::prepare('REVOKE ALL PRIVILEGES, GRANT OPTION FROM `' . $username . '`@`' . $host . '`');
|
||||
Database::pexecute($stmt, [], false);
|
||||
@@ -216,8 +219,9 @@ class DbManagerMySQL
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $host
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function enableUser($username = null, $host = null)
|
||||
public function enableUser(string $username, string $host)
|
||||
{
|
||||
// check whether user exists to avoid errors
|
||||
$exist_check_stmt = Database::prepare("SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '" . $username . "' AND host = '" . $host . "')");
|
||||
@@ -239,14 +243,14 @@ class DbManagerMySQL
|
||||
/**
|
||||
* return an array of all usernames used in that DBMS
|
||||
*
|
||||
* @param bool $user_only
|
||||
* if false, * will be selected from mysql.user and slightly different array will be generated
|
||||
* @param bool $user_only if false, will be selected from mysql.user and slightly different array will be generated
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getAllSqlUsers($user_only = true)
|
||||
public function getAllSqlUsers(bool $user_only = true): array
|
||||
{
|
||||
if ($user_only == false) {
|
||||
if (!$user_only) {
|
||||
$result_stmt = Database::prepare('SELECT * FROM mysql.user');
|
||||
} else {
|
||||
$result_stmt = Database::prepare('SELECT `User` FROM mysql.user');
|
||||
|
||||
@@ -33,7 +33,15 @@ use PDO;
|
||||
|
||||
class Dns
|
||||
{
|
||||
public static function getAllowedDomainEntry($domain_id, $area = 'customer', $userinfo = [])
|
||||
/**
|
||||
* @param int $domain_id
|
||||
* @param string $area
|
||||
* @param array $userinfo
|
||||
*
|
||||
* @return string|void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getAllowedDomainEntry(int $domain_id, string $area = 'customer', array $userinfo = [])
|
||||
{
|
||||
$dom_data = [
|
||||
'did' => $domain_id
|
||||
@@ -67,7 +75,15 @@ class Dns
|
||||
Response::standardError('dns_notfoundorallowed');
|
||||
}
|
||||
|
||||
public static function createDomainZone($domain_id, $froxlorhostname = false, $isMainButSubTo = false)
|
||||
/**
|
||||
* @param int|array $domain_id id of domain or in case of froxlorhostname, a domain-array with the needed data
|
||||
* @param bool $froxlorhostname
|
||||
* @param bool $isMainButSubTo
|
||||
*
|
||||
* @return DnsZone|void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function createDomainZone($domain_id, bool $froxlorhostname = false, bool $isMainButSubTo = false)
|
||||
{
|
||||
if (!$froxlorhostname) {
|
||||
// get domain-name
|
||||
@@ -136,7 +152,7 @@ class Dns
|
||||
if (!$froxlorhostname) {
|
||||
// additional required records for subdomains
|
||||
$subdomains_stmt = Database::prepare("
|
||||
SELECT `domain`, `iswildcarddomain`, `wwwserveralias` FROM `" . TABLE_PANEL_DOMAINS . "`
|
||||
SELECT `domain`, `iswildcarddomain`, `wwwserveralias`, `isemaildomain` FROM `" . TABLE_PANEL_DOMAINS . "`
|
||||
WHERE `parentdomainid` = :domainid
|
||||
");
|
||||
Database::pexecute($subdomains_stmt, [
|
||||
@@ -144,18 +160,31 @@ class Dns
|
||||
]);
|
||||
|
||||
while ($subdomain = $subdomains_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$sub_record = str_replace('.' . $domain['domain'], '', $subdomain['domain']);
|
||||
// Listing domains is enough as there currently is no support for choosing
|
||||
// different ips for a subdomain => use same IPs as toplevel
|
||||
self::addRequiredEntry(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries);
|
||||
self::addRequiredEntry(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries);
|
||||
self::addRequiredEntry($sub_record, 'A',$required_entries);
|
||||
self::addRequiredEntry($sub_record, 'AAAA', $required_entries);
|
||||
|
||||
// Check whether to add a www.-prefix
|
||||
if ($subdomain['iswildcarddomain'] == '1') {
|
||||
self::addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries);
|
||||
self::addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries);
|
||||
self::addRequiredEntry('*.' . $sub_record, 'A', $required_entries);
|
||||
self::addRequiredEntry('*.' . $sub_record, 'AAAA', $required_entries);
|
||||
} elseif ($subdomain['wwwserveralias'] == '1') {
|
||||
self::addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries);
|
||||
self::addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries);
|
||||
self::addRequiredEntry('www.' . $sub_record, 'A', $required_entries);
|
||||
self::addRequiredEntry('www.' . $sub_record, 'AAAA', $required_entries);
|
||||
}
|
||||
|
||||
// check for email ability
|
||||
if ($subdomain['isemaildomain'] == '1') {
|
||||
if (Settings::Get('spf.use_spf') == '1') {
|
||||
// check for SPF content later
|
||||
self::addRequiredEntry('@SPF@.' . $sub_record, 'TXT', $required_entries);
|
||||
}
|
||||
if (Settings::Get('dkim.use_dkim') == '1') {
|
||||
// check for DKIM content later
|
||||
self::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey.' . $sub_record, 'TXT', $required_entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,14 +229,17 @@ class Dns
|
||||
|
||||
// now generate all records and unset the required entries we have
|
||||
foreach ($dom_entries as $entry) {
|
||||
if (array_key_exists($entry['type'], $required_entries) && array_key_exists(md5($entry['record']), $required_entries[$entry['type']])) {
|
||||
if (array_key_exists($entry['type'], $required_entries) && array_key_exists(md5($entry['record']),
|
||||
$required_entries[$entry['type']])) {
|
||||
unset($required_entries[$entry['type']][md5($entry['record'])]);
|
||||
}
|
||||
if (Settings::Get('system.dns_createcaaentry') == '1' && $entry['type'] == 'CAA' && strtolower(substr($entry['content'], 0, 7)) == '"v=caa1') {
|
||||
if (Settings::Get('system.dns_createcaaentry') == '1' && $entry['type'] == 'CAA' && strtolower(substr($entry['content'],
|
||||
0, 7)) == '"v=caa1') {
|
||||
// unset special CAA required-entry
|
||||
unset($required_entries[$entry['type']][md5("@CAA@")]);
|
||||
}
|
||||
if (Settings::Get('spf.use_spf') == '1' && $entry['type'] == 'TXT' && $entry['record'] == '@' && (strtolower(substr($entry['content'], 0, 7)) == '"v=spf1' || strtolower(substr($entry['content'], 0, 6)) == 'v=spf1')) {
|
||||
if (Settings::Get('spf.use_spf') == '1' && $entry['type'] == 'TXT' && $entry['record'] == '@' && (strtolower(substr($entry['content'],
|
||||
0, 7)) == '"v=spf1' || strtolower(substr($entry['content'], 0, 6)) == 'v=spf1')) {
|
||||
// unset special spf required-entry
|
||||
unset($required_entries[$entry['type']][md5("@SPF@")]);
|
||||
}
|
||||
@@ -223,7 +255,8 @@ class Dns
|
||||
'*'
|
||||
] as $crecord
|
||||
) {
|
||||
if ($entry['type'] == 'CNAME' && $entry['record'] == '@' && (array_key_exists(md5($crecord), $required_entries['A']) || array_key_exists(md5($crecord), $required_entries['AAAA']))) {
|
||||
if ($entry['type'] == 'CNAME' && $entry['record'] == '@' && (array_key_exists(md5($crecord),
|
||||
$required_entries['A']) || array_key_exists(md5($crecord), $required_entries['AAAA']))) {
|
||||
unset($required_entries['A'][md5($crecord)]);
|
||||
unset($required_entries['AAAA'][md5($crecord)]);
|
||||
}
|
||||
@@ -238,13 +271,15 @@ class Dns
|
||||
'smtp'
|
||||
] as $crecord
|
||||
) {
|
||||
if ($entry['type'] == 'CNAME' && $entry['record'] == $crecord && (array_key_exists(md5($crecord), $required_entries['A']) || array_key_exists(md5($crecord), $required_entries['AAAA']))) {
|
||||
if ($entry['type'] == 'CNAME' && $entry['record'] == $crecord && (array_key_exists(md5($crecord),
|
||||
$required_entries['A']) || array_key_exists(md5($crecord),
|
||||
$required_entries['AAAA']))) {
|
||||
unset($required_entries['A'][md5($crecord)]);
|
||||
unset($required_entries['AAAA'][md5($crecord)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$zonerecords[] = new DnsEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'], $entry['ttl']);
|
||||
$zonerecords[] = new DnsEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'] ?? 0, $entry['ttl']);
|
||||
}
|
||||
|
||||
// add missing required entries
|
||||
@@ -275,7 +310,8 @@ class Dns
|
||||
foreach ($records as $record) {
|
||||
if ($type == 'A' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
|
||||
$zonerecords[] = new DnsEntry($record, 'A', $ip['ip']);
|
||||
} elseif ($type == 'AAAA' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
|
||||
} elseif ($type == 'AAAA' && filter_var($ip['ip'], FILTER_VALIDATE_IP,
|
||||
FILTER_FLAG_IPV6) !== false) {
|
||||
$zonerecords[] = new DnsEntry($record, 'AAAA', $ip['ip']);
|
||||
}
|
||||
}
|
||||
@@ -348,15 +384,34 @@ class Dns
|
||||
if ($type == 'TXT') {
|
||||
foreach ($records as $record) {
|
||||
if ($record == '@SPF@') {
|
||||
// spf for main-domain
|
||||
$txt_content = Settings::Get('spf.spf_entry');
|
||||
$zonerecords[] = new DnsEntry('@', 'TXT', self::encloseTXTContent($txt_content));
|
||||
} elseif ($record == 'dkim' . $domain['dkim_id'] . '._domainkey' && !empty($dkim_entries)) {
|
||||
// check for multiline entry
|
||||
$multiline = false;
|
||||
if (substr($dkim_entries[0], 0, 1) == '(') {
|
||||
$multiline = true;
|
||||
} elseif (strlen($record) > 6 && substr($record, 0, 6) == '@SPF@.') {
|
||||
// spf for subdomain
|
||||
$txt_content = Settings::Get('spf.spf_entry');
|
||||
$sub_record = substr($record, 6);
|
||||
$zonerecords[] = new DnsEntry($sub_record, 'TXT', self::encloseTXTContent($txt_content));
|
||||
} elseif (!empty($dkim_entries)) {
|
||||
// DKIM entries
|
||||
$dkim_record = 'dkim' . $domain['dkim_id'] . '._domainkey';
|
||||
if ($record == $dkim_record) {
|
||||
// dkim for main-domain
|
||||
// check for multiline entry
|
||||
$multiline = false;
|
||||
if (substr($dkim_entries[0], 0, 1) == '(') {
|
||||
$multiline = true;
|
||||
}
|
||||
$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dkim_entries[0], $multiline));
|
||||
} elseif (strlen($record) > strlen($dkim_record) && substr($record, 0, strlen($dkim_record)+1) == $dkim_record . '.') {
|
||||
// dkim for subdomain-domain
|
||||
// check for multiline entry
|
||||
$multiline = false;
|
||||
if (substr($dkim_entries[0], 0, 1) == '(') {
|
||||
$multiline = true;
|
||||
}
|
||||
$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dkim_entries[0], $multiline));
|
||||
}
|
||||
$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dkim_entries[0], $multiline));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -416,7 +471,8 @@ class Dns
|
||||
|
||||
if (!$isMainButSubTo) {
|
||||
$date = date('Ymd');
|
||||
$domain['bindserial'] = (preg_match('/^' . $date . '/', $domain['bindserial']) ? $domain['bindserial'] + 1 : $date . '00');
|
||||
$domain['bindserial'] = (preg_match('/^' . $date . '/',
|
||||
$domain['bindserial']) ? $domain['bindserial'] + 1 : $date . '00');
|
||||
if (!$froxlorhostname) {
|
||||
$upd_stmt = Database::prepare("
|
||||
UPDATE `" . TABLE_PANEL_DOMAINS . "` SET
|
||||
@@ -443,12 +499,19 @@ class Dns
|
||||
array_unshift($zonerecords, $soa_record);
|
||||
}
|
||||
|
||||
$zone = new DnsZone((int)Settings::Get('system.defaultttl'), $domain['domain'], $domain['bindserial'], $zonerecords);
|
||||
$zone = new DnsZone((int)Settings::Get('system.defaultttl'), $domain['domain'], $domain['bindserial'],
|
||||
$zonerecords);
|
||||
|
||||
return $zone;
|
||||
}
|
||||
|
||||
private static function addRequiredEntry($record = '@', $type = 'A', &$required = [])
|
||||
/**
|
||||
* @param string $record
|
||||
* @param string $type
|
||||
* @param array $required
|
||||
* @return void
|
||||
*/
|
||||
private static function addRequiredEntry(string $record = '@', string $type = 'A', array &$required = [])
|
||||
{
|
||||
if (!isset($required[$type])) {
|
||||
$required[$type] = [];
|
||||
@@ -456,7 +519,11 @@ class Dns
|
||||
$required[$type][md5($record)] = $record;
|
||||
}
|
||||
|
||||
private static function generateDkimEntries($domain)
|
||||
/**
|
||||
* @param array $domain
|
||||
* @return array
|
||||
*/
|
||||
private static function generateDkimEntries(array $domain): array
|
||||
{
|
||||
$zone_dkim = [];
|
||||
|
||||
@@ -486,7 +553,8 @@ class Dns
|
||||
}
|
||||
|
||||
// key
|
||||
$dkim_txt .= 'k=rsa;p=' . trim(preg_replace('/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s', '$1', str_replace("\n", '', $domain['dkim_pubkey']))) . ';';
|
||||
$dkim_txt .= 'k=rsa;p=' . trim(preg_replace('/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s',
|
||||
'$1', str_replace("\n", '', $domain['dkim_pubkey']))) . ';';
|
||||
|
||||
// service-type
|
||||
if (Settings::Get('dkim.dkim_servicetype') == '1') {
|
||||
@@ -503,10 +571,15 @@ class Dns
|
||||
return $zone_dkim;
|
||||
}
|
||||
|
||||
public static function encloseTXTContent($txt_content, $isMultiLine = false)
|
||||
/**
|
||||
* @param string $txt_content
|
||||
* @param bool $isMultiLine
|
||||
* @return string
|
||||
*/
|
||||
public static function encloseTXTContent(string $txt_content, bool $isMultiLine = false): string
|
||||
{
|
||||
// check that TXT content is enclosed in " "
|
||||
if ($isMultiLine == false && Settings::Get('system.dns_server') != 'PowerDNS') {
|
||||
if (!$isMultiLine && Settings::Get('system.dns_server') != 'PowerDNS') {
|
||||
if (substr($txt_content, 0, 1) != '"') {
|
||||
$txt_content = '"' . $txt_content;
|
||||
}
|
||||
@@ -526,10 +599,13 @@ class Dns
|
||||
return $txt_content;
|
||||
}
|
||||
|
||||
private static function escapeSoaAdminMail($email)
|
||||
/**
|
||||
* @param string $email
|
||||
* @return string
|
||||
*/
|
||||
private static function escapeSoaAdminMail(string $email): string
|
||||
{
|
||||
$mail_parts = explode("@", $email);
|
||||
$escpd_mail = str_replace(".", "\.", $mail_parts[0]) . "." . $mail_parts[1] . ".";
|
||||
return $escpd_mail;
|
||||
return str_replace(".", "\.", $mail_parts[0]) . "." . $mail_parts[1] . ".";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +29,22 @@ use Froxlor\Settings;
|
||||
|
||||
class DnsEntry
|
||||
{
|
||||
public $record;
|
||||
public $ttl;
|
||||
public $class = 'IN';
|
||||
public $type;
|
||||
public $priority;
|
||||
public $content;
|
||||
public string $record;
|
||||
public int $ttl;
|
||||
public string $class = 'IN';
|
||||
public string $type;
|
||||
public int $priority;
|
||||
public ?string $content;
|
||||
|
||||
public function __construct($record = '', $type = 'A', $content = null, $prio = 0, $ttl = 0, $class = 'IN')
|
||||
/**
|
||||
* @param string $record
|
||||
* @param string $type
|
||||
* @param string|null $content
|
||||
* @param int $prio
|
||||
* @param int $ttl
|
||||
* @param string $class
|
||||
*/
|
||||
public function __construct(string $record = '', string $type = 'A', string $content = null, int $prio = 0, int $ttl = 0, string $class = 'IN')
|
||||
{
|
||||
$this->record = $record;
|
||||
$this->type = $type;
|
||||
@@ -72,7 +80,6 @@ class DnsEntry
|
||||
// last line
|
||||
$_content .= "\t\t\t\t" . '"' . $_l . '")';
|
||||
}
|
||||
$result = $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "") . $_content . PHP_EOL;
|
||||
return $result;
|
||||
return $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "") . $_content . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,18 @@ use Froxlor\Settings;
|
||||
|
||||
class DnsZone
|
||||
{
|
||||
public $ttl;
|
||||
public $origin;
|
||||
public $serial;
|
||||
public $records;
|
||||
public int $ttl;
|
||||
public string $origin;
|
||||
public string $serial;
|
||||
public ?array $records;
|
||||
|
||||
public function __construct($ttl = 0, $origin = '', $serial = '', $records = null)
|
||||
/**
|
||||
* @param int $ttl
|
||||
* @param string $origin
|
||||
* @param string $serial
|
||||
* @param array|null $records
|
||||
*/
|
||||
public function __construct(int $ttl = 0, string $origin = '', string $serial = '', array $records = null)
|
||||
{
|
||||
$this->ttl = ($ttl <= 0 ? Settings::Get('system.defaultttl') : $ttl);
|
||||
$this->origin = $origin;
|
||||
@@ -44,13 +50,13 @@ class DnsZone
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$_zonefile = "\$TTL " . $this->ttl . PHP_EOL;
|
||||
$_zonefile .= "\$ORIGIN " . $this->origin . "." . PHP_EOL;
|
||||
$zone_file = "\$TTL " . $this->ttl . PHP_EOL;
|
||||
$zone_file .= "\$ORIGIN " . $this->origin . "." . PHP_EOL;
|
||||
if (!empty($this->records)) {
|
||||
foreach ($this->records as $record) {
|
||||
$_zonefile .= (string)$record;
|
||||
$zone_file .= (string)$record;
|
||||
}
|
||||
}
|
||||
return $_zonefile;
|
||||
return $zone_file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,18 +37,18 @@ class PowerDNS
|
||||
/**
|
||||
* remove all records and entries of a given domain
|
||||
*
|
||||
* @param array $domain
|
||||
* @param string|null $domain
|
||||
*/
|
||||
public static function cleanDomainZone($domain = null)
|
||||
public static function cleanDomainZone(string $domain = null)
|
||||
{
|
||||
if (is_array($domain) && isset($domain['domain'])) {
|
||||
if (!empty($domain)) {
|
||||
$pdns_domains_stmt = self::getDB()->prepare("SELECT `id`, `name` FROM `domains` WHERE `name` = :domain");
|
||||
$del_rec_stmt = self::getDB()->prepare("DELETE FROM `records` WHERE `domain_id` = :did");
|
||||
$del_meta_stmt = self::getDB()->prepare("DELETE FROM `domainmetadata` WHERE `domain_id` = :did");
|
||||
$del_dom_stmt = self::getDB()->prepare("DELETE FROM `domains` WHERE `id` = :did");
|
||||
|
||||
$pdns_domains_stmt->execute([
|
||||
'domain' => $domain['domain']
|
||||
'domain' => $domain
|
||||
]);
|
||||
$pdns_domain = $pdns_domains_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
@@ -67,16 +67,19 @@ class PowerDNS
|
||||
/**
|
||||
* get pdo database connection to powerdns database
|
||||
*
|
||||
* @return PDO
|
||||
* @return \PDO
|
||||
*/
|
||||
public static function getDB()
|
||||
public static function getDB(): \PDO
|
||||
{
|
||||
if (!isset(self::$pdns_db) || (self::$pdns_db instanceof PDO) == false) {
|
||||
if (!isset(self::$pdns_db) || !(self::$pdns_db instanceof PDO)) {
|
||||
self::connectToPdnsDb();
|
||||
}
|
||||
return self::$pdns_db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function connectToPdnsDb()
|
||||
{
|
||||
// get froxlor pdns config
|
||||
|
||||
@@ -41,8 +41,9 @@ class Domain
|
||||
*
|
||||
* @param int $domain_id
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getIpsOfDomain($domain_id)
|
||||
public static function getIpsOfDomain(int $domain_id = 0): array
|
||||
{
|
||||
if ($domain_id > 0) {
|
||||
$sel_stmt = Database::prepare("
|
||||
@@ -75,7 +76,7 @@ class Domain
|
||||
*
|
||||
* @return array array of enabled redirect-codes
|
||||
*/
|
||||
public static function getRedirectCodesArray()
|
||||
public static function getRedirectCodesArray(): array
|
||||
{
|
||||
$sql = "SELECT * FROM `" . TABLE_PANEL_REDIRECTCODES . "` WHERE `enabled` = '1' ORDER BY `id` ASC";
|
||||
$result_stmt = Database::query($sql);
|
||||
@@ -92,12 +93,12 @@ class Domain
|
||||
* returns the redirect-code for a given
|
||||
* domain-id
|
||||
*
|
||||
* @param integer $domainid
|
||||
* id of the domain
|
||||
* @param int $domainid id of the domain
|
||||
*
|
||||
* @return string redirect-code
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getDomainRedirectCode($domainid = 0)
|
||||
public static function getDomainRedirectCode(int $domainid = 0): string
|
||||
{
|
||||
// get system default
|
||||
$default = '301';
|
||||
@@ -128,12 +129,11 @@ class Domain
|
||||
* return an array of all enabled redirect-codes
|
||||
* for the settings form
|
||||
*
|
||||
* @param bool $add_desc
|
||||
* optional, default true, add the code-description
|
||||
* @param bool $add_desc optional, default true, add the code-description
|
||||
*
|
||||
* @return array array of enabled redirect-codes
|
||||
*/
|
||||
public static function getRedirectCodes($add_desc = true)
|
||||
public static function getRedirectCodes(bool $add_desc = true): array
|
||||
{
|
||||
$sql = "SELECT * FROM `" . TABLE_PANEL_REDIRECTCODES . "` WHERE `enabled` = '1' ORDER BY `id` ASC";
|
||||
$result_stmt = Database::query($sql);
|
||||
@@ -153,12 +153,12 @@ class Domain
|
||||
* returns the redirect-id for a given
|
||||
* domain-id
|
||||
*
|
||||
* @param integer $domainid
|
||||
* id of the domain
|
||||
* @param int $domainid id of the domain
|
||||
*
|
||||
* @return integer redirect-code-id
|
||||
* @return int redirect-code-id
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getDomainRedirectId($domainid = 0)
|
||||
public static function getDomainRedirectId(int $domainid = 0): int
|
||||
{
|
||||
$code = 1;
|
||||
if ($domainid > 0) {
|
||||
@@ -171,7 +171,7 @@ class Domain
|
||||
'domainid' => $domainid
|
||||
]);
|
||||
|
||||
if (is_array($result) && isset($result['redirect'])) {
|
||||
if ($result && isset($result['redirect'])) {
|
||||
$code = (int)$result['redirect'];
|
||||
}
|
||||
}
|
||||
@@ -179,16 +179,15 @@ class Domain
|
||||
}
|
||||
|
||||
/**
|
||||
* adds a redirectcode for a domain
|
||||
* adds a redirect-code for a domain
|
||||
*
|
||||
* @param integer $domainid
|
||||
* id of the domain to add the code for
|
||||
* @param integer $redirect
|
||||
* selected redirect-id
|
||||
* @param int $domainid id of the domain to add the code for
|
||||
* @param int $redirect selected redirect-id
|
||||
*
|
||||
* @return null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function addRedirectToDomain($domainid = 0, $redirect = 1)
|
||||
public static function addRedirectToDomain(int $domainid = 0, int $redirect = 1)
|
||||
{
|
||||
if ($domainid > 0) {
|
||||
$ins_stmt = Database::prepare("
|
||||
@@ -202,19 +201,18 @@ class Domain
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the redirectcode of a domain
|
||||
* updates the redirect-code of a domain
|
||||
* if redirect-code is false, nothing happens
|
||||
*
|
||||
* @param integer $domainid
|
||||
* id of the domain to update
|
||||
* @param integer $redirect
|
||||
* selected redirect-id or false
|
||||
* @param int $domainid id of the domain to update
|
||||
* @param int $redirect selected redirect-id
|
||||
*
|
||||
* @return null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function updateRedirectOfDomain($domainid = 0, $redirect = false)
|
||||
public static function updateRedirectOfDomain(int $domainid = 0, int $redirect = 0)
|
||||
{
|
||||
if ($redirect == false) {
|
||||
if (!$redirect) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -240,12 +238,12 @@ class Domain
|
||||
* check whether a domain has subdomains added as full-domains
|
||||
* #329
|
||||
*
|
||||
* @param int $id
|
||||
* domain-id
|
||||
* @param int $id domain-id
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function domainHasMainSubDomains($id = 0)
|
||||
public static function domainHasMainSubDomains(int $id): bool
|
||||
{
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT COUNT(`id`) as `mainsubs` FROM `" . TABLE_PANEL_DOMAINS . "`
|
||||
@@ -254,8 +252,8 @@ class Domain
|
||||
'id' => $id
|
||||
]);
|
||||
|
||||
if (isset($result['mainsubs']) && $result['mainsubs'] > 0) {
|
||||
return true;
|
||||
if ($result && isset($result['mainsubs'])) {
|
||||
return $result['mainsubs'] > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -264,12 +262,12 @@ class Domain
|
||||
* check whether a subof-domain exists
|
||||
* #329
|
||||
*
|
||||
* @param int $id
|
||||
* subof-domain-id
|
||||
* @param int $id subof-domain-id
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function domainMainToSubExists($id = 0)
|
||||
public static function domainMainToSubExists(int $id): bool
|
||||
{
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `id` = :id");
|
||||
@@ -278,8 +276,8 @@ class Domain
|
||||
]);
|
||||
$result = $result_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (isset($result['id']) && $result['id'] > 0) {
|
||||
return true;
|
||||
if ($result && isset($result['id'])) {
|
||||
return $result['id'] > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -289,9 +287,10 @@ class Domain
|
||||
*
|
||||
* @param int $domainid
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function domainHasSslIpPort($domainid = 0)
|
||||
public static function domainHasSslIpPort(int $domainid): bool
|
||||
{
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT `dt`.* FROM `" . TABLE_DOMAINTOIP . "` `dt`, `" . TABLE_PANEL_IPSANDPORTS . "` `iap`
|
||||
@@ -301,7 +300,7 @@ class Domain
|
||||
]);
|
||||
$result = $result_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (is_array($result) && isset($result['id_ipandports'])) {
|
||||
if ($result && isset($result['id_ipandports'])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -311,12 +310,12 @@ class Domain
|
||||
* returns true or false whether a given domain id
|
||||
* is the std-subdomain of a customer
|
||||
*
|
||||
* @param
|
||||
* int domain-id
|
||||
* @param int $did domain-id
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function isCustomerStdSubdomain($did = 0)
|
||||
public static function isCustomerStdSubdomain(int $did): bool
|
||||
{
|
||||
if ($did > 0) {
|
||||
$result_stmt = Database::prepare("
|
||||
@@ -327,21 +326,31 @@ class Domain
|
||||
'did' => $did
|
||||
]);
|
||||
|
||||
if (is_array($result) && isset($result['customerid']) && $result['customerid'] > 0) {
|
||||
return true;
|
||||
if ($result && isset($result['customerid'])) {
|
||||
return $result['customerid'] > 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function triggerLetsEncryptCSRForAliasDestinationDomain($aliasDestinationDomainID, $log)
|
||||
{
|
||||
if (isset($aliasDestinationDomainID) && $aliasDestinationDomainID > 0) {
|
||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "LetsEncrypt CSR triggered for domain ID " . $aliasDestinationDomainID);
|
||||
/**
|
||||
* @param int $aliasDestinationDomainID
|
||||
* @param FroxlorLogger $log
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function triggerLetsEncryptCSRForAliasDestinationDomain(
|
||||
int $aliasDestinationDomainID,
|
||||
FroxlorLogger $log
|
||||
) {
|
||||
if ($aliasDestinationDomainID > 0) {
|
||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO,
|
||||
"LetsEncrypt CSR triggered for domain ID " . $aliasDestinationDomainID);
|
||||
$upd_stmt = Database::prepare("UPDATE
|
||||
`" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
|
||||
SET
|
||||
`expirationdate` = null
|
||||
`validtodate` = null
|
||||
WHERE
|
||||
domainid = :domainid
|
||||
");
|
||||
@@ -351,7 +360,11 @@ class Domain
|
||||
}
|
||||
}
|
||||
|
||||
public static function doLetsEncryptCleanUp($domainname = null)
|
||||
/**
|
||||
* @param string $domainname
|
||||
* @return true
|
||||
*/
|
||||
public static function doLetsEncryptCleanUp(string $domainname): bool
|
||||
{
|
||||
// @ see \Froxlor\Cron\Http\LetsEncrypt\AcmeSh.php
|
||||
$acmesh = AcmeSh::getAcmeSh();
|
||||
@@ -374,18 +387,19 @@ class Domain
|
||||
/**
|
||||
* checks give path for security issues
|
||||
* and returns a string that can be appended
|
||||
* to a line for a open_basedir directive
|
||||
* to a line for an open_basedir directive
|
||||
*
|
||||
* @param string $path
|
||||
* the path to check and append
|
||||
* @param boolean $first
|
||||
* if true, no ':' will be prefixed to the path
|
||||
* @param string $path the path to check and append
|
||||
* @param bool $first if true, no ':' will be prefixed to the path
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function appendOpenBasedirPath($path = '', $first = false)
|
||||
public static function appendOpenBasedirPath(string $path = '', bool $first = false): string
|
||||
{
|
||||
if ($path != '' && $path != '/' && (!preg_match("#^/dev#i", $path) || preg_match("#^/dev/urandom#i", $path)) && !preg_match("#^/proc#i", $path) && !preg_match("#^/etc#i", $path) && !preg_match("#^/sys#i", $path) && !preg_match("#:#", $path)) {
|
||||
if ($path != '' && $path != '/' && (!preg_match("#^/dev#i", $path) || preg_match("#^/dev/urandom#i",
|
||||
$path)) && !preg_match("#^/proc#i", $path) && !preg_match("#^/etc#i",
|
||||
$path) && !preg_match("#^/sys#i", $path) && !preg_match("#:#", $path)) {
|
||||
if (preg_match("#^/dev/urandom#i", $path)) {
|
||||
$path = FileDir::makeCorrectFile($path);
|
||||
} else {
|
||||
@@ -394,7 +408,7 @@ class Domain
|
||||
|
||||
// check for php-version that requires the trailing
|
||||
// slash to be removed as it does not allow the usage
|
||||
// of the subfolders within the given folder, fixes #797
|
||||
// of the sub-folders within the given folder, fixes #797
|
||||
if ((PHP_MINOR_VERSION == 2 && PHP_VERSION_ID >= 50216) || PHP_VERSION_ID >= 50304) {
|
||||
// check trailing slash
|
||||
if (substr($path, -1, 1) == '/') {
|
||||
|
||||
@@ -30,8 +30,10 @@ use PDO;
|
||||
|
||||
class IpAddr
|
||||
{
|
||||
|
||||
public static function getIpAddresses()
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getIpAddresses(): array
|
||||
{
|
||||
$result_stmt = Database::query("
|
||||
SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC
|
||||
@@ -51,21 +53,30 @@ class IpAddr
|
||||
return $system_ipaddress_array;
|
||||
}
|
||||
|
||||
public static function getSslIpPortCombinations()
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getSslIpPortCombinations(): array
|
||||
{
|
||||
return [
|
||||
'' => lng('panel.none_value')
|
||||
] + self::getIpPortCombinations(true);
|
||||
}
|
||||
|
||||
public static function getIpPortCombinations($ssl = false)
|
||||
/**
|
||||
* @param bool $ssl
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getIpPortCombinations(bool $ssl = false): array
|
||||
{
|
||||
global $userinfo;
|
||||
|
||||
$additional_conditions_params = [];
|
||||
$additional_conditions_array = [];
|
||||
|
||||
if ($userinfo['ip'] != '-1') {
|
||||
if (!empty($userinfo) && $userinfo['ip'] != '-1') {
|
||||
$admin_ip_stmt = Database::prepare("
|
||||
SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = IN (:ipid)
|
||||
");
|
||||
|
||||
@@ -38,30 +38,24 @@ class FileDir
|
||||
* which had to be created below with correct Owner/Group
|
||||
* (Copied from cron_tasks.php:rev1189 as we'll need this more often in future)
|
||||
*
|
||||
* @param string $homeDir
|
||||
* The homedir of the user
|
||||
* @param string $dirToCreate
|
||||
* The dir which should be created
|
||||
* @param int $uid
|
||||
* The uid of the user
|
||||
* @param int $gid
|
||||
* The gid of the user
|
||||
* @param bool $placeindex
|
||||
* Place standard-index.html into the new folder
|
||||
* @param bool $allow_notwithinhomedir
|
||||
* Allow creating a directory out of the customers docroot
|
||||
* @param string $homeDir The homedir of the user
|
||||
* @param string $dirToCreate The dir which should be created
|
||||
* @param int $uid The uid of the user
|
||||
* @param int $gid The gid of the user
|
||||
* @param bool $placeindex Place standard-index.html into the new folder
|
||||
* @param bool $allow_notwithinhomedir Allow creating a directory out of the customers docroot
|
||||
*
|
||||
* @return bool true if everything went okay, false if something went wrong
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function mkDirWithCorrectOwnership(
|
||||
$homeDir,
|
||||
$dirToCreate,
|
||||
$uid,
|
||||
$gid,
|
||||
$placeindex = false,
|
||||
$allow_notwithinhomedir = false
|
||||
) {
|
||||
string $homeDir,
|
||||
string $dirToCreate,
|
||||
int $uid,
|
||||
int $gid,
|
||||
bool $placeindex = false,
|
||||
bool $allow_notwithinhomedir = false
|
||||
): bool {
|
||||
if ($homeDir != '' && $dirToCreate != '') {
|
||||
$homeDir = self::makeCorrectDir($homeDir);
|
||||
$dirToCreate = self::makeCorrectDir($dirToCreate);
|
||||
@@ -116,15 +110,14 @@ class FileDir
|
||||
* Function which returns a correct dirname, means to add slashes at the beginning and at the end if there weren't
|
||||
* some
|
||||
*
|
||||
* @param string $path
|
||||
* the path to correct
|
||||
* @param string $dir the path to correct
|
||||
*
|
||||
* @return string the corrected path
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function makeCorrectDir($dir)
|
||||
public static function makeCorrectDir(string $dir): string
|
||||
{
|
||||
if (is_string($dir) && strlen($dir) > 0) {
|
||||
if (strlen($dir) > 0) {
|
||||
$dir = trim($dir);
|
||||
if (substr($dir, -1, 1) != '/') {
|
||||
$dir .= '/';
|
||||
@@ -140,16 +133,15 @@ class FileDir
|
||||
/**
|
||||
* Function which returns a secure path, means to remove all multiple dots and slashes
|
||||
*
|
||||
* @param string $path
|
||||
* the path to secure
|
||||
* @param string $path the path to secure
|
||||
*
|
||||
* @return string the corrected path
|
||||
*/
|
||||
public static function makeSecurePath($path)
|
||||
public static function makeSecurePath(string $path): string
|
||||
{
|
||||
// check for bad characters, some are allowed with escaping
|
||||
// check for bad characters, some are allowed with escaping,
|
||||
// but we generally don't want them in our directory-names,
|
||||
// thx to aaronmueller for this snipped
|
||||
// thx to aaronmueller for this snippet
|
||||
$badchars = [
|
||||
':',
|
||||
';',
|
||||
@@ -161,7 +153,11 @@ class FileDir
|
||||
'$',
|
||||
'~',
|
||||
'?',
|
||||
"\0"
|
||||
"\0",
|
||||
"\n",
|
||||
"\r",
|
||||
"\t",
|
||||
"\f"
|
||||
];
|
||||
foreach ($badchars as $bc) {
|
||||
$path = str_replace($bc, "", $path);
|
||||
@@ -187,16 +183,13 @@ class FileDir
|
||||
/**
|
||||
* Wrapper around the exec command.
|
||||
*
|
||||
* @param string $exec_string
|
||||
* command to be executed
|
||||
* @param string $return_value
|
||||
* referenced variable where the output is stored
|
||||
* @param array $allowedChars
|
||||
* optional array of allowed characters in path/command
|
||||
* @param string $exec_string command to be executed
|
||||
* @param mixed $return_value referenced variable where the output is stored
|
||||
* @param ?array $allowedChars optional array of allowed characters in path/command
|
||||
*
|
||||
* @return array result of exec()
|
||||
*/
|
||||
public static function safe_exec($exec_string, &$return_value = false, $allowedChars = null)
|
||||
public static function safe_exec(string $exec_string, &$return_value = false, $allowedChars = null)
|
||||
{
|
||||
$disallowed = [
|
||||
';',
|
||||
@@ -241,19 +234,20 @@ class FileDir
|
||||
/**
|
||||
* store the default index-file in a given destination folder
|
||||
*
|
||||
* @param string $loginname
|
||||
* customers loginname
|
||||
* @param string $destination
|
||||
* path where to create the file
|
||||
* @param object $logger
|
||||
* FroxlorLogger object
|
||||
* @param boolean $force
|
||||
* force creation whatever the settings say (needed for task #2, create new user)
|
||||
* @param string $loginname customers loginname
|
||||
* @param string $destination path where to create the file
|
||||
* @param object $logger FroxlorLogger object
|
||||
* @param bool $force force creation whatever the settings say (needed for task #2, create new user)
|
||||
*
|
||||
* @return null
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function storeDefaultIndex($loginname = null, $destination = null, $logger = null, $force = false)
|
||||
{
|
||||
public static function storeDefaultIndex(
|
||||
string $loginname,
|
||||
string $destination,
|
||||
$logger = null,
|
||||
bool $force = false
|
||||
) {
|
||||
if ($force || (int)Settings::Get('system.store_index_file_subs') == 1) {
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT `t`.`value`, `c`.`email` AS `customer_email`, `a`.`email` AS `admin_email`, `c`.`loginname` AS `customer_login`, `a`.`loginname` AS `admin_login`
|
||||
@@ -302,18 +296,16 @@ class FileDir
|
||||
self::safe_exec('cp -a ' . Froxlor::getInstallDir() . '/templates/misc/standardcustomer/* ' . escapeshellarg($destination));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function which returns a correct filename, means to add a slash at the beginning if there wasn't one
|
||||
*
|
||||
* @param string $filename
|
||||
* the filename
|
||||
* @param string $filename the filename
|
||||
*
|
||||
* @return string the corrected filename
|
||||
*/
|
||||
public static function makeCorrectFile(string $filename)
|
||||
public static function makeCorrectFile(string $filename): string
|
||||
{
|
||||
if (trim($filename) == '') {
|
||||
$error = 'Given filename for function ' . __FUNCTION__ . ' is empty.' . "\n";
|
||||
@@ -329,21 +321,19 @@ class FileDir
|
||||
$filename = '/' . $filename;
|
||||
}
|
||||
|
||||
$filename = self::makeSecurePath($filename);
|
||||
return $filename;
|
||||
return self::makeSecurePath($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* checks a directory against disallowed paths which could
|
||||
* lead to a damaged system if you use them
|
||||
*
|
||||
* @param string $fieldname
|
||||
* @param array $fielddata
|
||||
* @param mixed $newfieldvalue
|
||||
* @param string|null $path
|
||||
*
|
||||
* @return boolean|array
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function checkDisallowedPaths($path = null)
|
||||
public static function checkDisallowedPaths(string $path): bool
|
||||
{
|
||||
/*
|
||||
* disallow base-directories and /
|
||||
@@ -381,12 +371,11 @@ class FileDir
|
||||
/**
|
||||
* Function which returns a correct destination for Postfix Virtual Table
|
||||
*
|
||||
* @param
|
||||
* string The destinations
|
||||
* @param string $destination The destinations
|
||||
*
|
||||
* @return string the corrected destinations
|
||||
* @author Florian Lippert <flo@syscp.org> (2003-2009)
|
||||
*/
|
||||
public static function makeCorrectDestination($destination)
|
||||
public static function makeCorrectDestination(string $destination): string
|
||||
{
|
||||
$search = '/ +/';
|
||||
$replace = ' ';
|
||||
@@ -406,27 +395,25 @@ class FileDir
|
||||
/**
|
||||
* Returns a valid html tag for the chosen $fieldType for paths
|
||||
*
|
||||
* @param
|
||||
* string path The path to start searching in
|
||||
* @param
|
||||
* integer uid The uid which must match the found directories
|
||||
* @param
|
||||
* integer gid The gid which must match the found directories
|
||||
* @param
|
||||
* string value the value for the input-field
|
||||
* @param string $path The path to start searching in
|
||||
* @param int $uid The uid which must match the found directories
|
||||
* @param int $gid The gid which must match the found directories
|
||||
* @param string $value the value for the input-field
|
||||
* @param bool $dom
|
||||
*
|
||||
* @return string The html tag for the chosen $fieldType
|
||||
* @return array
|
||||
*
|
||||
* @author Martin Burchert <martin.burchert@syscp.de>
|
||||
* @throws Exception
|
||||
* @author Manuel Bernhardt <manuel.bernhardt@syscp.de>
|
||||
* @author Martin Burchert <martin.burchert@syscp.de>
|
||||
*/
|
||||
public static function makePathfield($path, $uid, $gid, $value = '', $dom = false)
|
||||
public static function makePathfield(string $path, int $uid, int $gid, string $value = '', bool $dom = false): array
|
||||
{
|
||||
$value = str_replace($path, '', $value);
|
||||
$field = [];
|
||||
|
||||
// path is given without starting slash
|
||||
// but dirList holds the paths with starting slash
|
||||
// but dirList holds the paths with starting slash,
|
||||
// so we just add one here to get the correct
|
||||
// default path selected, #225
|
||||
if (substr($value, 0, 1) != '/' && !$dom) {
|
||||
@@ -460,7 +447,8 @@ class FileDir
|
||||
$field = [
|
||||
'type' => 'select',
|
||||
'select_var' => $_field,
|
||||
'selected' => $value
|
||||
'selected' => $value,
|
||||
'value' => $value
|
||||
];
|
||||
} else {
|
||||
$field = [
|
||||
@@ -480,16 +468,14 @@ class FileDir
|
||||
* This function checks every found directory if they match either $uid or $gid, if they do
|
||||
* the found directory is valid. It uses recursive-iterators to find subdirectories.
|
||||
*
|
||||
* @param string $path
|
||||
* the path to start searching in
|
||||
* @param int $uid
|
||||
* the uid which must match the found directories
|
||||
* @param int $gid
|
||||
* the gid which must match the found directories
|
||||
* @param string $path the path to start searching in
|
||||
* @param int $uid the uid which must match the found directories
|
||||
* @param int $gid the gid which must match the found directories
|
||||
*
|
||||
* @return array Array of found valid paths
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function findDirs($path, $uid, $gid)
|
||||
private static function findDirs(string $path, int $uid, int $gid): array
|
||||
{
|
||||
$_fileList = [];
|
||||
$path = self::makeCorrectDir($path);
|
||||
@@ -499,7 +485,8 @@ class FileDir
|
||||
// Will exclude everything under these directories
|
||||
$exclude = [
|
||||
'awstats',
|
||||
'webalizer'
|
||||
'webalizer',
|
||||
'goaccess'
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -565,7 +552,7 @@ class FileDir
|
||||
*
|
||||
* @return string functionname + parameter (not the file)
|
||||
*/
|
||||
private static function getImmutableFunction(bool $remove = false)
|
||||
private static function getImmutableFunction(bool $remove = false): string
|
||||
{
|
||||
if (self::isFreeBSD()) {
|
||||
// FreeBSD style
|
||||
@@ -585,7 +572,7 @@ class FileDir
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isFreeBSD(bool $exact = false)
|
||||
public static function isFreeBSD(bool $exact = false): bool
|
||||
{
|
||||
if (($exact && PHP_OS == 'FreeBSD') || (!$exact && stristr(PHP_OS, 'BSD'))) {
|
||||
return true;
|
||||
@@ -606,7 +593,7 @@ class FileDir
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public static function getFilesystemQuota()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user