Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
12da117cab | ||
|
|
ef48f4b48e | ||
|
|
aae6db52b5 |
@@ -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 -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -92,6 +92,7 @@ if ($userinfo['change_serversettings'] == '1') {
|
||||
|
||||
if ($distribution != "" && isset($_POST['finish'])) {
|
||||
unset($_POST['finish']);
|
||||
unset($_POST['csrf_token']);
|
||||
$params = $_POST;
|
||||
$params['distro'] = $distribution;
|
||||
$params['system'] = [];
|
||||
@@ -121,8 +122,6 @@ if ($userinfo['change_serversettings'] == '1') {
|
||||
'distribution' => $distribution
|
||||
]);
|
||||
} else {
|
||||
// @fixme check set distribution from settings
|
||||
|
||||
$cfg_formfield = [
|
||||
'config' => [
|
||||
'title' => lng('admin.configfiles.serverconfiguration'),
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -35,6 +35,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 +60,5 @@ $application->add(new UpdateCommand());
|
||||
$application->add(new InstallCommand());
|
||||
$application->add(new MasterCron());
|
||||
$application->add(new UserCommand());
|
||||
$application->add(new ValidateAcmeWebroot());
|
||||
$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",
|
||||
|
||||
814
composer.lock
generated
814
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') {
|
||||
|
||||
@@ -115,10 +115,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
|
||||
@@ -67,6 +68,11 @@ if (!empty($errid)) {
|
||||
$mail_body .= "User-Area: " . AREA . "\n";
|
||||
$mail_body .= "Froxlor-version: " . Froxlor::VERSION . "\n";
|
||||
$mail_body .= "DB-version: " . Froxlor::DBVERSION . "\n\n";
|
||||
try {
|
||||
$mail_body .= "Database: " . Database::getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
} catch (\Exception $e) {
|
||||
/* ignore */
|
||||
}
|
||||
$mail_body .= "End of report";
|
||||
$mail_html = nl2br($mail_body);
|
||||
|
||||
|
||||
@@ -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,10 @@ opcache.validate_timestamps'),
|
||||
('system', 'distribution', ''),
|
||||
('system', 'update_channel', 'stable'),
|
||||
('system', 'updatecheck_data', ''),
|
||||
('system', 'update_notify_last', '2.0.2'),
|
||||
('system', 'update_notify_last', '2.0.15'),
|
||||
('system', 'traffictool', 'goaccess'),
|
||||
('api', 'enabled', '0'),
|
||||
('api', 'customer_default', '1'),
|
||||
('2fa', 'enabled', '1'),
|
||||
('panel', 'decimal_places', '4'),
|
||||
('panel', 'adminmail', 'admin@SERVERNAME'),
|
||||
@@ -740,8 +742,8 @@ opcache.validate_timestamps'),
|
||||
('panel', 'logo_overridetheme', '0'),
|
||||
('panel', 'logo_overridecustom', '0'),
|
||||
('panel', 'settings_mode', '0'),
|
||||
('panel', 'version', '2.0.2'),
|
||||
('panel', 'db_version', '202212060');
|
||||
('panel', 'version', '2.0.15'),
|
||||
('panel', 'db_version', '202303150');
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `panel_tasks`;
|
||||
@@ -981,7 +983,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,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`;");
|
||||
@@ -67,9 +66,10 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;";
|
||||
Database::query($sql);
|
||||
// new customer allowed_mysqlserver field
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `allowed_mysqlserver` text NOT NULL;");
|
||||
Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` CHANGE COLUMN `allowed_phpconfigs` `allowed_phpconfigs` text NOT NULL;");
|
||||
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;");
|
||||
$has_customer_table_update_200 = true;
|
||||
// ftp_users adjustments
|
||||
Database::query("ALTER TABLE `" . TABLE_FTP_USERS . "` CHANGE COLUMN `password` `password` varchar(255) NOT NULL default '';");
|
||||
@@ -93,7 +93,8 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
|
||||
while ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
if (isset($dbserver['allowed_mysqlserver']) && !empty($dbserver['allowed_mysqlserver'])) {
|
||||
$allowed_mysqlserver = json_encode(explode(",", $dbserver['allowed_mysqlserver']));
|
||||
Database::pexecute($upd_stmt, ['allowed_mysql_server' => $allowed_mysqlserver, 'customerid' => $dbserver['customerid']]);
|
||||
Database::pexecute($upd_stmt,
|
||||
['allowed_mysql_server' => $allowed_mysqlserver, 'customerid' => $dbserver['customerid']]);
|
||||
}
|
||||
}
|
||||
Update::lastStepStatus(0);
|
||||
@@ -140,12 +141,13 @@ 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'] : '';
|
||||
Settings::AddNew("system.distribution", $system_distribution);
|
||||
@@ -182,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);
|
||||
@@ -203,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);
|
||||
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');
|
||||
}
|
||||
@@ -245,3 +257,209 @@ if (Froxlor::isFroxlorVersion('2.0.1')) {
|
||||
Update::showUpdateStep("Updating from 2.0.1 to 2.0.2", false);
|
||||
Froxlor::updateToVersion('2.0.2');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.2')) {
|
||||
Update::showUpdateStep("Updating from 2.0.2 to 2.0.3", false);
|
||||
Froxlor::updateToVersion('2.0.3');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.3')) {
|
||||
Update::showUpdateStep("Updating from 2.0.3 to 2.0.4", false);
|
||||
|
||||
$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')) {
|
||||
Update::showUpdateStep("Adjusting backward compatibility for cronjob");
|
||||
$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);
|
||||
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::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');
|
||||
}
|
||||
|
||||
@@ -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,11 +45,11 @@ 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -117,6 +117,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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([
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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("
|
||||
@@ -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 = [
|
||||
|
||||
@@ -404,20 +404,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;
|
||||
}
|
||||
@@ -559,7 +564,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);
|
||||
@@ -1322,19 +1327,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;
|
||||
}
|
||||
@@ -1523,7 +1534,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);
|
||||
@@ -1974,12 +1985,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 +2148,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']);
|
||||
|
||||
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_NOTICE,
|
||||
"[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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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("
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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') {
|
||||
|
||||
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']);
|
||||
}
|
||||
|
||||
@@ -196,6 +196,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,7 +227,9 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -277,7 +287,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 +316,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 +340,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 +356,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 +382,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 +465,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 +533,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 +569,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 +606,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) {
|
||||
|
||||
@@ -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']);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -1035,6 +1040,9 @@ 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";
|
||||
$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 +1128,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']);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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]]));
|
||||
$parentDomainCollection = (new Collection(SubDomains::class, $_SESSION['userinfo'],
|
||||
['sql_search' => ['d.parentdomainid' => 0]]));
|
||||
$addition = $parentDomainCollection != 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,14 +53,22 @@ class IpAddr
|
||||
return $system_ipaddress_array;
|
||||
}
|
||||
|
||||
public static function getSslIpPortCombinations()
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
@@ -480,16 +467,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 +484,8 @@ class FileDir
|
||||
// Will exclude everything under these directories
|
||||
$exclude = [
|
||||
'awstats',
|
||||
'webalizer'
|
||||
'webalizer',
|
||||
'goaccess'
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -565,7 +551,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 +571,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;
|
||||
|
||||
@@ -31,10 +31,10 @@ final class Froxlor
|
||||
{
|
||||
|
||||
// Main version variable
|
||||
const VERSION = '2.0.2';
|
||||
const VERSION = '2.0.15';
|
||||
|
||||
// Database version (YYYYMMDDC where C is a daily counter)
|
||||
const DBVERSION = '202212060';
|
||||
const DBVERSION = '202303150';
|
||||
|
||||
// Distribution branding-tag (used for Debian etc.)
|
||||
const BRANDING = '';
|
||||
@@ -45,7 +45,7 @@ final class Froxlor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getInstallDir()
|
||||
public static function getInstallDir(): string
|
||||
{
|
||||
return dirname(__DIR__, 2) . '/';
|
||||
}
|
||||
@@ -55,7 +55,7 @@ final class Froxlor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getVersion()
|
||||
public static function getVersion(): string
|
||||
{
|
||||
return self::VERSION;
|
||||
}
|
||||
@@ -65,7 +65,7 @@ final class Froxlor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getVersionString()
|
||||
public static function getVersionString(): string
|
||||
{
|
||||
return self::getFullVersion() . ' (' . self::DBVERSION . ')';
|
||||
}
|
||||
@@ -75,7 +75,7 @@ final class Froxlor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getFullVersion()
|
||||
public static function getFullVersion(): string
|
||||
{
|
||||
return self::VERSION . self::BRANDING;
|
||||
}
|
||||
@@ -85,12 +85,11 @@ final class Froxlor
|
||||
*
|
||||
* checks if a given version is not equal the current one
|
||||
*
|
||||
* @param string $to_check
|
||||
* version to check, if empty current version is used
|
||||
* @param string $to_check version to check, if empty current version is used
|
||||
*
|
||||
* @return bool true if version to check does not match, else false
|
||||
*/
|
||||
public static function hasUpdates($to_check = null)
|
||||
public static function hasUpdates(string $to_check = ''): bool
|
||||
{
|
||||
if (empty($to_check)) {
|
||||
$to_check = self::VERSION;
|
||||
@@ -102,16 +101,15 @@ final class Froxlor
|
||||
}
|
||||
|
||||
/**
|
||||
* Function hasUpdates
|
||||
* Function hasDbUpdates
|
||||
*
|
||||
* checks if a given database-version is not equal the current one
|
||||
*
|
||||
* @param int $to_check
|
||||
* version to check, if empty current dbversion is used
|
||||
* @param string $to_check version to check, if empty current dbversion is used
|
||||
*
|
||||
* @return bool true if version to check does not match, else false
|
||||
*/
|
||||
public static function hasDbUpdates($to_check = null)
|
||||
public static function hasDbUpdates(string $to_check = ''): bool
|
||||
{
|
||||
if (empty($to_check)) {
|
||||
$to_check = self::DBVERSION;
|
||||
@@ -127,12 +125,11 @@ final class Froxlor
|
||||
*
|
||||
* checks if a given database-version is the current one
|
||||
*
|
||||
* @param int $to_check
|
||||
* version to check
|
||||
* @param string $to_check version to check
|
||||
*
|
||||
* @return bool true if version to check matches, else false
|
||||
*/
|
||||
public static function isDatabaseVersion($to_check = null)
|
||||
public static function isDatabaseVersion(string $to_check): bool
|
||||
{
|
||||
if (Settings::Get('panel.frontend') == 'froxlor' && Settings::Get('panel.db_version') == $to_check) {
|
||||
return true;
|
||||
@@ -146,14 +143,14 @@ final class Froxlor
|
||||
* updates the panel.version field
|
||||
* to the given value (no checks here!)
|
||||
*
|
||||
* @param string $new_version
|
||||
* new-version
|
||||
* @param string $new_version new-version
|
||||
*
|
||||
* @return bool true on success, else false
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function updateToDbVersion($new_version = null)
|
||||
public static function updateToDbVersion(string $new_version): bool
|
||||
{
|
||||
if ($new_version !== null && $new_version != '') {
|
||||
if ($new_version != '') {
|
||||
$upd_stmt = Database::prepare("
|
||||
UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :newversion
|
||||
WHERE `settinggroup` = 'panel' AND `varname` = 'db_version'");
|
||||
@@ -172,14 +169,14 @@ final class Froxlor
|
||||
* updates the panel.version field
|
||||
* to the given value (no checks here!)
|
||||
*
|
||||
* @param string $new_version
|
||||
* new-version
|
||||
* @param string $new_version new-version
|
||||
*
|
||||
* @return bool true on success, else false
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function updateToVersion($new_version = null)
|
||||
public static function updateToVersion(string $new_version): bool
|
||||
{
|
||||
if ($new_version !== null && $new_version != '') {
|
||||
if ($new_version != '') {
|
||||
$upd_stmt = Database::prepare("
|
||||
UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :newversion
|
||||
WHERE `settinggroup` = 'panel' AND `varname` = 'version'");
|
||||
@@ -199,7 +196,7 @@ final class Froxlor
|
||||
*
|
||||
* @return bool true if panel is froxlor, else false
|
||||
*/
|
||||
public static function isFroxlor()
|
||||
public static function isFroxlor(): bool
|
||||
{
|
||||
if (Settings::Get('panel.frontend') !== null && Settings::Get('panel.frontend') == 'froxlor') {
|
||||
return true;
|
||||
@@ -213,12 +210,11 @@ final class Froxlor
|
||||
* checks if a given version is the
|
||||
* current one (and panel is froxlor)
|
||||
*
|
||||
* @param string $to_check
|
||||
* version to check
|
||||
* @param string $to_check version to check
|
||||
*
|
||||
* @return bool true if version to check matches, else false
|
||||
*/
|
||||
public static function isFroxlorVersion($to_check = null)
|
||||
public static function isFroxlorVersion(string $to_check): bool
|
||||
{
|
||||
if (Settings::Get('panel.frontend') == 'froxlor' && Settings::Get('panel.version') == $to_check) {
|
||||
return true;
|
||||
@@ -231,10 +227,11 @@ final class Froxlor
|
||||
*
|
||||
* @param int $length
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function genSessionId(int $length = 16)
|
||||
public static function genSessionId(int $length = 16): string
|
||||
{
|
||||
if (intval($length) <= 8) {
|
||||
if ($length <= 8) {
|
||||
$length = 16;
|
||||
}
|
||||
if (function_exists('random_bytes')) {
|
||||
@@ -256,9 +253,9 @@ final class Froxlor
|
||||
* @param string $a
|
||||
* @param string $b
|
||||
*
|
||||
* @return integer 0 if equal, 1 if a>b and -1 if b>a
|
||||
* @return int 0 if equal, 1 if a>b and -1 if b>a
|
||||
*/
|
||||
public static function versionCompare2($a, $b)
|
||||
public static function versionCompare2(string $a, string $b): int
|
||||
{
|
||||
// split version into pieces and remove trailing .0
|
||||
$a = explode(".", $a);
|
||||
@@ -295,7 +292,11 @@ final class Froxlor
|
||||
return (count($a) < count($b)) ? -1 : 0;
|
||||
}
|
||||
|
||||
private static function parseVersionArray(&$arr = null)
|
||||
/**
|
||||
* @param array|null $arr
|
||||
* @return void
|
||||
*/
|
||||
private static function parseVersionArray(array &$arr = null)
|
||||
{
|
||||
// -dev or -beta or -rc ?
|
||||
if (stripos($arr[count($arr) - 1], '-') !== false) {
|
||||
@@ -306,16 +307,20 @@ final class Froxlor
|
||||
$arr[] = '2'; // dev < beta < rc
|
||||
// number of rc
|
||||
$arr[] = substr($x[1], 2);
|
||||
} else if (stripos($x[1], 'beta') !== false) {
|
||||
$arr[] = '-1';
|
||||
$arr[] = '1'; // dev < beta < rc
|
||||
// number of beta
|
||||
$arr[] = substr($x[1], 3);
|
||||
} else if (stripos($x[1], 'dev') !== false) {
|
||||
$arr[] = '-1';
|
||||
$arr[] = '0'; // dev < beta < rc
|
||||
// number of dev
|
||||
$arr[] = substr($x[1], 3);
|
||||
} else {
|
||||
if (stripos($x[1], 'beta') !== false) {
|
||||
$arr[] = '-1';
|
||||
$arr[] = '1'; // dev < beta < rc
|
||||
// number of beta
|
||||
$arr[] = substr($x[1], 3);
|
||||
} else {
|
||||
if (stripos($x[1], 'dev') !== false) {
|
||||
$arr[] = '-1';
|
||||
$arr[] = '0'; // dev < beta < rc
|
||||
// number of dev
|
||||
$arr[] = substr($x[1], 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,38 +45,42 @@ class FroxlorLogger
|
||||
/**
|
||||
* current \Monolog\Logger object
|
||||
*
|
||||
* @var Logger
|
||||
* @var ?Logger
|
||||
*/
|
||||
private static $ml = null;
|
||||
private static ?Logger $ml = null;
|
||||
/**
|
||||
* LogTypes Array
|
||||
*
|
||||
* @var array
|
||||
* @var ?array
|
||||
*/
|
||||
private static $logtypes = null;
|
||||
private static ?array $logtypes = null;
|
||||
/**
|
||||
* whether to output log-messages to STDOUT (cron)
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $crondebug_flag = false;
|
||||
private static bool $crondebug_flag = false;
|
||||
/**
|
||||
* user info of logged in user
|
||||
* user info of logged-in user
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $userinfo = [];
|
||||
private static array $userinfo = [];
|
||||
/**
|
||||
* whether the logger object has already been initialized
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $is_initialized = false;
|
||||
private static bool $is_initialized = false;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param array $userinfo
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function __construct($userinfo = [])
|
||||
protected function __construct(array $userinfo = [])
|
||||
{
|
||||
$this->initMonolog();
|
||||
self::$userinfo = $userinfo;
|
||||
@@ -100,11 +104,17 @@ class FroxlorLogger
|
||||
self::$ml->pushHandler(new SyslogHandler('froxlor', LOG_USER, Logger::DEBUG));
|
||||
break;
|
||||
case 'file':
|
||||
$logger_logfile = Settings::Get('logger.logfile');
|
||||
$logger_logfile = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/logs/' . Settings::Get('logger.logfile'));
|
||||
// is_writable needs an existing file to check if it's actually writable
|
||||
@touch($logger_logfile);
|
||||
if (empty($logger_logfile) || !is_writable($logger_logfile)) {
|
||||
Settings::Set('logger.logfile', '/tmp/froxlor.log');
|
||||
Settings::Set('logger.logfile', 'froxlor.log');
|
||||
$logger_logfile = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/logs/froxlor.log');
|
||||
@touch($logger_logfile);
|
||||
if (empty($logger_logfile) || !is_writable($logger_logfile)) {
|
||||
// not writable in our own directory? Skip
|
||||
break;
|
||||
}
|
||||
}
|
||||
self::$ml->pushHandler(new StreamHandler($logger_logfile, Logger::DEBUG));
|
||||
break;
|
||||
@@ -137,8 +147,9 @@ class FroxlorLogger
|
||||
* @param array $userinfo
|
||||
*
|
||||
* @return FroxlorLogger
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getInstanceOf($userinfo = [])
|
||||
public static function getInstanceOf(array $userinfo = [])
|
||||
{
|
||||
if (empty($userinfo)) {
|
||||
$userinfo = [
|
||||
@@ -153,9 +164,9 @@ class FroxlorLogger
|
||||
*
|
||||
* @param int $action
|
||||
* @param int $type
|
||||
* @param string $text
|
||||
* @param ?string $text
|
||||
*/
|
||||
public function logAction($action = FroxlorLogger::USR_ACTION, $type = LOG_NOTICE, $text = null)
|
||||
public function logAction($action = FroxlorLogger::USR_ACTION, int $type = LOG_NOTICE, string $text = null)
|
||||
{
|
||||
// not logging normal stuff if not set to "paranoid" logging
|
||||
if (!self::$crondebug_flag && Settings::Get('logger.severity') == '1' && $type > LOG_NOTICE) {
|
||||
@@ -202,7 +213,11 @@ class FroxlorLogger
|
||||
}
|
||||
}
|
||||
|
||||
public function getLogLevelDesc($type)
|
||||
/**
|
||||
* @param int $type
|
||||
* @return string
|
||||
*/
|
||||
public function getLogLevelDesc(int $type): string
|
||||
{
|
||||
switch ($type) {
|
||||
case LOG_INFO:
|
||||
@@ -230,7 +245,11 @@ class FroxlorLogger
|
||||
return $_type;
|
||||
}
|
||||
|
||||
private function getActionTypeDesc($action)
|
||||
/**
|
||||
* @param $action
|
||||
* @return string
|
||||
*/
|
||||
private function getActionTypeDesc($action): string
|
||||
{
|
||||
switch ($action) {
|
||||
case FroxlorLogger::USR_ACTION:
|
||||
@@ -262,7 +281,7 @@ class FroxlorLogger
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function setCronLog(int $cronlog = 0)
|
||||
public function setCronLog(int $cronlog = 0): int
|
||||
{
|
||||
if ($cronlog < 0 || $cronlog > 2) {
|
||||
$cronlog = 0;
|
||||
|
||||
@@ -47,15 +47,17 @@ class Directory
|
||||
*
|
||||
* @param string $dir
|
||||
*/
|
||||
public function __construct($dir = null)
|
||||
public function __construct(string $dir = null)
|
||||
{
|
||||
$this->dir = $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether the directory has options set in panel_htaccess
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasUserOptions()
|
||||
public function hasUserOptions(): bool
|
||||
{
|
||||
$uo_stmt = Database::prepare("
|
||||
SELECT COUNT(`id`) as `usropts` FROM `" . TABLE_PANEL_HTACCESS . "` WHERE `path` = :dir
|
||||
@@ -63,7 +65,7 @@ class Directory
|
||||
$uo_res = Database::pexecute_first($uo_stmt, [
|
||||
'dir' => FileDir::makeCorrectDir($this->dir)
|
||||
]);
|
||||
if ($uo_res != false && isset($uo_res['usropts'])) {
|
||||
if ($uo_res && isset($uo_res['usropts'])) {
|
||||
return $uo_res['usropts'] > 0;
|
||||
}
|
||||
return false;
|
||||
@@ -71,8 +73,10 @@ class Directory
|
||||
|
||||
/**
|
||||
* check whether the directory is protected using panel_htpasswd
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isUserProtected()
|
||||
public function isUserProtected(): bool
|
||||
{
|
||||
$up_stmt = Database::prepare("
|
||||
SELECT COUNT(`id`) as `usrprot` FROM `" . TABLE_PANEL_HTPASSWDS . "` WHERE `path` = :dir
|
||||
@@ -80,7 +84,7 @@ class Directory
|
||||
$up_res = Database::pexecute_first($up_stmt, [
|
||||
'dir' => FileDir::makeCorrectDir($this->dir)
|
||||
]);
|
||||
if ($up_res != false && isset($up_res['usrprot'])) {
|
||||
if ($up_res && isset($up_res['usrprot'])) {
|
||||
return $up_res['usrprot'] > 0;
|
||||
}
|
||||
return false;
|
||||
@@ -90,12 +94,11 @@ class Directory
|
||||
* Checks if a given directory is valid for multiple configurations
|
||||
* or should rather be used as a single file
|
||||
*
|
||||
* @param bool $ifexists
|
||||
* also check whether file/dir exists
|
||||
* @param bool $ifexists also check whether file/dir exists
|
||||
*
|
||||
* @return bool true if usable as dir, false otherwise
|
||||
*/
|
||||
public function isConfigDir($ifexists = false)
|
||||
public function isConfigDir(bool $ifexists = false): bool
|
||||
{
|
||||
if (is_null($this->dir)) {
|
||||
trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' has been called with a null value', E_USER_WARNING);
|
||||
|
||||
@@ -33,6 +33,10 @@ class HttpClient
|
||||
/**
|
||||
* Executes simple GET request
|
||||
*
|
||||
* @param string $url
|
||||
* @param bool $follow_location
|
||||
* @param int $timeout
|
||||
*
|
||||
* @return bool|string
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -59,6 +63,10 @@ class HttpClient
|
||||
/**
|
||||
* Downloads and stores a file from an url
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $target
|
||||
*
|
||||
* @return bool|string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function fileGet(string $url, string $target)
|
||||
|
||||
@@ -37,7 +37,7 @@ class PhpConfig
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPhpConfigs()
|
||||
public static function getPhpConfigs(): array
|
||||
{
|
||||
$configs_array = [];
|
||||
|
||||
|
||||
@@ -36,16 +36,22 @@ class Statistics
|
||||
* Create or modify the AWStats configuration file for the given domain.
|
||||
* Modified by Berend Dekens to allow custom configurations.
|
||||
*
|
||||
* @param
|
||||
* logFile
|
||||
* @param
|
||||
* siteDomain
|
||||
* @param
|
||||
* hostAliases
|
||||
* @param string $logFile
|
||||
* @param string $siteDomain
|
||||
* @param string $hostAliases
|
||||
* @param string $customerDocroot
|
||||
* @param array $domain_data
|
||||
*
|
||||
* @return null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function createAWStatsConf($logFile, $siteDomain, $hostAliases, $customerDocroot, $awstats_params = [])
|
||||
{
|
||||
public static function createAWStatsConf(
|
||||
string $logFile,
|
||||
string $siteDomain,
|
||||
string $hostAliases,
|
||||
string $customerDocroot,
|
||||
array $domain_data = []
|
||||
) {
|
||||
// Generation header
|
||||
$header = "## GENERATED BY FROXLOR\n";
|
||||
$header2 = "## Do not remove the line above! This tells Froxlor to update this configuration\n## If you wish to manually change this configuration file, remove the first line to make sure Froxlor won't rebuild this file\n## Generated for domain {SITE_DOMAIN} on " . date('l dS \of F Y h:i:s A') . "\n";
|
||||
@@ -55,7 +61,7 @@ class Statistics
|
||||
FileDir::safe_exec('mkdir -p ' . escapeshellarg($awstats_dir));
|
||||
}
|
||||
// chown created folder, #258
|
||||
self::makeChownWithNewStats($awstats_params);
|
||||
self::makeChownWithNewStats($domain_data);
|
||||
|
||||
// weird but could happen...
|
||||
if (!is_dir(Settings::Get('system.awstats_conf'))) {
|
||||
@@ -127,16 +133,15 @@ class Statistics
|
||||
}
|
||||
|
||||
/**
|
||||
* chowns either awstats or webalizer folder,
|
||||
* either with webserver-user or - if fcgid
|
||||
* is used - the customers name, #258
|
||||
* chowns stats-tools folder, either with webserver-user or
|
||||
* if fcgid/php-fpm is used, the customers name, #258
|
||||
*
|
||||
* @param array $row
|
||||
* array if panel_customers
|
||||
* @param array $row array of panel_customers
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function makeChownWithNewStats($row)
|
||||
public static function makeChownWithNewStats(array $row)
|
||||
{
|
||||
// get correct user
|
||||
if ((Settings::Get('system.mod_fcgid') == '1' || Settings::Get('phpfpm.enabled') == '1') && isset($row['deactivated']) && $row['deactivated'] == '0') {
|
||||
|
||||
@@ -54,16 +54,15 @@ class IdnaWrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a domain name, a email address or a list of one of both.
|
||||
* Encode a domain name, an email address or a list of one of both.
|
||||
*
|
||||
* @param
|
||||
* string May be either a single domain name, e single email address or a list of one
|
||||
* @param string $to_encode May be either a single domain name, e single email address or a list of one
|
||||
* separated either by ',', ';' or ' '.
|
||||
*
|
||||
* @return string Returns either a single domain name, a single email address or a list of one of
|
||||
* both separated by the same string as the input.
|
||||
*/
|
||||
public function encode($to_encode)
|
||||
public function encode(string $to_encode): string
|
||||
{
|
||||
$to_encode = $this->isUtf8($to_encode) ? $to_encode : utf8_encode($to_encode);
|
||||
try {
|
||||
@@ -83,7 +82,7 @@ class IdnaWrapper
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function isUtf8($string = null)
|
||||
private function isUtf8(string $string)
|
||||
{
|
||||
if (function_exists("mb_detect_encoding")) {
|
||||
if (mb_detect_encoding($string, 'UTF-8, ISO-8859-1') === 'UTF-8') {
|
||||
@@ -119,16 +118,15 @@ class IdnaWrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a domain name, a email address or a list of one of both.
|
||||
* Decode a domain name, an email address or a list of one of both.
|
||||
*
|
||||
* @param
|
||||
* string May be either a single domain name, e single email address or a list of one
|
||||
* @param string $to_decode May be either a single domain name, e single email address or a list of one
|
||||
* separated either by ',', ';' or ' '.
|
||||
*
|
||||
* @return string Returns either a single domain name, a single email address or a list of one of
|
||||
* both separated by the same string as the input.
|
||||
*/
|
||||
public function decode($to_decode)
|
||||
public function decode(string $to_decode): string
|
||||
{
|
||||
return $this->idna_converter->decode($to_decode);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class Install
|
||||
public $phpVersion;
|
||||
public $formfield;
|
||||
public string $requiredVersion = '7.4.0';
|
||||
public array $requiredExtensions = ['session', 'ctype', 'xml', 'filter', 'posix', 'mbstring', 'curl', 'gmp', 'json'];
|
||||
public array $requiredExtensions = ['session', 'ctype', 'xml', 'filter', 'posix', 'mbstring', 'curl', 'gmp', 'json', 'gd'];
|
||||
public array $suggestedExtensions = ['bcmath', 'zip'];
|
||||
public array $suggestions = [];
|
||||
public array $criticals = [];
|
||||
|
||||
@@ -420,7 +420,7 @@ class Core
|
||||
}
|
||||
|
||||
$this->updateSetting($upd_stmt, $this->validatedData['activate_newsfeed'], 'admin', 'show_news_feed');
|
||||
$this->updateSetting($upd_stmt, dirname(__FILE__, 3), 'system', 'letsencryptchallengepath');
|
||||
$this->updateSetting($upd_stmt, dirname(__FILE__, 5), 'system', 'letsencryptchallengepath');
|
||||
|
||||
// insert the lastcronrun to be the installation date
|
||||
$this->updateSetting($upd_stmt, time(), 'system', 'lastcronrun');
|
||||
|
||||
@@ -101,7 +101,7 @@ class Preconfig
|
||||
$agree = [
|
||||
'title' => 'Check',
|
||||
'fields' => [
|
||||
'update_changesagreed' => ['type' => 'checkbox', 'value' => 1, 'label' => '<strong>I have read the update notifications above and I am aware of the changes made to my system.</strong>'],
|
||||
'update_changesagreed' => ['mandatory' => true, 'type' => 'checkrequired', 'value' => 1, 'label' => '<strong>I have read the update notifications above and I am aware of the changes made to my system.</strong>'],
|
||||
'update_preconfig' => ['type' => 'hidden', 'value' => 1]
|
||||
]
|
||||
];
|
||||
|
||||
@@ -68,10 +68,10 @@ class MailLogParser
|
||||
// Parse MDA traffic
|
||||
if (Settings::Get("system.mdaserver") == "dovecot") {
|
||||
$this->parseDovecotLog(Settings::Get("system.mdalog"));
|
||||
$this->parsePostfixLog(Settings::Get("system.mdalog") . ".1");
|
||||
$this->parseDovecotLog(Settings::Get("system.mdalog") . ".1");
|
||||
} elseif (Settings::Get("system.mdaserver") == "courier") {
|
||||
$this->parseCourierLog(Settings::Get("system.mdalog"));
|
||||
$this->parsePostfixLog(Settings::Get("system.mdalog") . ".1");
|
||||
$this->parseCourierLog(Settings::Get("system.mdalog") . ".1");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace Froxlor;
|
||||
|
||||
use Exception;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
use Net_DNS2_Exception;
|
||||
use Net_DNS2_Resolver;
|
||||
use Throwable;
|
||||
use voku\helper\AntiXSS;
|
||||
|
||||
@@ -41,9 +43,9 @@ class PhpHelper
|
||||
* @param array $list
|
||||
* @param string $key
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public static function sortListBy(&$list, $key = 'id')
|
||||
public static function sortListBy(array &$list, string $key = 'id'): bool
|
||||
{
|
||||
self::$sort_type = Settings::Get('panel.natsorting') == 1 ? SORT_NATURAL : SORT_STRING;
|
||||
self::$sort_key = $key;
|
||||
@@ -57,14 +59,10 @@ class PhpHelper
|
||||
* Wrapper around htmlentities to handle arrays, with the advantage that you
|
||||
* can select which fields should be handled by htmlentities
|
||||
*
|
||||
* @param array|string $subject
|
||||
* The subject array
|
||||
* @param string $fields
|
||||
* The fields which should be checked for, separated by spaces
|
||||
* @param int $quote_style
|
||||
* See php documentation about this
|
||||
* @param string $charset
|
||||
* See php documentation about this
|
||||
* @param array|string $subject The subject array
|
||||
* @param array|string $fields The fields which should be checked for, separated by spaces
|
||||
* @param int $quote_style See php documentation about this
|
||||
* @param string $charset See php documentation about this
|
||||
*
|
||||
* @return array|string The string or an array with htmlentities converted strings
|
||||
* @author Florian Lippert <flo@syscp.org> (2003-2009)
|
||||
@@ -77,7 +75,7 @@ class PhpHelper
|
||||
}
|
||||
|
||||
foreach ($subject as $field => $value) {
|
||||
if ((!is_array($fields) || empty($fields)) || (is_array($fields) && !empty($fields) && in_array($field, $fields))) {
|
||||
if ((!is_array($fields) || empty($fields)) || (in_array($field, $fields))) {
|
||||
// Just call ourselve to manage multi-dimensional arrays
|
||||
$subject[$field] = self::htmlentitiesArray($value, $fields, $quote_style, $charset);
|
||||
}
|
||||
@@ -92,45 +90,36 @@ class PhpHelper
|
||||
/**
|
||||
* Returns array with all empty-values removed
|
||||
*
|
||||
* @param array $source
|
||||
* The array to trim
|
||||
* @param array $source The array to trim
|
||||
* @return array The trim'med array
|
||||
*/
|
||||
public static function arrayTrim($source)
|
||||
public static function arrayTrim(array $source): array
|
||||
{
|
||||
$returnval = [];
|
||||
if (is_array($source)) {
|
||||
$source = array_map('trim', $source);
|
||||
$returnval = array_filter($source, function ($value) {
|
||||
return $value !== '';
|
||||
});
|
||||
} else {
|
||||
$returnval = $source;
|
||||
}
|
||||
return $returnval;
|
||||
$source = array_map('trim', $source);
|
||||
return array_filter($source, function ($value) {
|
||||
return $value !== '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces Strings in an array, with the advantage that you
|
||||
* can select which fields should be str_replace'd
|
||||
*
|
||||
* @param string|array $search
|
||||
* String or array of strings to search for
|
||||
* @param string|array $reaplce
|
||||
* String or array to replace with
|
||||
* @param string|array $subject
|
||||
* String or array The subject array
|
||||
* @param string $fields
|
||||
* string The fields which should be checked for, separated by spaces
|
||||
* @param string|array $search String or array of strings to search for
|
||||
* @param string|array $replace String or array to replace with
|
||||
* @param string|array $subject String or array The subject array
|
||||
* @param string|array $fields string The fields which should be checked for, separated by spaces
|
||||
*
|
||||
* @return string|array The str_replace'd array
|
||||
* @author Florian Lippert <flo@syscp.org> (2003-2009)
|
||||
*/
|
||||
public static function strReplaceArray($search, $replace, $subject, $fields = '')
|
||||
{
|
||||
if (is_array($subject)) {
|
||||
$fields = self::arrayTrim(explode(' ', $fields));
|
||||
if (!is_array($fields)) {
|
||||
$fields = self::arrayTrim(explode(' ', $fields));
|
||||
}
|
||||
foreach ($subject as $field => $value) {
|
||||
if ((!is_array($fields) || empty($fields)) || (is_array($fields) && !empty($fields) && in_array($field, $fields))) {
|
||||
if ((!is_array($fields) || empty($fields)) || (in_array($field, $fields))) {
|
||||
$subject[$field] = str_replace($search, $replace, $value);
|
||||
}
|
||||
}
|
||||
@@ -175,7 +164,8 @@ class PhpHelper
|
||||
$err_display .= '<br><p><pre>';
|
||||
$debug = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
foreach ($debug as $dline) {
|
||||
$err_display .= $dline['function'] . '() called at [' . str_replace(Froxlor::getInstallDir(), '', ($dline['file'] ?? 'unknown')) . ':' . ($dline['line'] ?? 0) . ']<br>';
|
||||
$err_display .= $dline['function'] . '() called at [' . str_replace(Froxlor::getInstallDir(), '',
|
||||
($dline['file'] ?? 'unknown')) . ':' . ($dline['line'] ?? 0) . ']<br>';
|
||||
}
|
||||
$err_display .= '</pre></p>';
|
||||
// end later
|
||||
@@ -191,9 +181,13 @@ class PhpHelper
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Throwable $exception
|
||||
* @return void
|
||||
*/
|
||||
public static function phpExceptionHandler(Throwable $exception)
|
||||
{
|
||||
if (!isset($_SERVER['SHELL']) || (isset($_SERVER['SHELL']) && $_SERVER['SHELL'] == '')) {
|
||||
if (!isset($_SERVER['SHELL']) || $_SERVER['SHELL'] == '') {
|
||||
// show
|
||||
UI::initTwig(true);
|
||||
UI::twig()->addGlobal('install_mode', '1');
|
||||
@@ -208,6 +202,10 @@ class PhpHelper
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ...$configdirs
|
||||
* @return array|null
|
||||
*/
|
||||
public static function loadConfigArrayDir(...$configdirs)
|
||||
{
|
||||
if (count($configdirs) <= 0) {
|
||||
@@ -222,7 +220,8 @@ class PhpHelper
|
||||
if (is_dir($data_dirname)) {
|
||||
$data_dirhandle = opendir($data_dirname);
|
||||
while (false !== ($data_filename = readdir($data_dirhandle))) {
|
||||
if ($data_filename != '.' && $data_filename != '..' && $data_filename != '' && substr($data_filename, -4) == '.php') {
|
||||
if ($data_filename != '.' && $data_filename != '..' && $data_filename != '' && substr($data_filename,
|
||||
-4) == '.php') {
|
||||
$data_files[] = $data_dirname . $data_filename;
|
||||
}
|
||||
}
|
||||
@@ -244,45 +243,64 @@ class PhpHelper
|
||||
* ipv6 aware gethostbynamel function
|
||||
*
|
||||
* @param string $host
|
||||
* @param boolean $try_a
|
||||
* default true
|
||||
* @return boolean|array
|
||||
* @param boolean $try_a default true
|
||||
* @param string|null $nameserver set additional resolver nameserver to use (e.g. 1.1.1.1)
|
||||
* @return bool|array
|
||||
*/
|
||||
public static function gethostbynamel6($host, $try_a = true)
|
||||
public static function gethostbynamel6(string $host, bool $try_a = true, string $nameserver = null)
|
||||
{
|
||||
$dns6 = @dns_get_record($host, DNS_AAAA);
|
||||
if (!is_array($dns6)) {
|
||||
// no record or failed to check
|
||||
$dns6 = [];
|
||||
}
|
||||
if ($try_a == true) {
|
||||
$dns4 = @dns_get_record($host, DNS_A);
|
||||
if (!is_array($dns4)) {
|
||||
// no record or failed to check
|
||||
$dns4 = [];
|
||||
}
|
||||
$dns = array_merge($dns4, $dns6);
|
||||
} else {
|
||||
$dns = $dns6;
|
||||
}
|
||||
$ips = [];
|
||||
foreach ($dns as $record) {
|
||||
if ($record["type"] == "A") {
|
||||
// always use compressed ipv6 format
|
||||
$ip = inet_ntop(inet_pton($record["ip"]));
|
||||
$ips[] = $ip;
|
||||
|
||||
try {
|
||||
// set the default nameservers to use, use the system default if none are provided
|
||||
$resolver = new Net_DNS2_Resolver($nameserver ? ['nameservers' => [$nameserver]] : []);
|
||||
|
||||
// get all ip addresses from the A record and normalize them
|
||||
if ($try_a) {
|
||||
try {
|
||||
$answer = $resolver->query($host, 'A')->answer;
|
||||
foreach ($answer as $rr) {
|
||||
if ($rr instanceof \Net_DNS2_RR_A) {
|
||||
$ips[] = inet_ntop(inet_pton($rr->address));
|
||||
}
|
||||
}
|
||||
} catch (Net_DNS2_Exception $e) {
|
||||
// we can't do anything here, just continue
|
||||
}
|
||||
}
|
||||
if ($record["type"] == "AAAA") {
|
||||
// always use compressed ipv6 format
|
||||
$ip = inet_ntop(inet_pton($record["ipv6"]));
|
||||
$ips[] = $ip;
|
||||
|
||||
// get all ip addresses from the AAAA record and normalize them
|
||||
try {
|
||||
$answer = $resolver->query($host, 'AAAA')->answer;
|
||||
foreach ($answer as $rr) {
|
||||
if ($rr instanceof \Net_DNS2_RR_AAAA) {
|
||||
$ips[] = inet_ntop(inet_pton($rr->address));
|
||||
}
|
||||
}
|
||||
} catch (Net_DNS2_Exception $e) {
|
||||
// we can't do anything here, just continue
|
||||
}
|
||||
} catch (Net_DNS2_Exception $e) {
|
||||
// fallback to php's dns_get_record if Net_DNS2 has no resolver available, but this may cause
|
||||
// problems if the system's dns is not configured correctly; for example, the acme pre-check
|
||||
// will fail because some providers put a local ip in /etc/hosts
|
||||
|
||||
// get all ip addresses from the A record and normalize them
|
||||
if ($try_a) {
|
||||
$answer = @dns_get_record($host, DNS_A);
|
||||
foreach ($answer as $rr) {
|
||||
$ips[] = inet_ntop(inet_pton($rr['ip']));
|
||||
}
|
||||
}
|
||||
|
||||
// get all ip addresses from the AAAA record and normalize them
|
||||
$answer = @dns_get_record($host, DNS_AAAA);
|
||||
foreach ($answer as $rr) {
|
||||
$ips[] = inet_ntop(inet_pton($rr['ipv6']));
|
||||
}
|
||||
}
|
||||
if (count($ips) < 1) {
|
||||
return false;
|
||||
} else {
|
||||
return $ips;
|
||||
}
|
||||
|
||||
return count($ips) > 0 ? $ips : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,7 +312,7 @@ class PhpHelper
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function randomStr($length)
|
||||
public static function randomStr(int $length): string
|
||||
{
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
return openssl_random_pseudo_bytes($length);
|
||||
@@ -303,20 +321,21 @@ class PhpHelper
|
||||
}
|
||||
|
||||
/**
|
||||
* Return human readable sizes
|
||||
* Return human-readable sizes
|
||||
*
|
||||
* @param int $size
|
||||
* size in bytes
|
||||
* @param string $max
|
||||
* maximum unit
|
||||
* @param string $system
|
||||
* 'si' for SI, 'bi' for binary prefixes
|
||||
* @param int $size size in bytes
|
||||
* @param ?string $max maximum unit
|
||||
* @param string $system 'si' for SI, 'bi' for binary prefixes
|
||||
* @param string $retstring string-format
|
||||
*
|
||||
* @param string $retstring
|
||||
* string
|
||||
* @return string
|
||||
*/
|
||||
public static function sizeReadable($size, $max = null, $system = 'si', $retstring = '%01.2f %s')
|
||||
{
|
||||
public static function sizeReadable(
|
||||
$size,
|
||||
?string $max = '',
|
||||
string $system = 'si',
|
||||
string $retstring = '%01.2f %s'
|
||||
): string {
|
||||
// Pick units
|
||||
$systems = [
|
||||
'si' => [
|
||||
@@ -342,7 +361,7 @@ class PhpHelper
|
||||
'size' => 1024
|
||||
]
|
||||
];
|
||||
$sys = isset($systems[$system]) ? $systems[$system] : $systems['si'];
|
||||
$sys = $systems[$system] ?? $systems['si'];
|
||||
|
||||
// Max unit to display
|
||||
$depth = count($sys['prefix']) - 1;
|
||||
@@ -363,14 +382,12 @@ class PhpHelper
|
||||
* Replaces all occurrences of variables defined in the second argument
|
||||
* in the first argument with their values.
|
||||
*
|
||||
* @param string $text
|
||||
* The string that should be searched for variables
|
||||
* @param array $vars
|
||||
* The array containing the variables with their values
|
||||
* @param string $text The string that should be searched for variables
|
||||
* @param array $vars The array containing the variables with their values
|
||||
*
|
||||
* @return string The submitted string with the variables replaced.
|
||||
*/
|
||||
public static function replaceVariables($text, $vars)
|
||||
public static function replaceVariables(string $text, array $vars): string
|
||||
{
|
||||
$pattern = "/\{([a-zA-Z0-9\-_]+)\}/";
|
||||
$matches = [];
|
||||
@@ -386,12 +403,22 @@ class PhpHelper
|
||||
}
|
||||
}
|
||||
|
||||
$text = str_replace('\n', "\n", $text);
|
||||
return $text;
|
||||
return str_replace('\n', "\n", $text);
|
||||
}
|
||||
|
||||
public static function recursive_array_search($needle, $haystack, &$keys = [], $currentKey = '')
|
||||
{
|
||||
/**
|
||||
* @param string $needle
|
||||
* @param array $haystack
|
||||
* @param array $keys
|
||||
* @param string $currentKey
|
||||
* @return true
|
||||
*/
|
||||
public static function recursive_array_search(
|
||||
string $needle,
|
||||
array $haystack,
|
||||
array &$keys = [],
|
||||
string $currentKey = ''
|
||||
): bool {
|
||||
foreach ($haystack as $key => $value) {
|
||||
$pathkey = empty($currentKey) ? $key : $currentKey . '.' . $key;
|
||||
if (is_array($value)) {
|
||||
@@ -406,13 +433,13 @@ class PhpHelper
|
||||
}
|
||||
|
||||
/**
|
||||
* function to check a super-global passed by reference
|
||||
* function to check a super-global passed by reference,
|
||||
* so it gets automatically updated
|
||||
*
|
||||
* @param array $global
|
||||
* @param AntiXSS $antiXss
|
||||
*/
|
||||
public static function cleanGlobal(&$global, &$antiXss)
|
||||
public static function cleanGlobal(array &$global, AntiXSS &$antiXss)
|
||||
{
|
||||
$ignored_fields = [
|
||||
'system_default_vhostconf',
|
||||
@@ -434,7 +461,12 @@ class PhpHelper
|
||||
}
|
||||
}
|
||||
|
||||
private static function sortListByGivenKey($a, $b): int
|
||||
/**
|
||||
* @param array $a
|
||||
* @param array $b
|
||||
* @return int
|
||||
*/
|
||||
private static function sortListByGivenKey(array $a, array $b): int
|
||||
{
|
||||
if (self::$sort_type == SORT_NATURAL) {
|
||||
return strnatcasecmp($a[self::$sort_key], $b[self::$sort_key]);
|
||||
@@ -467,35 +499,33 @@ class PhpHelper
|
||||
/**
|
||||
* Parse array to array string.
|
||||
*
|
||||
* @param $array
|
||||
* @param $key
|
||||
* @param array $array
|
||||
* @param ?string $key
|
||||
* @param int $depth
|
||||
* @return string
|
||||
*/
|
||||
public static function parseArrayToString($array, $key = null, int $depth = 1): string
|
||||
public static function parseArrayToString(array $array, string $key = null, int $depth = 1): string
|
||||
{
|
||||
$str = '';
|
||||
if (is_array($array)) {
|
||||
if (!is_null($key)) {
|
||||
$str .= self::tabPrefix(($depth-1), "'{$key}' => [\n");
|
||||
} else {
|
||||
$str .= self::tabPrefix(($depth-1), "[\n");
|
||||
}
|
||||
foreach ($array as $key => $value) {
|
||||
if (!is_array($value)) {
|
||||
if (is_bool($value)) {
|
||||
$str .= self::tabPrefix($depth, sprintf("'%s' => %s,\n", $key, $value ? 'true' : 'false'));
|
||||
} elseif (is_int($value)) {
|
||||
$str .= self::tabPrefix($depth, "'{$key}' => $value,\n");
|
||||
} else {
|
||||
$str .= self::tabPrefix($depth, "'{$key}' => '{$value}',\n");
|
||||
}
|
||||
} elseif (is_array($value)) {
|
||||
$str .= self::parseArrayToString($value, $key, ($depth + 1));
|
||||
}
|
||||
}
|
||||
$str .= self::tabPrefix(($depth-1), "],\n");
|
||||
if (!is_null($key)) {
|
||||
$str .= self::tabPrefix(($depth - 1), "'{$key}' => [\n");
|
||||
} else {
|
||||
$str .= self::tabPrefix(($depth - 1), "[\n");
|
||||
}
|
||||
foreach ($array as $key => $value) {
|
||||
if (!is_array($value)) {
|
||||
if (is_bool($value)) {
|
||||
$str .= self::tabPrefix($depth, sprintf("'%s' => %s,\n", $key, $value ? 'true' : 'false'));
|
||||
} elseif (is_int($value)) {
|
||||
$str .= self::tabPrefix($depth, "'{$key}' => $value,\n");
|
||||
} else {
|
||||
$str .= self::tabPrefix($depth, "'{$key}' => '{$value}',\n");
|
||||
}
|
||||
} else {
|
||||
$str .= self::parseArrayToString($value, $key, ($depth + 1));
|
||||
}
|
||||
}
|
||||
$str .= self::tabPrefix(($depth - 1), "],\n");
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace Froxlor;
|
||||
|
||||
use Exception;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\UI\Form;
|
||||
use Froxlor\Validate\Validate;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
@@ -79,14 +81,16 @@ class SImExporter
|
||||
$_data[$index] = $row['value'];
|
||||
}
|
||||
|
||||
if (array_key_exists($row['settinggroup'], $settings_definitions) && array_key_exists($row['varname'], $settings_definitions[$row['settinggroup']])) {
|
||||
if (array_key_exists($row['settinggroup'], $settings_definitions) && array_key_exists($row['varname'],
|
||||
$settings_definitions[$row['settinggroup']])) {
|
||||
// Export image file
|
||||
if ($settings_definitions[$row['settinggroup']][$row['varname']]['type'] === "image") {
|
||||
if ($row['value'] === "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
$_data[$index . '.image_data'] = base64_encode(file_get_contents(explode('?', $row['value'], 2)[0]));
|
||||
$_data[$index . '.image_data'] = base64_encode(file_get_contents(explode('?', $row['value'],
|
||||
2)[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,66 +144,84 @@ class SImExporter
|
||||
$_data['system.le_froxlor_redirect'] = 0;
|
||||
}
|
||||
}
|
||||
// store new data
|
||||
foreach ($_data as $index => $value) {
|
||||
$index_split = explode('.', $index, 3);
|
||||
|
||||
// Catch image_data and save it
|
||||
if (isset($index_split[2]) && $index_split[2] === 'image_data' && !empty($_data[$index_split[0] . '.' . $index_split[1]])) {
|
||||
$path = Froxlor::getInstallDir() . '/img/';
|
||||
if (!is_dir($path) && !mkdir($path, 0775)) {
|
||||
throw new Exception("img directory does not exist and cannot be created");
|
||||
}
|
||||
|
||||
// Make sure we can write to the upload directory
|
||||
if (!is_writable($path)) {
|
||||
if (!chmod($path, 0775)) {
|
||||
throw new Exception("Cannot write to img directory");
|
||||
}
|
||||
}
|
||||
|
||||
$img_data = base64_decode($value);
|
||||
$img_filename = Froxlor::getInstallDir() . '/' . str_replace('../', '', explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0]);
|
||||
|
||||
file_put_contents($img_filename, $img_data);
|
||||
|
||||
if (function_exists('finfo_open')) {
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mimetype = finfo_file($finfo, $img_filename);
|
||||
finfo_close($finfo);
|
||||
} else {
|
||||
$mimetype = mime_content_type($img_filename);
|
||||
}
|
||||
if (empty($mimetype)) {
|
||||
$mimetype = 'application/octet-stream';
|
||||
}
|
||||
if (!in_array($mimetype, ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) {
|
||||
@unlink($img_filename);
|
||||
throw new Exception("Uploaded file is not a valid image");
|
||||
}
|
||||
|
||||
$spl = explode('.', $img_filename);
|
||||
$file_extension = strtolower(array_pop($spl));
|
||||
unset($spl);
|
||||
|
||||
if (!in_array($file_extension, [
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'png',
|
||||
'gif'
|
||||
])) {
|
||||
@unlink($img_filename);
|
||||
throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
|
||||
$form_data = [];
|
||||
$image_data = [];
|
||||
// read in all current settings
|
||||
$current_settings = Settings::getAll();
|
||||
foreach ($current_settings as $setting_group => $setting) {
|
||||
foreach ($setting as $varname => $value) {
|
||||
// set all group/varname:values which are not in the import file
|
||||
if (!array_key_exists($setting_group . '.' . $varname, $_data)) {
|
||||
$_data[$setting_group . '.' . $varname] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
// re-format the array-key for Form::processForm
|
||||
foreach ($_data as $key => $value) {
|
||||
$index_split = explode('.', $key, 3);
|
||||
if (!isset($current_settings[$index_split[0]][$index_split[1]])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Settings::Set($index, $value);
|
||||
if (isset($index_split[2]) && $index_split[2] === 'image_data' && !empty($_data[$index_split[0] . '.' . $index_split[1]])) {
|
||||
$image_data[$key] = $value;
|
||||
} else {
|
||||
$form_data[str_replace(".", "_", $key)] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// store new data
|
||||
$settings_data = PhpHelper::loadConfigArrayDir(Froxlor::getInstallDir() . '/actions/admin/settings/');
|
||||
Settings::loadSettingsInto($settings_data);
|
||||
|
||||
if (Form::processForm($settings_data, $form_data, [], null, true)) {
|
||||
// save to DB
|
||||
Settings::Flush();
|
||||
|
||||
// Process image_data and save it
|
||||
if (count($image_data) > 0) {
|
||||
foreach ($image_data as $index => $value) {
|
||||
$index_split = explode('.', $index, 3);
|
||||
$path = Froxlor::getInstallDir() . '/img/';
|
||||
if (!is_dir($path) && !mkdir($path, 0775)) {
|
||||
throw new Exception("img directory does not exist and cannot be created");
|
||||
}
|
||||
|
||||
// Make sure we can write to the upload directory
|
||||
if (!is_writable($path)) {
|
||||
if (!chmod($path, 0775)) {
|
||||
throw new Exception("Cannot write to img directory");
|
||||
}
|
||||
}
|
||||
|
||||
if (Validate::validateBase64Image($value)) {
|
||||
$img_data = base64_decode($value);
|
||||
$img_filename = explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0];
|
||||
|
||||
$spl = explode('.', $img_filename);
|
||||
$file_extension = strtolower(array_pop($spl));
|
||||
unset($spl);
|
||||
|
||||
if (!in_array($file_extension, [
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'png',
|
||||
'gif'
|
||||
])) {
|
||||
throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
|
||||
}
|
||||
$img_filename = 'img/' . bin2hex(random_bytes(16)) . '.' . $file_extension;
|
||||
file_put_contents(Froxlor::getInstallDir() . '/' . $img_filename, $img_data);
|
||||
$img_index = $index_split[0].'.'.$index_split[1];
|
||||
Settings::Set($img_index, $img_filename . '?v=' . time());
|
||||
}
|
||||
}
|
||||
}
|
||||
// all good
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception("Importing settings failed");
|
||||
}
|
||||
// save to DB
|
||||
Settings::Flush();
|
||||
// all good
|
||||
return true;
|
||||
}
|
||||
throw new Exception("Invalid JSON data: " . json_last_error_msg());
|
||||
}
|
||||
|
||||
@@ -329,6 +329,12 @@ class Settings
|
||||
}
|
||||
}
|
||||
|
||||
public static function getAll() : array
|
||||
{
|
||||
self::init();
|
||||
return self::$data;
|
||||
}
|
||||
|
||||
/**
|
||||
* get value from config by identifier
|
||||
*/
|
||||
|
||||
@@ -30,10 +30,19 @@ use Froxlor\Database\Database;
|
||||
class FroxlorVhostSettings
|
||||
{
|
||||
|
||||
public static function hasVhostContainerEnabled($need_ssl = false)
|
||||
/**
|
||||
* @param bool $need_ssl
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function hasVhostContainerEnabled(bool $need_ssl = false): bool
|
||||
{
|
||||
$sel_stmt = Database::prepare("SELECT COUNT(*) as vcentries FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `vhostcontainer`= '1'" . ($need_ssl ? " AND `ssl` = '1'" : ""));
|
||||
$result = Database::pexecute_first($sel_stmt);
|
||||
return $result['vcentries'] > 0;
|
||||
if ($result) {
|
||||
return $result['vcentries'] > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ use Froxlor\PhpHelper;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\System\Cronjob;
|
||||
use Froxlor\System\IPTools;
|
||||
use Froxlor\Validate\Validate;
|
||||
use PDO;
|
||||
|
||||
class Store
|
||||
@@ -45,10 +46,21 @@ class Store
|
||||
{
|
||||
$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);
|
||||
|
||||
if ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'le_froxlor_enabled' && $newfieldvalue == '0') {
|
||||
Database::query("
|
||||
DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = '0'
|
||||
");
|
||||
if ($returnvalue !== false
|
||||
&& is_array($fielddata)
|
||||
&& isset($fielddata['settinggroup'])
|
||||
&& $fielddata['settinggroup'] == 'system'
|
||||
&& isset($fielddata['varname'])
|
||||
) {
|
||||
if ($fielddata['varname'] == 'le_froxlor_enabled' && $newfieldvalue == '0') {
|
||||
Database::query("
|
||||
DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = '0'
|
||||
");
|
||||
} elseif ($fielddata['varname'] == 'froxloraliases' && $newfieldvalue != $fielddata['value']) {
|
||||
Database::query("
|
||||
UPDATE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SET `validtodate`= NULL WHERE `domainid` = '0'
|
||||
");
|
||||
}
|
||||
}
|
||||
|
||||
return $returnvalue;
|
||||
@@ -415,40 +427,30 @@ class Store
|
||||
}
|
||||
|
||||
// Make sure mime-type matches an image
|
||||
if (function_exists('finfo_open')) {
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mimetype = finfo_file($finfo, $_FILES[$fieldname]['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
} else {
|
||||
$mimetype = mime_content_type($_FILES[$fieldname]['tmp_name']);
|
||||
}
|
||||
if (empty($mimetype)) {
|
||||
$mimetype = 'application/octet-stream';
|
||||
}
|
||||
if (!in_array($mimetype, ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) {
|
||||
throw new \Exception("Uploaded file is not a valid image");
|
||||
}
|
||||
$image_content = file_get_contents($_FILES[$fieldname]['tmp_name']);
|
||||
$value = base64_encode($image_content);
|
||||
if (Validate::validateBase64Image($value)) {
|
||||
$img_filename = $_FILES[$fieldname]['name'];
|
||||
|
||||
// Determine file extension
|
||||
$spl = explode('.', $_FILES[$fieldname]['name']);
|
||||
$file_extension = strtolower(array_pop($spl));
|
||||
unset($spl);
|
||||
$spl = explode('.', $img_filename);
|
||||
$file_extension = strtolower(array_pop($spl));
|
||||
unset($spl);
|
||||
|
||||
if (!in_array($file_extension, [
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'png',
|
||||
'gif'
|
||||
])) {
|
||||
throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
|
||||
if (!in_array($file_extension, [
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'png',
|
||||
'gif'
|
||||
])) {
|
||||
throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
|
||||
}
|
||||
$filename = bin2hex(random_bytes(16)) . '.' . $file_extension;
|
||||
// Move file
|
||||
if (!move_uploaded_file($_FILES[$fieldname]['tmp_name'], $path . $filename)) {
|
||||
throw new Exception("Unable to save image to img folder");
|
||||
}
|
||||
$save_to = 'img/' . $filename . '?v=' . time();
|
||||
}
|
||||
|
||||
// Move file
|
||||
if (!move_uploaded_file($_FILES[$fieldname]['tmp_name'], $path . $fielddata['image_name'] . '.' . $file_extension)) {
|
||||
throw new Exception("Unable to save image to img folder");
|
||||
}
|
||||
|
||||
$save_to = 'img/' . $fielddata['image_name'] . '.' . $file_extension . '?v=' . time();
|
||||
}
|
||||
|
||||
// Delete file?
|
||||
|
||||
@@ -100,32 +100,34 @@ class Cronjob
|
||||
|
||||
// now check if it differs from our settings
|
||||
if ($update_to_guid != Settings::Get('system.lastguid')) {
|
||||
$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Updating froxlor last guid to ' . $update_to_guid);
|
||||
$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE,
|
||||
'Updating froxlor last guid to ' . $update_to_guid);
|
||||
Settings::Set('system.lastguid', $update_to_guid);
|
||||
}
|
||||
} else {
|
||||
$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'File /etc/group not readable; cannot check for latest guid');
|
||||
$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE,
|
||||
'File /etc/group not readable; cannot check for latest guid');
|
||||
}
|
||||
} else {
|
||||
$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'File /etc/group not readable; cannot check for latest guid');
|
||||
$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE,
|
||||
'File /etc/group not readable; cannot check for latest guid');
|
||||
}
|
||||
} else {
|
||||
$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'File /etc/group does not exist; cannot check for latest guid');
|
||||
$mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE,
|
||||
'File /etc/group does not exist; cannot check for latest guid');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a task into the PANEL_TASKS-Table
|
||||
*
|
||||
* @param
|
||||
* int Type of task
|
||||
* @param
|
||||
* string Parameter (possible to pass multiple times)
|
||||
* @param int $type Type of task
|
||||
* @param string $params Parameter (possible to pass multiple times)
|
||||
*
|
||||
* @author Florian Lippert <flo@syscp.org> (2003-2009)
|
||||
* @throws Exception
|
||||
* @author Froxlor team <team@froxlor.org> (2010-)
|
||||
*/
|
||||
public static function inserttask($type, ...$params)
|
||||
public static function inserttask(int $type, ...$params)
|
||||
{
|
||||
// prepare the insert-statement
|
||||
$ins_stmt = Database::prepare("
|
||||
@@ -223,7 +225,7 @@ class Cronjob
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getCronjobsLastRun()
|
||||
public static function getCronjobsLastRun(): array
|
||||
{
|
||||
$query = "SELECT `lastrun`, `desc_lng_key` FROM `" . TABLE_PANEL_CRONRUNS . "` WHERE `isactive` = '1' ORDER BY `cronfile` ASC";
|
||||
$result = Database::query($query);
|
||||
@@ -238,14 +240,21 @@ class Cronjob
|
||||
return $cronjobs_last_run;
|
||||
}
|
||||
|
||||
public static function toggleCronStatus($module = null, $isactive = 0)
|
||||
/**
|
||||
* @param string $module
|
||||
* @param int $isactive
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function toggleCronStatus(string $module, int $isactive = 0)
|
||||
{
|
||||
if ($isactive != 1) {
|
||||
$isactive = 0;
|
||||
}
|
||||
|
||||
$upd_stmt = Database::prepare("
|
||||
UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `isactive` = :active WHERE `module` = :module");
|
||||
UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `isactive` = :active WHERE `module` = :module
|
||||
");
|
||||
Database::pexecute($upd_stmt, [
|
||||
'active' => $isactive,
|
||||
'module' => $module
|
||||
@@ -257,7 +266,7 @@ class Cronjob
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getOutstandingTasks()
|
||||
public static function getOutstandingTasks(): array
|
||||
{
|
||||
$query = "SELECT * FROM `" . TABLE_PANEL_TASKS . "` ORDER BY `type` ASC";
|
||||
$result = Database::query($query);
|
||||
@@ -309,7 +318,7 @@ class Cronjob
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function dieWithMail($message, $subject = "[froxlor] Cronjob error")
|
||||
public static function dieWithMail(string $message, string $subject = "[froxlor] Cronjob error")
|
||||
{
|
||||
if (Settings::Get('system.send_cron_errors') == '1') {
|
||||
$_mail = new Mailer(true);
|
||||
@@ -339,7 +348,12 @@ class Cronjob
|
||||
die($message);
|
||||
}
|
||||
|
||||
public static function updateLastRunOfCron($cronname)
|
||||
/**
|
||||
* @param string $cronname
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function updateLastRunOfCron(string $cronname)
|
||||
{
|
||||
$upd_stmt = Database::prepare("
|
||||
UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = UNIX_TIMESTAMP() WHERE `cronfile` = :cron;
|
||||
|
||||
@@ -41,7 +41,7 @@ class Crypt
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generatePassword(int $length = 0, bool $isSalt = false)
|
||||
public static function generatePassword(int $length = 0, bool $isSalt = false): string
|
||||
{
|
||||
$alpha_lower = 'abcdefghijklmnopqrstuvwxyz';
|
||||
$alpha_upper = strtoupper($alpha_lower);
|
||||
@@ -78,7 +78,7 @@ class Crypt
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function specialShuffle($str = null)
|
||||
private static function specialShuffle(string $str): string
|
||||
{
|
||||
$len = mb_strlen($str);
|
||||
$sploded = [];
|
||||
@@ -94,7 +94,7 @@ class Crypt
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAvailablePasswordHashes()
|
||||
public static function getAvailablePasswordHashes(): array
|
||||
{
|
||||
// get available pwd-hases
|
||||
$available_pwdhashes = [
|
||||
@@ -120,31 +120,40 @@ class Crypt
|
||||
* we check against the length, if not matched
|
||||
* an error message will be output and 'exit' is called
|
||||
*
|
||||
* @param string $password
|
||||
* the password to validate
|
||||
* @param string $password the password to validate
|
||||
* @param bool $json_response
|
||||
*
|
||||
* @return string either the password or an errormessage+exit
|
||||
*/
|
||||
public static function validatePassword($password = null, $json_response = false)
|
||||
public static function validatePassword(string $password, bool $json_response = false): string
|
||||
{
|
||||
if (Settings::Get('panel.password_min_length') > 0) {
|
||||
$password = Validate::validate($password, Settings::Get('panel.password_min_length'), '/^.{' . (int)Settings::Get('panel.password_min_length') . ',}$/D', 'notrequiredpasswordlength', [], $json_response);
|
||||
$password = Validate::validate($password, Settings::Get('panel.password_min_length'),
|
||||
'/^.{' . (int)Settings::Get('panel.password_min_length') . ',}$/D', 'notrequiredpasswordlength', [],
|
||||
$json_response);
|
||||
}
|
||||
|
||||
if (Settings::Get('panel.password_regex') != '') {
|
||||
$password = Validate::validate($password, Settings::Get('panel.password_regex'), Settings::Get('panel.password_regex'), 'notrequiredpasswordcomplexity', [], $json_response);
|
||||
$password = Validate::validate($password, Settings::Get('panel.password_regex'),
|
||||
Settings::Get('panel.password_regex'), 'notrequiredpasswordcomplexity', [], $json_response);
|
||||
} else {
|
||||
if (Settings::Get('panel.password_alpha_lower')) {
|
||||
$password = Validate::validate($password, '/.*[a-z]+.*/', '/.*[a-z]+.*/', 'notrequiredpasswordcomplexity', [], $json_response);
|
||||
$password = Validate::validate($password, '/.*[a-z]+.*/', '/.*[a-z]+.*/',
|
||||
'notrequiredpasswordcomplexity', [], $json_response);
|
||||
}
|
||||
if (Settings::Get('panel.password_alpha_upper')) {
|
||||
$password = Validate::validate($password, '/.*[A-Z]+.*/', '/.*[A-Z]+.*/', 'notrequiredpasswordcomplexity', [], $json_response);
|
||||
$password = Validate::validate($password, '/.*[A-Z]+.*/', '/.*[A-Z]+.*/',
|
||||
'notrequiredpasswordcomplexity', [], $json_response);
|
||||
}
|
||||
if (Settings::Get('panel.password_numeric')) {
|
||||
$password = Validate::validate($password, '/.*[0-9]+.*/', '/.*[0-9]+.*/', 'notrequiredpasswordcomplexity', [], $json_response);
|
||||
$password = Validate::validate($password, '/.*[0-9]+.*/', '/.*[0-9]+.*/',
|
||||
'notrequiredpasswordcomplexity', [], $json_response);
|
||||
}
|
||||
if (Settings::Get('panel.password_special_char_required')) {
|
||||
$password = Validate::validate($password, '/.*[' . preg_quote(Settings::Get('panel.password_special_char'), '/') . ']+.*/', '/.*[' . preg_quote(Settings::Get('panel.password_special_char'), '/') . ']+.*/', 'notrequiredpasswordcomplexity', [], $json_response);
|
||||
$password = Validate::validate($password,
|
||||
'/.*[' . preg_quote(Settings::Get('panel.password_special_char'), '/') . ']+.*/',
|
||||
'/.*[' . preg_quote(Settings::Get('panel.password_special_char'), '/') . ']+.*/',
|
||||
'notrequiredpasswordcomplexity', [], $json_response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,19 +168,20 @@ class Crypt
|
||||
* additionally it updates the hash if the system settings changed
|
||||
* or if the very old md5() sum is used
|
||||
*
|
||||
* @param array $userinfo
|
||||
* user-data from table
|
||||
* @param string $password
|
||||
* the password to validate
|
||||
* @param string $table
|
||||
* either panel_customers or panel_admins
|
||||
* @param string $uid
|
||||
* user-id-field in $table
|
||||
* @param array $userinfo user-data from table
|
||||
* @param string $password the password to validate
|
||||
* @param string $table either panel_customers or panel_admins
|
||||
* @param string $uid user-id-field in $table
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function validatePasswordLogin($userinfo = null, $password = null, $table = 'panel_customers', $uid = 'customerid')
|
||||
{
|
||||
public static function validatePasswordLogin(
|
||||
array $userinfo,
|
||||
string $password,
|
||||
string $table = 'panel_customers',
|
||||
string $uid = 'customerid'
|
||||
): bool {
|
||||
$algo = Settings::Get('system.passwordcryptfunc') !== null ? Settings::Get('system.passwordcryptfunc') : PASSWORD_DEFAULT;
|
||||
if (is_numeric($algo)) {
|
||||
// old setting format
|
||||
@@ -188,7 +198,7 @@ class Crypt
|
||||
$update_hash = true;
|
||||
}
|
||||
|
||||
if ($pwd_hash == $pwd_check || password_verify($password, $pwd_hash)) {
|
||||
if ($pwd_hash === $pwd_check || password_verify($password, $pwd_hash)) {
|
||||
// check for update of hash (only if our database is ready to handle the bigger string)
|
||||
$is_ready = Froxlor::versionCompare2("0.9.33", Froxlor::getVersion()) <= 0;
|
||||
if ((password_needs_rehash($pwd_hash, $algo) || $update_hash) && $is_ready) {
|
||||
@@ -209,24 +219,21 @@ class Crypt
|
||||
/**
|
||||
* Make encrypted password from clear text password
|
||||
*
|
||||
* @param string $password
|
||||
* Password to be encrypted
|
||||
* @param bool $htpasswd
|
||||
* optional whether to generate a SHA1 password for directory protection
|
||||
* @param bool $ftpd
|
||||
* optional generates sha256 password strings for proftpd/pureftpd
|
||||
* @param string $password Password to be encrypted
|
||||
* @param bool $htpasswd optional whether to generate a bcrypt password for directory protection
|
||||
* @param bool $ftpd optional generates sha256 password strings for proftpd/pureftpd
|
||||
*
|
||||
* @return string encrypted password
|
||||
*/
|
||||
public static function makeCryptPassword(string $password, bool $htpasswd = false, bool $ftpd = false)
|
||||
public static function makeCryptPassword(string $password, bool $htpasswd = false, bool $ftpd = false): string
|
||||
{
|
||||
if ($htpasswd || $ftpd) {
|
||||
if ($ftpd) {
|
||||
// sha256 compatible for proftpd and pure-ftpd
|
||||
return crypt($password, '$5$' . self::generatePassword(16, true) . '$');
|
||||
}
|
||||
// sha1 hash for dir-protection
|
||||
return '{SHA}' . base64_encode(sha1($password, true));
|
||||
// bcrypt hash for dir-protection
|
||||
return password_hash($password, PASSWORD_BCRYPT);
|
||||
}
|
||||
// crypt using the specified crypt-algorithm or system default
|
||||
$algo = Settings::Get('system.passwordcryptfunc') !== null ? Settings::Get('system.passwordcryptfunc') : PASSWORD_DEFAULT;
|
||||
|
||||
@@ -36,7 +36,7 @@ class IPTools
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function cidr2NetmaskAddr($cidr)
|
||||
public static function cidr2NetmaskAddr(string $cidr): string
|
||||
{
|
||||
$ta = substr($cidr, strpos($cidr, '/') + 1) * 1;
|
||||
$netmask = str_split(str_pad(str_pad('', $ta, '1'), 32, '0'), 8);
|
||||
@@ -52,7 +52,7 @@ class IPTools
|
||||
* Checks whether the given $ip is in range of given ip/cidr range
|
||||
*
|
||||
* @param array $ip_cidr 0 => ip, 1 => netmask in decimal, e.g. [0 => '123.123.123.123', 1 => 24]
|
||||
* @param string $ip ip-address to check
|
||||
* @param string $ip ip-address to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -77,7 +77,7 @@ class IPTools
|
||||
*
|
||||
* @return string|bool ip address on success, false on failure
|
||||
*/
|
||||
public static function is_ipv6($address)
|
||||
public static function is_ipv6(string $address)
|
||||
{
|
||||
return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
|
||||
}
|
||||
@@ -86,7 +86,7 @@ class IPTools
|
||||
* Checks whether the given ipv6 $ip is in range of given ip/cidr range
|
||||
*
|
||||
* @param array $ip_cidr 0 => ip, 1 => netmask in decimal, e.g. [0 => '123:123::1', 1 => 64]
|
||||
* @param string $ip ip-address to check
|
||||
* @param string $ip ip-address to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -130,6 +130,10 @@ class IPTools
|
||||
return $in_range;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $addr
|
||||
* @return false|string
|
||||
*/
|
||||
private static function inet6_expand(string $addr)
|
||||
{
|
||||
// Check if there are segments missing, insert if necessary
|
||||
@@ -139,7 +143,7 @@ class IPTools
|
||||
$part[1] = explode(':', $part[1]);
|
||||
$missing = [];
|
||||
for ($i = 0; $i < (8 - (count($part[0]) + count($part[1]))); $i++) {
|
||||
array_push($missing, '0000');
|
||||
$missing[] = '0000';
|
||||
}
|
||||
$missing = array_merge($part[0], $missing);
|
||||
$part = array_merge($missing, $part[1]);
|
||||
@@ -163,17 +167,18 @@ class IPTools
|
||||
}
|
||||
}
|
||||
|
||||
private static function inet6_prefix_to_mask($prefix)
|
||||
/**
|
||||
* @param int $prefix
|
||||
* @return false|string
|
||||
*/
|
||||
private static function inet6_prefix_to_mask(int $prefix)
|
||||
{
|
||||
/* Make sure the prefix is a number between 1 and 127 (inclusive) */
|
||||
$prefix = intval($prefix);
|
||||
if ($prefix < 0 || $prefix > 128) {
|
||||
return false;
|
||||
}
|
||||
$mask = '0b';
|
||||
for ($i = 0; $i < $prefix; $i++) {
|
||||
$mask .= '1';
|
||||
}
|
||||
$mask .= str_repeat('1', $prefix);
|
||||
for ($i = strlen($mask) - 2; $i < 128; $i++) {
|
||||
$mask .= '0';
|
||||
}
|
||||
@@ -188,7 +193,11 @@ class IPTools
|
||||
return inet_ntop(inet_pton($result));
|
||||
}
|
||||
|
||||
private static function ip2long6($ip)
|
||||
/**
|
||||
* @param string $ip
|
||||
* @return string
|
||||
*/
|
||||
private static function ip2long6(string $ip): string
|
||||
{
|
||||
$ip_n = inet_pton($ip);
|
||||
$bits = 15; // 16 x 8 bit = 128bit
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
namespace Froxlor\System;
|
||||
|
||||
use Froxlor\Settings;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
|
||||
class Mailer extends PHPMailer
|
||||
@@ -34,9 +35,9 @@ class Mailer extends PHPMailer
|
||||
/**
|
||||
* class constructor
|
||||
*
|
||||
* @param bool $exceptions
|
||||
* whether to throw exceptions or not
|
||||
* @param bool $exceptions whether to throw exceptions or not
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(bool $exceptions = false)
|
||||
{
|
||||
|
||||
@@ -32,7 +32,7 @@ use Monolog\Logger;
|
||||
class MysqlHandler extends AbstractProcessingHandler
|
||||
{
|
||||
|
||||
protected static $froxlorLevels = [
|
||||
protected static array $froxlorLevels = [
|
||||
Logger::DEBUG => LOG_DEBUG,
|
||||
Logger::INFO => LOG_INFO,
|
||||
Logger::NOTICE => LOG_NOTICE,
|
||||
@@ -47,11 +47,10 @@ class MysqlHandler extends AbstractProcessingHandler
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param bool|int $level
|
||||
* Debug level which this handler should store
|
||||
* @param bool|int $level Debug level which this handler should store
|
||||
* @param bool $bubble
|
||||
*/
|
||||
public function __construct($level = Logger::DEBUG, $bubble = true)
|
||||
public function __construct($level = Logger::DEBUG, bool $bubble = true)
|
||||
{
|
||||
parent::__construct($level, $bubble);
|
||||
}
|
||||
@@ -66,8 +65,8 @@ class MysqlHandler extends AbstractProcessingHandler
|
||||
{
|
||||
$this->insert([
|
||||
':message' => $record['message'],
|
||||
':contextUser' => (isset($record['context']['user']) ? $record['context']['user'] : 'unknown'),
|
||||
':contextAction' => (isset($record['context']['action']) ? $record['context']['action'] : '0'),
|
||||
':contextUser' => ($record['context']['user'] ?? 'unknown'),
|
||||
':contextAction' => ($record['context']['action'] ?? '0'),
|
||||
':level' => self::$froxlorLevels[$record['level']],
|
||||
':datetime' => $record['datetime']->format('U')
|
||||
]);
|
||||
@@ -79,7 +78,7 @@ class MysqlHandler extends AbstractProcessingHandler
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
protected function insert(array $data)
|
||||
protected function insert(array $data): bool
|
||||
{
|
||||
if ($this->pdoStatement === null) {
|
||||
$sql = "INSERT INTO `panel_syslog` SET `text` = :message, `user` = :contextUser, `action` = :contextAction, `type` = :level, `date` = :datetime";
|
||||
|
||||
@@ -32,9 +32,16 @@ use Froxlor\Api\Commands\Traffic as TrafficAPI;
|
||||
|
||||
class Traffic
|
||||
{
|
||||
public static function getCustomerStats($userinfo, $range = null): array
|
||||
/**
|
||||
* @param array $userinfo
|
||||
* @param ?string $range
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getCustomerStats(array $userinfo, string $range = null): array
|
||||
{
|
||||
$trafficCollectionObj = (new Collection(TrafficAPI::class, $userinfo, self::getParamsByRange($range, ['customer_traffic' => true,])));
|
||||
$trafficCollectionObj = (new Collection(TrafficAPI::class, $userinfo,
|
||||
self::getParamsByRange($range, ['customer_traffic' => true,])));
|
||||
if ($userinfo['adminsession'] == 1) {
|
||||
$trafficCollectionObj->has('customer', Customers::class, 'customerid', 'customerid');
|
||||
}
|
||||
@@ -58,15 +65,15 @@ class Traffic
|
||||
$years[$item['year']]['ftp'] += ($item['ftp_up'] + $item['ftp_down']);
|
||||
$years[$item['year']]['mail'] += $item['mail'];
|
||||
// per month
|
||||
$months[$item['month'].'/'.$item['year']]['total'] += ($item['http'] + $item['ftp_up'] + $item['ftp_down'] + $item['mail']);
|
||||
$months[$item['month'].'/'.$item['year']]['http'] += $item['http'];
|
||||
$months[$item['month'].'/'.$item['year']]['ftp'] += ($item['ftp_up'] + $item['ftp_down']);
|
||||
$months[$item['month'].'/'.$item['year']]['mail'] += $item['mail'];
|
||||
$months[$item['month'] . '/' . $item['year']]['total'] += ($item['http'] + $item['ftp_up'] + $item['ftp_down'] + $item['mail']);
|
||||
$months[$item['month'] . '/' . $item['year']]['http'] += $item['http'];
|
||||
$months[$item['month'] . '/' . $item['year']]['ftp'] += ($item['ftp_up'] + $item['ftp_down']);
|
||||
$months[$item['month'] . '/' . $item['year']]['mail'] += $item['mail'];
|
||||
// per day
|
||||
$days[$item['day'].'.'.$item['month'].'.'.$item['year']]['total'] += ($item['http'] + $item['ftp_up'] + $item['ftp_down'] + $item['mail']);
|
||||
$days[$item['day'].'.'.$item['month'].'.'.$item['year']]['http'] += $item['http'];
|
||||
$days[$item['day'].'.'.$item['month'].'.'.$item['year']]['ftp'] += ($item['ftp_up'] + $item['ftp_down']);
|
||||
$days[$item['day'].'.'.$item['month'].'.'.$item['year']]['mail'] += $item['mail'];
|
||||
$days[$item['day'] . '.' . $item['month'] . '.' . $item['year']]['total'] += ($item['http'] + $item['ftp_up'] + $item['ftp_down'] + $item['mail']);
|
||||
$days[$item['day'] . '.' . $item['month'] . '.' . $item['year']]['http'] += $item['http'];
|
||||
$days[$item['day'] . '.' . $item['month'] . '.' . $item['year']]['ftp'] += ($item['ftp_up'] + $item['ftp_down']);
|
||||
$days[$item['day'] . '.' . $item['month'] . '.' . $item['year']]['mail'] += $item['mail'];
|
||||
}
|
||||
|
||||
// calculate overview for given range from users
|
||||
@@ -94,7 +101,13 @@ class Traffic
|
||||
];
|
||||
}
|
||||
|
||||
private static function getParamsByRange($range = null, array $params = [])
|
||||
/**
|
||||
* @param ?string $range
|
||||
* @param array $params
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function getParamsByRange(string $range = null, array $params = []): array
|
||||
{
|
||||
$dateParams = [];
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class Mysql
|
||||
public static function dbserver(array $attributes): string
|
||||
{
|
||||
// get sql-root access data
|
||||
Database::needRoot(true, (int)$attributes['data']);
|
||||
Database::needRoot(true, (int)$attributes['data'], false);
|
||||
Database::needSqlData();
|
||||
$sql_root = Database::getSqlData();
|
||||
Database::needRoot(false);
|
||||
|
||||
@@ -92,7 +92,7 @@ class Text
|
||||
$result = $attributes['fields'];
|
||||
$apikey_data = include Froxlor::getInstallDir() . '/lib/formfields/formfield.api_key.php';
|
||||
|
||||
$body = UI::twig()->render(UI::getTheme() . '/user/inline-form.html.twig', [
|
||||
$body = UI::twig()->render(UI::validateThemeTemplate('/user/inline-form.html.twig'), [
|
||||
'formaction' => $linker->getLink(['section' => 'index', 'page' => 'apikeys']),
|
||||
'formdata' => $apikey_data['apikey'],
|
||||
'editid' => $attributes['fields']['id']
|
||||
|
||||
@@ -110,7 +110,7 @@ class Collection
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPagination(array $columns, array $default_sorting = []): Collection
|
||||
public function withPagination(array $columns, array $default_sorting = [], array $pagination_additional_params = []): Collection
|
||||
{
|
||||
// Get only searchable columns
|
||||
$sortableColumns = [];
|
||||
@@ -121,7 +121,7 @@ class Collection
|
||||
}
|
||||
|
||||
// Prepare pagination
|
||||
$this->pagination = new Pagination($sortableColumns, $this->count(), (int)Settings::Get('panel.paging'), $default_sorting);
|
||||
$this->pagination = new Pagination($sortableColumns, $this->count(), (int)Settings::Get('panel.paging'), $default_sorting, $pagination_additional_params);
|
||||
$this->params = array_merge($this->params, $this->pagination->getApiCommandParams());
|
||||
|
||||
return $this;
|
||||
|
||||
@@ -203,7 +203,7 @@ class Form
|
||||
return $returnvalue;
|
||||
}
|
||||
|
||||
public static function processForm(&$form, &$input, $url_params = [], $part = null, $settings_all = [], $settings_part = null, $only_enabledisable = false)
|
||||
public static function processForm(&$form, &$input, $url_params = [], $part = null, bool $settings_all = false, $settings_part = null, bool $only_enabledisable = false)
|
||||
{
|
||||
if (\Froxlor\Validate\Form::validateFormDefinition($form)) {
|
||||
$submitted_fields = [];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user