Compare commits

...

121 Commits

Author SHA1 Message Date
Michael Kaufmann
d357bded60 Merge remote-tracking branch 'origin/main' into v2.2 2024-08-08 09:28:05 +02:00
Michael Kaufmann
292741516a set version to 2.2.0-rc3 for upcoming release-candidate
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-08-08 09:25:02 +02:00
Michael Kaufmann
27db472a0c Merge remote-tracking branch 'origin/main' into v2.2 2024-08-08 09:22:02 +02:00
Michael Kaufmann
fc4041e88c fixing reports being sent daily under specific conditions; update dependencies
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-08-08 09:18:49 +02:00
Michael Kaufmann
75bc0142a0 add missing use-statement for opcacheinfo page; ease ClientConnectRate ban-filter for proftpd; allow null-mx entry in dns-editor, fixes #1263
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-07-27 19:51:55 +02:00
Michael Kaufmann
b888e920f4 merge current enhancements from main branch to v2.2 (#1261) 2024-07-21 10:41:24 +02:00
Michael Kaufmann
585b16d199 set version to 2.2.0-rc2 for upcoming release-candidate
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-07-21 10:15:53 +02:00
Michael Kaufmann
4d3cf5da9a add column '2fa status' for customers and admins
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-07-20 23:26:53 +02:00
Michael Kaufmann
2dae780e0b implement 2fa remember browser, fixes #1259
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-07-20 10:16:48 +02:00
Michael Kaufmann
bda24d7d63 show email-only domains in customers list for potential dns entries information (if necessary)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-07-19 22:16:41 +02:00
Michael Kaufmann
9d47d670a1 fix correctly handling catchall-flag when updating email-address, fixes #1260
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-07-17 21:21:47 +02:00
Michael Kaufmann
b3dc7f9187 set version to 2.2.0-rc1 for upcoming release-candidate
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-07-05 11:43:39 +02:00
Michael Kaufmann
1d246fee02 check custom database-name against supported maximum length of username/databasename of used dbms, fixes #1258
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-06-25 10:37:49 +02:00
Michael Kaufmann
10e87a909a update dependencies
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-06-22 12:45:30 +02:00
Michael Kaufmann
0a3caa9f9b show required dns entries to admin and customer for a domain if nameserver-feature is not used
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-06-22 12:43:09 +02:00
dependabot[bot]
820326a7e0 Bump braces from 3.0.2 to 3.0.3 (#1257)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-21 17:10:48 +02:00
Michael Kaufmann
3a2e70f79f more info on preconfig regarding antispam feature and what will happen if not enabled; add comma to allowed characters in log-messages; make admin-username case-insensitive in the installation; show php-settings when adding/editing a domain as customer only if php is enbaled for the customer
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-06-19 10:39:15 +02:00
Starcalc
f8032b1952 Update README.md (#1255)
Corrected commands as provided in https://docs.froxlor.org/latest/general/installation/apt-package.html - the commands starting with "echo" do NOT work.
2024-06-13 10:46:34 +02:00
rex2630
a0794cbbf1 Finished Czech translation of the froxlor panel (#1254)
* Fully translated froxlor panel to Czech
2024-06-08 08:00:39 +02:00
Michael Kaufmann
a3139da388 add/correct missing language-strings
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-06-01 10:55:26 +02:00
Michael Kaufmann
3dd6a7d2ac add missing tasks-description for rebuilding antispam configuration
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-31 16:05:22 +02:00
Michael Kaufmann
062e610ae7 trigger antispam config-rebuild if dkim-flag changed for domain or a new domain with dkim=1 has been created
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-31 15:50:29 +02:00
Michael Kaufmann
5dc9aa34ba fix superfluous 'mkdir' when creating '/var/lib/rspamd/dkim/'
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-31 15:25:05 +02:00
Michael Kaufmann
d4a6c2cacc fix issues in login when 'login with domain' is activated; improved php8.3 compatibity; updated ubuntu noble config-template for dovecot and proftpd
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-31 08:41:18 +02:00
Michael Kaufmann
1f5982e8a0 update dependencies
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-16 08:32:55 +02:00
Michael Kaufmann
c89d320957 use Request-wrapper-class for every access to superglobal
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-16 08:30:35 +02:00
Michael Kaufmann
a602865fee fix force version re-check button
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-12 14:56:47 +02:00
Michael Kaufmann
597f338353 add force-updatecheck renew icon for update-check popover
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-12 14:15:05 +02:00
Michael Kaufmann
cda0b3116f make docs url dynamic based on (night/testing) version
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-12 13:51:03 +02:00
Michael Kaufmann
73182a6909 check for already existing symlink to bin/froxlor-cli; remove some very old dkim related settings
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-12 11:09:48 +02:00
envoyr
0d86340a4c fix session_sessiontimeout request and add missing language string
Signed-off-by: envoyr <hello@envoyr.com>
2024-05-11 18:38:56 +02:00
Michael Kaufmann
1a5680d2a8 never allow {{ }} in user-input 2024-05-10 17:23:25 +02:00
Michael Kaufmann
c07ff16274 Merge branch 'main' of github.com:froxlor/Froxlor 2024-05-10 17:09:59 +02:00
rex2630
cf18140499 Automatic assigment of "worker_processes" in nginx (#1252)
* Upgrade of nginx config by default reference
2024-05-10 17:09:25 +02:00
Michael Kaufmann
9f44b21a04 check for global customer mysql user existence when updating password
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-10 12:37:38 +02:00
Michael Kaufmann
7934684982 use Request-wrapper-class for every access to $_GET superglobal
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-09 16:03:46 +02:00
Michael Kaufmann
fce310049a use Request-wrapper-class for every access to $_POST superglobal
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-09 15:48:23 +02:00
Michael Kaufmann
914204b483 Merge branch 'main' of github.com:froxlor/Froxlor 2024-05-07 19:45:13 +02:00
rex2630
fc3f0d8ebf Add config for Ubuntu 24.04 - Noble Numbat (#1251)
* Add config for Ubuntu 24.04 - Noble Numbat

* Use php 8.3 by default
2024-05-07 19:45:00 +02:00
Michael Kaufmann
27753962cf use default caching_sha2_password auth plugin for mysql8
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-03 09:02:22 +02:00
Michael Kaufmann
63b21f385d mysql8 does not automatically load mysql_native_password-plugin anymore (should not be necessary anyway)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-03 08:36:52 +02:00
Michael Kaufmann
1b44ee2e06 Merge pull request from GHSA-x525-54hf-xr53
* do not log unvalidated user-input to mysql-log (if enabled)

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>

* clean log-text to only allow a subset of special characters

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>

* clean log-text when selecting from database to avoid possible previously added malicious entries

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>

---------

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-05-03 07:54:13 +02:00
Michael Kaufmann
7f8b36e0bd select homedir/maildir from emails if called interally as it is also called by customers via EmailAccounts.delete()
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-04-28 14:03:38 +02:00
Michael Kaufmann
71746f8dac select homedir/maildir from emails if called by admin
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-04-28 13:58:27 +02:00
Michael Kaufmann
d6b8eb08c0 add delete-userfiles flag for Domain.delete() to remove email-account data on the filesystem (if any); fixes #1239
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-04-28 13:49:07 +02:00
Michael Kaufmann
7d99244b9d higher delay and dont reset input to wrong value to avoid not being able to enter a date manually without datetime-picker; fixes #1243
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-04-28 12:11:42 +02:00
Michael Kaufmann
0109c2d26f do not hide nameserver settings via js if email-only is selected for the domain; fixes #1248
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-04-28 12:00:49 +02:00
dependabot[bot]
c1bc422677 Bump vite from 4.5.2 to 4.5.3 (#1247)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.5.2 to 4.5.3.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v4.5.3/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-27 10:23:48 +02:00
Michael Kaufmann
5625503e2d add compatibility for mariadb-dump executable instead of mysqldump
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-04-27 10:22:42 +02:00
Michael Kaufmann
61ae182ba7 update updater to latest stable release; refactored modal-action-button for UI fixed
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-29 11:40:08 +01:00
Michael Kaufmann
b49f20af95 fix copy-to-clipboard button
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-27 12:59:48 +01:00
Michael Kaufmann
1f4f1d8203 fix domains speciallogfile ajax-check/note; improve ajax ip check in admin_ipsandports
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-27 11:07:55 +01:00
Michael Kaufmann
ff4c54a9d5 also add logfiles to virtual-host if it's a redirect
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-27 10:17:02 +01:00
Michael Kaufmann
bb83e78c64 fix missing csrf tokens for some ajax requests
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-27 10:08:13 +01:00
Wiebe Cazemier
7c3e89ccc0 Fix "expires" option cannot have a year greater than 9999 (#1246)
This fixes the exception: '"expires" option cannot have a year greater
than 9999', which happens on upgrade from Debian 11 to 12. The session
timeout in the DB is 9999999999999, so we constrain the value.
2024-03-23 15:14:11 +01:00
Michael Kaufmann
76c23cf9b1 wrap SetHandler to php-fpm in file-exists check, as we do for customer-domains already
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-17 08:23:57 +01:00
Michael Kaufmann
ed6154fa4b Merge branch 'main' of github.com:Froxlor/Froxlor 2024-03-17 08:10:24 +01:00
dependabot[bot]
f22c1db8cb Bump follow-redirects from 1.15.4 to 1.15.6 (#1244)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-17 08:08:20 +01:00
Michael Kaufmann
ee7b47c3c0 correctly save pass_authorizationheader flag for php-configs if FCGID is used; correctly add 'FcgidPassHeader' for froxlor-vhost itself if set
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-11 08:00:26 +01:00
Michael Kaufmann
537b274b4c correctly validate if a symlink is within the customers home-directory if it's not an absolute path; fixes #1242
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-08 09:23:21 +01:00
Michael Kaufmann
d8b86fc3c5 correctly disabled ssl-related settings when domain update sets ssl-enbled flag to false; fixes #1241
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-04 16:48:35 +01:00
Michael Kaufmann
b675c84ae4 correctly add user-wide mysql-user when creating user with mysql-resources (accesst to all databases starting with the loginname)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-04 10:21:46 +01:00
Michael Kaufmann
c0fdc62032 correctly convert allowed_mysqlserver json-string to array
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-03 11:44:29 +01:00
Michael Kaufmann
b14eaf454c reset Database::needRoot flag after root-user session
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-03 11:34:57 +01:00
Michael Kaufmann
3503d605cc update workflow actions
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-03 10:47:18 +01:00
Michael Kaufmann
2fc319b991 fix typo
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-03 10:39:22 +01:00
Michael Kaufmann
d86da23187 remove unused hidden-settings; correct setting-language-strings-layout; add blacklist for usernames when creating a Customer which may lead to internal issues
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-03 10:35:58 +01:00
Michael Kaufmann
70b3e61f4c re-trigger vhost regeneration on tmp. ssl-redirect
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-03 10:32:32 +01:00
Michael Kaufmann
fb5958f5d4 fix current stable version in updater for nightly users (switching from stable/testing)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-11 20:06:38 +01:00
Michael Kaufmann
8132976559 implement 'master database user for customers'; fixes #1227
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-11 10:27:18 +01:00
sro0
686ca84a30 Ensure that DMARC entries are generated as subdomain, Allow overwriting of DMARC and SPF subdomain records (#1237)
* Ensure that DMARC entries are generated as subdomain
- see https://datatracker.ietf.org/doc/html/rfc7489#section-6.1

* Add tests for DNS DMARC

* Allow custom SPF and DMARC subdomain records to replace default records

* Improve tests for DMARC, add DMARC tests for subdomain
2024-02-09 08:11:41 +01:00
sro0
953baec023 Allow service ftpserver to be specified via configuration-template-xml default (#1234) 2024-02-09 08:06:57 +01:00
Michael Kaufmann
396274d954 fix adding/editing domains as customer when php is not enabled for the domain; don't add custom-vhost-content to deactivated domain-vhosts
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-05 22:09:49 +01:00
Michael Kaufmann
4e23b9652c fix regression bug in 'incorrect top-5 customers' sorting in traffic-overview which leads to incorrect customer-links due to wrong indexing in the array; fixes #1236
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-04 19:54:19 +01:00
Michael Kaufmann
594e61408d also fix unittests accordingly
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-03 13:53:08 +01:00
Michael Kaufmann
ece4b34f25 fix password crypt hash being always evaluated to argon2i as the case always returns true if PASSWORD_ARGON2I is defined but the froxlor setting might be set to another hash leading to a useless password
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-03 10:12:36 +01:00
Michael Kaufmann
9c70976018 fix check for allowed_phpconfigs if using mod_php when adding/editing a customer
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-30 19:31:21 +01:00
Michael Kaufmann
594d7d84bb Merge branch 'main' of github.com:Froxlor/Froxlor 2024-01-29 20:27:12 +01:00
sro0
9d4bc94aef Rename dovecot config file generated be renew hook to ensure it gets included (#1233)
after default froxlor config file
2024-01-29 20:26:38 +01:00
Michael Kaufmann
f03b49d0db api documentation additions for Customers.add()/update() and Admins.add()/update()
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-28 10:03:56 +01:00
Michael Kaufmann
bcf0818faf set correct channel for update-check if switching from apt-installed stable/testing to nightly
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-26 13:56:01 +01:00
Michael Kaufmann
dd765089c9 fix wrong setting-name for dkim-keylength when generating dkim-keys
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-26 13:55:14 +01:00
Michael Kaufmann
a7ee5e0ae3 create empty dns-server config if no (dns-enabled) domain is determined; fixes #1230
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-24 08:38:32 +01:00
Michael Kaufmann
2629718b22 add new 'http2 on' directive for nginx >=1.25.1
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-23 00:01:12 +01:00
dependabot[bot]
c4cf8ededc Bump vite from 4.4.12 to 4.5.2 (#1229)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.4.12 to 4.5.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v4.5.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 08:40:05 +01:00
Michael Kaufmann
9b20f4ac39 fix wrong order of ecc/non-ecc in proftpd config adjustment for let's encrypt renew-hook
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-19 16:09:24 +01:00
Michael Kaufmann
616dcb1fda use correct syntax for postconf in Let's Encrypt renew-hook service-configuration replacement; add missing language strings
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-19 16:01:31 +01:00
Michael Kaufmann
bc1892d4ec fix incorrect top-5 customers in traffic overview for admins; show manual update command if webupdate is disabled
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-19 09:21:38 +01:00
sro0
83047019b0 Check for argon2 support before using constant PASSWORD_ARGON2X (#1228) 2024-01-16 21:34:17 +01:00
dependabot[bot]
8fa286a71d Bump follow-redirects from 1.15.3 to 1.15.4 (#1222)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-14 09:41:46 +01:00
Michael Kaufmann
f420551888 added configuration adjustment for prodtpd if renew-hook for lets encrypt is used; updater-compatibility if gui_access field is not present yet (froxlor <2.2); removed depercated gentoo config templates
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-14 09:40:33 +01:00
sro0
854c930696 Ensure XPath for ConfigDaemon matches exactly one element (#1224) 2024-01-13 12:31:15 +01:00
Michael Kaufmann
8740947323 initial integration of let's encrypt renew-hook for froxlor-vhost; refs #1186
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-13 11:12:48 +01:00
Michael Kaufmann
e684de687f implement dmarc to dns-zones; fixes #662
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-07 11:00:07 +01:00
Michael Kaufmann
284def5832 add gui_access flag to admins and customers to allow/disallow login to the webui; fixes #1219
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-07 10:23:02 +01:00
Michael Kaufmann
9c23013777 disable pam auth in dovecot for debian bookworm (like the other distros do it)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-07 09:05:38 +01:00
Michael Kaufmann
75af5c6a1a build nightly only from main branch #2
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-06 15:01:28 +01:00
Michael Kaufmann
2a348cf34e build nightly only from main branch
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-06 14:53:45 +01:00
Michael Kaufmann
089bec7255 convert preexisting dkim public keys to new format in updater if antispam is enabled, else remove all old/invalid values from domains
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-06 14:48:13 +01:00
Michael Kaufmann
c393317adb add v2.1 branch to security md as currently supported as well as update main-branch version; add field.disabled attribute to formfield-input-template
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-06 14:46:54 +01:00
Michael Kaufmann
734d6888c8 backup rspamd configs in config-templates; add 'antispam' to valid_keys for config-json file; test existence of file in config-backup-function
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-05 16:52:40 +01:00
Michael Kaufmann
ba11b0ab7d version-check remote-result-testing not yet possible with new workflow of development
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-05 16:12:44 +01:00
Michael Kaufmann
1054095b3b merge gone wrong, corrected sql
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-05 15:39:59 +01:00
Michael Kaufmann
b15f99b1e1 implementation start of rspam/antispam feature
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-05 15:37:04 +01:00
Michael Kaufmann
63bbcd4e00 add missing language string
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-05 14:48:28 +01:00
Michael Kaufmann
49d67d7c27 set version to 2.1.4 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-05 14:33:45 +01:00
Michael Kaufmann
7cc4c9fedb possibility to specify sender address for froxlor as the admin-email address, custom or empty for system-default; fixes #1217
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-04 08:29:19 +01:00
Michael Kaufmann
afd110a6ed use correct regex for dnscheck-resolver; fixes #1220
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-03 07:35:28 +01:00
Michael Kaufmann
7cdf6c8d64 don't output ipv6 in brackets for system.ipaddress setting as the brackets will be added to the value resulting in an invalid mysql-access-host; fixes #1215
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-01 17:04:02 +01:00
Michael Kaufmann
60621da243 dont use deprecated 'mysql_native_password' for mysql8; fixes #1214
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-01 12:24:45 +01:00
Michael Kaufmann
96ccdda304 use different language string for password-placeholder when adding a new customer; fixes #1216
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-31 18:01:39 +01:00
Michael Kaufmann
4073984fd7 traffic-cron: check for standardsubdomain to be in the domainlist array to avoid undefined index if e.g. an alias was set to the standardsubdomain
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-31 16:16:01 +01:00
Michael Kaufmann
ea31c8a64d fix font-color in apcu info; clear updatecheck-cache for nightly users
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-30 23:30:52 +01:00
Michael Kaufmann
832ee07e0e Don't show stats-icon for domains with redirect; hide goaccess output in traffic cron and keepalive database connection for long-running log-analysis; use same certificate-file if child-domain inherits the parentdomains certificate data (avoid possible http 421 Misdirected Request)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-29 10:15:45 +01:00
Michael Kaufmann
b542b140c6 set version to 2.1.3 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-22 14:33:11 +01:00
Michael Kaufmann
ac89fc7120 adjust order of css files
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-22 14:30:23 +01:00
Michael Kaufmann
150858485d include custom.css from config.json if preset correctly
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-21 10:59:15 +01:00
Michael Kaufmann
e7810e2066 correctly merge fielddetails with prefetched-formfielddata in form-processing
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-20 09:39:01 +01:00
Michael Kaufmann
4879446567 domains in php-configs are not sortable
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-19 17:17:51 +01:00
Michael Kaufmann
43eff78088 use panel.password_min_length setting for Froxlor.generatePassword() default length parameter; allow '::1' as valid mysql localhost value; wrapper to clean output for cli installation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-16 20:20:58 +01:00
Gamerboy59
55a2ae3801 Add manual_config install var to cli (#1208)
Make the manual_config var, which is available to the web installer, usuable for the cli installer too. If manual_config is set to true skip else (not set or false) proceed with auto config.
2023-12-16 20:13:58 +01:00
174 changed files with 7087 additions and 3388 deletions

View File

@@ -12,7 +12,7 @@ jobs:
mariadb-version: [ 10.11, 10.5 ] mariadb-version: [ 10.11, 10.5 ]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Setup PHP, with composer and extensions - name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
@@ -53,11 +53,11 @@ jobs:
name: Create nightly/testing tarball name: Create nightly/testing tarball
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: froxlor needs: froxlor
if: ${{ github.event_name == 'push' }} if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Setup PHP with PECL extension - name: Setup PHP with PECL extension
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
@@ -70,7 +70,7 @@ jobs:
run: composer install --no-dev run: composer install --no-dev
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: '20.x' node-version: '20.x'
@@ -119,7 +119,7 @@ jobs:
mv froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip.sha256 dist/ mv froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip.sha256 dist/
- name: Deploy nightly to server - name: Deploy nightly to server
uses: easingthemes/ssh-deploy@v3.4.3 uses: easingthemes/ssh-deploy@main
env: env:
ARGS: "-rltDzvO --chown=${{ secrets.WEB_USER }}:${{ secrets.WEB_USER }}" ARGS: "-rltDzvO --chown=${{ secrets.WEB_USER }}:${{ secrets.WEB_USER }}"
SOURCE: "dist/" SOURCE: "dist/"

View File

@@ -12,7 +12,7 @@ jobs:
mysql-version: [8.0, 5.7] mysql-version: [8.0, 5.7]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Setup PHP, with composer and extensions - name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
@@ -39,16 +39,7 @@ jobs:
- name: Wait for database - name: Wait for database
run: sleep 15 run: sleep 15
- name: Setup database (8.0) - name: Setup database
if: matrix.mysql-version == '8.0'
run: |
mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI -e "CREATE USER 'froxlor010'@'%' IDENTIFIED WITH mysql_native_password BY 'fr0xl0r.TravisCI';"
mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI -e "GRANT ALL ON froxlor010.* TO 'froxlor010'@'%';"
php -r "echo include('install/froxlor.sql.php');" > /tmp/froxlor.sql
mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI froxlor010 < /tmp/froxlor.sql
- name: Setup database (5.7)
if: matrix.mysql-version == '5.7'
run: | run: |
mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI -e "CREATE USER 'froxlor010'@'%' IDENTIFIED BY 'fr0xl0r.TravisCI';" mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI -e "CREATE USER 'froxlor010'@'%' IDENTIFIED BY 'fr0xl0r.TravisCI';"
mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI -e "GRANT ALL ON froxlor010.* TO 'froxlor010'@'%';" mysql -h 127.0.0.1 --protocol=TCP -u root -pfr0xl0r.TravisCI -e "GRANT ALL ON froxlor010.* TO 'froxlor010'@'%';"

View File

@@ -33,6 +33,7 @@ use Froxlor\FroxlorLogger;
use Froxlor\FroxlorTwoFactorAuth; use Froxlor\FroxlorTwoFactorAuth;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\UI\Response; use Froxlor\UI\Response;
use Froxlor\PhpHelper; use Froxlor\PhpHelper;
use Froxlor\User; use Froxlor\User;
@@ -63,7 +64,7 @@ if ($action == 'delete') {
]); ]);
Response::standardSuccess('2fa.2fa_removed'); Response::standardSuccess('2fa.2fa_removed');
} elseif ($action == 'preadd') { } elseif ($action == 'preadd') {
$type = isset($_POST['type_2fa']) ? $_POST['type_2fa'] : '0'; $type = Request::post('type_2fa', '0');
$data = ""; $data = "";
if ($type > 0) { if ($type > 0) {
@@ -107,9 +108,9 @@ if ($action == 'delete') {
Response::dynamicError('Select one of the possible values for 2FA'); Response::dynamicError('Select one of the possible values for 2FA');
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
$type = isset($_POST['type_2fa']) ? $_POST['type_2fa'] : '0'; $type = Request::post('type_2fa', '0');
$data = isset($_POST['data_2fa']) ? $_POST['data_2fa'] : ''; $data = Request::post('data_2fa', '');
$code = isset($_POST['codevalidation']) ? $_POST['codevalidation'] : ''; $code = Request::post('codevalidation', '');
// validate // validate
$result = $tfa->verifyCode($data, $code, 3); $result = $tfa->verifyCode($data, $code, 3);

View File

@@ -58,17 +58,17 @@ https://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](https://files.fro
#### Debian #### Debian
``` ```
apt-get -y install apt-transport-https lsb-release ca-certificates curl apt -y install apt-transport-https lsb-release ca-certificates curl gnupg
curl -sSLo /usr/share/keyrings/deb.froxlor.org-froxlor.gpg https://deb.froxlor.org/froxlor.gpg curl -sSLo /usr/share/keyrings/deb.froxlor.org-froxlor.gpg https://deb.froxlor.org/froxlor.gpg
echo sh -c '"deb [signed-by=/usr/share/keyrings/deb.froxlor.org-froxlor.gpg] https://deb.froxlor.org/debian $(lsb_release -sc) main" > /etc/apt/sources.list.d/froxlor.list' sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.froxlor.org-froxlor.gpg] https://deb.froxlor.org/debian $(lsb_release -sc) main" > /etc/apt/sources.list.d/froxlor.list'
``` ```
#### Ubuntu #### Ubuntu
``` ```
apt-get -y install apt-transport-https lsb-release ca-certificates curl apt -y install apt-transport-https lsb-release ca-certificates curl gnupg
curl -sSLo /usr/share/keyrings/deb.froxlor.org-froxlor.gpg https://deb.froxlor.org/froxlor.gpg curl -sSLo /usr/share/keyrings/deb.froxlor.org-froxlor.gpg https://deb.froxlor.org/froxlor.gpg
echo sh -c '"deb [signed-by=/usr/share/keyrings/deb.froxlor.org-froxlor.gpg] https://deb.froxlor.org/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/froxlor.list' sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.froxlor.org-froxlor.gpg] https://deb.froxlor.org/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/froxlor.list'
``` ```
## Contributing ## Contributing

View File

@@ -10,7 +10,8 @@ With that, good luck hacking us ;)
## Supported versions ## Supported versions
- ️✅ **2.1.x** (`main` git-branch) - ️✅ **2.2.x** (`main` git-branch)
- ️✅ **2.1.x** (`v2.1` git-branch)
- ❌ 2.0.x (`2.0.x`-tags) - ❌ 2.0.x (`2.0.x`-tags)
- ❌ 0.10.x (`0.10.x`-tags) - ❌ 0.10.x (`0.10.x`-tags)
- ❌ other git-branches - ❌ other git-branches

View File

@@ -35,6 +35,7 @@ return [
'varname' => 'sessiontimeout', 'varname' => 'sessiontimeout',
'type' => 'number', 'type' => 'number',
'min' => 60, 'min' => 60,
'max' => 31536000,
'default' => 600, 'default' => 600,
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],

View File

@@ -180,18 +180,6 @@ return [
'default' => true, 'default' => true,
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],
'system_httpuser' => [
'settinggroup' => 'system',
'varname' => 'httpuser',
'type' => 'hidden',
'default' => 'www-data'
],
'system_httpgroup' => [
'settinggroup' => 'system',
'varname' => 'httpgroup',
'type' => 'hidden',
'default' => 'www-data'
],
'system_report_enable' => [ 'system_report_enable' => [
'label' => lng('serversettings.report.report'), 'label' => lng('serversettings.report.report'),
'settinggroup' => 'system', 'settinggroup' => 'system',

View File

@@ -176,6 +176,7 @@ return [
'varname' => 'mod_fcgid_httpuser', 'varname' => 'mod_fcgid_httpuser',
'type' => 'text', 'type' => 'text',
'default' => 'froxlorlocal', 'default' => 'froxlorlocal',
'string_emptyallowed' => false,
'save_method' => 'storeSettingWebserverFcgidFpmUser', 'save_method' => 'storeSettingWebserverFcgidFpmUser',
'websrv_avail' => [ 'websrv_avail' => [
'apache2' 'apache2'
@@ -193,6 +194,7 @@ return [
'type' => 'text', 'type' => 'text',
'default' => 'froxlorlocal', 'default' => 'froxlorlocal',
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'string_emptyallowed' => false,
'websrv_avail' => [ 'websrv_avail' => [
'apache2' 'apache2'
], ],
@@ -243,6 +245,7 @@ return [
'varname' => 'vhost_httpuser', 'varname' => 'vhost_httpuser',
'type' => 'text', 'type' => 'text',
'default' => 'froxlorlocal', 'default' => 'froxlorlocal',
'string_emptyallowed' => false,
'save_method' => 'storeSettingWebserverFcgidFpmUser', 'save_method' => 'storeSettingWebserverFcgidFpmUser',
'visible' => Settings::Get('phpfpm.enabled') && call_user_func([ 'visible' => Settings::Get('phpfpm.enabled') && call_user_func([
'\Froxlor\Settings\FroxlorVhostSettings', '\Froxlor\Settings\FroxlorVhostSettings',
@@ -256,6 +259,7 @@ return [
'varname' => 'vhost_httpgroup', 'varname' => 'vhost_httpgroup',
'type' => 'text', 'type' => 'text',
'default' => 'froxlorlocal', 'default' => 'froxlorlocal',
'string_emptyallowed' => false,
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'visible' => Settings::Get('phpfpm.enabled') && call_user_func([ 'visible' => Settings::Get('phpfpm.enabled') && call_user_func([
'\Froxlor\Settings\FroxlorVhostSettings', '\Froxlor\Settings\FroxlorVhostSettings',

View File

@@ -248,11 +248,40 @@ return [
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'le_domain_dnscheck_resolver', 'varname' => 'le_domain_dnscheck_resolver',
'type' => 'text', 'type' => 'text',
'string_regexp' => '/^(([0-9]+ [a-z0-9\-\._]+, ?)*[0-9]+ [a-z0-9\-\._]+)?$/i', 'string_type' => 'validate_ip',
'string_emptyallowed' => true, 'string_emptyallowed' => true,
'default' => '', 'default' => '',
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField',
] 'advanced_mode' => true
],
'system_le_renew_services' => [
'label' => lng('serversettings.le_renew_services'),
'settinggroup' => 'system',
'varname' => 'le_renew_services',
'type' => 'select',
'default' => '',
'select_mode' => 'multiple',
'option_emptyallowed' => true,
'select_var' => [
'' => lng('panel.none_value'),
'postfix' => 'postfix (smtp)',
'dovecot' => 'dovecot (imap/pop3)',
'proftpd' => 'proftpd (ftp)',
],
'save_method' => 'storeSettingField',
'advanced_mode' => true
],
'system_le_renew_hook' => [
'label' => lng('serversettings.le_renew_hook'),
'settinggroup' => 'system',
'varname' => 'le_renew_hook',
'type' => 'text',
'string_regexp' => '/^[a-z0-9\/\._\- ]+$/i',
'default' => 'systemctl restart postfix dovecot proftpd',
'save_method' => 'storeSettingField',
'advanced_mode' => true,
'required_otp' => true
],
] ]
] ]
] ]

View File

@@ -0,0 +1,111 @@
<?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
*/
return [
'groups' => [
'antispam' => [
'title' => lng('admin.antispam_settings'),
'icon' => 'fa-solid fa-clipboard-check',
'fields' => [
'antispam_activated' => [
'label' => lng('antispam.activated'),
'settinggroup' => 'antispam',
'varname' => 'activated',
'type' => 'checkbox',
'default' => true,
'overview_option' => true,
'save_method' => 'storeSettingFieldInsertAntispamTask',
],
'antispam_config_file' => [
'label' => lng('antispam.config_file'),
'settinggroup' => 'antispam',
'varname' => 'config_file',
'type' => 'text',
'string_type' => 'file',
'default' => '/etc/rspamd/local.d/froxlor_settings.conf',
'save_method' => 'storeSettingFieldInsertAntispamTask',
'requires_reconf' => ['antispam']
],
'antispam_reload_command' => [
'label' => lng('antispam.reload_command'),
'settinggroup' => 'antispam',
'varname' => 'reload_command',
'type' => 'text',
'string_regexp' => '/^[a-z0-9\/\._\- ]+$/i',
'default' => 'service rspamd restart',
'save_method' => 'storeSettingField',
'required_otp' => true
],
'antispam_dkim_keylength' => [
'label' => lng('antispam.dkim_keylength'),
'settinggroup' => 'antispam',
'varname' => 'dkim_keylength',
'type' => 'select',
'default' => '1024',
'select_var' => [
'1024' => '1024 Bit',
'2048' => '2048 Bit'
],
'save_method' => 'storeSettingFieldInsertBindTask',
'advanced_mode' => true,
],
'spf_use_spf' => [
'label' => lng('spf.use_spf'),
'settinggroup' => 'spf',
'varname' => 'use_spf',
'type' => 'checkbox',
'default' => false,
'save_method' => 'storeSettingField',
],
'spf_spf_entry' => [
'label' => lng('spf.spf_entry'),
'settinggroup' => 'spf',
'varname' => 'spf_entry',
'type' => 'text',
'string_regexp' => '/^v=spf[a-z0-9:~?\s.-]+$/i',
'default' => 'v=spf1 a mx -all',
'save_method' => 'storeSettingField'
],
'dmarc_use_dmarc' => [
'label' => lng('dmarc.use_dmarc'),
'settinggroup' => 'dmarc',
'varname' => 'use_dmarc',
'type' => 'checkbox',
'default' => false,
'save_method' => 'storeSettingField',
],
'dmarc_dmarc_entry' => [
'label' => lng('dmarc.dmarc_entry'),
'settinggroup' => 'dmarc',
'varname' => 'dmarc_entry',
'type' => 'text',
'string_regexp' => '/^v=dmarc1(.+)$/i',
'default' => 'v=DMARC1; p=none;',
'save_method' => 'storeSettingField'
]
]
]
]
];

View File

@@ -1,146 +0,0 @@
<?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
*/
use Froxlor\Settings;
return [
'groups' => [
'dkim' => [
'title' => lng('admin.dkimsettings'),
'icon' => 'fa-solid fa-fingerprint',
'fields' => [
'dkim_use_dkim' => [
'label' => lng('dkim.use_dkim'),
'settinggroup' => 'dkim',
'varname' => 'use_dkim',
'type' => 'checkbox',
'default' => false,
'save_method' => 'storeSettingFieldInsertBindTask',
'overview_option' => true
],
'dkim_dkim_prefix' => [
'label' => lng('dkim.dkim_prefix'),
'settinggroup' => 'dkim',
'varname' => 'dkim_prefix',
'type' => 'text',
'string_type' => 'dir',
'default' => '/etc/postfix/dkim/',
'save_method' => 'storeSettingField'
],
'dkim_privkeysuffix' => [
'label' => lng('dkim.privkeysuffix'),
'settinggroup' => 'dkim',
'varname' => 'privkeysuffix',
'type' => 'text',
'string_regexp' => '/^[a-z0-9\._]+$/i',
'default' => '.priv',
'save_method' => 'storeSettingField',
'advanced_mode' => true
],
'dkim_dkim_domains' => [
'label' => lng('dkim.dkim_domains'),
'settinggroup' => 'dkim',
'varname' => 'dkim_domains',
'type' => 'text',
'string_regexp' => '/^[a-z0-9\._]+$/i',
'default' => 'domains',
'save_method' => 'storeSettingField'
],
'dkim_dkim_dkimkeys' => [
'label' => lng('dkim.dkim_dkimkeys'),
'settinggroup' => 'dkim',
'varname' => 'dkim_dkimkeys',
'type' => 'text',
'string_regexp' => '/^[a-z0-9\._]+$/i',
'default' => 'dkim-keys.conf',
'save_method' => 'storeSettingField'
],
'dkim_dkim_algorithm' => [
'label' => lng('dkim.dkim_algorithm'),
'settinggroup' => 'dkim',
'varname' => 'dkim_algorithm',
'type' => 'select',
'default' => 'all',
'select_mode' => 'multiple',
'select_var' => [
'all' => 'All',
'sha1' => 'SHA1',
'sha256' => 'SHA256'
],
'save_method' => 'storeSettingFieldInsertBindTask',
'advanced_mode' => true
],
'dkim_dkim_servicetype' => [
'label' => lng('dkim.dkim_servicetype'),
'settinggroup' => 'dkim',
'varname' => 'dkim_servicetype',
'type' => 'select',
'default' => '0',
'select_var' => [
'0' => 'All',
'1' => 'E-Mail'
],
'save_method' => 'storeSettingFieldInsertBindTask',
'advanced_mode' => true
],
'dkim_dkim_keylength' => [
'label' => [
'title' => lng('dkim.dkim_keylength.title'),
'description' => lng('dkim.dkim_keylength.description', [Settings::Get('dkim.dkim_prefix')])
],
'settinggroup' => 'dkim',
'varname' => 'dkim_keylength',
'type' => 'select',
'default' => '1024',
'select_var' => [
'1024' => '1024 Bit',
'2048' => '2048 Bit'
],
'save_method' => 'storeSettingFieldInsertBindTask'
],
'dkim_dkim_notes' => [
'label' => lng('dkim.dkim_notes'),
'settinggroup' => 'dkim',
'varname' => 'dkim_notes',
'type' => 'text',
'string_regexp' => '/^[a-z0-9\._]+$/i',
'default' => '',
'save_method' => 'storeSettingFieldInsertBindTask',
'advanced_mode' => true
],
'dkim_dkimrestart_command' => [
'label' => lng('dkim.dkimrestart_command'),
'settinggroup' => 'dkim',
'varname' => 'dkimrestart_command',
'type' => 'text',
'string_regexp' => '/^[a-z0-9\/\._\- ]+$/i',
'default' => '/etc/init.d/dkim-filter restart',
'save_method' => 'storeSettingField',
'required_otp' => true
]
]
]
]
];

View File

@@ -106,7 +106,7 @@ if (($page == 'admins' || $page == 'overview') && $userinfo['change_serversettin
Response::standardError('youcantdeleteyourself'); Response::standardError('youcantdeleteyourself');
} }
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
Admins::getLocal($userinfo, [ Admins::getLocal($userinfo, [
'id' => $id 'id' => $id
])->delete(); ])->delete();
@@ -122,9 +122,9 @@ if (($page == 'admins' || $page == 'overview') && $userinfo['change_serversettin
} }
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Admins::getLocal($userinfo, $_POST)->add(); Admins::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -159,9 +159,9 @@ if (($page == 'admins' || $page == 'overview') && $userinfo['change_serversettin
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if ($result['loginname'] != '') { if ($result['loginname'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Admins::getLocal($userinfo, $_POST)->update(); Admins::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }

View File

@@ -33,6 +33,7 @@
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\UI\Response; use Froxlor\UI\Response;
use Froxlor\UI\HTML; use Froxlor\UI\HTML;
@@ -42,7 +43,7 @@ require __DIR__ . '/lib/init.php';
$horizontal_bar_size = 950; // 1280px window width $horizontal_bar_size = 950; // 1280px window width
if ($action == 'delete' && function_exists('apcu_clear_cache') && $userinfo['change_serversettings'] == '1') { if ($action == 'delete' && function_exists('apcu_clear_cache') && $userinfo['change_serversettings'] == '1') {
if ($_POST['send'] == 'send') { if (Request::post('send') == 'send') {
apcu_clear_cache(); apcu_clear_cache();
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "cleared APCu cache"); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "cleared APCu cache");
header('Location: ' . $linker->getLink([ header('Location: ' . $linker->getLink([

View File

@@ -32,6 +32,7 @@ use Froxlor\FileDir;
use Froxlor\Install\AutoUpdate; use Froxlor\Install\AutoUpdate;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\UI\Response; use Froxlor\UI\Response;
if ($page != 'error') { if ($page != 'error') {
@@ -110,7 +111,7 @@ if ($page == 'overview') {
} // download the new archive } // download the new archive
elseif ($page == 'getdownload') { elseif ($page == 'getdownload') {
// retrieve the new version from the form // retrieve the new version from the form
$newversion = isset($_POST['newversion']) ? $_POST['newversion'] : null; $newversion = Request::post('newversion');
$result = 6; $result = 6;
// valid? // valid?
@@ -130,8 +131,8 @@ elseif ($page == 'getdownload') {
]); ]);
} // extract and install new version } // extract and install new version
elseif ($page == 'extract') { elseif ($page == 'extract') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$toExtract = isset($_POST['archive']) ? $_POST['archive'] : null; $toExtract = Request::post('archive');
$localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract); $localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Extracting " . $localArchive . " to " . Froxlor::getInstallDir()); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Extracting " . $localArchive . " to " . Froxlor::getInstallDir());
$result = AutoUpdate::extractZip($localArchive); $result = AutoUpdate::extractZip($localArchive);
@@ -145,7 +146,7 @@ elseif ($page == 'extract') {
// redirect to update-page // redirect to update-page
Response::redirectTo('admin_updates.php'); Response::redirectTo('admin_updates.php');
} else { } else {
$toExtract = isset($_GET['archive']) ? $_GET['archive'] : null; $toExtract = Request::get('archive');
$localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract); $localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
} }
@@ -192,7 +193,7 @@ elseif ($page == 'extract') {
} // display error } // display error
elseif ($page == 'error') { elseif ($page == 'error') {
// retrieve error-number via url-parameter // retrieve error-number via url-parameter
$errno = isset($_GET['errno']) ? (int)$_GET['errno'] : 0; $errno = Request::get('errno', 0);
// 2 = no Zlib // 2 = no Zlib
// 3 = custom version detected // 3 = custom version detected

View File

@@ -93,14 +93,14 @@ if ($userinfo['change_serversettings'] == '1') {
asort($distributions_select); asort($distributions_select);
} }
if ($distribution != "" && isset($_POST['finish'])) { if ($distribution != "" && !empty(Request::post('finish'))) {
$valid_keys = ['http', 'dns', 'smtp', 'mail', 'ftp', 'system', 'distro']; $valid_keys = ['http', 'dns', 'smtp', 'mail', 'antispam', 'ftp', 'system', 'distro'];
unset($_POST['finish']); unset($_POST['finish']);
unset($_POST['csrf_token']); unset($_POST['csrf_token']);
$params = $_POST; $params = Request::postAll();
$params['distro'] = $distribution; $params['distro'] = $distribution;
$params['system'] = []; $params['system'] = [];
foreach ($_POST['system'] as $sysdaemon) { foreach (Request::post('system', []) as $sysdaemon) {
$params['system'][] = $sysdaemon; $params['system'][] = $sysdaemon;
} }
// validate params // validate params

View File

@@ -68,9 +68,9 @@ if (($page == 'cronjobs' || $page == 'overview') && $userinfo['change_serversett
} }
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if ($result['cronfile'] != '') { if ($result['cronfile'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Cronjobs::getLocal($userinfo, $_POST)->update(); Cronjobs::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }

View File

@@ -98,7 +98,7 @@ if (($page == 'customers' || $page == 'overview') && $userinfo['customers'] != '
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "switched user and is now '" . $destination_user . "'"); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "switched user and is now '" . $destination_user . "'");
$target = (isset($_GET['target']) ? $_GET['target'] : 'index'); $target = Request::get('target', 'index');
$redirect = "customer_" . $target . ".php"; $redirect = "customer_" . $target . ".php";
if (!file_exists(Froxlor::getInstallDir() . "/" . $redirect)) { if (!file_exists(Froxlor::getInstallDir() . "/" . $redirect)) {
$redirect = "customer_index.php"; $redirect = "customer_index.php";
@@ -119,7 +119,7 @@ if (($page == 'customers' || $page == 'overview') && $userinfo['customers'] != '
} }
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
$json_result = Customers::getLocal($userinfo, [ $json_result = Customers::getLocal($userinfo, [
'id' => $id 'id' => $id
@@ -147,11 +147,11 @@ if (($page == 'customers' || $page == 'overview') && $userinfo['customers'] != '
} }
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
$json_result = Customers::getLocal($userinfo, [ $json_result = Customers::getLocal($userinfo, [
'id' => $id, 'id' => $id,
'delete_userfiles' => (isset($_POST['delete_userfiles']) ? (int)$_POST['delete_userfiles'] : 0) 'delete_userfiles' => Request::post('delete_userfiles', 0)
])->delete(); ])->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
@@ -167,9 +167,9 @@ if (($page == 'customers' || $page == 'overview') && $userinfo['customers'] != '
], $result['loginname']); ], $result['loginname']);
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Customers::getLocal($userinfo, $_POST)->add(); Customers::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -243,9 +243,9 @@ if (($page == 'customers' || $page == 'overview') && $userinfo['customers'] != '
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if ($result['loginname'] != '') { if ($result['loginname'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Customers::getLocal($userinfo, $_POST)->update(); Customers::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }

View File

@@ -100,9 +100,9 @@ if ($page == 'domains' || $page == 'overview') {
]); ]);
if ($result['domain'] != '') { if ($result['domain'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send' && $alias_check['count'] == 0) { if (Request::post('send') == 'send' && $alias_check['count'] == 0) {
try { try {
Domains::getLocal($userinfo, $_POST)->delete(); Domains::getLocal($userinfo, Request::postAll())->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -113,7 +113,7 @@ if ($page == 'domains' || $page == 'overview') {
} elseif ($alias_check['count'] > 0) { } elseif ($alias_check['count'] > 0) {
Response::standardError('domains_cantdeletedomainwithaliases'); Response::standardError('domains_cantdeletedomainwithaliases');
} else { } else {
HTML::askYesNo('admin_domain_reallydelete', $filename, [ HTML::askYesNoWithCheckbox('admin_domain_reallydelete', 'admin_customer_alsoremovemail', $filename, [
'id' => $id, 'id' => $id,
'page' => $page, 'page' => $page,
'action' => $action 'action' => $action
@@ -121,9 +121,9 @@ if ($page == 'domains' || $page == 'overview') {
} }
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Domains::getLocal($userinfo, $_POST)->add(); Domains::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -355,13 +355,13 @@ if ($page == 'domains' || $page == 'overview') {
$usedips[] = $ipsresultrow['id_ipandports']; $usedips[] = $ipsresultrow['id_ipandports'];
} }
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
// remove ssl ip/ports if set is empty // remove ssl ip/ports if set is empty
if (!isset($_POST['ssl_ipandport']) || empty($_POST['ssl_ipandport'])) { if (empty(Request::post('ssl_ipandport'))) {
$_POST['remove_ssl_ipandport'] = true; $_POST['remove_ssl_ipandport'] = true;
} }
Domains::getLocal($userinfo, $_POST)->update(); Domains::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -572,13 +572,13 @@ if ($page == 'domains' || $page == 'overview') {
} }
} }
} elseif ($action == 'jqGetCustomerPHPConfigs') { } elseif ($action == 'jqGetCustomerPHPConfigs') {
$customerid = intval($_POST['customerid']); $customerid = intval(Request::post('customerid'));
$allowed_phpconfigs = Customer::getCustomerDetail($customerid, 'allowed_phpconfigs'); $allowed_phpconfigs = Customer::getCustomerDetail($customerid, 'allowed_phpconfigs');
echo !empty($allowed_phpconfigs) ? $allowed_phpconfigs : json_encode([]); echo !empty($allowed_phpconfigs) ? $allowed_phpconfigs : json_encode([]);
exit(); exit();
} elseif ($action == 'jqSpeciallogfileNote') { } elseif ($action == 'jqSpeciallogfileNote') {
$domainid = intval($_POST['id']); $domainid = intval(Request::post('id'));
$newval = intval($_POST['newval']); $newval = intval(Request::post('newval'));
try { try {
$json_result = Domains::getLocal($userinfo, [ $json_result = Domains::getLocal($userinfo, [
'id' => $domainid 'id' => $domainid
@@ -594,9 +594,9 @@ if ($page == 'domains' || $page == 'overview') {
echo 0; echo 0;
exit(); exit();
} elseif ($action == 'import') { } elseif ($action == 'import') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$separator = Validate::validate($_POST['separator'], 'separator'); $separator = Validate::validate(Request::post('separator'), 'separator');
$offset = (int)Validate::validate($_POST['offset'], 'offset', "/[0-9]/i"); $offset = (int)Validate::validate(Request::post('offset'), 'offset', "/[0-9]/i");
$file_name = $_FILES['file']['tmp_name']; $file_name = $_FILES['file']['tmp_name'];
@@ -636,9 +636,9 @@ if ($page == 'domains' || $page == 'overview') {
]); ]);
} }
} elseif ($action == 'duplicate') { } elseif ($action == 'duplicate') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Domains::getLocal($userinfo, $_POST)->duplicate(); Domains::getLocal($userinfo, Request::postAll())->duplicate();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }

View File

@@ -55,7 +55,7 @@ if ($action == 'logout') {
$result = $result['switched_user']; $result = $result['switched_user'];
session_regenerate_id(true); session_regenerate_id(true);
CurrentUser::setData($result); CurrentUser::setData($result);
$target = (isset($_GET['target']) ? $_GET['target'] : 'index'); $target = Request::get('target', 'index');
$redirect = "admin_" . $target . ".php"; $redirect = "admin_" . $target . ".php";
if (!file_exists(\Froxlor\Froxlor::getInstallDir() . "/" . $redirect)) { if (!file_exists(\Froxlor\Froxlor::getInstallDir() . "/" . $redirect)) {
$redirect = "admin_index.php"; $redirect = "admin_index.php";
@@ -111,7 +111,7 @@ if ($page == 'overview') {
$overview['number_domains'] = $number_domains['number_domains']; $overview['number_domains'] = $number_domains['number_domains'];
if ((isset($_GET['lookfornewversion']) && $_GET['lookfornewversion'] == 'yes') || (isset($lookfornewversion) && $lookfornewversion == 'yes')) { if (Request::get('lookfornewversion') == 'yes' || (isset($lookfornewversion) && $lookfornewversion == 'yes')) {
try { try {
$json_result = Froxlor::getLocal($userinfo)->checkUpdate(); $json_result = Froxlor::getLocal($userinfo)->checkUpdate();
} catch (Exception $e) { } catch (Exception $e) {
@@ -201,16 +201,16 @@ if ($page == 'overview') {
$languages = Language::getLanguages(); $languages = Language::getLanguages();
if (!empty($_POST)) { if (!empty($_POST)) {
if ($_POST['send'] == 'changepassword') { if (Request::post('send') == 'changepassword') {
$old_password = Validate::validate($_POST['old_password'], 'old password'); $old_password = Validate::validate(Request::post('old_password'), 'old password');
if (!Crypt::validatePasswordLogin($userinfo, $old_password, TABLE_PANEL_ADMINS, 'adminid')) { if (!Crypt::validatePasswordLogin($userinfo, $old_password, TABLE_PANEL_ADMINS, 'adminid')) {
Response::standardError('oldpasswordnotcorrect'); Response::standardError('oldpasswordnotcorrect');
} }
try { try {
$new_password = Crypt::validatePassword($_POST['new_password'], 'new password'); $new_password = Crypt::validatePassword(Request::post('new_password'), 'new password');
$new_password_confirm = Crypt::validatePassword($_POST['new_password_confirm'], 'new password confirm'); $new_password_confirm = Crypt::validatePassword(Request::post('new_password_confirm'), 'new password confirm');
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -244,9 +244,9 @@ if ($page == 'overview') {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'changed password'); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'changed password');
Response::redirectTo($filename); Response::redirectTo($filename);
} }
} elseif ($_POST['send'] == 'changetheme') { } elseif (Request::post('send') == 'changetheme') {
if (Settings::Get('panel.allow_theme_change_admin') == 1) { if (Settings::Get('panel.allow_theme_change_admin') == 1) {
$theme = Validate::validate($_POST['theme'], 'theme'); $theme = Validate::validate(Request::post('theme'), 'theme');
try { try {
Admins::getLocal($userinfo, [ Admins::getLocal($userinfo, [
'id' => $userinfo['adminid'], 'id' => $userinfo['adminid'],
@@ -259,8 +259,8 @@ if ($page == 'overview') {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "changed his/her theme to '" . $theme . "'"); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "changed his/her theme to '" . $theme . "'");
} }
Response::redirectTo($filename); Response::redirectTo($filename);
} elseif ($_POST['send'] == 'changelanguage') { } elseif (Request::post('send') == 'changelanguage') {
$def_language = Validate::validate($_POST['def_language'], 'default language'); $def_language = Validate::validate(Request::post('def_language'), 'default language');
if (isset($languages[$def_language])) { if (isset($languages[$def_language])) {
try { try {

View File

@@ -70,7 +70,7 @@ if (($page == 'ipsandports' || $page == 'overview') && $userinfo['change_servers
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['id']) && $result['id'] == $id) { if (isset($result['id']) && $result['id'] == $id) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
IpsAndPorts::getLocal($userinfo, [ IpsAndPorts::getLocal($userinfo, [
'id' => $id 'id' => $id
@@ -91,9 +91,9 @@ if (($page == 'ipsandports' || $page == 'overview') && $userinfo['change_servers
} }
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
IpsAndPorts::getLocal($userinfo, $_POST)->add(); IpsAndPorts::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -119,9 +119,9 @@ if (($page == 'ipsandports' || $page == 'overview') && $userinfo['change_servers
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if ($result['ip'] != '') { if ($result['ip'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
IpsAndPorts::getLocal($userinfo, $_POST)->update(); IpsAndPorts::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -141,9 +141,11 @@ if (($page == 'ipsandports' || $page == 'overview') && $userinfo['change_servers
} }
} }
} elseif ($action == 'jqCheckIP') { } elseif ($action == 'jqCheckIP') {
$ip = $_POST['ip'] ?? ""; $ip = Request::post('ip', '');
if ((filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE) == false) { if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) {
// returns notice if private network detected so we can display it echo json_encode('<div id="ipnote" class="invalid-feedback">'.lng('error.invalidip', [$ip]).'</div>');
} elseif (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE)) {
// returns notice if private network detected, so we can display it
echo json_encode(lng('admin.ipsandports.ipnote')); echo json_encode(lng('admin.ipsandports.ipnote'));
} else { } else {
echo 0; echo 0;

View File

@@ -31,6 +31,7 @@ use Froxlor\UI\Collection;
use Froxlor\UI\HTML; use Froxlor\UI\HTML;
use Froxlor\UI\Listing; use Froxlor\UI\Listing;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\UI\Response; use Froxlor\UI\Response;
if ($page == 'log' && $userinfo['change_serversettings'] == '1') { if ($page == 'log' && $userinfo['change_serversettings'] == '1') {
@@ -55,7 +56,7 @@ if ($page == 'log' && $userinfo['change_serversettings'] == '1') {
] ]
]); ]);
} elseif ($action == 'truncate') { } elseif ($action == 'truncate') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
SysLog::getLocal($userinfo, [ SysLog::getLocal($userinfo, [
'min_to_keep' => 10 'min_to_keep' => 10

View File

@@ -42,11 +42,11 @@ if ($page == 'message') {
if ($action == '') { if ($action == '') {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'viewed panel_message'); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'viewed panel_message');
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
if ($_POST['recipient'] == 0 && $userinfo['customers_see_all'] == '1') { if (Request::post('recipient', -1) == 0 && $userinfo['customers_see_all'] == '1') {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'sending messages to admins'); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'sending messages to admins');
$result = Database::query('SELECT `name`, `email` FROM `' . TABLE_PANEL_ADMINS . "`"); $result = Database::query('SELECT `name`, `email` FROM `' . TABLE_PANEL_ADMINS . "`");
} elseif ($_POST['recipient'] == 1) { } elseif (Request::post('recipient', -1) == 1) {
if ($userinfo['customers_see_all'] == '1') { if ($userinfo['customers_see_all'] == '1') {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'sending messages to ALL customers'); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, 'sending messages to ALL customers');
$result = Database::query('SELECT `firstname`, `name`, `company`, `email` FROM `' . TABLE_PANEL_CUSTOMERS . "`"); $result = Database::query('SELECT `firstname`, `name`, `company`, `email` FROM `' . TABLE_PANEL_CUSTOMERS . "`");
@@ -63,8 +63,8 @@ if ($page == 'message') {
Response::standardError('norecipientsgiven'); Response::standardError('norecipientsgiven');
} }
$subject = $_POST['subject']; $subject = Request::post('subject');
$message = wordwrap($_POST['message'], 70); $message = wordwrap(Request::post('message'), 70);
if (!empty($message)) { if (!empty($message)) {
$mailcounter = 0; $mailcounter = 0;
@@ -107,7 +107,7 @@ if ($page == 'message') {
} }
} }
} elseif ($action == 'showsuccess') { } elseif ($action == 'showsuccess') {
$sentitems = isset($_GET['sentitems']) ? (int)$_GET['sentitems'] : 0; $sentitems = Request::get('sentitems', 0);
if ($sentitems == 0) { if ($sentitems == 0) {
$note_type = 'info'; $note_type = 'info';

View File

@@ -70,7 +70,7 @@ if (($page == 'mysqlserver' || $page == 'overview') && $userinfo['change_servers
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['id']) && $result['id'] == $id) { if (isset($result['id']) && $result['id'] == $id) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
MysqlServer::getLocal($userinfo, [ MysqlServer::getLocal($userinfo, [
'id' => $id 'id' => $id
@@ -91,9 +91,9 @@ if (($page == 'mysqlserver' || $page == 'overview') && $userinfo['change_servers
} }
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
MysqlServer::getLocal($userinfo, $_POST)->add(); MysqlServer::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -119,9 +119,9 @@ if (($page == 'mysqlserver' || $page == 'overview') && $userinfo['change_servers
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['id']) && $result['id'] == $id) { if (isset($result['id']) && $result['id'] == $id) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
MysqlServer::getLocal($userinfo, $_POST)->update(); MysqlServer::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }

View File

@@ -35,10 +35,11 @@ require __DIR__ . '/lib/init.php';
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\UI\HTML; use Froxlor\UI\HTML;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\UI\Response; use Froxlor\UI\Response;
if ($action == 'reset' && function_exists('opcache_reset') && $userinfo['change_serversettings'] == '1') { if ($action == 'reset' && function_exists('opcache_reset') && $userinfo['change_serversettings'] == '1') {
if ($_POST['send'] == 'send') { if (Request::post('send') == 'send') {
opcache_reset(); opcache_reset();
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "reset OPcache"); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "reset OPcache");
header('Location: ' . $linker->getLink([ header('Location: ' . $linker->getLink([

View File

@@ -62,9 +62,9 @@ if ($page == 'overview') {
if ($action == 'add') { if ($action == 'add') {
if ((int)$userinfo['change_serversettings'] == 1) { if ((int)$userinfo['change_serversettings'] == 1) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
PhpSettings::getLocal($userinfo, $_POST)->add(); PhpSettings::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -114,7 +114,7 @@ if ($page == 'overview') {
if ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config if ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config
{ {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
PhpSettings::getLocal($userinfo, [ PhpSettings::getLocal($userinfo, [
'id' => $id 'id' => $id
@@ -148,9 +148,9 @@ if ($page == 'overview') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1) { if ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
PhpSettings::getLocal($userinfo, $_POST)->update(); PhpSettings::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -200,9 +200,9 @@ if ($page == 'overview') {
if ($action == 'add') { if ($action == 'add') {
if ((int)$userinfo['change_serversettings'] == 1) { if ((int)$userinfo['change_serversettings'] == 1) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
FpmDaemons::getLocal($userinfo, $_POST)->add(); FpmDaemons::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -239,9 +239,9 @@ if ($page == 'overview') {
if ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config if ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config
{ {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
FpmDaemons::getLocal($userinfo, $_POST)->delete(); FpmDaemons::getLocal($userinfo, Request::postAll())->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -271,9 +271,9 @@ if ($page == 'overview') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1) { if ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['change_serversettings'] == 1) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
FpmDaemons::getLocal($userinfo, $_POST)->update(); FpmDaemons::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }

View File

@@ -73,7 +73,7 @@ if ($page == '' || $page == 'overview') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['adminid'] == $result['adminid']) { if ($result['id'] != 0 && $result['id'] == $id && (int)$userinfo['adminid'] == $result['adminid']) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
HostingPlans::getLocal($userinfo, [ HostingPlans::getLocal($userinfo, [
'id' => $id 'id' => $id
@@ -96,9 +96,9 @@ if ($page == '' || $page == 'overview') {
Response::standardError('nopermissionsorinvalidid'); Response::standardError('nopermissionsorinvalidid');
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
HostingPlans::getLocal($userinfo, $_POST)->add(); HostingPlans::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -176,9 +176,9 @@ if ($page == '' || $page == 'overview') {
} }
$result['allowed_phpconfigs'] = json_encode($result['allowed_phpconfigs']); $result['allowed_phpconfigs'] = json_encode($result['allowed_phpconfigs']);
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
HostingPlans::getLocal($userinfo, $_POST)->update(); HostingPlans::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }

View File

@@ -47,10 +47,10 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
$settings_data = PhpHelper::loadConfigArrayDir('./actions/admin/settings/'); $settings_data = PhpHelper::loadConfigArrayDir('./actions/admin/settings/');
Settings::loadSettingsInto($settings_data); Settings::loadSettingsInto($settings_data);
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$_part = isset($_GET['part']) ? $_GET['part'] : ''; $_part = Request::get('part', '');
if ($_part == '') { if ($_part == '') {
$_part = isset($_POST['part']) ? $_POST['part'] : ''; $_part = Request::post('part', '');
} }
if ($_part != '') { if ($_part != '') {
@@ -69,12 +69,12 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
} }
// check if the session timeout is too low #815 // check if the session timeout is too low #815
if (isset($_POST['session_sessiontimeout']) && $_POST['session_sessiontimeout'] < 60) { if (!empty(Request::post('session_sessiontimeout')) && intval(Request::post('session_sessiontimeout', 0)) < 60) {
Response::standardError(['session_timeout', 'session_timeout_desc']); Response::standardError(['session_timeout', 'session_timeout_desc']);
} }
try { try {
if (Form::processForm($settings_data, $_POST, [ if (Form::processForm($settings_data, Request::postAll(), [
'filename' => $filename, 'filename' => $filename,
'action' => $action, 'action' => $action,
'page' => $page, 'page' => $page,
@@ -97,9 +97,9 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
Response::dynamicError($e->getMessage(), $e->getCode()); Response::dynamicError($e->getMessage(), $e->getCode());
} }
} else { } else {
$_part = isset($_GET['part']) ? $_GET['part'] : ''; $_part = Request::get('part', '');
if ($_part == '') { if ($_part == '') {
$_part = isset($_POST['part']) ? $_POST['part'] : ''; $_part = Request::post('part', '');
} }
$fields = Form::buildForm($settings_data, $_part); $fields = Form::buildForm($settings_data, $_part);
@@ -140,7 +140,7 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
'phpinfo' => $phpinfo 'phpinfo' => $phpinfo
]); ]);
} elseif ($page == 'rebuildconfigs' && $userinfo['change_serversettings'] == '1') { } elseif ($page == 'rebuildconfigs' && $userinfo['change_serversettings'] == '1') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "rebuild configfiles"); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "rebuild configfiles");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
Cronjob::inserttask(TaskId::CREATE_QUOTA); Cronjob::inserttask(TaskId::CREATE_QUOTA);
@@ -158,7 +158,7 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
]); ]);
} }
} elseif ($page == 'updatecounters' && $userinfo['change_serversettings'] == '1') { } elseif ($page == 'updatecounters' && $userinfo['change_serversettings'] == '1') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "updated resource-counters"); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "updated resource-counters");
$updatecounters = User::updateCounters(true); $updatecounters = User::updateCounters(true);
UI::view('user/resource-counter.html.twig', [ UI::view('user/resource-counter.html.twig', [
@@ -170,7 +170,7 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
]); ]);
} }
} elseif ($page == 'wipecleartextmailpws' && $userinfo['change_serversettings'] == '1') { } elseif ($page == 'wipecleartextmailpws' && $userinfo['change_serversettings'] == '1') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "wiped all cleartext mail passwords"); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "wiped all cleartext mail passwords");
Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password` = '';"); Database::query("UPDATE `" . TABLE_MAIL_USERS . "` SET `password` = '';");
Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = '0' WHERE `settinggroup` = 'system' AND `varname` = 'mailpwcleartext'"); Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = '0' WHERE `settinggroup` = 'system' AND `varname` = 'mailpwcleartext'");
@@ -181,7 +181,7 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
]); ]);
} }
} elseif ($page == 'wipequotas' && $userinfo['change_serversettings'] == '1') { } elseif ($page == 'wipequotas' && $userinfo['change_serversettings'] == '1') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "wiped all mailquotas"); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "wiped all mailquotas");
// Set the quota to 0 which means unlimited // Set the quota to 0 which means unlimited
@@ -194,7 +194,7 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
]); ]);
} }
} elseif ($page == 'enforcequotas' && $userinfo['change_serversettings'] == '1') { } elseif ($page == 'enforcequotas' && $userinfo['change_serversettings'] == '1') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
// Fetch all accounts // Fetch all accounts
$result_stmt = Database::query("SELECT `quota`, `customerid` FROM `" . TABLE_MAIL_USERS . "`"); $result_stmt = Database::query("SELECT `quota`, `customerid` FROM `" . TABLE_MAIL_USERS . "`");
@@ -233,9 +233,9 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
} }
} elseif ($page == 'integritycheck' && $userinfo['change_serversettings'] == '1') { } elseif ($page == 'integritycheck' && $userinfo['change_serversettings'] == '1') {
$integrity = new IntegrityCheck(); $integrity = new IntegrityCheck();
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$integrity->fixAll(); $integrity->fixAll();
} elseif (isset($_GET['action']) && $_GET['action'] == "fix") { } elseif (Request::get('action') == "fix") {
HTML::askYesNo('admin_integritycheck_reallyfix', $filename, [ HTML::askYesNo('admin_integritycheck_reallyfix', $filename, [
'page' => $page 'page' => $page
]); ]);
@@ -273,7 +273,7 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
Response::standardError('jsonextensionnotfound'); Response::standardError('jsonextensionnotfound');
} }
if (isset($_GET['action']) && $_GET['action'] == "export") { if (Request::get('action') == "export") {
// export // export
try { try {
$json_result = Froxlor::getLocal($userinfo)->exportSettings(); $json_result = Froxlor::getLocal($userinfo)->exportSettings();
@@ -285,9 +285,9 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
header('Content-type: application/json'); header('Content-type: application/json');
echo $json_export; echo $json_export;
exit(); exit();
} elseif (isset($_GET['action']) && $_GET['action'] == "import") { } elseif (Request::get('action') == "import") {
// import // import
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
// get uploaded file // get uploaded file
if (isset($_FILES["import_file"]["tmp_name"])) { if (isset($_FILES["import_file"]["tmp_name"])) {
$imp_content = file_get_contents($_FILES["import_file"]["tmp_name"]); $imp_content = file_get_contents($_FILES["import_file"]["tmp_name"]);
@@ -330,8 +330,8 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
$note_type = 'info'; $note_type = 'info';
$note_msg = lng('admin.smtptestnote'); $note_msg = lng('admin.smtptestnote');
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$test_addr = isset($_POST['test_addr']) ? $_POST['test_addr'] : null; $test_addr = Request::post('test_addr');
// Initialize the mailingsystem // Initialize the mailingsystem
$testmail = new PHPMailer(true); $testmail = new PHPMailer(true);

View File

@@ -192,7 +192,7 @@ if ($action == '') {
$result = $result_stmt->fetch(PDO::FETCH_ASSOC); $result = $result_stmt->fetch(PDO::FETCH_ASSOC);
if ($result['varname'] != '') { if ($result['varname'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$del_stmt = Database::prepare(" $del_stmt = Database::prepare("
DELETE FROM `" . TABLE_PANEL_TEMPLATES . "` DELETE FROM `" . TABLE_PANEL_TEMPLATES . "`
WHERE `adminid` = :adminid WHERE `adminid` = :adminid
@@ -228,7 +228,7 @@ if ($action == '') {
if (Database::num_rows() > 0) { if (Database::num_rows() > 0) {
$row = $result_stmt->fetch(PDO::FETCH_ASSOC); $row = $result_stmt->fetch(PDO::FETCH_ASSOC);
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$del_stmt = Database::prepare(" $del_stmt = Database::prepare("
DELETE FROM `" . TABLE_PANEL_TEMPLATES . "` DELETE FROM `" . TABLE_PANEL_TEMPLATES . "`
WHERE `adminid` = :adminid AND `id` = :id"); WHERE `adminid` = :adminid AND `id` = :id");
@@ -251,13 +251,13 @@ if ($action == '') {
Response::standardError('templatenotfound'); Response::standardError('templatenotfound');
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['prepare']) && $_POST['prepare'] == 'prepare') { if (Request::post('prepare') == 'prepare') {
// email templates // email templates
$language = htmlentities(Validate::validate($_POST['language'], 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect')); $language = htmlentities(Validate::validate(Request::post('language'), 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect'));
if (!array_key_exists($language, $languages)) { if (!array_key_exists($language, $languages)) {
Response::standardError('templatelanguageinvalid'); Response::standardError('templatelanguageinvalid');
} }
$template = Validate::validate($_POST['template'], 'template'); $template = Validate::validate(Request::post('template'), 'template');
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT COUNT(*) as def FROM `" . TABLE_PANEL_TEMPLATES . "` SELECT COUNT(*) as def FROM `" . TABLE_PANEL_TEMPLATES . "`
@@ -289,15 +289,15 @@ if ($action == '') {
'formdata' => $template_add_data['template_add'], 'formdata' => $template_add_data['template_add'],
'replacers' => $template_add_data['template_replacers'] 'replacers' => $template_add_data['template_replacers']
]); ]);
} elseif (isset($_POST['send']) && $_POST['send'] == 'send' && !isset($_POST['filesend'])) { } elseif (Request::post('send') == 'send' && empty(Request::post('filesend'))) {
// email templates // email templates
$language = htmlentities(Validate::validate($_POST['language'], 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect')); $language = htmlentities(Validate::validate(Request::post('language'), 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect'));
if (!array_key_exists($language, $languages)) { if (!array_key_exists($language, $languages)) {
Response::standardError('templatelanguageinvalid'); Response::standardError('templatelanguageinvalid');
} }
$template = Validate::validate($_POST['template'], 'template'); $template = Validate::validate(Request::post('template'), 'template');
$subject = Validate::validate($_POST['subject'], 'subject', '/^[^\r\n\0]+$/', 'nosubjectcreate'); $subject = Validate::validate(Request::post('subject'), 'subject', '/^[^\r\n\0]+$/', 'nosubjectcreate');
$mailbody = Validate::validate($_POST['mailbody'], 'mailbody', '/^[^\0]+$/', 'nomailbodycreate'); $mailbody = Validate::validate(Request::post('mailbody'), 'mailbody', '/^[^\0]+$/', 'nomailbodycreate');
$templates = []; $templates = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT `varname` FROM `" . TABLE_PANEL_TEMPLATES . "` SELECT `varname` FROM `" . TABLE_PANEL_TEMPLATES . "`
@@ -347,10 +347,10 @@ if ($action == '') {
'page' => $page 'page' => $page
]); ]);
} }
} elseif (isset($_POST['filesend']) && $_POST['filesend'] == 'filesend') { } elseif (Request::post('filesend') == 'filesend') {
// file templates // file templates
$template = Validate::validate($_POST['template'], 'template'); $template = Validate::validate(Request::post('template'), 'template');
$filecontent = Validate::validate($_POST['filecontent'], 'filecontent', '/^[^\0]+$/', 'filecontentnotset'); $filecontent = Validate::validate(Request::post('filecontent'), 'filecontent', '/^[^\0]+$/', 'filecontentnotset');
$ins_stmt = Database::prepare(" $ins_stmt = Database::prepare("
INSERT INTO `" . TABLE_PANEL_TEMPLATES . "` SET INSERT INTO `" . TABLE_PANEL_TEMPLATES . "` SET
@@ -371,7 +371,7 @@ if ($action == '') {
Response::redirectTo($filename, [ Response::redirectTo($filename, [
'page' => $page 'page' => $page
]); ]);
} elseif (!isset($_GET['files'])) { } elseif (empty(Request::get('files'))) {
// email templates // email templates
$add = false; $add = false;
$language_options = []; $language_options = [];
@@ -483,9 +483,9 @@ if ($action == '') {
$result = $result_stmt->fetch(PDO::FETCH_ASSOC); $result = $result_stmt->fetch(PDO::FETCH_ASSOC);
if ($result['varname'] != '') { if ($result['varname'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$subject = Validate::validate($_POST['subject'], 'subject', '/^[^\r\n\0]+$/', 'nosubjectcreate'); $subject = Validate::validate(Request::post('subject'), 'subject', '/^[^\r\n\0]+$/', 'nosubjectcreate');
$mailbody = Validate::validate($_POST['mailbody'], 'mailbody', '/^[^\0]+$/', 'nomailbodycreate'); $mailbody = Validate::validate(Request::post('mailbody'), 'mailbody', '/^[^\0]+$/', 'nomailbodycreate');
$upd_stmt = Database::prepare(" $upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET
@@ -551,8 +551,8 @@ if ($action == '') {
$row = $result_stmt->fetch(PDO::FETCH_ASSOC); $row = $result_stmt->fetch(PDO::FETCH_ASSOC);
// filetemplates // filetemplates
if (isset($_POST['filesend']) && $_POST['filesend'] == 'filesend') { if (Request::post('filesend') == 'filesend') {
$filecontent = Validate::validate($_POST['filecontent'], 'filecontent', '/^[^\0]+$/', 'filecontentnotset'); $filecontent = Validate::validate(Request::post('filecontent'), 'filecontent', '/^[^\0]+$/', 'filecontentnotset');
$upd_stmt = Database::prepare(" $upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET
`value` = :value `value` = :value

View File

@@ -34,6 +34,7 @@ use Froxlor\Install\Update;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\System\Cronjob; use Froxlor\System\Cronjob;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\UI\Response; use Froxlor\UI\Response;
use Froxlor\User; use Froxlor\User;
@@ -48,8 +49,8 @@ if ($page == 'overview') {
$successful_update = false; $successful_update = false;
$message = ''; $message = '';
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
if ((isset($_POST['update_preconfig']) && isset($_POST['update_changesagreed']) && intval($_POST['update_changesagreed']) != 0) || !isset($_POST['update_preconfig'])) { if ((!empty(Request::post('update_preconfig')) && intval(Request::post('update_changesagreed', 0)) != 0) || empty(Request::post('update_preconfig'))) {
include_once Froxlor::getInstallDir() . 'install/updatesql.php'; include_once Froxlor::getInstallDir() . 'install/updatesql.php';
User::updateCounters(); User::updateCounters();

View File

@@ -61,7 +61,7 @@ if ($action == 'delete' && $id > 0) {
'section' => 'index', 'section' => 'index',
'page' => $page 'page' => $page
]); ]);
} elseif (isset($_POST['send']) && $_POST['send'] == 'send' && $action == 'deletesure' && $id > 0) { } elseif (Request::post('send') == 'send' && $action == 'deletesure' && $id > 0) {
$chk = (AREA == 'admin' && $userinfo['customers_see_all'] == '1') ? true : false; $chk = (AREA == 'admin' && $userinfo['customers_see_all'] == '1') ? true : false;
if (AREA == 'customer') { if (AREA == 'customer') {
$chk_stmt = Database::prepare(" $chk_stmt = Database::prepare("
@@ -94,7 +94,7 @@ if ($action == 'delete' && $id > 0) {
]); ]);
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$ins_stmt = Database::prepare(" $ins_stmt = Database::prepare("
INSERT INTO `" . TABLE_API_KEYS . "` SET INSERT INTO `" . TABLE_API_KEYS . "` SET
`apikey` = :key, `secret` = :secret, `adminid` = :aid, `customerid` = :cid, `valid_until` = '-1', `allowed_from` = '' `apikey` = :key, `secret` = :secret, `adminid` = :aid, `customerid` = :cid, `valid_until` = '-1', `allowed_from` = ''

View File

@@ -57,7 +57,7 @@
"pear/net_dns2": "^1.5", "pear/net_dns2": "^1.5",
"amnuts/opcache-gui": "^3.4", "amnuts/opcache-gui": "^3.4",
"league/commonmark": "^2.4" "league/commonmark": "^2.4"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^9", "phpunit/phpunit": "^9",
"ext-pcntl": "*", "ext-pcntl": "*",

641
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,7 @@
const AREA = 'customer'; const AREA = 'customer';
require __DIR__ . '/lib/init.php'; require __DIR__ . '/lib/init.php';
use Froxlor\Api\Commands\SubDomains as SubDomains; use Froxlor\Api\Commands\SubDomains;
use Froxlor\CurrentUser; use Froxlor\CurrentUser;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Domain\Domain; use Froxlor\Domain\Domain;
@@ -72,7 +72,7 @@ if ($page == 'overview' || $page == 'domains') {
} }
$actions_links[] = [ $actions_links[] = [
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/domains/', 'href' => \Froxlor\Froxlor::getDocsUrl() . 'user-guide/domains/',
'target' => '_blank', 'target' => '_blank',
'icon' => 'fa-solid fa-circle-info', 'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary' 'class' => 'btn-outline-secondary'
@@ -106,9 +106,9 @@ if ($page == 'overview' || $page == 'domains') {
]); ]);
if (isset($result['parentdomainid']) && $result['parentdomainid'] != '0' && $alias_check['count'] == 0) { if (isset($result['parentdomainid']) && $result['parentdomainid'] != '0' && $alias_check['count'] == 0) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
SubDomains::getLocal($userinfo, $_POST)->delete(); SubDomains::getLocal($userinfo, Request::postAll())->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -127,9 +127,9 @@ if ($page == 'overview' || $page == 'domains') {
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if ($userinfo['subdomains_used'] < $userinfo['subdomains'] || $userinfo['subdomains'] == '-1') { if ($userinfo['subdomains_used'] < $userinfo['subdomains'] || $userinfo['subdomains'] == '-1') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
SubDomains::getLocal($userinfo, $_POST)->add(); SubDomains::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -248,9 +248,9 @@ if ($page == 'overview' || $page == 'domains') {
Response::standardError('domaincannotbeedited', $result['domain']); Response::standardError('domaincannotbeedited', $result['domain']);
} }
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
SubDomains::getLocal($userinfo, $_POST)->update(); SubDomains::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -395,8 +395,8 @@ if ($page == 'overview' || $page == 'domains') {
Response::standardError('domains_canteditdomain'); Response::standardError('domains_canteditdomain');
} }
} elseif ($action == 'jqSpeciallogfileNote') { } elseif ($action == 'jqSpeciallogfileNote') {
$domainid = intval($_POST['id']); $domainid = intval(Request::post('id'));
$newval = intval($_POST['newval']); $newval = intval(Request::post('newval'));
try { try {
$json_result = SubDomains::getLocal($userinfo, [ $json_result = SubDomains::getLocal($userinfo, [
'id' => $domainid 'id' => $domainid

View File

@@ -30,6 +30,7 @@ use Froxlor\Api\Commands\EmailAccounts;
use Froxlor\Api\Commands\EmailDomains; use Froxlor\Api\Commands\EmailDomains;
use Froxlor\Api\Commands\EmailForwarders; use Froxlor\Api\Commands\EmailForwarders;
use Froxlor\Api\Commands\Emails; use Froxlor\Api\Commands\Emails;
use Froxlor\Cron\Mail\Rspamd;
use Froxlor\CurrentUser; use Froxlor\CurrentUser;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
@@ -76,7 +77,7 @@ if ($page == 'overview' || $page == 'emails') {
} }
$actions_links[] = [ $actions_links[] = [
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/emails/', 'href' => \Froxlor\Froxlor::getDocsUrl() . 'user-guide/emails/',
'target' => '_blank', 'target' => '_blank',
'icon' => 'fa-solid fa-circle-info', 'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary' 'class' => 'btn-outline-secondary'
@@ -138,7 +139,7 @@ if ($page == 'email_domain') {
]; ];
} }
$actions_links[] = [ $actions_links[] = [
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/emails/', 'href' => \Froxlor\Froxlor::getDocsUrl() . 'user-guide/emails/',
'target' => '_blank', 'target' => '_blank',
'icon' => 'fa-solid fa-circle-info', 'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary' 'class' => 'btn-outline-secondary'
@@ -160,11 +161,11 @@ if ($page == 'email_domain') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['email']) && $result['email'] != '') { if (isset($result['email']) && $result['email'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Emails::getLocal($userinfo, [ Emails::getLocal($userinfo, [
'id' => $id, 'id' => $id,
'delete_userfiles' => ($_POST['delete_userfiles'] ?? 0) 'delete_userfiles' => Request::post('delete_userfiles', 0)
])->delete(); ])->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
@@ -187,9 +188,9 @@ if ($page == 'email_domain') {
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if ($userinfo['emails_used'] < $userinfo['emails'] || $userinfo['emails'] == '-1') { if ($userinfo['emails_used'] < $userinfo['emails'] || $userinfo['emails'] == '-1') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
$json_result = Emails::getLocal($userinfo, $_POST)->add(); $json_result = Emails::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -244,7 +245,16 @@ if ($page == 'email_domain') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['email']) && $result['email'] != '') { if (isset($result['email']) && $result['email'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try {
Emails::getLocal($userinfo, [
'id' => $id,
'spam_tag_level' => Request::post('spam_tag_level', Rspamd::DEFAULT_MARK_LVL),
'spam_kill_level' => Request::post('spam_kill_level', Rspamd::DEFAULT_REJECT_LVL)
])->update();
} catch (Exception $e) {
Response::dynamicError($e->getMessage());
}
Response::redirectTo($filename, [ Response::redirectTo($filename, [
'page' => $page 'page' => $page
]); ]);
@@ -291,6 +301,54 @@ if ($page == 'email_domain') {
'editid' => $id 'editid' => $id
]); ]);
} }
} elseif ($action == 'togglebypass' && $id != 0) {
try {
$json_result = Emails::getLocal($userinfo, [
'id' => $id
])->get();
} catch (Exception $e) {
Response::dynamicError($e->getMessage());
}
$result = json_decode($json_result, true)['data'];
try {
Emails::getLocal($userinfo, [
'id' => $id,
'bypass_spam' => ($result['bypass_spam'] == '1' ? 0 : 1)
])->update();
} catch (Exception $e) {
Response::dynamicError($e->getMessage());
}
Response::redirectTo($filename, [
'page' => $page,
'domainid' => $email_domainid,
'action' => 'edit',
'id' => $id,
]);
} elseif ($action == 'togglegreylist' && $id != 0) {
try {
$json_result = Emails::getLocal($userinfo, [
'id' => $id
])->get();
} catch (Exception $e) {
Response::dynamicError($e->getMessage());
}
$result = json_decode($json_result, true)['data'];
try {
Emails::getLocal($userinfo, [
'id' => $id,
'policy_greylist' => ($result['policy_greylist'] == '1' ? 0 : 1)
])->update();
} catch (Exception $e) {
Response::dynamicError($e->getMessage());
}
Response::redirectTo($filename, [
'page' => $page,
'domainid' => $email_domainid,
'action' => 'edit',
'id' => $id,
]);
} elseif ($action == 'togglecatchall' && $id != 0) { } elseif ($action == 'togglecatchall' && $id != 0) {
try { try {
$json_result = Emails::getLocal($userinfo, [ $json_result = Emails::getLocal($userinfo, [
@@ -329,9 +387,9 @@ if ($page == 'email_domain') {
} }
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
EmailAccounts::getLocal($userinfo, $_POST)->add(); EmailAccounts::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -400,9 +458,9 @@ if ($page == 'email_domain') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['popaccountid']) && $result['popaccountid'] != '') { if (isset($result['popaccountid']) && $result['popaccountid'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
EmailAccounts::getLocal($userinfo, $_POST)->update(); EmailAccounts::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -459,9 +517,9 @@ if ($page == 'email_domain') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['popaccountid']) && $result['popaccountid'] != '') { if (isset($result['popaccountid']) && $result['popaccountid'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
EmailAccounts::getLocal($userinfo, $_POST)->update(); EmailAccounts::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -518,9 +576,9 @@ if ($page == 'email_domain') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['popaccountid']) && $result['popaccountid'] != '') { if (isset($result['popaccountid']) && $result['popaccountid'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
EmailAccounts::getLocal($userinfo, $_POST)->delete(); EmailAccounts::getLocal($userinfo, Request::postAll())->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -554,9 +612,9 @@ if ($page == 'email_domain') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['email']) && $result['email'] != '') { if (isset($result['email']) && $result['email'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
EmailForwarders::getLocal($userinfo, $_POST)->add(); EmailForwarders::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -616,22 +674,15 @@ if ($page == 'email_domain') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['destination']) && $result['destination'] != '') { if (isset($result['destination']) && $result['destination'] != '') {
if (isset($_POST['forwarderid'])) { $forwarderid = Request::any('forwarderid', 0);
$forwarderid = intval($_POST['forwarderid']);
} elseif (isset($_GET['forwarderid'])) {
$forwarderid = intval($_GET['forwarderid']);
} else {
$forwarderid = 0;
}
$result['destination'] = explode(' ', $result['destination']); $result['destination'] = explode(' ', $result['destination']);
if (isset($result['destination'][$forwarderid]) && $result['email'] != $result['destination'][$forwarderid]) { if (isset($result['destination'][$forwarderid]) && $result['email'] != $result['destination'][$forwarderid]) {
$forwarder = $result['destination'][$forwarderid]; $forwarder = $result['destination'][$forwarderid];
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
EmailForwarders::getLocal($userinfo, $_POST)->delete(); EmailForwarders::getLocal($userinfo, Request::postAll())->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }

View File

@@ -75,7 +75,7 @@ if ($page == 'overview' || $page == 'htpasswds') {
]; ];
$actions_links[] = [ $actions_links[] = [
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/extras/', 'href' => \Froxlor\Froxlor::getDocsUrl() . 'user-guide/extras/',
'target' => '_blank', 'target' => '_blank',
'icon' => 'fa-solid fa-circle-info', 'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary' 'class' => 'btn-outline-secondary'
@@ -97,9 +97,9 @@ if ($page == 'overview' || $page == 'htpasswds') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['username']) && $result['username'] != '') { if (isset($result['username']) && $result['username'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
DirProtections::getLocal($userinfo, $_POST)->delete(); DirProtections::getLocal($userinfo, Request::postAll())->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -119,9 +119,9 @@ if ($page == 'overview' || $page == 'htpasswds') {
} }
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
DirProtections::getLocal($userinfo, $_POST)->add(); DirProtections::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -149,9 +149,9 @@ if ($page == 'overview' || $page == 'htpasswds') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['username']) && $result['username'] != '') { if (isset($result['username']) && $result['username'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
DirProtections::getLocal($userinfo, $_POST)->update(); DirProtections::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -200,7 +200,7 @@ if ($page == 'overview' || $page == 'htpasswds') {
]; ];
$actions_links[] = [ $actions_links[] = [
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/extras/', 'href' => \Froxlor\Froxlor::getDocsUrl() . 'user-guide/extras/',
'target' => '_blank', 'target' => '_blank',
'icon' => 'fa-solid fa-circle-info', 'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary' 'class' => 'btn-outline-secondary'
@@ -222,9 +222,9 @@ if ($page == 'overview' || $page == 'htpasswds') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['customerid']) && $result['customerid'] != '' && $result['customerid'] == $userinfo['customerid']) { if (isset($result['customerid']) && $result['customerid'] != '' && $result['customerid'] == $userinfo['customerid']) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
DirOptions::getLocal($userinfo, $_POST)->delete(); DirOptions::getLocal($userinfo, Request::postAll())->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -240,9 +240,9 @@ if ($page == 'overview' || $page == 'htpasswds') {
} }
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
DirOptions::getLocal($userinfo, $_POST)->add(); DirOptions::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -271,9 +271,9 @@ if ($page == 'overview' || $page == 'htpasswds') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if ((isset($result['customerid'])) && ($result['customerid'] != '') && ($result['customerid'] == $userinfo['customerid'])) { if ((isset($result['customerid'])) && ($result['customerid'] != '') && ($result['customerid'] == $userinfo['customerid'])) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
DirOptions::getLocal($userinfo, $_POST)->update(); DirOptions::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -306,10 +306,10 @@ if ($page == 'overview' || $page == 'htpasswds') {
if (Settings::Get('system.exportenabled') == 1) { if (Settings::Get('system.exportenabled') == 1) {
if ($action == 'abort') { if ($action == 'abort') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "customer_extras::export - aborted scheduled data export job"); $log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "customer_extras::export - aborted scheduled data export job");
try { try {
DataDump::getLocal($userinfo, $_POST)->delete(); DataDump::getLocal($userinfo, Request::postAll())->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -336,9 +336,9 @@ if ($page == 'overview' || $page == 'htpasswds') {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
DataDump::getLocal($userinfo, $_POST)->add(); DataDump::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -349,7 +349,7 @@ if ($page == 'overview' || $page == 'htpasswds') {
$actions_links = [ $actions_links = [
[ [
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/extras/', 'href' => \Froxlor\Froxlor::getDocsUrl() . 'user-guide/extras/',
'target' => '_blank', 'target' => '_blank',
'icon' => 'fa-solid fa-circle-info', 'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary' 'class' => 'btn-outline-secondary'

View File

@@ -65,7 +65,7 @@ if ($page == 'overview' || $page == 'accounts') {
]; ];
} }
$actions_links[] = [ $actions_links[] = [
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/ftp-accounts/', 'href' => \Froxlor\Froxlor::getDocsUrl() . 'user-guide/ftp-accounts/',
'target' => '_blank', 'target' => '_blank',
'icon' => 'fa-solid fa-circle-info', 'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary' 'class' => 'btn-outline-secondary'
@@ -87,9 +87,9 @@ if ($page == 'overview' || $page == 'accounts') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['username']) && $result['username'] != $userinfo['loginname']) { if (isset($result['username']) && $result['username'] != $userinfo['loginname']) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Ftps::getLocal($userinfo, $_POST)->delete(); Ftps::getLocal($userinfo, Request::postAll())->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -108,9 +108,9 @@ if ($page == 'overview' || $page == 'accounts') {
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if ($userinfo['ftps_used'] < $userinfo['ftps'] || $userinfo['ftps'] == '-1') { if ($userinfo['ftps_used'] < $userinfo['ftps'] || $userinfo['ftps'] == '-1') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Ftps::getLocal($userinfo, $_POST)->add(); Ftps::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -164,9 +164,9 @@ if ($page == 'overview' || $page == 'accounts') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['username']) && $result['username'] != '') { if (isset($result['username']) && $result['username'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Ftps::getLocal($userinfo, $_POST)->update(); Ftps::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }

View File

@@ -30,6 +30,7 @@ use Froxlor\Api\Commands\Customers as Customers;
use Froxlor\Cron\TaskId; use Froxlor\Cron\TaskId;
use Froxlor\CurrentUser; use Froxlor\CurrentUser;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Database\DbManager;
use Froxlor\Froxlor; use Froxlor\Froxlor;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\Language; use Froxlor\Language;
@@ -37,6 +38,7 @@ use Froxlor\Settings;
use Froxlor\System\Cronjob; use Froxlor\System\Cronjob;
use Froxlor\System\Crypt; use Froxlor\System\Crypt;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\UI\Response; use Froxlor\UI\Response;
use Froxlor\Validate\Validate; use Froxlor\Validate\Validate;
@@ -54,7 +56,7 @@ if ($action == 'logout') {
$result = $result['switched_user']; $result = $result['switched_user'];
session_regenerate_id(true); session_regenerate_id(true);
CurrentUser::setData($result); CurrentUser::setData($result);
$target = (isset($_GET['target']) ? $_GET['target'] : 'index'); $target = Request::get('target', 'index');
$redirect = "admin_" . $target . ".php"; $redirect = "admin_" . $target . ".php";
if (!file_exists(Froxlor::getInstallDir() . "/" . $redirect)) { if (!file_exists(Froxlor::getInstallDir() . "/" . $redirect)) {
$redirect = "admin_index.php"; $redirect = "admin_index.php";
@@ -140,16 +142,16 @@ if ($page == 'overview') {
$languages = Language::getLanguages(); $languages = Language::getLanguages();
if (!empty($_POST)) { if (!empty($_POST)) {
if ($_POST['send'] == 'changepassword') { if (Request::post('send') == 'changepassword') {
$old_password = Validate::validate($_POST['old_password'], 'old password'); $old_password = Validate::validate(Request::post('old_password'), 'old password');
if (!Crypt::validatePasswordLogin($userinfo, $old_password, TABLE_PANEL_CUSTOMERS, 'customerid')) { if (!Crypt::validatePasswordLogin($userinfo, $old_password, TABLE_PANEL_CUSTOMERS, 'customerid')) {
Response::standardError('oldpasswordnotcorrect'); Response::standardError('oldpasswordnotcorrect');
} }
try { try {
$new_password = Crypt::validatePassword($_POST['new_password'], 'new password'); $new_password = Crypt::validatePassword(Request::post('new_password'), 'new password');
$new_password_confirm = Crypt::validatePassword($_POST['new_password_confirm'], 'new password confirm'); $new_password_confirm = Crypt::validatePassword(Request::post('new_password_confirm'), 'new password confirm');
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -184,7 +186,7 @@ if ($page == 'overview') {
$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, 'changed password'); $log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, 'changed password');
// Update ftp password // Update ftp password
if (isset($_POST['change_main_ftp']) && $_POST['change_main_ftp'] == 'true') { if (Request::post('change_main_ftp') == 'true') {
$cryptPassword = Crypt::makeCryptPassword($new_password); $cryptPassword = Crypt::makeCryptPassword($new_password);
$stmt = Database::prepare("UPDATE `" . TABLE_FTP_USERS . "` $stmt = Database::prepare("UPDATE `" . TABLE_FTP_USERS . "`
SET `password` = :password SET `password` = :password
@@ -200,7 +202,7 @@ if ($page == 'overview') {
} }
// Update statistics password // Update statistics password
if (isset($_POST['change_stats']) && $_POST['change_stats'] == 'true') { if (Request::post('change_stats') == 'true') {
$new_stats_password = Crypt::makeCryptPassword($new_password, true); $new_stats_password = Crypt::makeCryptPassword($new_password, true);
$stmt = Database::prepare("UPDATE `" . TABLE_PANEL_HTPASSWDS . "` $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_HTPASSWDS . "`
@@ -216,11 +218,32 @@ if ($page == 'overview') {
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
} }
// Update global myqsl user password
if ($userinfo['mysqls'] != 0 && Request::post('change_global_mysql') == 'true') {
$allowed_mysqlservers = json_decode($userinfo['allowed_mysqlserver'] ?? '[]', true);
foreach ($allowed_mysqlservers as $dbserver) {
// require privileged access for target db-server
Database::needRoot(true, $dbserver, false);
// get DbManager
$dbm = new DbManager($log);
// 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) {
if ($dbm->getManager()->userExistsOnHost($userinfo['loginname'], $mysql_access_host)) {
$dbm->getManager()->grantPrivilegesTo($userinfo['loginname'], $new_password, $mysql_access_host, false, true);
} else {
// create global mysql user if not exists
$dbm->getManager()->grantPrivilegesTo($userinfo['loginname'], $new_password, $mysql_access_host, false, false, true);
}
}
$dbm->getManager()->flushPrivileges();
}
}
Response::redirectTo($filename); Response::redirectTo($filename);
} }
} elseif ($_POST['send'] == 'changetheme') { } elseif (Request::post('send') == 'changetheme') {
if (Settings::Get('panel.allow_theme_change_customer') == 1) { if (Settings::Get('panel.allow_theme_change_customer') == 1) {
$theme = Validate::validate($_POST['theme'], 'theme'); $theme = Validate::validate(Request::post('theme'), 'theme');
try { try {
Customers::getLocal($userinfo, [ Customers::getLocal($userinfo, [
'id' => $userinfo['customerid'], 'id' => $userinfo['customerid'],
@@ -233,8 +256,8 @@ if ($page == 'overview') {
$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "changed default theme to '" . $theme . "'"); $log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "changed default theme to '" . $theme . "'");
} }
Response::redirectTo($filename); Response::redirectTo($filename);
} elseif ($_POST['send'] == 'changelanguage') { } elseif (Request::post('send') == 'changelanguage') {
$def_language = Validate::validate($_POST['def_language'], 'default language'); $def_language = Validate::validate(Request::post('def_language'), 'default language');
if (isset($languages[$def_language])) { if (isset($languages[$def_language])) {
try { try {
Customers::getLocal($userinfo, [ Customers::getLocal($userinfo, [

View File

@@ -30,8 +30,10 @@ use Froxlor\Api\Commands\Mysqls;
use Froxlor\Api\Commands\MysqlServer; use Froxlor\Api\Commands\MysqlServer;
use Froxlor\CurrentUser; use Froxlor\CurrentUser;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Database\DbManager;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\System\Crypt;
use Froxlor\UI\Collection; use Froxlor\UI\Collection;
use Froxlor\UI\HTML; use Froxlor\UI\HTML;
use Froxlor\UI\Listing; use Froxlor\UI\Listing;
@@ -74,17 +76,32 @@ if ($page == 'overview' || $page == 'mysqls') {
]; ];
} }
$view = 'user/table.html.twig';
if ($collection->count() > 0) {
$view = 'user/table-note.html.twig';
$actions_links[] = [
'href' => $linker->getLink(['section' => 'mysql', 'page' => 'mysqls', 'action' => 'global_user']),
'label' => lng('mysql.edit_global_user'),
'icon' => 'fa-solid fa-user-tie',
'class' => 'btn-outline-secondary'
];
}
$actions_links[] = [ $actions_links[] = [
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/databases/', 'href' => \Froxlor\Froxlor::getDocsUrl() . 'user-guide/databases/',
'target' => '_blank', 'target' => '_blank',
'icon' => 'fa-solid fa-circle-info', 'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary' 'class' => 'btn-outline-secondary'
]; ];
UI::view('user/table.html.twig', [ UI::view($view, [
'listing' => Listing::format($collection, $mysql_list_data, 'mysql_list'), 'listing' => Listing::format($collection, $mysql_list_data, 'mysql_list'),
'actions_links' => $actions_links, 'actions_links' => $actions_links,
'entity_info' => lng('mysql.description') 'entity_info' => lng('mysql.description'),
// alert-box
'type' => 'info',
'alert_msg' => lng('mysql.globaluserinfo', [$userinfo['loginname']]),
]); ]);
} elseif ($action == 'delete' && $id != 0) { } elseif ($action == 'delete' && $id != 0) {
try { try {
@@ -106,9 +123,9 @@ if ($page == 'overview' || $page == 'mysqls') {
$result['dbserver'] = 0; $result['dbserver'] = 0;
} }
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Mysqls::getLocal($userinfo, $_POST)->delete(); Mysqls::getLocal($userinfo, Request::postAll())->delete();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -129,9 +146,9 @@ if ($page == 'overview' || $page == 'mysqls') {
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if ($userinfo['mysqls_used'] < $userinfo['mysqls'] || $userinfo['mysqls'] == '-1') { if ($userinfo['mysqls_used'] < $userinfo['mysqls'] || $userinfo['mysqls'] == '-1') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
Mysqls::getLocal($userinfo, $_POST)->add(); Mysqls::getLocal($userinfo, Request::postAll())->add();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -169,9 +186,9 @@ if ($page == 'overview' || $page == 'mysqls') {
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
if (isset($result['databasename']) && $result['databasename'] != '') { if (isset($result['databasename']) && $result['databasename'] != '') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
try { try {
$json_result = Mysqls::getLocal($userinfo, $_POST)->update(); $json_result = Mysqls::getLocal($userinfo, Request::postAll())->update();
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -199,5 +216,45 @@ if ($page == 'overview' || $page == 'mysqls') {
]); ]);
} }
} }
} elseif ($action == 'global_user') {
$allowed_mysqlservers = json_decode($userinfo['allowed_mysqlserver'] ?? '[]', true);
if ($userinfo['mysqls'] == 0 || empty($allowed_mysqlservers)) {
Response::dynamicError('No permission');
}
if (Request::post('send') == 'send') {
$new_password = Crypt::validatePassword(Request::post('mysql_password'));
foreach ($allowed_mysqlservers as $dbserver) {
// require privileged access for target db-server
Database::needRoot(true, $dbserver, false);
// get DbManager
$dbm = new DbManager($log);
// 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) {
if ($dbm->getManager()->userExistsOnHost($userinfo['loginname'], $mysql_access_host)) {
// update password
$dbm->getManager()->grantPrivilegesTo($userinfo['loginname'], $new_password, $mysql_access_host, false, true, true);
} else {
// create missing user
$dbm->getManager()->grantPrivilegesTo($userinfo['loginname'], $new_password, $mysql_access_host, false, false, true);
}
}
$dbm->getManager()->flushPrivileges();
}
Response::redirectTo($filename, [
'page' => 'overview'
]);
} else {
$mysql_global_user_data = include_once dirname(__FILE__) . '/lib/formfields/customer/mysql/formfield.mysql_global_user.php';
UI::view('user/form.html.twig', [
'formaction' => $linker->getLink(['section' => 'mysql', 'page' => 'mysqls', 'action' => 'global_user']),
'formdata' => $mysql_global_user_data['mysql_global_user'],
'editid' => $id
]);
}
} }
} }

View File

@@ -30,6 +30,7 @@ if (!defined('AREA')) {
use Froxlor\Api\Commands\DomainZones; use Froxlor\Api\Commands\DomainZones;
use Froxlor\Dns\Dns; use Froxlor\Dns\Dns;
use Froxlor\Settings;
use Froxlor\UI\Collection; use Froxlor\UI\Collection;
use Froxlor\UI\HTML; use Froxlor\UI\HTML;
use Froxlor\UI\Listing; use Froxlor\UI\Listing;
@@ -42,11 +43,11 @@ use Froxlor\UI\Response;
$domain_id = (int)Request::any('domain_id'); $domain_id = (int)Request::any('domain_id');
$record = isset($_POST['dns_record']) ? trim($_POST['dns_record']) : null; $record = Request::post('dns_record');
$type = isset($_POST['dns_type']) ? $_POST['dns_type'] : 'A'; $type = Request::post('dns_type', 'A');
$prio = isset($_POST['dns_mxp']) ? (int)$_POST['dns_mxp'] : null; $prio = Request::post('dns_mxp');
$content = isset($_POST['dns_content']) ? trim($_POST['dns_content']) : null; $content = Request::post('dns_content');
$ttl = isset($_POST['dns_ttl']) ? (int)$_POST['dns_ttl'] : 18000; $ttl = (int)Request::post('dns_ttl', Settings::get('system.defaultttl'));
// get domain-name // get domain-name
$domain = Dns::getAllowedDomainEntry($domain_id, AREA, $userinfo); $domain = Dns::getAllowedDomainEntry($domain_id, AREA, $userinfo);
@@ -71,7 +72,7 @@ if ($action == 'add_record' && !empty($_POST)) {
$errors = str_replace("\n", "<br>", $e->getMessage()); $errors = str_replace("\n", "<br>", $e->getMessage());
} }
} elseif ($action == 'delete') { } elseif ($action == 'delete') {
$entry_id = isset($_GET['id']) ? (int)$_GET['id'] : 0; $entry_id = (int)Request::get('id', 0);
HTML::askYesNo('dnsentry_reallydelete', $filename, [ HTML::askYesNo('dnsentry_reallydelete', $filename, [
'id' => $entry_id, 'id' => $entry_id,
'domain_id' => $domain_id, 'domain_id' => $domain_id,
@@ -82,9 +83,9 @@ if ($action == 'add_record' && !empty($_POST)) {
'page' => $page, 'page' => $page,
'domain_id' => $domain_id 'domain_id' => $domain_id
]); ]);
} elseif (isset($_POST['send']) && $_POST['send'] == 'send' && $action == 'deletesure' && !empty($_POST)) { } elseif (Request::post('send') == 'send' && $action == 'deletesure' && !empty($_POST)) {
$entry_id = isset($_POST['id']) ? (int)$_POST['id'] : 0; $entry_id = (int)Request::post('id', 0);
$domain_id = isset($_POST['domain_id']) ? (int)$_POST['domain_id'] : 0; $domain_id = (int)Request::post('domain_id', 0);
// remove entry // remove entry
if ($entry_id > 0 && $domain_id > 0) { if ($entry_id > 0 && $domain_id > 0) {
try { try {

View File

@@ -77,7 +77,7 @@ if (!empty($errid)) {
$mail_html = nl2br($mail_body); $mail_html = nl2br($mail_body);
// send actual report to dev-team // send actual report to dev-team
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (Request::post('send') == 'send') {
// send mail and say thanks // send mail and say thanks
$_mailerror = false; $_mailerror = false;
try { try {

182
index.php
View File

@@ -54,7 +54,7 @@ if ($action == '2fa_entercode') {
Response::redirectTo('index.php'); Response::redirectTo('index.php');
exit(); exit();
} }
$smessage = isset($_GET['showmessage']) ? (int)$_GET['showmessage'] : 0; $smessage = (int)Request::get('showmessage', 0);
$message = ""; $message = "";
if ($smessage > 0) { if ($smessage > 0) {
$message = lng('error.2fa_wrongcode'); $message = lng('error.2fa_wrongcode');
@@ -71,7 +71,8 @@ if ($action == '2fa_entercode') {
Response::redirectTo('index.php'); Response::redirectTo('index.php');
exit(); exit();
} }
$code = isset($_POST['2fa_code']) ? $_POST['2fa_code'] : null; $code = Request::post('2fa_code');
$remember = Request::post('2fa_remember');
// verify entered code // verify entered code
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname')); $tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
// get user-data // get user-data
@@ -105,13 +106,6 @@ if ($action == '2fa_entercode') {
$userinfo['adminsession'] = $isadmin; $userinfo['adminsession'] = $isadmin;
$userinfo['userid'] = $uid; $userinfo['userid'] = $uid;
// if not successful somehow - start again
if (!finishLogin($userinfo)) {
Response::redirectTo('index.php', [
'showmessage' => '2'
]);
}
// when using email-2fa, remove the one-time-code // when using email-2fa, remove the one-time-code
if ($userinfo['type_2fa'] == '1') { if ($userinfo['type_2fa'] == '1') {
$del_stmt = Database::prepare("UPDATE " . $table . " SET `data_2fa` = '' WHERE `" . $field . "` = :uid"); $del_stmt = Database::prepare("UPDATE " . $table . " SET `data_2fa` = '' WHERE `" . $field . "` = :uid");
@@ -119,6 +113,42 @@ if ($action == '2fa_entercode') {
'uid' => $uid 'uid' => $uid
]); ]);
} }
// when remember is activated, set the cookie
if ($remember) {
$selector = base64_encode(Froxlor::genSessionId(9));
$authenticator = Froxlor::genSessionId(33);
$valid_until = time()+60*60*24*30;
$ins_stmt = Database::prepare("
INSERT INTO `".TABLE_PANEL_2FA_TOKENS."` SET
`selector` = :selector,
`token` = :authenticator,
`userid` = :userid,
`valid_until` = :valid_until
");
Database::pexecute($ins_stmt, [
'selector' => $selector,
'authenticator' => hash('sha256', $authenticator),
'userid' => $uid,
'valid_until' => $valid_until
]);
$cookie_params = [
'expires' => $valid_until, // 30 days
'path' => '/',
'domain' => UI::getCookieHost(),
'secure' => UI::requestIsHttps(),
'httponly' => true,
'samesite' => 'Strict'
];
setcookie('frx_2fa_remember', $selector.':'.base64_encode($authenticator), $cookie_params);
}
// if not successful somehow - start again
if (!finishLogin($userinfo)) {
Response::redirectTo('index.php', [
'showmessage' => '2'
]);
}
exit(); exit();
} }
// wrong 2fa code - treat like "wrong password" // wrong 2fa code - treat like "wrong password"
@@ -162,30 +192,41 @@ if ($action == '2fa_entercode') {
exit(); exit();
} elseif ($action == 'login') { } elseif ($action == 'login') {
if (!empty($_POST)) { if (!empty($_POST)) {
$loginname = Validate::validate($_POST['loginname'], 'loginname'); $loginname = Validate::validate(Request::post('loginname'), 'loginname');
$password = Validate::validate($_POST['password'], 'password'); $password = Validate::validate(Request::post('password'), 'password');
$stmt = Database::prepare("SELECT `loginname` AS `customer` FROM `" . TABLE_PANEL_CUSTOMERS . "` $select_additional = '';
WHERE `loginname`= :loginname"); if (Settings::Get('panel.db_version') >= 202312230) {
$select_additional = ' AND `gui_access` = 1';
}
$stmt = Database::prepare("
SELECT `loginname` AS `customer`
FROM `" . TABLE_PANEL_CUSTOMERS . "`
WHERE `loginname`= :loginname" .
$select_additional
);
Database::pexecute($stmt, [ Database::pexecute($stmt, [
"loginname" => $loginname "loginname" => $loginname
]); ]);
$row = $stmt->fetch(PDO::FETCH_ASSOC); $row = $stmt->fetch(PDO::FETCH_ASSOC);
$is_admin = false;
$table = "";
if ($row && $row['customer'] == $loginname) { if ($row && $row['customer'] == $loginname) {
$table = "`" . TABLE_PANEL_CUSTOMERS . "`"; $table = "`" . TABLE_PANEL_CUSTOMERS . "`";
$uid = 'customerid'; $uid = 'customerid';
$adminsession = '0'; $adminsession = '0';
$is_admin = false;
} else { } else {
$is_admin = true;
if ((int)Settings::Get('login.domain_login') == 1) { if ((int)Settings::Get('login.domain_login') == 1) {
$domainname = $idna_convert->encode(preg_replace([ $domainname = $idna_convert->encode(preg_replace([
'/\:(\d)+$/', '/\:(\d)+$/',
'/^https?\:\/\//' '/^https?\:\/\//'
], '', $loginname)); ], '', $loginname));
$stmt = Database::prepare("SELECT `customerid` FROM `" . TABLE_PANEL_DOMAINS . "` $stmt = Database::prepare("
WHERE `domain` = :domain"); SELECT `customerid`
FROM `" . TABLE_PANEL_DOMAINS . "`
WHERE `domain` = :domain
");
Database::pexecute($stmt, [ Database::pexecute($stmt, [
"domain" => $domainname "domain" => $domainname
]); ]);
@@ -194,8 +235,11 @@ if ($action == '2fa_entercode') {
if (isset($row2['customerid']) && $row2['customerid'] > 0) { if (isset($row2['customerid']) && $row2['customerid'] > 0) {
$loginname = Customer::getCustomerDetail($row2['customerid'], 'loginname'); $loginname = Customer::getCustomerDetail($row2['customerid'], 'loginname');
if ($loginname !== false) { if ($loginname !== false) {
$stmt = Database::prepare("SELECT `loginname` AS `customer` FROM `" . TABLE_PANEL_CUSTOMERS . "` $stmt = Database::prepare("
WHERE `loginname`= :loginname"); SELECT `loginname` AS `customer`
FROM `" . TABLE_PANEL_CUSTOMERS . "`
WHERE `loginname`= :loginname
");
Database::pexecute($stmt, [ Database::pexecute($stmt, [
"loginname" => $loginname "loginname" => $loginname
]); ]);
@@ -204,13 +248,17 @@ if ($action == '2fa_entercode') {
$table = "`" . TABLE_PANEL_CUSTOMERS . "`"; $table = "`" . TABLE_PANEL_CUSTOMERS . "`";
$uid = 'customerid'; $uid = 'customerid';
$adminsession = '0'; $adminsession = '0';
$is_admin = false;
} }
} }
} }
} }
} }
if (empty($table)) {
// try login as admin of no customer-login method worked
$is_admin = true;
}
if ((Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) && $is_admin == false) { if ((Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) && $is_admin == false) {
Response::redirectTo('index.php'); Response::redirectTo('index.php');
exit(); exit();
@@ -218,9 +266,11 @@ if ($action == '2fa_entercode') {
if ($is_admin) { if ($is_admin) {
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) { if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
$stmt = Database::prepare("SELECT `loginname` AS `admin` FROM `" . TABLE_PANEL_ADMINS . "` $stmt = Database::prepare("
SELECT `loginname` AS `admin` FROM `" . TABLE_PANEL_ADMINS . "`
WHERE `loginname`= :loginname WHERE `loginname`= :loginname
AND `change_serversettings` = '1'"); AND `change_serversettings` = '1'
");
Database::pexecute($stmt, [ Database::pexecute($stmt, [
"loginname" => $loginname "loginname" => $loginname
]); ]);
@@ -231,8 +281,16 @@ if ($action == '2fa_entercode') {
exit(); exit();
} }
} else { } else {
$stmt = Database::prepare("SELECT `loginname` AS `admin` FROM `" . TABLE_PANEL_ADMINS . "` $select_additional = '';
WHERE `loginname`= :loginname"); if (Settings::Get('panel.db_version') >= 202312230) {
$select_additional = ' AND `gui_access` = 1';
}
$stmt = Database::prepare("
SELECT `loginname` AS `admin`
FROM `" . TABLE_PANEL_ADMINS . "`
WHERE `loginname`= :loginname" .
$select_additional
);
Database::pexecute($stmt, [ Database::pexecute($stmt, [
"loginname" => $loginname "loginname" => $loginname
]); ]);
@@ -248,7 +306,7 @@ if ($action == '2fa_entercode') {
$rstlog = FroxlorLogger::getInstanceOf([ $rstlog = FroxlorLogger::getInstanceOf([
'loginname' => $_SERVER['REMOTE_ADDR'] 'loginname' => $_SERVER['REMOTE_ADDR']
]); ]);
$rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, "Unknown user '" . $loginname . "' tried to login."); $rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, "Unknown user tried to login.");
Response::redirectTo('index.php', [ Response::redirectTo('index.php', [
'showmessage' => '2' 'showmessage' => '2'
@@ -257,8 +315,9 @@ if ($action == '2fa_entercode') {
} }
} }
$userinfo_stmt = Database::prepare("SELECT * FROM $table $userinfo_stmt = Database::prepare("
WHERE `loginname`= :loginname"); SELECT * FROM $table WHERE `loginname`= :loginname
");
Database::pexecute($userinfo_stmt, [ Database::pexecute($userinfo_stmt, [
"loginname" => $loginname "loginname" => $loginname
]); ]);
@@ -281,9 +340,11 @@ if ($action == '2fa_entercode') {
} else { } else {
// login correct // login correct
// reset loginfail_counter, set lastlogin_succ // reset loginfail_counter, set lastlogin_succ
$stmt = Database::prepare("UPDATE $table $stmt = Database::prepare("
SET `lastlogin_succ`= :lastlogin_succ, `loginfail_count`='0' UPDATE $table
WHERE `$uid`= :uid"); SET `lastlogin_succ`= :lastlogin_succ, `loginfail_count`='0'
WHERE `$uid`= :uid
");
Database::pexecute($stmt, [ Database::pexecute($stmt, [
"lastlogin_succ" => time(), "lastlogin_succ" => time(),
"uid" => $userinfo[$uid] "uid" => $userinfo[$uid]
@@ -293,9 +354,11 @@ if ($action == '2fa_entercode') {
} }
} else { } else {
// login incorrect // login incorrect
$stmt = Database::prepare("UPDATE $table $stmt = Database::prepare("
UPDATE $table
SET `lastlogin_fail`= :lastlogin_fail, `loginfail_count`=`loginfail_count`+1 SET `lastlogin_fail`= :lastlogin_fail, `loginfail_count`=`loginfail_count`+1
WHERE `$uid`= :uid"); WHERE `$uid`= :uid
");
Database::pexecute($stmt, [ Database::pexecute($stmt, [
"lastlogin_fail" => time(), "lastlogin_fail" => time(),
"uid" => $userinfo[$uid] "uid" => $userinfo[$uid]
@@ -305,7 +368,7 @@ if ($action == '2fa_entercode') {
$rstlog = FroxlorLogger::getInstanceOf([ $rstlog = FroxlorLogger::getInstanceOf([
'loginname' => $_SERVER['REMOTE_ADDR'] 'loginname' => $_SERVER['REMOTE_ADDR']
]); ]);
$rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, "User '" . $loginname . "' tried to login with wrong password."); $rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, "User tried to login with wrong password.");
unset($userinfo); unset($userinfo);
Response::redirectTo('index.php', [ Response::redirectTo('index.php', [
@@ -316,6 +379,22 @@ if ($action == '2fa_entercode') {
// 2FA activated // 2FA activated
if (Settings::Get('2fa.enabled') == '1' && $userinfo['type_2fa'] > 0) { if (Settings::Get('2fa.enabled') == '1' && $userinfo['type_2fa'] > 0) {
// check for remember cookie
if (!empty($_COOKIE['frx_2fa_remember'])) {
list($selector, $authenticator) = explode(':', $_COOKIE['frx_2fa_remember']);
$sel_stmt = Database::prepare("SELECT `token` FROM `".TABLE_PANEL_2FA_TOKENS."` WHERE `selector` = :selector AND `userid` = :uid AND `valid_until` >= UNIX_TIMESTAMP()");
$token_check = Database::pexecute_first($sel_stmt, ['selector' => $selector, 'uid' => $userinfo[$uid]]);
if ($token_check && hash_equals($token_check['token'], hash('sha256', base64_decode($authenticator)))) {
if (!finishLogin($userinfo)) {
Response::redirectTo('index.php', [
'showmessage' => '2'
]);
}
exit();
}
}
// redirect to code-enter-page // redirect to code-enter-page
$_SESSION['secret_2fa'] = ($userinfo['type_2fa'] == 2 ? $userinfo['data_2fa'] : 'email'); $_SESSION['secret_2fa'] = ($userinfo['type_2fa'] == 2 ? $userinfo['data_2fa'] : 'email');
$_SESSION['uid_2fa'] = $userinfo[$uid]; $_SESSION['uid_2fa'] = $userinfo[$uid];
@@ -383,7 +462,7 @@ if ($action == '2fa_entercode') {
} }
exit(); exit();
} else { } else {
$smessage = isset($_GET['showmessage']) ? (int)$_GET['showmessage'] : 0; $smessage = (int)Request::get('showmessage', 0);
$message = ''; $message = '';
$successmessage = ''; $successmessage = '';
@@ -420,25 +499,20 @@ if ($action == '2fa_entercode') {
} }
// Pass the last used page if needed // Pass the last used page if needed
$lastscript = ""; $lastscript = Request::any('script', '');
if (isset($_REQUEST['script']) && $_REQUEST['script'] != "") { if (!empty($lastscript)) {
$lastscript = $_REQUEST['script'];
$lastscript = str_replace("..", "", $lastscript); $lastscript = str_replace("..", "", $lastscript);
$lastscript = htmlspecialchars($lastscript, ENT_QUOTES); $lastscript = htmlspecialchars($lastscript, ENT_QUOTES);
if (!file_exists(__DIR__ . "/" . $lastscript)) { if (file_exists(__DIR__ . "/" . $lastscript)) {
$_SESSION['lastscript'] = $lastscript;
} else {
$lastscript = ""; $lastscript = "";
} }
} }
$lastqrystr = ""; $lastqrystr = Request::any('qrystr', '');
if (isset($_REQUEST['qrystr']) && $_REQUEST['qrystr'] != "") {
$lastqrystr = urlencode($_REQUEST['qrystr']);
}
if (!empty($lastscript)) {
$_SESSION['lastscript'] = $lastscript;
}
if (!empty($lastqrystr)) { if (!empty($lastqrystr)) {
$lastqrystr = urlencode($lastqrystr);
$_SESSION['lastqrystr'] = $lastqrystr; $_SESSION['lastqrystr'] = $lastqrystr;
} }
@@ -456,8 +530,8 @@ if ($action == 'forgotpwd') {
$message = ''; $message = '';
if (!empty($_POST)) { if (!empty($_POST)) {
$loginname = Validate::validate($_POST['loginname'], 'loginname'); $loginname = Validate::validate(Request::post('loginname'), 'loginname');
$email = Validate::validateEmail($_POST['loginemail']); $email = Validate::validateEmail(Request::post('loginemail'));
$result_stmt = Database::prepare("SELECT `adminid`, `customerid`, `customernumber`, `firstname`, `name`, `company`, `email`, `loginname`, `def_language`, `deactivated` FROM `" . TABLE_PANEL_CUSTOMERS . "` $result_stmt = Database::prepare("SELECT `adminid`, `customerid`, `customernumber`, `firstname`, `name`, `company`, `email`, `loginname`, `def_language`, `deactivated` FROM `" . TABLE_PANEL_CUSTOMERS . "`
WHERE `loginname`= :loginname WHERE `loginname`= :loginname
AND `email`= :email"); AND `email`= :email");
@@ -624,7 +698,7 @@ if ($action == 'forgotpwd') {
$rstlog = FroxlorLogger::getInstanceOf([ $rstlog = FroxlorLogger::getInstanceOf([
'loginname' => 'password_reset' 'loginname' => 'password_reset'
]); ]);
$rstlog->logAction(FroxlorLogger::USR_ACTION, LOG_WARNING, "User '" . $loginname . "' requested to set a new password, but was not found in database!"); $rstlog->logAction(FroxlorLogger::USR_ACTION, LOG_WARNING, "Unknown user requested to set a new password, but was not found in database!");
$message = lng('login.usernotfound'); $message = lng('login.usernotfound');
} }
@@ -654,9 +728,9 @@ if ($action == 'resetpwd') {
"oldest" => time() - 86400 "oldest" => time() - 86400
]); ]);
if (isset($_GET['resetcode']) && strlen($_GET['resetcode']) == 50) { $activationcode = Request::get('resetcode');
if (!empty($activationcode) && strlen($activationcode) == 50) {
// Check if activation code is valid // Check if activation code is valid
$activationcode = $_GET['resetcode'];
$timestamp = substr($activationcode, 15, 10); $timestamp = substr($activationcode, 15, 10);
$third = substr($activationcode, 25, 15); $third = substr($activationcode, 25, 15);
$check = substr($activationcode, 40, 10); $check = substr($activationcode, 40, 10);
@@ -671,8 +745,8 @@ if ($action == 'resetpwd') {
if ($result !== false) { if ($result !== false) {
try { try {
$new_password = Crypt::validatePassword($_POST['new_password'], true); $new_password = Crypt::validatePassword(Request::post('new_password'), true);
$new_password_confirm = Crypt::validatePassword($_POST['new_password_confirm'], true); $new_password_confirm = Crypt::validatePassword(Request::post('new_password_confirm'), true);
} catch (Exception $e) { } catch (Exception $e) {
$message = $e->getMessage(); $message = $e->getMessage();
} }
@@ -801,8 +875,8 @@ function finishLogin($userinfo)
$theme = $userinfo['theme']; $theme = $userinfo['theme'];
} else { } else {
$theme = Settings::Get('panel.default_theme'); $theme = Settings::Get('panel.default_theme');
CurrentUser::setField('theme', $theme);
} }
CurrentUser::setField('theme', $theme);
$qryparams = []; $qryparams = [];
if (!empty($_SESSION['lastqrystr'])) { if (!empty($_SESSION['lastqrystr'])) {

View File

@@ -94,6 +94,10 @@ CREATE TABLE `mail_virtual` (
`popaccountid` int(11) NOT NULL default '0', `popaccountid` int(11) NOT NULL default '0',
`iscatchall` tinyint(1) unsigned NOT NULL default '0', `iscatchall` tinyint(1) unsigned NOT NULL default '0',
`description` varchar(255) NOT NULL DEFAULT '', `description` varchar(255) NOT NULL DEFAULT '',
`spam_tag_level` float(4,1) NOT NULL DEFAULT 7.0,
`spam_kill_level` float(4,1) NOT NULL DEFAULT 14.0,
`bypass_spam` tinyint(1) NOT NULL default '0',
`policy_greylist` tinyint(1) NOT NULL default '1',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `email` (`email`) KEY `email` (`email`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci; ) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
@@ -155,6 +159,7 @@ CREATE TABLE `panel_admins` (
`type_2fa` tinyint(1) NOT NULL default '0', `type_2fa` tinyint(1) NOT NULL default '0',
`data_2fa` varchar(25) NOT NULL default '', `data_2fa` varchar(25) NOT NULL default '',
`api_allowed` tinyint(1) NOT NULL default '1', `api_allowed` tinyint(1) NOT NULL default '1',
`gui_access` tinyint(1) NOT NULL default '1',
PRIMARY KEY (`adminid`), PRIMARY KEY (`adminid`),
UNIQUE KEY `loginname` (`loginname`) UNIQUE KEY `loginname` (`loginname`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC; ) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC;
@@ -223,6 +228,7 @@ CREATE TABLE `panel_customers` (
`api_allowed` tinyint(1) NOT NULL default '1', `api_allowed` tinyint(1) NOT NULL default '1',
`logviewenabled` tinyint(1) NOT NULL default '0', `logviewenabled` tinyint(1) NOT NULL default '0',
`allowed_mysqlserver` text NOT NULL, `allowed_mysqlserver` text NOT NULL,
`gui_access` tinyint(1) NOT NULL default '1',
PRIMARY KEY (`customerid`), PRIMARY KEY (`customerid`),
UNIQUE KEY `loginname` (`loginname`) UNIQUE KEY `loginname` (`loginname`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC; ) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC;
@@ -380,22 +386,18 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES
('logger', 'logfile', ''), ('logger', 'logfile', ''),
('logger', 'logtypes', 'syslog,mysql'), ('logger', 'logtypes', 'syslog,mysql'),
('logger', 'severity', '1'), ('logger', 'severity', '1'),
('dkim', 'use_dkim', '0'), ('antispam', 'activated', '0'),
('dkim', 'dkim_prefix', '/etc/postfix/dkim/'), ('antispam', 'config_file', '/etc/rspamd/local.d/froxlor_settings.conf'),
('dkim', 'dkim_domains', 'domains'), ('antispam', 'reload_command', 'service rspamd restart'),
('dkim', 'dkim_dkimkeys', 'dkim-keys.conf'), ('antispam', 'dkim_keylength', '1024'),
('dkim', 'dkimrestart_command', 'service dkim-filter restart'),
('dkim', 'privkeysuffix', '.priv'),
('admin', 'show_news_feed', '0'), ('admin', 'show_news_feed', '0'),
('admin', 'show_version_login', '0'), ('admin', 'show_version_login', '0'),
('admin', 'show_version_footer', '0'), ('admin', 'show_version_footer', '0'),
('caa', 'caa_entry', ''), ('caa', 'caa_entry', ''),
('spf', 'use_spf', '0'), ('spf', 'use_spf', '0'),
('spf', 'spf_entry', 'v=spf1 a mx -all'), ('spf', 'spf_entry', 'v=spf1 a mx -all'),
('dkim', 'dkim_algorithm', 'all'), ('dmarc', 'use_dmarc', '0'),
('dkim', 'dkim_keylength', '1024'), ('dmarc', 'dmarc_entry', 'v=DMARC1; p=none;'),
('dkim', 'dkim_servicetype', '0'),
('dkim', 'dkim_notes', ''),
('defaultwebsrverrhandler', 'enabled', '0'), ('defaultwebsrverrhandler', 'enabled', '0'),
('defaultwebsrverrhandler', 'err401', ''), ('defaultwebsrverrhandler', 'err401', ''),
('defaultwebsrverrhandler', 'err403', ''), ('defaultwebsrverrhandler', 'err403', ''),
@@ -636,6 +638,8 @@ opcache.validate_timestamps'),
('system', 'available_shells', ''), ('system', 'available_shells', ''),
('system', 'le_froxlor_enabled', '0'), ('system', 'le_froxlor_enabled', '0'),
('system', 'le_froxlor_redirect', '0'), ('system', 'le_froxlor_redirect', '0'),
('system', 'le_renew_hook', 'systemctl restart postfix dovecot proftpd'),
('system', 'le_renew_services', ''),
('system', 'letsencryptacmeconf', '/etc/apache2/conf-enabled/acme.conf'), ('system', 'letsencryptacmeconf', '/etc/apache2/conf-enabled/acme.conf'),
('system', 'mail_use_smtp', '0'), ('system', 'mail_use_smtp', '0'),
('system', 'mail_smtp_host', 'localhost'), ('system', 'mail_smtp_host', 'localhost'),
@@ -686,7 +690,7 @@ opcache.validate_timestamps'),
('api', 'customer_default', '1'), ('api', 'customer_default', '1'),
('2fa', 'enabled', '1'), ('2fa', 'enabled', '1'),
('panel', 'decimal_places', '4'), ('panel', 'decimal_places', '4'),
('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'adminmail', 'ADMIN_MAIL'),
('panel', 'phpmyadmin_url', ''), ('panel', 'phpmyadmin_url', ''),
('panel', 'webmail_url', ''), ('panel', 'webmail_url', ''),
('panel', 'webftp_url', ''), ('panel', 'webftp_url', ''),
@@ -726,8 +730,8 @@ opcache.validate_timestamps'),
('panel', 'logo_overridecustom', '0'), ('panel', 'logo_overridecustom', '0'),
('panel', 'settings_mode', '0'), ('panel', 'settings_mode', '0'),
('panel', 'menu_collapsed', '1'), ('panel', 'menu_collapsed', '1'),
('panel', 'version', '2.1.2'), ('panel', 'version', '2.2.0-rc3'),
('panel', 'db_version', '202312120'); ('panel', 'db_version', '202407200');
DROP TABLE IF EXISTS `panel_tasks`; DROP TABLE IF EXISTS `panel_tasks`;
@@ -1045,4 +1049,15 @@ CREATE TABLE `panel_loginlinks` (
`allowed_from` text NOT NULL, `allowed_from` text NOT NULL,
UNIQUE KEY `loginname` (`loginname`) UNIQUE KEY `loginname` (`loginname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
DROP TABLE IF EXISTS `panel_2fa_tokens`;
CREATE TABLE `panel_2fa_tokens` (
`id` int(11) NOT NULL auto_increment,
`selector` varchar(20) NOT NULL,
`token` varchar(200) NOT NULL,
`userid` int(11) NOT NULL default '0',
`valid_until` int(15) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
FROXLORSQL; FROXLORSQL;

View File

@@ -99,7 +99,6 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
} }
Update::lastStepStatus(0); Update::lastStepStatus(0);
Update::showUpdateStep("Cleaning up old files");
$to_clean = array( $to_clean = array(
"install/lib", "install/lib",
"install/lng", "install/lng",
@@ -121,30 +120,7 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
"lng/swedish.lng.php", "lng/swedish.lng.php",
"scripts", "scripts",
); );
$disabled = explode(',', ini_get('disable_functions')); Update::cleanOldFiles($to_clean);
$exec_allowed = !in_array('exec', $disabled);
$del_list = "";
foreach ($to_clean as $filedir) {
$complete_filedir = Froxlor::getInstallDir() . $filedir;
if (file_exists($complete_filedir)) {
if ($exec_allowed) {
FileDir::safe_exec("rm -rf " . escapeshellarg($complete_filedir));
} else {
$del_list .= "rm -rf " . escapeshellarg($complete_filedir) . PHP_EOL;
}
}
}
if ($exec_allowed) {
Update::lastStepStatus(0);
} else {
if (empty($del_list)) {
// 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::showUpdateStep("Adding new settings"); 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;

View File

@@ -149,7 +149,6 @@ if (Froxlor::isFroxlorVersion('2.1.0-rc2')) {
} }
if (Froxlor::isDatabaseVersion('202311260')) { if (Froxlor::isDatabaseVersion('202311260')) {
Update::showUpdateStep("Cleaning up old files");
$to_clean = array( $to_clean = array(
"install/updates/froxlor/update_2.x.inc.php", "install/updates/froxlor/update_2.x.inc.php",
"install/updates/preconfig/preconfig_2.x.inc.php", "install/updates/preconfig/preconfig_2.x.inc.php",
@@ -175,33 +174,8 @@ if (Froxlor::isDatabaseVersion('202311260')) {
"templates/Froxlor/user/change_theme.html.twig", "templates/Froxlor/user/change_theme.html.twig",
"tests/Backup/CustomerBackupsTest.php" "tests/Backup/CustomerBackupsTest.php"
); );
$disabled = explode(',', ini_get('disable_functions')); Update::cleanOldFiles($to_clean);
$exec_allowed = !in_array('exec', $disabled);
$del_list = "";
foreach ($to_clean as $filedir) {
$complete_filedir = Froxlor::getInstallDir() . $filedir;
if (file_exists($complete_filedir)) {
if ($exec_allowed) {
FileDir::safe_exec("rm -rf " . escapeshellarg($complete_filedir));
} else {
$del_list .= "rm -rf " . escapeshellarg($complete_filedir) . PHP_EOL;
}
}
}
if ($exec_allowed) {
Update::lastStepStatus(0);
} else {
if (empty($del_list)) {
// 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>'
);
}
}
Froxlor::updateToDbVersion('202312050'); Froxlor::updateToDbVersion('202312050');
} }
@@ -216,7 +190,6 @@ if (Froxlor::isFroxlorVersion('2.1.0')) {
} }
if (Froxlor::isDatabaseVersion('202312050')) { if (Froxlor::isDatabaseVersion('202312050')) {
Update::showUpdateStep("Cleaning up old files");
$to_clean = array( $to_clean = array(
"lib/configfiles/centos7.xml", "lib/configfiles/centos7.xml",
"lib/configfiles/centos8.xml", "lib/configfiles/centos8.xml",
@@ -225,33 +198,8 @@ if (Froxlor::isDatabaseVersion('202312050')) {
"lib/configfiles/buster.xml", "lib/configfiles/buster.xml",
"lib/configfiles/bionic.xml", "lib/configfiles/bionic.xml",
); );
$disabled = explode(',', ini_get('disable_functions')); Update::cleanOldFiles($to_clean);
$exec_allowed = !in_array('exec', $disabled);
$del_list = "";
foreach ($to_clean as $filedir) {
$complete_filedir = Froxlor::getInstallDir() . $filedir;
if (file_exists($complete_filedir)) {
if ($exec_allowed) {
FileDir::safe_exec("rm -rf " . escapeshellarg($complete_filedir));
} else {
$del_list .= "rm -rf " . escapeshellarg($complete_filedir) . PHP_EOL;
}
}
}
if ($exec_allowed) {
Update::lastStepStatus(0);
} else {
if (empty($del_list)) {
// 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>'
);
}
}
Froxlor::updateToDbVersion('202312100'); Froxlor::updateToDbVersion('202312100');
} }
@@ -269,3 +217,38 @@ if (Froxlor::isFroxlorVersion('2.1.1')) {
Update::showUpdateStep("Updating from 2.1.1 to 2.1.2", false); Update::showUpdateStep("Updating from 2.1.1 to 2.1.2", false);
Froxlor::updateToVersion('2.1.2'); Froxlor::updateToVersion('2.1.2');
} }
if (Froxlor::isFroxlorVersion('2.1.2')) {
Update::showUpdateStep("Updating from 2.1.2 to 2.1.3", false);
Froxlor::updateToVersion('2.1.3');
}
if (Froxlor::isFroxlorVersion('2.1.3')) {
Update::showUpdateStep("Updating from 2.1.3 to 2.1.4", false);
Froxlor::updateToVersion('2.1.4');
}
if (Froxlor::isFroxlorVersion('2.1.4')) {
Update::showUpdateStep("Updating from 2.1.4 to 2.1.5", false);
Froxlor::updateToVersion('2.1.5');
}
if (Froxlor::isFroxlorVersion('2.1.5')) {
Update::showUpdateStep("Updating from 2.1.5 to 2.1.6", false);
Froxlor::updateToVersion('2.1.6');
}
if (Froxlor::isFroxlorVersion('2.1.6')) {
Update::showUpdateStep("Updating from 2.1.6 to 2.1.7", false);
Froxlor::updateToVersion('2.1.7');
}
if (Froxlor::isFroxlorVersion('2.1.7')) {
Update::showUpdateStep("Updating from 2.1.7 to 2.1.8", false);
Froxlor::updateToVersion('2.1.8');
}
if (Froxlor::isFroxlorVersion('2.1.8')) {
Update::showUpdateStep("Updating from 2.1.8 to 2.1.9", false);
Froxlor::updateToVersion('2.1.9');
}

View File

@@ -0,0 +1,152 @@
<?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
*/
use Froxlor\Database\Database;
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'] == '')) {
header('Location: ../../../../index.php');
exit();
}
}
if (Froxlor::isFroxlorVersion('2.1.9')) {
Update::showUpdateStep("Enhancing virtual email table");
Database::query("ALTER TABLE `" . TABLE_MAIL_VIRTUAL . "` ADD `spam_tag_level` float(4,1) NOT NULL DEFAULT 7.0;");
Database::query("ALTER TABLE `" . TABLE_MAIL_VIRTUAL . "` ADD `spam_kill_level` float(4,1) NOT NULL DEFAULT 14.0;");
Database::query("ALTER TABLE `" . TABLE_MAIL_VIRTUAL . "` ADD `bypass_spam` tinyint(1) NOT NULL default '0';");
Database::query("ALTER TABLE `" . TABLE_MAIL_VIRTUAL . "` ADD `policy_greylist` tinyint(1) NOT NULL default '1';");
Update::lastStepStatus(0);
Update::showUpdateStep("Adjusting settings");
$antispam_activated = $_POST['antispam_activated'] ?? 0;
Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'antispam', `varname` = 'activated', `value` = '" . (int)$antispam_activated . "' WHERE `settinggroup` = 'dkim' AND `varname` = 'use_dkim';");
Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'antispam', `varname` = 'reload_command', `value` = 'service rspamd restart' WHERE `settinggroup` = 'dkim' AND `varname` = 'dkimrestart_command';");
Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'antispam', `varname` = 'config_file', `value` = '/etc/rspamd/local.d/froxlor_settings.conf' WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_prefix';");
Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'antispam' WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_keylength';");
Settings::AddNew("dmarc.use_dmarc", "0");
Settings::AddNew("dmarc.dmarc_entry", "v=DMARC1; p=none;");
Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'dkim' AND `varname` = 'privkeysuffix';");
Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_domains';");
Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_algorithm';");
Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_notes';");
Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_add_adsp';");
Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_dkimkeys';");
Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_servicetype';");
Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'dkim' AND `varname` = 'dkim_add_adsppolicy';");
Update::lastStepStatus(0);
if ($antispam_activated) {
Update::showUpdateStep("Converting existing domainkeys");
$sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `dkim` = '1' AND `dkim_pubkey` <> ''");
Database::pexecute($sel_stmt);
$upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `dkim_pubkey` = :pkey WHERE `id` = :did");
while ($domain = $sel_stmt->fetch(\PDO::FETCH_ASSOC)) {
$pubkey = trim(preg_replace(
'/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s',
'$1',
str_replace("\n", '', $domain['dkim_pubkey'])
));
Database::pexecute($upd_stmt, ['pkey' => $pubkey, 'did' => $domain['id']]);
}
Update::lastStepStatus(0);
Update::showUpdateStep("Configure antispam services");
$froxlorCliBin = Froxlor::getInstallDir() . '/bin/froxlor-cli';
$currentDistro = Settings::Get('system.distribution');
$manual_command = <<<EOC
{$froxlorCliBin} froxlor:config-services -a '{"http":"x","dns":"x","smtp":"x","mail":"x","antispam":"rspamd","ftp":"x","distro":"{$currentDistro}","system":[]}'
EOC;
Update::lastStepStatus(
1,
'manual action needed',
"Please run the following command manually as root:<br><pre>" . $manual_command . "</pre>"
);
} else {
Update::showUpdateStep("Removing existing domainkeys because antispam is disabled");
Database::query("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `dkim` = '0', `dkim_id` = '0', `dkim_privkey` = '', `dkim_pubkey` = '' WHERE `dkim` = '1';");
Update::lastStepStatus(1, '!!!');
}
Update::showUpdateStep("Enhancing admin and user table");
Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` ADD `gui_access` tinyint(1) NOT NULL default '1';");
Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `gui_access` tinyint(1) NOT NULL default '1';");
Update::lastStepStatus(0);
$to_clean = [
'actions/admin/settings/180.dkim.php',
'actions/admin/settings/185.spf.php',
];
Update::cleanOldFiles($to_clean);
Froxlor::updateToDbVersion('202312230');
Froxlor::updateToVersion('2.2.0-dev1');
}
if (Froxlor::isDatabaseVersion('202312230')) {
Update::showUpdateStep("Adding new settings");
Settings::AddNew("system.le_renew_services", "");
Settings::AddNew("system.le_renew_hook", "systemctl restart postfix dovecot proftpd");
Update::lastStepStatus(0);
Froxlor::updateToDbVersion('202401090');
}
if (Froxlor::isFroxlorVersion('2.2.0-dev1')) {
Update::showUpdateStep("Updating from 2.2.0-dev1 to 2.2.0-rc1", false);
Froxlor::updateToVersion('2.2.0-rc1');
}
if (Froxlor::isDatabaseVersion('202401090')) {
Update::showUpdateStep("Adding new table for 2fa tokens");
Database::query("DROP TABLE IF EXISTS `panel_2fa_tokens`;");
$sql = "CREATE TABLE `panel_2fa_tokens` (
`id` int(11) NOT NULL auto_increment,
`selector` varchar(20) NOT NULL,
`token` varchar(200) NOT NULL,
`userid` int(11) NOT NULL default '0',
`valid_until` int(15) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;";
Database::query($sql);
Update::lastStepStatus(0);
Froxlor::updateToDbVersion('202407200');
}
if (Froxlor::isFroxlorVersion('2.2.0-rc1')) {
Update::showUpdateStep("Updating from 2.2.0-rc1 to 2.2.0-rc2", false);
Froxlor::updateToVersion('2.2.0-rc2');
}
if (Froxlor::isFroxlorVersion('2.2.0-rc2')) {
Update::showUpdateStep("Updating from 2.2.0-rc2 to 2.2.0-rc3", false);
Froxlor::updateToVersion('2.2.0-rc3');
}

View File

@@ -23,31 +23,26 @@
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2 * @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/ */
return [ use Froxlor\Install\Update;
'groups' => [
'spf' => [ $preconfig = [
'title' => lng('admin.spfsettings'), 'title' => '2.2.x updates',
'icon' => 'fa-solid fa-clipboard-check', 'fields' => []
'fields' => [
'spf_use_spf' => [
'label' => lng('spf.use_spf'),
'settinggroup' => 'spf',
'varname' => 'use_spf',
'type' => 'checkbox',
'default' => false,
'save_method' => 'storeSettingField',
'overview_option' => true
],
'spf_spf_entry' => [
'label' => lng('spf.spf_entry'),
'settinggroup' => 'spf',
'varname' => 'spf_entry',
'type' => 'text',
'string_regexp' => '/^v=spf[a-z0-9:~?\s.-]+$/i',
'default' => 'v=spf1 a mx -all',
'save_method' => 'storeSettingField'
]
]
]
]
]; ];
$return = [];
if (Update::versionInUpdate($current_version, '2.2.0-dev1')) {
$has_preconfig = true;
$description = 'Froxlor now features antispam configurations using rspamd. Would you like to enable the antispam feature (required re-configuration of services)?<br><strong>ATTENTION:</strong> When not enabled and the former DomainKey feature was used, keep in mind that all existing domainkeys for all domain are being removed and the dkim-flag disabled for the domains.';
$question = '<strong>Enable antispam (recommended)</strong>&nbsp;';
$return['antispam_activated'] = [
'type' => 'checkbox',
'value' => 1,
'checked' => 0,
'label' => $question,
'prior_infotext' => $description
];
}
$preconfig['fields'] = $return;
return $preconfig;

View File

@@ -55,6 +55,7 @@ if (Froxlor::isFroxlor()) {
include_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_0.10.inc.php')); include_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_0.10.inc.php'));
include_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_2.0.inc.php')); include_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_2.0.inc.php'));
include_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_2.1.inc.php')); include_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_2.1.inc.php'));
include_once(FileDir::makeCorrectFile(dirname(__FILE__) . '/updates/froxlor/update_2.2.inc.php'));
// Check Froxlor - database integrity (only happens after all updates are done, so we know the db-layout is okay) // Check Froxlor - database integrity (only happens after all updates are done, so we know the db-layout is okay)
Update::showUpdateStep("Checking database integrity"); Update::showUpdateStep("Checking database integrity");

View File

@@ -193,7 +193,8 @@ class Ajax
UI::initTwig(); UI::initTwig();
try { try {
$json_result = \Froxlor\Api\Commands\Froxlor::getLocal($this->userinfo)->checkUpdate(); $force = Request::get('force', 0);
$json_result = \Froxlor\Api\Commands\Froxlor::getLocal($this->userinfo, ['force' => $force])->checkUpdate();
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
$result['full_version'] = Froxlor::getFullVersion(); $result['full_version'] = Froxlor::getFullVersion();
$result['dbversion'] = Froxlor::DBVERSION; $result['dbversion'] = Froxlor::DBVERSION;

View File

@@ -156,7 +156,7 @@ class GlobalSearch
], ],
'result_key' => 'domain_ace', 'result_key' => 'domain_ace',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'domain_ace', 'title_args' => 'domain_ace',
'href' => 'admin_domains.php?page=domains&searchfield=d.domain_ace&searchtext=' 'href' => 'admin_domains.php?page=domains&searchfield=d.domain_ace&searchtext='
] ]
@@ -172,7 +172,7 @@ class GlobalSearch
'result_key' => 'ip', 'result_key' => 'ip',
'result_groupkey' => 'ip', 'result_groupkey' => 'ip',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'ip', 'title_args' => 'ip',
'href' => 'admin_ipsandports.php?page=ipsandports&searchfield=ip&searchtext=' 'href' => 'admin_ipsandports.php?page=ipsandports&searchfield=ip&searchtext='
] ]
@@ -186,7 +186,7 @@ class GlobalSearch
], ],
'result_key' => 'id', 'result_key' => 'id',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'name', 'title_args' => 'name',
'href' => 'admin_plans.php?page=overview&searchfield=id&searchtext=' 'href' => 'admin_plans.php?page=overview&searchfield=id&searchtext='
] ]
@@ -201,7 +201,7 @@ class GlobalSearch
], ],
'result_key' => 'id', 'result_key' => 'id',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'description', 'title_args' => 'description',
'href' => 'admin_phpsettings.php?page=overview&searchfield=id&searchtext=' 'href' => 'admin_phpsettings.php?page=overview&searchfield=id&searchtext='
] ]
@@ -215,7 +215,7 @@ class GlobalSearch
], ],
'result_key' => 'id', 'result_key' => 'id',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'description', 'title_args' => 'description',
'href' => 'admin_phpsettings.php?page=fpmdaemons&searchfield=id&searchtext=' 'href' => 'admin_phpsettings.php?page=fpmdaemons&searchfield=id&searchtext='
] ]
@@ -234,7 +234,7 @@ class GlobalSearch
], ],
'result_key' => 'loginname', 'result_key' => 'loginname',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'name', 'title_args' => 'name',
'href' => 'admin_admins.php?page=admins&searchfield=loginname&searchtext=' 'href' => 'admin_admins.php?page=admins&searchfield=loginname&searchtext='
] ]
@@ -252,7 +252,7 @@ class GlobalSearch
], ],
'result_key' => 'domain_ace', 'result_key' => 'domain_ace',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'domain_ace', 'title_args' => 'domain_ace',
'href' => 'customer_domains.php?page=domains&searchfield=d.domain_ace&searchtext=' 'href' => 'customer_domains.php?page=domains&searchfield=d.domain_ace&searchtext='
] ]
@@ -266,7 +266,7 @@ class GlobalSearch
], ],
'result_key' => 'email', 'result_key' => 'email',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'email', 'title_args' => 'email',
'href' => 'customer_email.php?page=email_domain&domainid={domainid}&searchfield=m.email&searchtext=' 'href' => 'customer_email.php?page=email_domain&domainid={domainid}&searchfield=m.email&searchtext='
] ]
@@ -279,7 +279,7 @@ class GlobalSearch
], ],
'result_key' => 'domain', 'result_key' => 'domain',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'domain', 'title_args' => 'domain',
'href' => 'customer_email.php?page=emails&searchfield=d.domain&searchtext=' 'href' => 'customer_email.php?page=emails&searchfield=d.domain&searchtext='
] ]
@@ -293,7 +293,7 @@ class GlobalSearch
], ],
'result_key' => 'databasename', 'result_key' => 'databasename',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'databasename', 'title_args' => 'databasename',
'href' => 'customer_mysql.php?page=mysqls&searchfield=databasename&searchtext=' 'href' => 'customer_mysql.php?page=mysqls&searchfield=databasename&searchtext='
] ]
@@ -307,7 +307,7 @@ class GlobalSearch
], ],
'result_key' => 'username', 'result_key' => 'username',
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['\\Froxlor\\Ajax\\GlobalSearch', 'getFieldFromResult'],
'title_args' => 'username', 'title_args' => 'username',
'href' => 'customer_ftp.php?page=accounts&searchfield=username&searchtext=' 'href' => 'customer_ftp.php?page=accounts&searchfield=username&searchtext='
] ]

View File

@@ -140,12 +140,18 @@ class Admins extends ApiCommand implements ResourceEntity
* create a new admin user * create a new admin user
* *
* @param string $name * @param string $name
* required, name of the adminstrator
* @param string $email * @param string $email
* required, email address of the administrator
* @param string $new_loginname * @param string $new_loginname
* required, loginname/username of the administrator
* @param string $admin_password * @param string $admin_password
* optional, default auto-generated * optional, default auto-generated
* @param string $def_language * @param string $def_language
* optional, default is system-default language * * optional, ISO 639-1 language code (e.g. 'en', 'de', see lng-folder for supported languages),
* * default is system-default language
* @param bool $gui_access
* optional, allow login via webui, if false ONLY the login via webui is disallowed; default true
* @param bool $api_allowed * @param bool $api_allowed
* optional, default is true if system setting api.enabled is true, else false * optional, default is true if system setting api.enabled is true, else false
* @param string $custom_notes * @param string $custom_notes
@@ -219,6 +225,7 @@ class Admins extends ApiCommand implements ResourceEntity
// parameters // parameters
$def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage')); $def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage'));
$gui_access = $this->getBoolParam('gui_access', true, true);
$api_allowed = $this->getBoolParam('api_allowed', true, Settings::Get('api.enabled')); $api_allowed = $this->getBoolParam('api_allowed', true, Settings::Get('api.enabled'));
$custom_notes = $this->getParam('custom_notes', true, ''); $custom_notes = $this->getParam('custom_notes', true, '');
$custom_notes_show = $this->getBoolParam('custom_notes_show', true, 0); $custom_notes_show = $this->getBoolParam('custom_notes_show', true, 0);
@@ -316,6 +323,7 @@ class Admins extends ApiCommand implements ResourceEntity
'name' => $name, 'name' => $name,
'email' => $email, 'email' => $email,
'lang' => $def_language, 'lang' => $def_language,
'gui_access' => $gui_access,
'api_allowed' => $api_allowed, 'api_allowed' => $api_allowed,
'change_serversettings' => $change_serversettings, 'change_serversettings' => $change_serversettings,
'customers' => $customers, 'customers' => $customers,
@@ -344,6 +352,7 @@ class Admins extends ApiCommand implements ResourceEntity
`name` = :name, `name` = :name,
`email` = :email, `email` = :email,
`def_language` = :lang, `def_language` = :lang,
`gui_access` = :gui_access,
`api_allowed` = :api_allowed, `api_allowed` = :api_allowed,
`change_serversettings` = :change_serversettings, `change_serversettings` = :change_serversettings,
`customers` = :customers, `customers` = :customers,
@@ -430,7 +439,10 @@ class Admins extends ApiCommand implements ResourceEntity
* @param string $admin_password * @param string $admin_password
* optional, default auto-generated * optional, default auto-generated
* @param string $def_language * @param string $def_language
* optional, default is system-default language * * optional, ISO 639-1 language code (e.g. 'en', 'de', see lng-folder for supported languages),
* * default is system-default language
* @param bool $gui_access
* * optional, allow login via webui, if false ONLY the login via webui is disallowed; default true
* @param bool $api_allowed * @param bool $api_allowed
* optional, default is true if system setting api.enabled is true, else false * optional, default is true if system setting api.enabled is true, else false
* @param string $custom_notes * @param string $custom_notes
@@ -524,6 +536,7 @@ class Admins extends ApiCommand implements ResourceEntity
// you cannot edit some of the details of yourself // you cannot edit some of the details of yourself
if ($result['adminid'] == $this->getUserDetail('adminid')) { if ($result['adminid'] == $this->getUserDetail('adminid')) {
$gui_access = $result['gui_access'];
$api_allowed = $result['api_allowed']; $api_allowed = $result['api_allowed'];
$deactivated = $result['deactivated']; $deactivated = $result['deactivated'];
$customers = $result['customers']; $customers = $result['customers'];
@@ -542,6 +555,7 @@ class Admins extends ApiCommand implements ResourceEntity
$traffic = $result['traffic']; $traffic = $result['traffic'];
$ipaddress = ($result['ip'] != -1 ? json_decode($result['ip'], true) : -1); $ipaddress = ($result['ip'] != -1 ? json_decode($result['ip'], true) : -1);
} else { } else {
$gui_access = $this->getBoolParam('gui_access', true, $result['gui_access']);
$api_allowed = $this->getBoolParam('api_allowed', true, $result['api_allowed']); $api_allowed = $this->getBoolParam('api_allowed', true, $result['api_allowed']);
$deactivated = $this->getBoolParam('deactivated', true, $result['deactivated']); $deactivated = $this->getBoolParam('deactivated', true, $result['deactivated']);
@@ -665,6 +679,7 @@ class Admins extends ApiCommand implements ResourceEntity
'name' => $name, 'name' => $name,
'email' => $email, 'email' => $email,
'lang' => $def_language, 'lang' => $def_language,
'gui_access' => $gui_access,
'api_allowed' => $api_allowed, 'api_allowed' => $api_allowed,
'change_serversettings' => $change_serversettings, 'change_serversettings' => $change_serversettings,
'customers' => $customers, 'customers' => $customers,
@@ -694,6 +709,7 @@ class Admins extends ApiCommand implements ResourceEntity
`name` = :name, `name` = :name,
`email` = :email, `email` = :email,
`def_language` = :lang, `def_language` = :lang,
`gui_access` = :gui_access,
`api_allowed` = :api_allowed, `api_allowed` = :api_allowed,
`change_serversettings` = :change_serversettings, `change_serversettings` = :change_serversettings,
`customers` = :customers, `customers` = :customers,

View File

@@ -171,6 +171,7 @@ class Customers extends ApiCommand implements ResourceEntity
* create a new customer with default ftp-user and standard-subdomain (if wanted) * create a new customer with default ftp-user and standard-subdomain (if wanted)
* *
* @param string $email * @param string $email
* required, email address of new customer
* @param string $name * @param string $name
* optional if company is set, else required * optional if company is set, else required
* @param string $firstname * @param string $firstname
@@ -189,8 +190,11 @@ class Customers extends ApiCommand implements ResourceEntity
* optional * optional
* @param int $customernumber * @param int $customernumber
* optional * optional
* @param string $def_language , * @param string $def_language
* optional, default is system-default language * optional, ISO 639-1 language code (e.g. 'en', 'de', see lng-folder for supported languages),
* default is system-default language
* @param bool $gui_access
* optional, allow login via webui, if false ONLY the login via webui is disallowed; default true
* @param bool $api_allowed * @param bool $api_allowed
* optional, default is true if system setting api.enabled is true, else false * optional, default is true if system setting api.enabled is true, else false
* @param int $gender * @param int $gender
@@ -271,7 +275,7 @@ class Customers extends ApiCommand implements ResourceEntity
* optional, specify a hosting-plan to set certain resource-values from the plan * optional, specify a hosting-plan to set certain resource-values from the plan
* instead of specifying them * instead of specifying them
* @param array $allowed_mysqlserver * @param array $allowed_mysqlserver
* optional, array of IDs of defined mysql-servers the customer is allowed to use, * optional, array of IDs of defined mysql-servers the customer is allowed to use,
* default is to allow the default dbserver (id=0) * default is to allow the default dbserver (id=0)
* *
* @access admin * @access admin
@@ -297,6 +301,7 @@ class Customers extends ApiCommand implements ResourceEntity
$fax = $this->getParam('fax', true, ''); $fax = $this->getParam('fax', true, '');
$customernumber = $this->getParam('customernumber', true, ''); $customernumber = $this->getParam('customernumber', true, '');
$def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage')); $def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage'));
$gui_access = $this->getBoolParam('gui_access', true, 1);
$api_allowed = $this->getBoolParam('api_allowed', true, (Settings::Get('api.enabled') && Settings::Get('api.customer_default'))); $api_allowed = $this->getBoolParam('api_allowed', true, (Settings::Get('api.enabled') && Settings::Get('api.customer_default')));
$gender = (int)$this->getParam('gender', true, 0); $gender = (int)$this->getParam('gender', true, 0);
$custom_notes = $this->getParam('custom_notes', true, ''); $custom_notes = $this->getParam('custom_notes', true, '');
@@ -400,11 +405,14 @@ class Customers extends ApiCommand implements ResourceEntity
$allowed_phpconfigs = array_map('intval', $allowed_phpconfigs); $allowed_phpconfigs = array_map('intval', $allowed_phpconfigs);
if (empty($allowed_phpconfigs) && $phpenabled == 1) { if (empty($allowed_phpconfigs) && $phpenabled == 1) {
Response::standardError('customerphpenabledbutnoconfig', '', true); // only required if not using mod_php
if ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {
Response::standardError('customerphpenabledbutnoconfig', '', true);
}
} }
$allowed_mysqlserver = array(); $allowed_mysqlserver = array();
if (! empty($p_allowed_mysqlserver) && is_array($p_allowed_mysqlserver)) { if (!empty($p_allowed_mysqlserver) && is_array($p_allowed_mysqlserver)) {
foreach ($p_allowed_mysqlserver as $allowed_ms) { foreach ($p_allowed_mysqlserver as $allowed_ms) {
$allowed_ms = intval($allowed_ms); $allowed_ms = intval($allowed_ms);
$allowed_mysqlserver[] = $allowed_ms; $allowed_mysqlserver[] = $allowed_ms;
@@ -452,6 +460,28 @@ class Customers extends ApiCommand implements ResourceEntity
if (function_exists('posix_getpwnam') && !in_array("posix_getpwnam", explode(",", ini_get('disable_functions'))) && posix_getpwnam($loginname)) { if (function_exists('posix_getpwnam') && !in_array("posix_getpwnam", explode(",", ini_get('disable_functions'))) && posix_getpwnam($loginname)) {
Response::standardError('loginnameissystemaccount', $loginname, true); Response::standardError('loginnameissystemaccount', $loginname, true);
} }
// blacklist some system-internal names that might lead to issues
Database::needSqlData();
$sqldata = Database::getSqlData();
Database::needRoot(true);
Database::needSqlData();
$sqlrdata = Database::getSqlData();
$login_blacklist = [
'root',
'admin',
'froxroot',
'froxlor',
$sqldata['user'],
$sqldata['db'],
$sqlrdata['user'],
];
unset($sqldata);
unset($sqlrdata);
$login_blacklist = array_unique($login_blacklist);
if (in_array($loginname, $login_blacklist)) {
Response::standardError('loginnameisreservedname', $loginname, true);
}
} else { } else {
$accountnumber = intval(Settings::Get('system.lastaccountnumber')) + 1; $accountnumber = intval(Settings::Get('system.lastaccountnumber')) + 1;
$loginname = Settings::Get('customer.accountprefix') . $accountnumber; $loginname = Settings::Get('customer.accountprefix') . $accountnumber;
@@ -515,6 +545,7 @@ class Customers extends ApiCommand implements ResourceEntity
'email' => $email, 'email' => $email,
'customerno' => $customernumber, 'customerno' => $customernumber,
'lang' => $def_language, 'lang' => $def_language,
'gui_access' => $gui_access,
'api_allowed' => $api_allowed, 'api_allowed' => $api_allowed,
'docroot' => $documentroot, 'docroot' => $documentroot,
'guid' => $guid, 'guid' => $guid,
@@ -557,6 +588,7 @@ class Customers extends ApiCommand implements ResourceEntity
`email` = :email, `email` = :email,
`customernumber` = :customerno, `customernumber` = :customerno,
`def_language` = :lang, `def_language` = :lang,
`gui_access` = :gui_access,
`api_allowed` = :api_allowed, `api_allowed` = :api_allowed,
`documentroot` = :docroot, `documentroot` = :docroot,
`guid` = :guid, `guid` = :guid,
@@ -729,6 +761,22 @@ class Customers extends ApiCommand implements ResourceEntity
} }
} }
// Create default mysql-user if enabled
if ($mysqls != 0) {
foreach ($allowed_mysqlserver as $dbserver) {
// require privileged access for target db-server
Database::needRoot(true, $dbserver, false);
// get DbManager
$dbm = new DbManager($this->logger());
// 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) {
$dbm->getManager()->grantPrivilegesTo($loginname, $password, $mysql_access_host, false, false, true);
}
$dbm->getManager()->flushPrivileges();
Database::needRoot(false);
}
}
if ($sendpassword == '1') { if ($sendpassword == '1') {
$srv_hostname = Settings::Get('system.hostname'); $srv_hostname = Settings::Get('system.hostname');
if (Settings::Get('system.froxlordirectlyviahostname') == '0') { if (Settings::Get('system.froxlordirectlyviahostname') == '0') {
@@ -928,6 +976,7 @@ class Customers extends ApiCommand implements ResourceEntity
* @param string $loginname * @param string $loginname
* optional, the loginname * optional, the loginname
* @param string $email * @param string $email
* optional
* @param string $name * @param string $name
* optional if company is set, else required * optional if company is set, else required
* @param string $firstname * @param string $firstname
@@ -946,8 +995,11 @@ class Customers extends ApiCommand implements ResourceEntity
* optional * optional
* @param int $customernumber * @param int $customernumber
* optional * optional
* @param string $def_language , * @param string $def_language
* optional, default is system-default language * * optional, ISO 639-1 language code (e.g. 'en', 'de', see lng-folder for supported languages),
* * default is system-default language
* @param bool $gui_access
* optional, allow login via webui, if false ONLY the login via webui is disallowed; default true
* @param bool $api_allowed * @param bool $api_allowed
* optional, default is true if system setting api.enabled is true, else false * optional, default is true if system setting api.enabled is true, else false
* @param int $gender * @param int $gender
@@ -958,7 +1010,7 @@ class Customers extends ApiCommand implements ResourceEntity
* optional, whether to show the content of custom_notes to the customer, default 0 * optional, whether to show the content of custom_notes to the customer, default 0
* (false) * (false)
* @param string $new_customer_password * @param string $new_customer_password
* optional, iset new password * optional, set new password
* @param bool $sendpassword * @param bool $sendpassword
* optional, whether to send the password to the customer after creation, default 0 * optional, whether to send the password to the customer after creation, default 0
* (false) * (false)
@@ -1026,7 +1078,7 @@ class Customers extends ApiCommand implements ResourceEntity
* @param string $theme * @param string $theme
* optional, change theme * optional, change theme
* @param array $allowed_mysqlserver * @param array $allowed_mysqlserver
* optional, array of IDs of defined mysql-servers the customer is allowed to use, * optional, array of IDs of defined mysql-servers the customer is allowed to use,
* default is to allow the default dbserver (id=0) * default is to allow the default dbserver (id=0)
* *
* @access admin, customer * @access admin, customer
@@ -1062,6 +1114,7 @@ class Customers extends ApiCommand implements ResourceEntity
$fax = $this->getParam('fax', true, $result['fax']); $fax = $this->getParam('fax', true, $result['fax']);
$customernumber = $this->getParam('customernumber', true, $result['customernumber']); $customernumber = $this->getParam('customernumber', true, $result['customernumber']);
$def_language = $this->getParam('def_language', true, $result['def_language']); $def_language = $this->getParam('def_language', true, $result['def_language']);
$gui_access = $this->getBoolParam('gui_access', true, $result['gui_access']);
$api_allowed = $this->getBoolParam('api_allowed', true, $result['api_allowed']); $api_allowed = $this->getBoolParam('api_allowed', true, $result['api_allowed']);
$gender = (int)$this->getParam('gender', true, $result['gender']); $gender = (int)$this->getParam('gender', true, $result['gender']);
$custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']);
@@ -1114,14 +1167,17 @@ class Customers extends ApiCommand implements ResourceEntity
$allowed_phpconfigs = array_map('intval', $allowed_phpconfigs); $allowed_phpconfigs = array_map('intval', $allowed_phpconfigs);
} }
if (empty($allowed_phpconfigs) && $phpenabled == 1) { if (empty($allowed_phpconfigs) && $phpenabled == 1) {
Response::standardError('customerphpenabledbutnoconfig', '', true); // only required if not using mod_php
if ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {
Response::standardError('customerphpenabledbutnoconfig', '', true);
}
} }
// add permission for allowed mysql usage if customer was not allowed to use mysql prior // add permission for allowed mysql usage if customer was not allowed to use mysql prior
if ($result['mysqls'] == 0 && ($mysqls == -1 || $mysqls > 0)) { if ($result['mysqls'] == 0 && ($mysqls == -1 || $mysqls > 0)) {
$allowed_mysqlserver = $this->getParam('allowed_mysqlserver', true, [0]); $allowed_mysqlserver = $this->getParam('allowed_mysqlserver', true, [0]);
} }
if (! empty($allowed_mysqlserver)) { if (!empty($allowed_mysqlserver)) {
$allowed_mysqlserver = array_map('intval', $allowed_mysqlserver); $allowed_mysqlserver = array_map('intval', $allowed_mysqlserver);
} }
@@ -1279,12 +1335,34 @@ class Customers extends ApiCommand implements ResourceEntity
]); ]);
$upd_stmt = Database::prepare(" $upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `deactivated`= :deactivated WHERE `customerid` = :customerid"); UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `deactivated`= :deactivated WHERE `customerid` = :customerid
");
Database::pexecute($upd_stmt, [ Database::pexecute($upd_stmt, [
'deactivated' => $deactivated, 'deactivated' => $deactivated,
'customerid' => $id 'customerid' => $id
]); ]);
// enable/disable global mysql-user (loginname)
$current_allowed_mysqlserver = isset($result['allowed_mysqlserver']) && !empty($result['allowed_mysqlserver']) ? json_decode($result['allowed_mysqlserver'], true) : [];
foreach ($current_allowed_mysqlserver as $dbserver) {
// require privileged access for target db-server
Database::needRoot(true, $dbserver, false);
// get DbManager
$dbm = new DbManager($this->logger());
foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {
// Prevent access, if deactivated
if ($deactivated) {
// failsafe if user has been deleted manually (requires MySQL 4.1.2+)
$dbm->getManager()->disableUser($result['loginname'], $mysql_access_host);
} else {
// Otherwise grant access
$dbm->getManager()->enableUser($result['loginname'], $mysql_access_host, true);
}
}
$dbm->getManager()->flushPrivileges();
Database::needRoot(false);
}
// Retrieve customer's databases // Retrieve customer's databases
$databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`"); $databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`");
Database::pexecute($databases_stmt, [ Database::pexecute($databases_stmt, [
@@ -1305,9 +1383,7 @@ class Customers extends ApiCommand implements ResourceEntity
$last_dbserver = $row_database['dbserver']; $last_dbserver = $row_database['dbserver'];
} }
foreach (array_unique(explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {
$mysql_access_host = trim($mysql_access_host);
// Prevent access, if deactivated // Prevent access, if deactivated
if ($deactivated) { if ($deactivated) {
// failsafe if user has been deleted manually (requires MySQL 4.1.2+) // failsafe if user has been deleted manually (requires MySQL 4.1.2+)
@@ -1396,6 +1472,7 @@ class Customers extends ApiCommand implements ResourceEntity
'logviewenabled' => $logviewenabled, 'logviewenabled' => $logviewenabled,
'custom_notes' => $custom_notes, 'custom_notes' => $custom_notes,
'custom_notes_show' => $custom_notes_show, 'custom_notes_show' => $custom_notes_show,
'gui_access' => $gui_access,
'api_allowed' => $api_allowed, 'api_allowed' => $api_allowed,
'allowed_mysqlserver' => empty($allowed_mysqlserver) ? "" : json_encode($allowed_mysqlserver) 'allowed_mysqlserver' => empty($allowed_mysqlserver) ? "" : json_encode($allowed_mysqlserver)
]; ];
@@ -1439,6 +1516,7 @@ class Customers extends ApiCommand implements ResourceEntity
`logviewenabled` = :logviewenabled, `logviewenabled` = :logviewenabled,
`custom_notes` = :custom_notes, `custom_notes` = :custom_notes,
`custom_notes_show` = :custom_notes_show, `custom_notes_show` = :custom_notes_show,
`gui_access` = :gui_access,
`api_allowed` = :api_allowed, `api_allowed` = :api_allowed,
`allowed_mysqlserver` = :allowed_mysqlserver"; `allowed_mysqlserver` = :allowed_mysqlserver";
$upd_query .= $admin_upd_query; $upd_query .= $admin_upd_query;
@@ -1596,6 +1674,21 @@ class Customers extends ApiCommand implements ResourceEntity
]); ]);
$id = $result['customerid']; $id = $result['customerid'];
// remove global mysql-user (loginname)
$current_allowed_mysqlserver = isset($result['allowed_mysqlserver']) && !empty($result['allowed_mysqlserver']) ? json_decode($result['allowed_mysqlserver'], true) : [];
foreach ($current_allowed_mysqlserver as $dbserver) {
// require privileged access for target db-server
Database::needRoot(true, $dbserver, false);
// get DbManager
$dbm = new DbManager($this->logger());
foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {
$dbm->getManager()->deleteUser($result['loginname'], $mysql_access_host);
}
$dbm->getManager()->flushPrivileges();
Database::needRoot(false);
}
// remove all databases
$databases_stmt = Database::prepare(" $databases_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DATABASES . "` SELECT * FROM `" . TABLE_PANEL_DATABASES . "`
WHERE `customerid` = :id ORDER BY `dbserver` WHERE `customerid` = :id ORDER BY `dbserver`
@@ -1611,8 +1704,8 @@ class Customers extends ApiCommand implements ResourceEntity
$priv_changed = false; $priv_changed = false;
while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) {
if ($last_dbserver != $row_database['dbserver']) { if ($last_dbserver != $row_database['dbserver']) {
Database::needRoot(true, $row_database['dbserver']);
$dbm->getManager()->flushPrivileges(); $dbm->getManager()->flushPrivileges();
Database::needRoot(true, $row_database['dbserver']);
$last_dbserver = $row_database['dbserver']; $last_dbserver = $row_database['dbserver'];
} }
$dbm->getManager()->deleteDatabase($row_database['databasename']); $dbm->getManager()->deleteDatabase($row_database['databasename']);

View File

@@ -115,7 +115,7 @@ class DomainZones extends ApiCommand implements ResourceEntity
// validation // validation
$errors = []; $errors = [];
if (empty($record)) { if (empty(trim($record))) {
$record = "@"; $record = "@";
} }
@@ -227,7 +227,7 @@ class DomainZones extends ApiCommand implements ResourceEntity
// remove it for checks // remove it for checks
$content = substr($content, 0, -1); $content = substr($content, 0, -1);
} }
if (!Validate::validateDomain($content)) { if (!empty($content) && !Validate::validateDomain($content)) {
$errors[] = lng('error.dns_mx_needdom'); $errors[] = lng('error.dns_mx_needdom');
} else { } else {
// check whether there is a CNAME-record for the same resource // check whether there is a CNAME-record for the same resource
@@ -244,6 +244,10 @@ class DomainZones extends ApiCommand implements ResourceEntity
} }
// append trailing dot (again) // append trailing dot (again)
$content .= '.'; $content .= '.';
// if content is only ".", the prio needs to be 0 which results in a "null mx" entry
if ($content == '.' && $prio != 0) {
$prio = 0;
}
} elseif ($type == 'NS') { } elseif ($type == 'NS') {
// check for trailing dot // check for trailing dot
if (substr($content, -1) == '.') { if (substr($content, -1) == '.') {

View File

@@ -519,7 +519,8 @@ class Domains extends ApiCommand implements ResourceEntity
$mod_fcgid_maxrequests = '-1'; $mod_fcgid_maxrequests = '-1';
} }
} else { } else {
$phpenabled = '1'; // set default to whether the customer has php enabled or not
$phpenabled = $customer['phpenabled'];
$openbasedir = '1'; $openbasedir = '1';
if ((int)Settings::Get('phpfpm.enabled') == 1) { if ((int)Settings::Get('phpfpm.enabled') == 1) {
@@ -830,6 +831,9 @@ class Domains extends ApiCommand implements ResourceEntity
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
// Using nameserver, insert a task which rebuilds the server config // Using nameserver, insert a task which rebuilds the server config
Cronjob::inserttask(TaskId::REBUILD_DNS); Cronjob::inserttask(TaskId::REBUILD_DNS);
if ($dkim == '1') {
Cronjob::inserttask(TaskId::REBUILD_RSPAMD);
}
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] added domain '" . $domain . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] added domain '" . $domain . "'");
@@ -1407,7 +1411,7 @@ class Domains extends ApiCommand implements ResourceEntity
$zonefile = $result['zonefile']; $zonefile = $result['zonefile'];
} }
if (Settings::Get('dkim.use_dkim') != '1') { if (Settings::Get('antispam.activated') != '1') {
$dkim = $result['dkim']; $dkim = $result['dkim'];
} }
@@ -1527,13 +1531,12 @@ class Domains extends ApiCommand implements ResourceEntity
// enabled ssl for the domain but no ssl ip/port is selected // enabled ssl for the domain but no ssl ip/port is selected
Response::standardError('nosslippportgiven', '', true); Response::standardError('nosslippportgiven', '', true);
} }
if (Settings::Get('system.use_ssl') == "0" || empty($ssl_ipandports)) { if (Settings::Get('system.use_ssl') == "0" || empty($ssl_ipandports) || !$sslenabled) {
$ssl_redirect = 0; $ssl_redirect = 0;
$letsencrypt = 0; $letsencrypt = 0;
$http2 = 0; $http2 = 0;
// we need this for the json_encode // act like $remove_ssl_ipandport
// if ssl is disabled or no ssl-ip/port exists $ssl_ipandports = [];
$ssl_ipandports[] = -1;
// HSTS // HSTS
$hsts_maxage = 0; $hsts_maxage = 0;
@@ -1675,6 +1678,10 @@ class Domains extends ApiCommand implements ResourceEntity
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
} }
if ($dkim != $result['dkim']) {
Cronjob::inserttask(TaskId::REBUILD_RSPAMD);
}
if ($speciallogfile != $result['speciallogfile'] && $speciallogverified != '1') { if ($speciallogfile != $result['speciallogfile'] && $speciallogverified != '1') {
$speciallogfile = $result['speciallogfile']; $speciallogfile = $result['speciallogfile'];
} }
@@ -2098,6 +2105,8 @@ class Domains extends ApiCommand implements ResourceEntity
* @param bool $is_stdsubdomain * @param bool $is_stdsubdomain
* optional, default false, specify whether it's a std-subdomain you are deleting as it does not count * optional, default false, specify whether it's a std-subdomain you are deleting as it does not count
* as subdomain-resource * as subdomain-resource
* @param bool $delete_userfiles
* optional, delete email account files on filesystem (if any), default false
* *
* @access admin * @access admin
* @return string json-encoded array * @return string json-encoded array
@@ -2109,7 +2118,8 @@ class Domains extends ApiCommand implements ResourceEntity
$id = $this->getParam('id', true, 0); $id = $this->getParam('id', true, 0);
$dn_optional = $id > 0; $dn_optional = $id > 0;
$domainname = $this->getParam('domainname', $dn_optional, ''); $domainname = $this->getParam('domainname', $dn_optional, '');
$is_stdsubdomain = $this->getParam('is_stdsubdomain', true, 0); $is_stdsubdomain = $this->getBoolParam('is_stdsubdomain', true, 0);
$delete_user_emailfiles = $this->getBoolParam('delete_userfiles', true, 0);
$result = $this->apiCall('Domains.get', [ $result = $this->apiCall('Domains.get', [
'id' => $id, 'id' => $id,
@@ -2133,6 +2143,14 @@ class Domains extends ApiCommand implements ResourceEntity
$idString = implode(' OR ', $idString); $idString = implode(' OR ', $idString);
if ($idString != '') { if ($idString != '') {
if ($delete_user_emailfiles) {
// determine all connected email-accounts
$emailaccount_sel = Database::prepare("SELECT `email`, `homedir`, `maildir` FROM `" . TABLE_MAIL_USERS . "` WHERE " . $idString);
Database::pexecute($emailaccount_sel, $paramString, true, true);
while ($emailacc_row = $emailaccount_sel->fetch(PDO::FETCH_ASSOC)) {
Cronjob::inserttask(TaskId::DELETE_EMAIL_DATA, $emailacc_row['email'], FileDir::makeCorrectDir($emailacc_row['homedir'] . '/' . $emailacc_row['maildir']));
}
}
$del_stmt = Database::prepare(" $del_stmt = Database::prepare("
DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE " . $idString); DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE " . $idString);
Database::pexecute($del_stmt, $paramString, true, true); Database::pexecute($del_stmt, $paramString, true, true);

View File

@@ -157,10 +157,10 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
// prefix hash-algo // prefix hash-algo
switch (Settings::Get('system.passwordcryptfunc')) { switch (Settings::Get('system.passwordcryptfunc')) {
case PASSWORD_ARGON2I: case 'argon2i':
$cpPrefix = '{ARGON2I}'; $cpPrefix = '{ARGON2I}';
break; break;
case PASSWORD_ARGON2ID: case 'argon2id':
$cpPrefix = '{ARGON2ID}'; $cpPrefix = '{ARGON2ID}';
break; break;
default: default:
@@ -404,10 +404,10 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
$password = Crypt::validatePassword($password, true); $password = Crypt::validatePassword($password, true);
// prefix hash-algo // prefix hash-algo
switch (Settings::Get('system.passwordcryptfunc')) { switch (Settings::Get('system.passwordcryptfunc')) {
case PASSWORD_ARGON2I: case 'argon2i':
$cpPrefix = '{ARGON2I}'; $cpPrefix = '{ARGON2I}';
break; break;
case PASSWORD_ARGON2ID: case 'argon2id':
$cpPrefix = '{ARGON2ID}'; $cpPrefix = '{ARGON2ID}';
break; break;
default: default:
@@ -523,7 +523,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,
'emailaddr' => $emailaddr 'emailaddr' => $emailaddr
]); ], true);
$id = $result['id']; $id = $result['id'];
if (empty($result['popaccountid']) || $result['popaccountid'] == 0) { if (empty($result['popaccountid']) || $result['popaccountid'] == 0) {
@@ -563,7 +563,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
} }
if ($delete_userfiles) { if ($delete_userfiles) {
Cronjob::inserttask(TaskId::DELETE_EMAIL_DATA, $customer['loginname'], $result['email_full']); Cronjob::inserttask(TaskId::DELETE_EMAIL_DATA, $customer['loginname'], FileDir::makeCorrectDir($result['homedir'] . '/' . $result['maildir']));
} }
// decrease usage for customer // decrease usage for customer

View File

@@ -28,10 +28,12 @@ namespace Froxlor\Api\Commands;
use Exception; use Exception;
use Froxlor\Api\ApiCommand; use Froxlor\Api\ApiCommand;
use Froxlor\Api\ResourceEntity; use Froxlor\Api\ResourceEntity;
use Froxlor\Cron\TaskId;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\Idna\IdnaWrapper; use Froxlor\Idna\IdnaWrapper;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\System\Cronjob;
use Froxlor\UI\Response; use Froxlor\UI\Response;
use Froxlor\Validate\Validate; use Froxlor\Validate\Validate;
use PDO; use PDO;
@@ -49,6 +51,14 @@ class Emails extends ApiCommand implements ResourceEntity
* name of the address before @ * name of the address before @
* @param string $domain * @param string $domain
* domain-name for the email-address * domain-name for the email-address
* @param float $spam_tag_level
* optional, score which is required to tag emails as spam, default: 7.0
* @param float $spam_kill_level
* optional, score which is required to discard emails, default: 14.0
* @param boolean $bypass_spam
* optional, disable spam-filter entirely, default: no
* @param boolean $policy_greylist
* optional, enable grey-listing, default: yes
* @param boolean $iscatchall * @param boolean $iscatchall
* optional, make this address a catchall address, default: no * optional, make this address a catchall address, default: no
* @param int $customerid * @param int $customerid
@@ -74,6 +84,10 @@ class Emails extends ApiCommand implements ResourceEntity
$domain = $this->getParam('domain'); $domain = $this->getParam('domain');
// parameters // parameters
$spam_tag_level = $this->getParam('spam_tag_level', true, '7.0');
$spam_kill_level = $this->getParam('spam_kill_level', true, '14.0');
$bypass_spam = $this->getBoolParam('bypass_spam', true, 0);
$policy_greylist = $this->getBoolParam('policy_greylist', true, 1);
$iscatchall = $this->getBoolParam('iscatchall', true, 0); $iscatchall = $this->getBoolParam('iscatchall', true, 0);
$description = $this->getParam('description', true, ''); $description = $this->getParam('description', true, '');
@@ -140,11 +154,19 @@ class Emails extends ApiCommand implements ResourceEntity
} }
} }
$spam_tag_level = Validate::validate($spam_tag_level, 'spam_tag_level', '/^\d{1,}(\.\d{1,2})?$/', '', [7.0], true);
$spam_kill_level = Validate::validate($spam_kill_level, 'spam_kill_level', '/^\d{1,}(\.\d{1,2})?$/', '', [14.0], true);
$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);
$stmt = Database::prepare(" $stmt = Database::prepare("
INSERT INTO `" . TABLE_MAIL_VIRTUAL . "` SET INSERT INTO `" . TABLE_MAIL_VIRTUAL . "` SET
`customerid` = :cid, `customerid` = :cid,
`email` = :email, `email` = :email,
`email_full` = :email_full, `email_full` = :email_full,
`spam_tag_level` = :spam_tag_level,
`spam_kill_level` = :spam_kill_level,
`bypass_spam` = :bypass_spam,
`policy_greylist` = :policy_greylist,
`iscatchall` = :iscatchall, `iscatchall` = :iscatchall,
`domainid` = :domainid, `domainid` = :domainid,
`description` = :description `description` = :description
@@ -153,6 +175,10 @@ class Emails extends ApiCommand implements ResourceEntity
"cid" => $customer['customerid'], "cid" => $customer['customerid'],
"email" => $email, "email" => $email,
"email_full" => $email_full, "email_full" => $email_full,
"spam_tag_level" => $spam_tag_level,
"spam_kill_level" => $spam_kill_level,
"bypass_spam" => $bypass_spam,
"policy_greylist" => $policy_greylist,
"iscatchall" => $iscatchall, "iscatchall" => $iscatchall,
"domainid" => $domain_check['id'], "domainid" => $domain_check['id'],
"description" => $description "description" => $description
@@ -162,6 +188,7 @@ class Emails extends ApiCommand implements ResourceEntity
// update customer usage // update customer usage
Customers::increaseUsage($customer['customerid'], 'emails_used'); Customers::increaseUsage($customer['customerid'], 'emails_used');
Cronjob::inserttask(TaskId::REBUILD_RSPAMD);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email address '" . $email_full . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email address '" . $email_full . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
@@ -194,12 +221,12 @@ class Emails extends ApiCommand implements ResourceEntity
$customer_ids = $this->getAllowedCustomerIds('email'); $customer_ids = $this->getAllowedCustomerIds('email');
$params['idea'] = ($id <= 0 ? $emailaddr : $id); $params['idea'] = ($id <= 0 ? $emailaddr : $id);
$result_stmt = Database::prepare("SELECT v.`id`, v.`email`, v.`email_full`, v.`iscatchall`, v.`destination`, v.`customerid`, v.`popaccountid`, v.`domainid`, v.`description`, u.`quota`, u.`imap`, u.`pop3`, u.`postfix`, u.`mboxsize` $result_stmt = Database::prepare("SELECT v.*, u.`quota`, u.`imap`, u.`pop3`, u.`postfix`, u.`mboxsize` " . ($this->isInternal() ? ", `u`.`homedir`, `u`.`maildir`" : "") . "
FROM `" . TABLE_MAIL_VIRTUAL . "` v FROM `" . TABLE_MAIL_VIRTUAL . "` v
LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id` LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id`
WHERE v.`customerid` IN (" . implode(", ", $customer_ids) . ") WHERE v.`customerid` IN (" . implode(", ", $customer_ids) . ")
AND " . (is_numeric($params['idea']) ? "v.`id`= :idea" : "(v.`email` = :idea OR v.`email_full` = :idea)") AND " . (is_numeric($params['idea']) ? "v.`id`= :idea" : "(v.`email` = :idea OR v.`email_full` = :idea)"
); ));
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get email address '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get email address '" . $result['email_full'] . "'");
@@ -220,6 +247,14 @@ class Emails extends ApiCommand implements ResourceEntity
* optional, required when called as admin (if $loginname is not specified) * optional, required when called as admin (if $loginname is not specified)
* @param string $loginname * @param string $loginname
* optional, required when called as admin (if $customerid is not specified) * optional, required when called as admin (if $customerid is not specified)
* @param float $spam_tag_level
* optional, score which is required to tag emails as spam, default: 7.0
* @param float $spam_kill_level
* optional, score which is required to discard emails, default: 14.0
* @param boolean $bypass_spam
* optional, disable spam-filter entirely, default: no
* @param boolean $policy_greylist
* optional, enable grey-listing, default: yes
* @param boolean $iscatchall * @param boolean $iscatchall
* optional * optional
* @param string $description * @param string $description
@@ -235,15 +270,6 @@ class Emails extends ApiCommand implements ResourceEntity
throw new Exception("You cannot access this resource", 405); throw new Exception("You cannot access this resource", 405);
} }
// if enabling catchall is not allowed by settings, we do not need
// to run update()
if (Settings::Get('catchall.catchall_enabled') != '1') {
Response::standardError([
'operationnotpermitted',
'featureisdisabled'
], 'catchall', true);
}
$id = $this->getParam('id', true, 0); $id = $this->getParam('id', true, 0);
$ea_optional = $id > 0; $ea_optional = $id > 0;
$emailaddr = $this->getParam('emailaddr', $ea_optional, ''); $emailaddr = $this->getParam('emailaddr', $ea_optional, '');
@@ -255,48 +281,78 @@ class Emails extends ApiCommand implements ResourceEntity
$id = $result['id']; $id = $result['id'];
// parameters // parameters
$spam_tag_level = $this->getParam('spam_tag_level', true, $result['spam_tag_level']);
$spam_kill_level = $this->getParam('spam_kill_level', true, $result['spam_kill_level']);
$bypass_spam = $this->getBoolParam('bypass_spam', true, $result['bypass_spam']);
$policy_greylist = $this->getBoolParam('policy_greylist', true, $result['policy_greylist']);
$iscatchall = $this->getBoolParam('iscatchall', true, $result['iscatchall']); $iscatchall = $this->getBoolParam('iscatchall', true, $result['iscatchall']);
$description = $this->getParam('description', true, $result['description']); $description = $this->getParam('description', true, $result['description']);
// if enabling catchall is not allowed by settings, we do not need
// to run update()
if ($iscatchall && $result['iscatchall'] == 0 && Settings::Get('catchall.catchall_enabled') != '1') {
Response::standardError([
'operationnotpermitted',
'featureisdisabled'
], 'catchall', true);
}
// get needed customer info to reduce the email-address-counter by one // get needed customer info to reduce the email-address-counter by one
$customer = $this->getCustomerData(); $customer = $this->getCustomerData();
// check for catchall-flag // check for catchall-flag
$email = $result['email_full'];
if ($iscatchall) { if ($iscatchall) {
$iscatchall = '1'; $iscatchall = '1';
$email_parts = explode('@', $result['email_full']); $email = $result['email'];
$email = '@' . $email_parts[1]; // update only required if it was not a catchall before
// catchall check if ($result['iscatchall'] == 0) {
$stmt = Database::prepare(" $email_parts = explode('@', $result['email_full']);
SELECT `email_full` FROM `" . TABLE_MAIL_VIRTUAL . "` $email = '@' . $email_parts[1];
WHERE `email` = :email AND `customerid` = :cid AND `iscatchall` = '1' // catchall check
"); $stmt = Database::prepare("
$params = [ SELECT `email_full` FROM `" . TABLE_MAIL_VIRTUAL . "`
"email" => $email, WHERE `email` = :email AND `customerid` = :cid AND `iscatchall` = '1'
"cid" => $customer['customerid'] ");
]; $params = [
$email_check = Database::pexecute_first($stmt, $params, true, true); "email" => $email,
if ($email_check) { "cid" => $customer['customerid']
Response::standardError('youhavealreadyacatchallforthisdomain', '', true); ];
$email_check = Database::pexecute_first($stmt, $params, true, true);
if ($email_check) {
Response::standardError('youhavealreadyacatchallforthisdomain', '', true);
}
} }
} else {
$iscatchall = '0';
$email = $result['email_full'];
} }
$spam_tag_level = Validate::validate($spam_tag_level, 'spam_tag_level', '/^\d{1,}(\.\d{1,2})?$/', '', [7.0], true);
$spam_kill_level = Validate::validate($spam_kill_level, 'spam_kill_level', '/^\d{1,}(\.\d{1,2})?$/', '', [14.0], true);
$description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true);
$stmt = Database::prepare(" $stmt = Database::prepare("
UPDATE `" . TABLE_MAIL_VIRTUAL . "` UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET
SET `email` = :email , `iscatchall` = :caflag, `description` = :description `email` = :email ,
`spam_tag_level` = :spam_tag_level,
`spam_kill_level` = :spam_kill_level,
`bypass_spam` = :bypass_spam,
`policy_greylist` = :policy_greylist,
`iscatchall` = :caflag,
`description` = :description
WHERE `customerid`= :cid AND `id`= :id WHERE `customerid`= :cid AND `id`= :id
"); ");
$params = [ $params = [
"email" => $email, "email" => $email,
"spam_tag_level" => $spam_tag_level,
"spam_kill_level" => $spam_kill_level,
"bypass_spam" => $bypass_spam,
"policy_greylist" => $policy_greylist,
"caflag" => $iscatchall, "caflag" => $iscatchall,
"description" => $description, "description" => $description,
"cid" => $customer['customerid'], "cid" => $customer['customerid'],
"id" => $id "id" => $id
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
Cronjob::inserttask(TaskId::REBUILD_RSPAMD);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
@@ -334,7 +390,7 @@ class Emails extends ApiCommand implements ResourceEntity
$result = []; $result = [];
$query_fields = []; $query_fields = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT m.`id`, m.`domainid`, m.`email`, m.`email_full`, m.`iscatchall`, m.`destination`, m.`popaccountid`, d.`domain`, u.`quota`, u.`imap`, u.`pop3`, u.`postfix`, u.`mboxsize` SELECT m.*, d.`domain`, u.`quota`, u.`imap`, u.`pop3`, u.`postfix`, u.`mboxsize`
FROM `" . TABLE_MAIL_VIRTUAL . "` m FROM `" . TABLE_MAIL_VIRTUAL . "` m
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON (m.`domainid` = d.`id`) LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON (m.`domainid` = d.`id`)
LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON (m.`popaccountid` = u.`id`) LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON (m.`popaccountid` = u.`id`)

View File

@@ -259,14 +259,15 @@ class Froxlor extends ApiCommand
* returns a random password based on froxlor settings for min-length, included characters, etc. * returns a random password based on froxlor settings for min-length, included characters, etc.
* *
* @param int $length * @param int $length
* optional length of password, defaults to 10 * optional length of password, defaults to 0 (panel.password_min_length)
* *
* @access admin, customer * @access admin, customer
* @return string * @return string
* @throws Exception
*/ */
public function generatePassword() public function generatePassword(): string
{ {
$length = $this->getParam('length', true, 10); $length = $this->getParam('length', true, 0);
return $this->response(Crypt::generatePassword($length)); return $this->response(Crypt::generatePassword($length));
} }

View File

@@ -54,7 +54,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
* @param string $description * @param string $description
* optional, description for database * optional, description for database
* @param string $custom_suffix * @param string $custom_suffix
* optional, name for database * optional, name for database if customer.mysqlprefix setting is set to "DBNAME"
* @param bool $sendinfomail * @param bool $sendinfomail
* optional, send created resource-information to customer, default: false * optional, send created resource-information to customer, default: false
* @param int $customerid * @param int $customerid
@@ -110,6 +110,9 @@ class Mysqls extends ApiCommand implements ResourceEntity
$dbm = new DbManager($this->logger()); $dbm = new DbManager($this->logger());
if (strtoupper(Settings::Get('customer.mysqlprefix')) == 'DBNAME' && !empty($databasename)) { if (strtoupper(Settings::Get('customer.mysqlprefix')) == 'DBNAME' && !empty($databasename)) {
if (strlen($newdb_params['loginname'] . '_' . $databasename) > Database::getSqlUsernameLength()) {
throw new Exception("Database name cannot be longer than " . (Database::getSqlUsernameLength() - strlen($newdb_params['loginname'] . '_')) . " characters.", 406);
}
$username = $dbm->createDatabase($newdb_params['loginname'] . '_' . $databasename, $password, $dbserver); $username = $dbm->createDatabase($newdb_params['loginname'] . '_' . $databasename, $password, $dbserver);
} else { } else {
$username = $dbm->createDatabase($newdb_params['loginname'], $password, $dbserver, $newdb_params['mysql_lastaccountnumber']); $username = $dbm->createDatabase($newdb_params['loginname'], $password, $dbserver, $newdb_params['mysql_lastaccountnumber']);

View File

@@ -222,8 +222,8 @@ class PhpSettings extends ApiCommand implements ResourceEntity
* optional request terminate timeout if FPM is used, default is '60s' * optional request terminate timeout if FPM is used, default is '60s'
* @param string $phpfpm_reqslowtimeout * @param string $phpfpm_reqslowtimeout
* optional request slowlog timeout if FPM is used, default is '5s' * optional request slowlog timeout if FPM is used, default is '5s'
* @param bool $phpfpm_pass_authorizationheader * @param bool $pass_authorizationheader
* optional whether to pass authorization header to webserver if FPM is used, default is 0 (false) * optional whether to pass authorization header to webserver if FPM/FCGID is used, default is 0 (false)
* @param bool $override_fpmconfig * @param bool $override_fpmconfig
* optional whether to override fpm-daemon-config value for the following settings if FPM is used, * optional whether to override fpm-daemon-config value for the following settings if FPM is used,
* default is 0 (false) * default is 0 (false)
@@ -276,7 +276,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
$fpm_enableslowlog = $this->getBoolParam('phpfpm_enable_slowlog', true, 0); $fpm_enableslowlog = $this->getBoolParam('phpfpm_enable_slowlog', true, 0);
$fpm_reqtermtimeout = $this->getParam('phpfpm_reqtermtimeout', true, "60s"); $fpm_reqtermtimeout = $this->getParam('phpfpm_reqtermtimeout', true, "60s");
$fpm_reqslowtimeout = $this->getParam('phpfpm_reqslowtimeout', true, "5s"); $fpm_reqslowtimeout = $this->getParam('phpfpm_reqslowtimeout', true, "5s");
$fpm_pass_authorizationheader = $this->getBoolParam('phpfpm_pass_authorizationheader', true, 0); $pass_authorizationheader = $this->getBoolParam('pass_authorizationheader', true, 0);
$override_fpmconfig = $this->getBoolParam('override_fpmconfig', true, 0); $override_fpmconfig = $this->getBoolParam('override_fpmconfig', true, 0);
$def_fpmconfig = $this->apiCall('FpmDaemons.get', [ $def_fpmconfig = $this->apiCall('FpmDaemons.get', [
@@ -312,7 +312,6 @@ class PhpSettings extends ApiCommand implements ResourceEntity
$fpm_enableslowlog = 0; $fpm_enableslowlog = 0;
$fpm_reqtermtimeout = 0; $fpm_reqtermtimeout = 0;
$fpm_reqslowtimeout = 0; $fpm_reqslowtimeout = 0;
$fpm_pass_authorizationheader = 0;
$override_fpmconfig = 0; $override_fpmconfig = 0;
} elseif (Settings::Get('phpfpm.enabled') == 1) { } elseif (Settings::Get('phpfpm.enabled') == 1) {
$fpm_reqtermtimeout = Validate::validate($fpm_reqtermtimeout, 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', [], true); $fpm_reqtermtimeout = Validate::validate($fpm_reqtermtimeout, 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', [], true);
@@ -377,7 +376,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
'fpmreqslow' => $fpm_reqslowtimeout, 'fpmreqslow' => $fpm_reqslowtimeout,
'phpsettings' => $phpsettings, 'phpsettings' => $phpsettings,
'fpmsettingid' => $fpm_config_id, 'fpmsettingid' => $fpm_config_id,
'fpmpassauth' => $fpm_pass_authorizationheader, 'fpmpassauth' => $pass_authorizationheader,
'ofc' => $override_fpmconfig, 'ofc' => $override_fpmconfig,
'pm' => $pmanager, 'pm' => $pmanager,
'max_children' => $max_children, 'max_children' => $max_children,
@@ -464,7 +463,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
* optional request terminate timeout if FPM is used, default is '60s' * optional request terminate timeout if FPM is used, default is '60s'
* @param string $phpfpm_reqslowtimeout * @param string $phpfpm_reqslowtimeout
* optional request slowlog timeout if FPM is used, default is '5s' * optional request slowlog timeout if FPM is used, default is '5s'
* @param bool $phpfpm_pass_authorizationheader * @param bool $pass_authorizationheader
* optional whether to pass authorization header to webserver if FPM is used, default is 0 (false) * optional whether to pass authorization header to webserver if FPM is used, default is 0 (false)
* @param bool $override_fpmconfig * @param bool $override_fpmconfig
* optional whether to override fpm-daemon-config value for the following settings if FPM is used, * optional whether to override fpm-daemon-config value for the following settings if FPM is used,
@@ -516,7 +515,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
$fpm_enableslowlog = $this->getBoolParam('phpfpm_enable_slowlog', true, $result['fpm_slowlog']); $fpm_enableslowlog = $this->getBoolParam('phpfpm_enable_slowlog', true, $result['fpm_slowlog']);
$fpm_reqtermtimeout = $this->getParam('phpfpm_reqtermtimeout', true, $result['fpm_reqterm']); $fpm_reqtermtimeout = $this->getParam('phpfpm_reqtermtimeout', true, $result['fpm_reqterm']);
$fpm_reqslowtimeout = $this->getParam('phpfpm_reqslowtimeout', true, $result['fpm_reqslow']); $fpm_reqslowtimeout = $this->getParam('phpfpm_reqslowtimeout', true, $result['fpm_reqslow']);
$fpm_pass_authorizationheader = $this->getBoolParam('phpfpm_pass_authorizationheader', true, $result['pass_authorizationheader']); $pass_authorizationheader = $this->getBoolParam('pass_authorizationheader', true, $result['pass_authorizationheader']);
$override_fpmconfig = $this->getBoolParam('override_fpmconfig', true, $result['override_fpmconfig']); $override_fpmconfig = $this->getBoolParam('override_fpmconfig', true, $result['override_fpmconfig']);
$pmanager = $this->getParam('pm', true, $result['pm']); $pmanager = $this->getParam('pm', true, $result['pm']);
$max_children = $this->getParam('max_children', true, $result['max_children']); $max_children = $this->getParam('max_children', true, $result['max_children']);
@@ -548,7 +547,6 @@ class PhpSettings extends ApiCommand implements ResourceEntity
$fpm_enableslowlog = 0; $fpm_enableslowlog = 0;
$fpm_reqtermtimeout = 0; $fpm_reqtermtimeout = 0;
$fpm_reqslowtimeout = 0; $fpm_reqslowtimeout = 0;
$fpm_pass_authorizationheader = 0;
$override_fpmconfig = 0; $override_fpmconfig = 0;
} elseif (Settings::Get('phpfpm.enabled') == 1) { } elseif (Settings::Get('phpfpm.enabled') == 1) {
$fpm_reqtermtimeout = Validate::validate($fpm_reqtermtimeout, 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', [], true); $fpm_reqtermtimeout = Validate::validate($fpm_reqtermtimeout, 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', [], true);
@@ -614,7 +612,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
'fpmreqslow' => $fpm_reqslowtimeout, 'fpmreqslow' => $fpm_reqslowtimeout,
'phpsettings' => $phpsettings, 'phpsettings' => $phpsettings,
'fpmsettingid' => $fpm_config_id, 'fpmsettingid' => $fpm_config_id,
'fpmpassauth' => $fpm_pass_authorizationheader, 'fpmpassauth' => $pass_authorizationheader,
'ofc' => $override_fpmconfig, 'ofc' => $override_fpmconfig,
'pm' => $pmanager, 'pm' => $pmanager,
'max_children' => $max_children, 'max_children' => $max_children,

View File

@@ -296,21 +296,24 @@ class SubDomains extends ApiCommand implements ResourceEntity
// assign default config // assign default config
$phpsid_result['phpsettingid'] = 1; $phpsid_result['phpsettingid'] = 1;
} }
// check whether the customer has chosen its own php-config
if ($phpsettingid > 0 && $phpsettingid != $phpsid_result['phpsettingid']) {
$phpsid_result['phpsettingid'] = intval($phpsettingid);
}
$allowed_phpconfigs = $customer['allowed_phpconfigs']; if ($domain_check['phpenabled'] == 1) {
if (!empty($allowed_phpconfigs)) { // check whether the customer has chosen its own php-config
$allowed_phpconfigs = json_decode($allowed_phpconfigs, true); if ($phpsettingid > 0 && $phpsettingid != $phpsid_result['phpsettingid']) {
} else { $phpsid_result['phpsettingid'] = intval($phpsettingid);
$allowed_phpconfigs = []; }
}
// only with fcgid/fpm enabled will it be possible to select a php-setting $allowed_phpconfigs = $customer['allowed_phpconfigs'];
if ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) { if (!empty($allowed_phpconfigs)) {
if (!in_array($phpsid_result['phpsettingid'], $allowed_phpconfigs)) { $allowed_phpconfigs = json_decode($allowed_phpconfigs, true);
Response::standardError('notallowedphpconfigused', '', true); } else {
$allowed_phpconfigs = [];
}
// only with fcgid/fpm enabled will it be possible to select a php-setting
if ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {
if (!in_array($phpsid_result['phpsettingid'], $allowed_phpconfigs)) {
Response::standardError('notallowedphpconfigused', '', true);
}
} }
} }
@@ -797,7 +800,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
$allowed_phpconfigs = []; $allowed_phpconfigs = [];
} }
// only with fcgid/fpm enabled will it be possible to select a php-setting // only with fcgid/fpm enabled will it be possible to select a php-setting
if ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) { if ((int)$result['phpenabled'] == 1 && ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1)) {
if (!in_array($phpsettingid, $allowed_phpconfigs)) { if (!in_array($phpsettingid, $allowed_phpconfigs)) {
Response::standardError('notallowedphpconfigused', '', true); Response::standardError('notallowedphpconfigused', '', true);
} }
@@ -980,9 +983,11 @@ class SubDomains extends ApiCommand implements ResourceEntity
'`d`.`letsencrypt`', '`d`.`letsencrypt`',
'`d`.`registration_date`', '`d`.`registration_date`',
'`d`.`termination_date`', '`d`.`termination_date`',
'`d`.`deactivated`' '`d`.`deactivated`',
'`d`.`email_only`',
]; ];
} }
$query_fields = []; $query_fields = [];
// prepare select statement // prepare select statement
@@ -993,7 +998,6 @@ class SubDomains extends ApiCommand implements ResourceEntity
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `da` ON `da`.`aliasdomain`=`d`.`id` LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `da` ON `da`.`aliasdomain`=`d`.`id`
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `pd` ON `pd`.`id`=`d`.`parentdomainid` LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `pd` ON `pd`.`id`=`d`.`parentdomainid`
WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ") WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ")
AND `d`.`email_only` = '0'
" . $this->getSearchWhere($query_fields, true) . " GROUP BY `d`.`id` ORDER BY `parentdomainname` ASC, `d`.`parentdomainid` ASC " . $this->getOrderBy(true) . $this->getLimit()); " . $this->getSearchWhere($query_fields, true) . " GROUP BY `d`.`id` ORDER BY `parentdomainname` ASC, `d`.`parentdomainid` ASC " . $this->getOrderBy(true) . $this->getLimit());
$result = []; $result = [];
@@ -1089,13 +1093,13 @@ class SubDomains extends ApiCommand implements ResourceEntity
$this->getUserDetail('customerid') $this->getUserDetail('customerid')
]; ];
} }
if (!empty($customer_ids)) { if (!empty($customer_ids)) {
// prepare select statement // prepare select statement
$domains_stmt = Database::prepare(" $domains_stmt = Database::prepare("
SELECT COUNT(*) as num_subdom SELECT COUNT(*) as num_subdom
FROM `" . TABLE_PANEL_DOMAINS . "` `d` FROM `" . TABLE_PANEL_DOMAINS . "` `d`
WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ") WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ")
AND `d`.`email_only` = '0'
"); ");
$result = Database::pexecute_first($domains_stmt, null, true, true); $result = Database::pexecute_first($domains_stmt, null, true, true);
if ($result) { if ($result) {

View File

@@ -90,6 +90,8 @@ class SysLog extends ApiCommand implements ResourceEntity
} }
Database::pexecute($result_stmt, $query_fields, true, true); Database::pexecute($result_stmt, $query_fields, true, true);
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
// clean log-text
$row['text'] = preg_replace("/[^\w @#\"':.,()\[\]+\-_\/\\\!]/i", "_", $row['text']);
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list log-entries"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list log-entries");

View File

@@ -43,11 +43,10 @@ final class ConfigServices extends CliCommand
{ {
private $yes_to_all_supported = [ private $yes_to_all_supported = [
'bookworm', 'bookworm',
'bionic',
'bullseye', 'bullseye',
'buster',
'focal', 'focal',
'jammy', 'jammy',
'noble',
]; ];
protected function configure() protected function configure()
@@ -172,8 +171,8 @@ final class ConfigServices extends CliCommand
$distributions_select_data = []; $distributions_select_data = [];
//set default os. //set default os.
$os_dist = ['ID' => 'bullseye']; $os_dist = ['ID' => 'bookworm'];
$os_version = ['0' => '11']; $os_version = ['0' => '12'];
$os_default = $os_dist['ID']; $os_default = $os_dist['ID'];
//read os-release //read os-release
@@ -402,7 +401,7 @@ final class ConfigServices extends CliCommand
case "file": case "file":
if (array_key_exists('content', $action)) { if (array_key_exists('content', $action)) {
$output->writeln('<comment>Creating file "' . $action['name'] . '"</>'); $output->writeln('<comment>Creating file "' . $action['name'] . '"</>');
file_put_contents($action['name'], trim(strtr($action['content'], $replace_arr))); file_put_contents($action['name'], trim(strtr($action['content'], $replace_arr)) . PHP_EOL);
} elseif (array_key_exists('subcommands', $action)) { } elseif (array_key_exists('subcommands', $action)) {
foreach ($action['subcommands'] as $fileaction) { foreach ($action['subcommands'] as $fileaction) {
if (array_key_exists('execute', $fileaction) && $fileaction['execute'] == "pre") { if (array_key_exists('execute', $fileaction) && $fileaction['execute'] == "pre") {
@@ -411,7 +410,7 @@ final class ConfigServices extends CliCommand
exec(strtr($fileaction['content'], $replace_arr)); exec(strtr($fileaction['content'], $replace_arr));
} elseif ($fileaction['type'] == 'file') { } elseif ($fileaction['type'] == 'file') {
$output->writeln('<comment>Creating file "' . $fileaction['name'] . '"</>'); $output->writeln('<comment>Creating file "' . $fileaction['name'] . '"</>');
file_put_contents($fileaction['name'], trim(strtr($fileaction['content'], $replace_arr))); file_put_contents($fileaction['name'], trim(strtr($fileaction['content'], $replace_arr)) . PHP_EOL);
} }
} }
} }
@@ -514,6 +513,7 @@ final class ConfigServices extends CliCommand
'<WEBSERVER_GROUP>' => Settings::Get('system.httpgroup'), '<WEBSERVER_GROUP>' => Settings::Get('system.httpgroup'),
'<SSL_CERT_FILE>' => Settings::Get('system.ssl_cert_file'), '<SSL_CERT_FILE>' => Settings::Get('system.ssl_cert_file'),
'<SSL_KEY_FILE>' => Settings::Get('system.ssl_key_file'), '<SSL_KEY_FILE>' => Settings::Get('system.ssl_key_file'),
'<ADMIN_MAIL>' => Settings::Get('panel.adminmail'),
]; ];
} }
} }

View File

@@ -27,10 +27,13 @@ namespace Froxlor\Cli;
use Exception; use Exception;
use Froxlor\Config\ConfigParser; use Froxlor\Config\ConfigParser;
use Froxlor\Database\Database;
use Froxlor\Froxlor; use Froxlor\Froxlor;
use Froxlor\Install\Install; use Froxlor\Install\Install;
use Froxlor\Install\Install\Core; use Froxlor\Install\Install\Core;
use Froxlor\Settings;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@@ -50,7 +53,8 @@ final class InstallCommand extends Command
$this->setDescription('Installation process to use instead of web-ui'); $this->setDescription('Installation process to use instead of web-ui');
$this->addArgument('input-file', InputArgument::OPTIONAL, 'Optional JSON array file to use for unattended installations'); $this->addArgument('input-file', InputArgument::OPTIONAL, 'Optional JSON array file to use for unattended installations');
$this->addOption('print-example-file', 'p', InputOption::VALUE_NONE, 'Outputs an example JSON content to be used with the input file parameter') $this->addOption('print-example-file', 'p', InputOption::VALUE_NONE, 'Outputs an example JSON content to be used with the input file parameter')
->addOption('create-userdata-from-str', 'c', InputOption::VALUE_REQUIRED, 'Creates lib/userdata.inc.php file from string created by web-install process'); ->addOption('create-userdata-from-str', 'c', InputOption::VALUE_REQUIRED, 'Creates lib/userdata.inc.php file from string created by web-install process')
->addOption('show-sysinfo', 's', InputOption::VALUE_NONE, 'Outputs system information about your froxlor installation');
} }
/** /**
@@ -72,6 +76,15 @@ final class InstallCommand extends Command
return self::INVALID; return self::INVALID;
} }
if ($input->getOption('show-sysinfo') !== false) {
if (!file_exists(Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {
$output->writeln("<error>Could not find froxlor's userdata.inc.php file. You can use this parameter only with an installed froxlor system.</>");
return self::INVALID;
}
$this->printSysInfo($output);
return self::SUCCESS;
}
session_start(); session_start();
require __DIR__ . '/install.functions.php'; require __DIR__ . '/install.functions.php';
@@ -211,7 +224,7 @@ final class InstallCommand extends Command
$ask_field = false; $ask_field = false;
} }
$fielddata['value'] = $this->formfielddata[$fieldname] ?? ($fielddata['value'] ?? null); $fielddata['value'] = $this->formfielddata[$fieldname] ?? ($fielddata['value'] ?? null);
$fielddata['label'] = strip_tags(str_replace("<br>", " ", $fielddata['label'])); $fielddata['label'] = $this->cliTextFormat($fielddata['label'], " ");
if ($ask_field) { if ($ask_field) {
if ($fielddata['type'] == 'password') { if ($fielddata['type'] == 'password') {
$this->formfielddata[$fieldname] = $this->io->askHidden($fielddata['label'], function ($value) use ($fielddata) { $this->formfielddata[$fieldname] = $this->io->askHidden($fielddata['label'], function ($value) use ($fielddata) {
@@ -267,14 +280,16 @@ final class InstallCommand extends Command
case 4: case 4:
$section = $inst->formfield['install']['sections']['step' . $step] ?? []; $section = $inst->formfield['install']['sections']['step' . $step] ?? [];
$this->io->section($section['title']); $this->io->section($section['title']);
$this->io->note($section['description']); $this->io->note($this->cliTextFormat($section['description']));
$cmdfield = $section['fields']['system']; $cmdfield = $section['fields']['system'];
$this->io->success([ $this->io->success([
$cmdfield['label'], $cmdfield['label'],
$cmdfield['value'] $cmdfield['value']
]); ]);
if (!empty($decoded_input) || $this->io->confirm('Execute command now?', false)) { if (!isset($decoded_input['manual_config']) || (bool)$decoded_input['manual_config'] === false) {
passthru($cmdfield['value']); if (!empty($decoded_input) || $this->io->confirm('Execute command now?', false)) {
passthru($cmdfield['value']);
}
} }
break; break;
} }
@@ -305,7 +320,7 @@ final class InstallCommand extends Command
$json_output = []; $json_output = [];
foreach ($fields['install']['sections'] as $section => $section_fields) { foreach ($fields['install']['sections'] as $section => $section_fields) {
foreach ($section_fields['fields'] as $name => $field) { foreach ($section_fields['fields'] as $name => $field) {
if ($name == 'system' || $name == 'manual_config' || $name == 'target_servername') { if ($name == 'system' || $name == 'target_servername') {
continue; continue;
} }
if ($field['type'] == 'text' || $field['type'] == 'email') { if ($field['type'] == 'text' || $field['type'] == 'email') {
@@ -318,7 +333,7 @@ final class InstallCommand extends Command
$fieldval = '******'; $fieldval = '******';
} elseif ($field['type'] == 'select') { } elseif ($field['type'] == 'select') {
$fieldval = implode("|", array_keys($field['select_var'])); $fieldval = implode("|", array_keys($field['select_var']));
} else if ($field['type'] == 'checkbox') { } elseif ($field['type'] == 'checkbox') {
$fieldval = "1|0"; $fieldval = "1|0";
} else { } else {
$fieldval = "?"; $fieldval = "?";
@@ -346,4 +361,61 @@ final class InstallCommand extends Command
curl_close($ch); curl_close($ch);
fclose($fp); fclose($fp);
} }
private function printSysInfo(OutputInterface $output)
{
$php_sapi = 'mod_php';
$php_version = phpversion();
if (Settings::Get('system.mod_fcgid') == '1') {
$php_sapi = 'FCGID';
if (Settings::Get('system.mod_fcgid_ownvhost') == '1') {
$php_sapi .= ' (+ froxlor)';
}
} elseif (Settings::Get('phpfpm.enabled') == '1') {
$php_sapi = 'PHP-FPM';
if (Settings::Get('phpfpm.enabled_ownvhost') == '1') {
$php_sapi .= ' (+ froxlor)';
}
}
$kernel = 'unknown';
if (function_exists('posix_uname')) {
$kernel_nfo = posix_uname();
$kernel = $kernel_nfo['release'] . ' (' . $kernel_nfo['machine'] . ')';
}
$ips = [];
$ips_stmt = Database::query("SELECT CONCAT(`ip`, ' (', `port`, ')') as ipaddr FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `id`");
while ($ip = $ips_stmt->fetch(\PDO::FETCH_ASSOC)) {
$ips[] = $ip['ipaddr'];
}
$table = new Table($output);
$table
->setHeaders([
'Key', 'Value'
])
->setRows([
['Froxlor', Froxlor::getVersionString()],
['Update-channel', Settings::Get('system.update_channel')],
['Hostname', Settings::Get('system.hostname')],
['Install-dir', Froxlor::getInstallDir()],
['PHP CLI', $php_version],
['PHP SAPI', $php_sapi],
['Webserver', Settings::Get('system.webserver')],
['Kernel', $kernel],
['Database', Database::getAttribute(\PDO::ATTR_SERVER_VERSION)],
['Distro config', Settings::Get('system.distribution')],
['IP addresses', implode("\n", $ips)],
]);
$table->setStyle('box');
$table->render();
}
private function cliTextFormat(string $text, string $nl_char = "\n"): string
{
$text = str_replace(['<br>', '<br/>', '<br />'], [$nl_char, $nl_char, $nl_char], $text);
return strip_tags($text);
}
} }

View File

@@ -52,7 +52,7 @@ final class MasterCron extends CliCommand
$this->setName('froxlor:cron'); $this->setName('froxlor:cron');
$this->setDescription('Regulary perform tasks created by froxlor'); $this->setDescription('Regulary perform tasks created by froxlor');
$this->addArgument('job', InputArgument::IS_ARRAY, 'Job(s) to run'); $this->addArgument('job', InputArgument::IS_ARRAY, 'Job(s) to run');
$this->addOption('run-task', 'r', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Run a specific task [1 = re-generate configs, 4 = re-generate dns zones, 10 = re-set quotas, 99 = re-create cron.d-file]') $this->addOption('run-task', 'r', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Run a specific task [1 = re-generate configs, 4 = re-generate dns zones, 9 = re-generate rspamd configs, 10 = re-set quotas, 99 = re-create cron.d-file]')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces given job or, if none given, forces re-generating of config-files (webserver, nameserver, etc.)') ->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces given job or, if none given, forces re-generating of config-files (webserver, nameserver, etc.)')
->addOption('debug', 'd', InputOption::VALUE_NONE, 'Output debug information about what is going on to STDOUT.') ->addOption('debug', 'd', InputOption::VALUE_NONE, 'Output debug information about what is going on to STDOUT.')
->addOption('no-fork', 'N', InputOption::VALUE_NONE, 'Do not fork to background (traffic cron only).'); ->addOption('no-fork', 'N', InputOption::VALUE_NONE, 'Do not fork to background (traffic cron only).');
@@ -77,6 +77,7 @@ final class MasterCron extends CliCommand
if (empty($jobs) || in_array('tasks', $jobs)) { if (empty($jobs) || in_array('tasks', $jobs)) {
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
Cronjob::inserttask(TaskId::REBUILD_DNS); Cronjob::inserttask(TaskId::REBUILD_DNS);
Cronjob::inserttask(TaskId::REBUILD_RSPAMD);
Cronjob::inserttask(TaskId::CREATE_QUOTA); Cronjob::inserttask(TaskId::CREATE_QUOTA);
Cronjob::inserttask(TaskId::REBUILD_CRON); Cronjob::inserttask(TaskId::REBUILD_CRON);
$jobs[] = 'tasks'; $jobs[] = 'tasks';
@@ -95,7 +96,7 @@ final class MasterCron extends CliCommand
if ($input->getOption('run-task')) { if ($input->getOption('run-task')) {
$tasks_to_run = $input->getOption('run-task'); $tasks_to_run = $input->getOption('run-task');
foreach ($tasks_to_run as $ttr) { foreach ($tasks_to_run as $ttr) {
if (in_array($ttr, [TaskId::REBUILD_VHOST, TaskId::REBUILD_DNS, TaskId::CREATE_QUOTA, TaskId::REBUILD_CRON])) { if (in_array($ttr, [TaskId::REBUILD_VHOST, TaskId::REBUILD_DNS, TaskId::REBUILD_RSPAMD, TaskId::CREATE_QUOTA, TaskId::REBUILD_CRON])) {
Cronjob::inserttask($ttr); Cronjob::inserttask($ttr);
$jobs[] = 'tasks'; $jobs[] = 'tasks';
} else { } else {
@@ -170,8 +171,9 @@ final class MasterCron extends CliCommand
FroxlorLogger::getInstanceOf()->setCronLog(0); FroxlorLogger::getInstanceOf()->setCronLog(0);
} }
// clean up possible old login-links // clean up possible old login-links and 2fa tokens
Database::query("DELETE FROM `" . TABLE_PANEL_LOGINLINKS . "` WHERE `valid_until` < UNIX_TIMESTAMP()"); Database::query("DELETE FROM `" . TABLE_PANEL_LOGINLINKS . "` WHERE `valid_until` < UNIX_TIMESTAMP()");
Database::query("DELETE FROM `" . TABLE_PANEL_2FA_TOKENS . "` WHERE `valid_until` < UNIX_TIMESTAMP()");
return $result; return $result;
} }
@@ -263,6 +265,8 @@ final class MasterCron extends CliCommand
if ($jobcount > 0) { if ($jobcount > 0) {
if (Settings::Get('system.nssextrausers') == 1) { if (Settings::Get('system.nssextrausers') == 1) {
Extrausers::generateFiles($this->cronLog); Extrausers::generateFiles($this->cronLog);
// reload crond as shell users might use crontab and the user is only known to crond if reloaded
FileDir::safe_exec(escapeshellcmd(Settings::Get('system.crondreload')));
return; return;
} }
@@ -275,6 +279,8 @@ final class MasterCron extends CliCommand
FileDir::safe_exec('nscd -i group 1> /dev/null', $false_val, [ FileDir::safe_exec('nscd -i group 1> /dev/null', $false_val, [
'>' '>'
]); ]);
// reload crond as shell users might use crontab and the user is only known to crond if reloaded
FileDir::safe_exec(escapeshellcmd(Settings::Get('system.crondreload')));
} }
} }
} }

View File

@@ -58,8 +58,9 @@ final class UpdateCommand extends CliCommand
if ($input->getOption('database')) { if ($input->getOption('database')) {
$result = $this->validateRequirements($output, true); $result = $this->validateRequirements($output, true);
if ($result == self::SUCCESS) { if ($result == self::SUCCESS) {
require Froxlor::getInstallDir() . '/lib/functions.php';
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) { if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
$output->writeln('<info>' . lng('updates.dbupdate_required') . '</>'); $output->writeln('<info>' . lng('update.dbupdate_required') . '</>');
if ($input->getOption('check-only')) { if ($input->getOption('check-only')) {
$output->writeln('<comment>Doing nothing because of "check-only" flag.</>'); $output->writeln('<comment>Doing nothing because of "check-only" flag.</>');
} else { } else {
@@ -110,7 +111,7 @@ final class UpdateCommand extends CliCommand
$newversionavail = true; $newversionavail = true;
$output->writeln('<comment>' . $text . '</>'); $output->writeln('<comment>' . $text . '</>');
$result = self::SUCCESS; $result = self::SUCCESS;
} else if ($aucheck < 0 || $aucheck > 1) { } elseif ($aucheck < 0 || $aucheck > 1) {
if ($input->getOption('integer-return')) { if ($input->getOption('integer-return')) {
$output->write(-1); $output->write(-1);
return self::INVALID; return self::INVALID;

View File

@@ -91,6 +91,9 @@ class ConfigDaemon
$this->fullxml = $xml; $this->fullxml = $xml;
$this->xpath = $xpath; $this->xpath = $xpath;
$this->daemon = $this->fullxml->xpath($this->xpath); $this->daemon = $this->fullxml->xpath($this->xpath);
if (count($this->daemon) !== 1) {
throw new Exception('XPath "' . $this->xpath . '" didn\'t return exactly one element');
}
$attributes = $this->daemon[0]->attributes(); $attributes = $this->daemon[0]->attributes();
if ($attributes['title'] != '') { if ($attributes['title'] != '') {
$this->title = $this->parseContent((string)$attributes['title']); $this->title = $this->parseContent((string)$attributes['title']);
@@ -409,7 +412,7 @@ class ConfigDaemon
} }
$return[] = [ $return[] = [
'type' => 'command', 'type' => 'command',
'content' => $cmd . ' "' . $this->parseContent($attributes['name']) . '" "' . $this->parseContent($attributes['name']) . '.frx.bak"', 'content' => '[ -f ' . $this->parseContent($attributes['name']) . ' ] && ' . $cmd . ' "' . $this->parseContent($attributes['name']) . '" "' . $this->parseContent($attributes['name']) . '.frx.bak"',
'execute' => "pre" 'execute' => "pre"
]; ];
} }

View File

@@ -117,7 +117,7 @@ class ConfigDisplay
'<SQL_UNPRIVILEGED_PASSWORD>' => 'FROXLOR_MYSQL_PASSWORD', '<SQL_UNPRIVILEGED_PASSWORD>' => 'FROXLOR_MYSQL_PASSWORD',
'<SQL_DB>' => $sql['db'], '<SQL_DB>' => $sql['db'],
'<SQL_HOST>' => $sql['host'], '<SQL_HOST>' => $sql['host'],
'<SQL_SOCKET>' => isset($sql['socket']) ? $sql['socket'] : null, '<SQL_SOCKET>' => $sql['socket'] ?? null,
'<SERVERNAME>' => Settings::Get('system.hostname'), '<SERVERNAME>' => Settings::Get('system.hostname'),
'<SERVERIP>' => Settings::Get('system.ipaddress'), '<SERVERIP>' => Settings::Get('system.ipaddress'),
'<NAMESERVERS>' => Settings::Get('system.nameservers'), '<NAMESERVERS>' => Settings::Get('system.nameservers'),
@@ -127,12 +127,15 @@ class ConfigDisplay
'<VIRTUAL_GID_MAPS>' => Settings::Get('system.vmail_gid'), '<VIRTUAL_GID_MAPS>' => Settings::Get('system.vmail_gid'),
'<SSLPROTOCOLS>' => (Settings::Get('system.use_ssl') == '1') ? 'imaps pop3s' : '', '<SSLPROTOCOLS>' => (Settings::Get('system.use_ssl') == '1') ? 'imaps pop3s' : '',
'<CUSTOMER_TMP>' => FileDir::makeCorrectDir($customer_tmpdir), '<CUSTOMER_TMP>' => FileDir::makeCorrectDir($customer_tmpdir),
'<BASE_PATH>' => FileDir::makeCorrectDir(Froxlor::getInstallDir()), '<BASE_PATH>' => Froxlor::getInstallDir(),
'<BIND_CONFIG_PATH>' => FileDir::makeCorrectDir(Settings::Get('system.bindconf_directory')), '<BIND_CONFIG_PATH>' => FileDir::makeCorrectDir(Settings::Get('system.bindconf_directory')),
'<WEBSERVER_RELOAD_CMD>' => Settings::Get('system.apachereload_command'), '<WEBSERVER_RELOAD_CMD>' => Settings::Get('system.apachereload_command'),
'<CUSTOMER_LOGS>' => FileDir::makeCorrectDir(Settings::Get('system.logfiles_directory')), '<CUSTOMER_LOGS>' => FileDir::makeCorrectDir(Settings::Get('system.logfiles_directory')),
'<FPM_IPCDIR>' => FileDir::makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir')), '<FPM_IPCDIR>' => FileDir::makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir')),
'<WEBSERVER_GROUP>' => Settings::Get('system.httpgroup') '<WEBSERVER_GROUP>' => Settings::Get('system.httpgroup'),
'<SSL_CERT_FILE>' => Settings::Get('system.ssl_cert_file'),
'<SSL_KEY_FILE>' => Settings::Get('system.ssl_key_file'),
'<ADMIN_MAIL>' => Settings::Get('panel.adminmail'),
]; ];
$commands_pre = ""; $commands_pre = "";

View File

@@ -55,18 +55,17 @@ class Bind extends DnsBase
$domains = $this->getDomainList(); $domains = $this->getDomainList();
if (empty($domains)) { if (empty($domains)) {
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); $this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, not creating any zones...');
return; $this->bindconf_file = '';
} } else {
$this->bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n";
$this->bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n"; foreach ($domains as $domain) {
if ($domain['is_child']) {
foreach ($domains as $domain) { // domains that are subdomains to other main domains are handled by recursion within walkDomainList()
if ($domain['is_child']) { continue;
// domains that are subdomains to other main domains are handled by recursion within walkDomainList() }
continue; $this->walkDomainList($domain, $domains);
} }
$this->walkDomainList($domain, $domains);
} }
$bindconf_file_handler = fopen(FileDir::makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); $bindconf_file_handler = fopen(FileDir::makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w');

View File

@@ -117,85 +117,6 @@ abstract class DnsBase
} }
} }
public function writeDKIMconfigs()
{
if (Settings::Get('dkim.use_dkim') == '1') {
if (!file_exists(FileDir::makeCorrectDir(Settings::Get('dkim.dkim_prefix')))) {
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('dkim.dkim_prefix'))));
FileDir::safe_exec('mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir(Settings::Get('dkim.dkim_prefix'))));
}
$dkimdomains = '';
$dkimkeys = '';
$result_domains_stmt = Database::query("
SELECT `id`, `domain`, `dkim`, `dkim_id`, `dkim_pubkey`, `dkim_privkey`
FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `dkim` = '1' ORDER BY `id` ASC
");
while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {
$privkey_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim' . $domain['dkim_id'] . Settings::Get('dkim.privkeysuffix'));
$pubkey_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim' . $domain['dkim_id'] . '.public');
if ($domain['dkim_privkey'] == '' || $domain['dkim_pubkey'] == '') {
$max_dkim_id_stmt = Database::query("SELECT MAX(`dkim_id`) as `max_dkim_id` FROM `" . TABLE_PANEL_DOMAINS . "`");
$max_dkim_id = $max_dkim_id_stmt->fetch(PDO::FETCH_ASSOC);
$domain['dkim_id'] = (int)$max_dkim_id['max_dkim_id'] + 1;
$privkey_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim' . $domain['dkim_id'] . Settings::Get('dkim.privkeysuffix'));
FileDir::safe_exec('openssl genrsa -out ' . escapeshellarg($privkey_filename) . ' ' . Settings::Get('dkim.dkim_keylength'));
$domain['dkim_privkey'] = file_get_contents($privkey_filename);
FileDir::safe_exec("chmod 0640 " . escapeshellarg($privkey_filename));
$pubkey_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim' . $domain['dkim_id'] . '.public');
FileDir::safe_exec('openssl rsa -in ' . escapeshellarg($privkey_filename) . ' -pubout -outform pem -out ' . escapeshellarg($pubkey_filename));
$domain['dkim_pubkey'] = file_get_contents($pubkey_filename);
FileDir::safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename));
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_DOMAINS . "` SET
`dkim_id` = :dkimid,
`dkim_privkey` = :privkey,
`dkim_pubkey` = :pubkey
WHERE `id` = :id
");
$upd_data = [
'dkimid' => $domain['dkim_id'],
'privkey' => $domain['dkim_privkey'],
'pubkey' => $domain['dkim_pubkey'],
'id' => $domain['id']
];
Database::pexecute($upd_stmt, $upd_data);
}
if (!file_exists($privkey_filename) && $domain['dkim_privkey'] != '') {
$privkey_file_handler = fopen($privkey_filename, "w");
fwrite($privkey_file_handler, $domain['dkim_privkey']);
fclose($privkey_file_handler);
FileDir::safe_exec("chmod 0640 " . escapeshellarg($privkey_filename));
}
if (!file_exists($pubkey_filename) && $domain['dkim_pubkey'] != '') {
$pubkey_file_handler = fopen($pubkey_filename, "w");
fwrite($pubkey_file_handler, $domain['dkim_pubkey']);
fclose($pubkey_file_handler);
FileDir::safe_exec("chmod 0644 " . escapeshellarg($pubkey_filename));
}
$dkimdomains .= $domain['domain'] . "\n";
$dkimkeys .= "*@" . $domain['domain'] . ":" . $domain['domain'] . ":" . $privkey_filename . "\n";
}
$dkimdomains_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/' . Settings::Get('dkim.dkim_domains'));
$dkimdomains_file_handler = fopen($dkimdomains_filename, "w");
fwrite($dkimdomains_file_handler, $dkimdomains);
fclose($dkimdomains_file_handler);
$dkimkeys_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/' . Settings::Get('dkim.dkim_dkimkeys'));
$dkimkeys_file_handler = fopen($dkimkeys_filename, "w");
fwrite($dkimkeys_file_handler, $dkimkeys);
fclose($dkimkeys_file_handler);
FileDir::safe_exec(escapeshellcmd(Settings::Get('dkim.dkimrestart_command')));
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Dkim-milter reloaded');
}
}
protected function getDomainList() protected function getDomainList()
{ {
$result_domains_stmt = Database::query(" $result_domains_stmt = Database::query("

View File

@@ -45,18 +45,16 @@ class PowerDNS extends DnsBase
$this->clearZoneTables($domains); $this->clearZoneTables($domains);
if (empty($domains)) { if (empty($domains)) {
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); $this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, not creating any zones...');
return; } else {
} foreach ($domains as $domain) {
if ($domain['is_child']) {
foreach ($domains as $domain) { // domains that are subdomains to other main domains are handled by recursion within walkDomainList()
if ($domain['is_child']) { continue;
// domains that are subdomains to other main domains are handled by recursion within walkDomainList() }
continue; $this->walkDomainList($domain, $domains);
} }
$this->walkDomainList($domain, $domains);
} }
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'PowerDNS database updated'); $this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'PowerDNS database updated');
$this->reloadDaemon(); $this->reloadDaemon();
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Task4 finished'); $this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Task4 finished');

View File

@@ -25,19 +25,21 @@
namespace Froxlor\Cron\Http; namespace Froxlor\Cron\Http;
use Froxlor\Froxlor;
use Froxlor\Cron\Http\Php\PhpInterface; use Froxlor\Cron\Http\Php\PhpInterface;
use Froxlor\Cron\TaskId;
use Froxlor\Customer\Customer; use Froxlor\Customer\Customer;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Domain\Domain; use Froxlor\Domain\Domain;
use Froxlor\FileDir; use Froxlor\FileDir;
use Froxlor\Froxlor;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\Http\Directory; use Froxlor\Http\Directory;
use Froxlor\Http\Statistics; use Froxlor\Http\Statistics;
use Froxlor\PhpHelper; use Froxlor\PhpHelper;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\Validate\Validate; use Froxlor\System\Cronjob;
use Froxlor\System\Crypt; use Froxlor\System\Crypt;
use Froxlor\Validate\Validate;
use PDO; use PDO;
class Apache extends HttpConfigBase class Apache extends HttpConfigBase
@@ -133,6 +135,7 @@ class Apache extends HttpConfigBase
if (Settings::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) { if (Settings::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) {
$this->virtualhosts_data[$vhosts_filename] .= '# temp. disabled ssl-redirect due to Let\'s Encrypt certificate generation.' . PHP_EOL; $this->virtualhosts_data[$vhosts_filename] .= '# temp. disabled ssl-redirect due to Let\'s Encrypt certificate generation.' . PHP_EOL;
$is_redirect = false; $is_redirect = false;
Cronjob::inserttask(TaskId::REBUILD_VHOST);
} else { } else {
$_sslport = $this->checkAlternativeSslPort(); $_sslport = $this->checkAlternativeSslPort();
@@ -159,7 +162,7 @@ class Apache extends HttpConfigBase
if (Settings::Get('system.froxlordirectlyviahostname')) { if (Settings::Get('system.froxlordirectlyviahostname')) {
$relpath = "/"; $relpath = "/";
} else { } else {
$relpath = "/".basename(Froxlor::getInstallDir()); $relpath = "/" . basename(Froxlor::getInstallDir());
} }
// protect lib/userdata.inc.php // protect lib/userdata.inc.php
$this->virtualhosts_data[$vhosts_filename] .= ' <Directory "' . rtrim($relpath, "/") . '/lib/">' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' <Directory "' . rtrim($relpath, "/") . '/lib/">' . "\n";
@@ -205,7 +208,9 @@ class Apache extends HttpConfigBase
]; ];
$php = new PhpInterface($domain); $php = new PhpInterface($domain);
$phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost')); $phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost'));
if ($phpconfig['pass_authorizationheader'] == '1') {
$this->virtualhosts_data[$vhosts_filename] .= ' FcgidPassHeader Authorization' . "\n";
}
$starter_filename = FileDir::makeCorrectFile($configdir . '/php-fcgi-starter'); $starter_filename = FileDir::makeCorrectFile($configdir . '/php-fcgi-starter');
$this->virtualhosts_data[$vhosts_filename] .= ' SuexecUserGroup "' . Settings::Get('system.mod_fcgid_httpuser') . '" "' . Settings::Get('system.mod_fcgid_httpgroup') . '"' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SuexecUserGroup "' . Settings::Get('system.mod_fcgid_httpuser') . '" "' . Settings::Get('system.mod_fcgid_httpgroup') . '"' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' <Directory "' . $mypath . '">' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' <Directory "' . $mypath . '">' . "\n";
@@ -276,7 +281,9 @@ class Apache extends HttpConfigBase
// start block, cut off last pipe and close block // start block, cut off last pipe and close block
$filesmatch = '(' . str_replace(".", "\.", substr($filesmatch, 0, -1)) . ')'; $filesmatch = '(' . str_replace(".", "\.", substr($filesmatch, 0, -1)) . ')';
$this->virtualhosts_data[$vhosts_filename] .= ' <FilesMatch \.' . $filesmatch . '$>' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' <FilesMatch \.' . $filesmatch . '$>' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' <If "-f %{SCRIPT_FILENAME}">' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' </If>' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' </FilesMatch>' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' </FilesMatch>' . "\n";
if ($phpconfig['pass_authorizationheader'] == '1') { if ($phpconfig['pass_authorizationheader'] == '1') {
$this->virtualhosts_data[$vhosts_filename] .= ' <Directory "' . $mypath . '">' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' <Directory "' . $mypath . '">' . "\n";
@@ -816,6 +823,7 @@ class Apache extends HttpConfigBase
$modrew_red = ' [R=' . $code . ';L,NE]'; $modrew_red = ' [R=' . $code . ';L,NE]';
} }
$vhost_content .= $this->getLogfiles($domain);
// redirect everything, not only root-directory, #541 // redirect everything, not only root-directory, #541
$vhost_content .= ' <IfModule mod_rewrite.c>' . "\n"; $vhost_content .= ' <IfModule mod_rewrite.c>' . "\n";
$vhost_content .= ' RewriteEngine On' . "\n"; $vhost_content .= ' RewriteEngine On' . "\n";
@@ -842,24 +850,26 @@ class Apache extends HttpConfigBase
} }
$vhost_content .= $this->getLogfiles($domain); $vhost_content .= $this->getLogfiles($domain);
if ($domain['specialsettings'] != '' && ($ssl_vhost == false || ($ssl_vhost == true && $domain['include_specialsettings'] == 1))) { if ($this->deactivated == false) {
$vhost_content .= $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; if ($domain['specialsettings'] != '' && ($ssl_vhost == false || ($ssl_vhost == true && $domain['include_specialsettings'] == 1))) {
} $vhost_content .= $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
}
if ($domain['ssl_specialsettings'] != '' && $ssl_vhost == true) { if ($domain['ssl_specialsettings'] != '' && $ssl_vhost == true) {
$vhost_content .= $this->processSpecialConfigTemplate($domain['ssl_specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; $vhost_content .= $this->processSpecialConfigTemplate($domain['ssl_specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
} }
if ($_vhost_content != '') { if ($_vhost_content != '') {
$vhost_content .= $_vhost_content; $vhost_content .= $_vhost_content;
} }
if (Settings::Get('system.default_vhostconf') != '' && ($ssl_vhost == false || ($ssl_vhost == true && Settings::Get('system.include_default_vhostconf') == 1))) { if (Settings::Get('system.default_vhostconf') != '' && ($ssl_vhost == false || ($ssl_vhost == true && Settings::Get('system.include_default_vhostconf') == 1))) {
$vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; $vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
} }
if (Settings::Get('system.default_sslvhostconf') != '' && $ssl_vhost == true) { if (Settings::Get('system.default_sslvhostconf') != '' && $ssl_vhost == true) {
$vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_sslvhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; $vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_sslvhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
}
} }
} }
@@ -964,8 +974,8 @@ class Apache extends HttpConfigBase
if ($domain['openbasedir'] == '1') { if ($domain['openbasedir'] == '1') {
if ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], ":") !== false) { if ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], ":") !== false) {
$_phpappendopenbasedir = Domain::appendOpenBasedirPath($domain['customerroot'], true); $_phpappendopenbasedir = Domain::appendOpenBasedirPath($domain['customerroot'], true);
} else if ($domain['openbasedir_path'] == '2' && strpos(dirname($domain['documentroot']).'/', $domain['customerroot']) !== false) { } else if ($domain['openbasedir_path'] == '2' && strpos(dirname($domain['documentroot']) . '/', $domain['customerroot']) !== false) {
$_phpappendopenbasedir = Domain::appendOpenBasedirPath(dirname($domain['documentroot']).'/', true); $_phpappendopenbasedir = Domain::appendOpenBasedirPath(dirname($domain['documentroot']) . '/', true);
} else { } else {
$_phpappendopenbasedir = Domain::appendOpenBasedirPath($domain['documentroot'], true); $_phpappendopenbasedir = Domain::appendOpenBasedirPath($domain['documentroot'], true);
} }
@@ -1013,10 +1023,10 @@ class Apache extends HttpConfigBase
} }
$statDocroot = FileDir::makeCorrectFile($domain['customerroot'] . '/' . $statTool . $statDomain); $statDocroot = FileDir::makeCorrectFile($domain['customerroot'] . '/' . $statTool . $statDomain);
$stats_text .= ' Alias /'.$statTool.' "' . $statDocroot . '"' . "\n"; $stats_text .= ' Alias /' . $statTool . ' "' . $statDocroot . '"' . "\n";
// awstats special requirement for icons // awstats special requirement for icons
if ($statTool == 'awstats') { if ($statTool == 'awstats') {
$stats_text .= ' Alias /awstats-icon "' . FileDir::makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n"; $stats_text .= ' Alias /awstats-icon "' . FileDir::makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n";
} }
return $stats_text; return $stats_text;

View File

@@ -43,23 +43,29 @@ class DomainSSL
* domain-array as reference so we can set the corresponding array-indices * domain-array as reference so we can set the corresponding array-indices
* *
* @return null * @return null
* @throws \Exception
*/ */
public function setDomainSSLFilesArray(array &$domain = null) public function setDomainSSLFilesArray(array &$domain = null)
{ {
// check if the domain itself has a certificate defined // check if the domain itself has a certificate defined
$dom_certs_stmt = Database::prepare(" $dom_certs_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = :domid SELECT s.*, d.domain
FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON d.id = s.domainid
WHERE s.`domainid` = :domid
"); ");
$dom_certs = Database::pexecute_first($dom_certs_stmt, [ $dom_certs = Database::pexecute_first($dom_certs_stmt, [
'domid' => $domain['id'] 'domid' => $domain['id']
]); ]);
$parent_certificate = false;
if (!is_array($dom_certs) || !isset($dom_certs['ssl_cert_file']) || $dom_certs['ssl_cert_file'] == '') { if (!is_array($dom_certs) || !isset($dom_certs['ssl_cert_file']) || $dom_certs['ssl_cert_file'] == '') {
// maybe its parent? // maybe its parent?
if (isset($domain['parentdomainid']) && $domain['parentdomainid'] != 0) { if (isset($domain['parentdomainid']) && $domain['parentdomainid'] != 0) {
$dom_certs = Database::pexecute_first($dom_certs_stmt, [ $dom_certs = Database::pexecute_first($dom_certs_stmt, [
'domid' => $domain['parentdomainid'] 'domid' => $domain['parentdomainid']
]); ]);
$parent_certificate = true;
} }
} }
@@ -73,8 +79,8 @@ class DomainSSL
} }
// make correct files for the certificates // make correct files for the certificates
$ssl_files = [ $ssl_files = [
'ssl_cert_file' => FileDir::makeCorrectFile($sslcertpath . '/' . $domain['domain'] . '.crt'), 'ssl_cert_file' => FileDir::makeCorrectFile($sslcertpath . '/' . ($parent_certificate ? $dom_certs['domain'] : $domain['domain']) . '.crt'),
'ssl_key_file' => FileDir::makeCorrectFile($sslcertpath . '/' . $domain['domain'] . '.key') 'ssl_key_file' => FileDir::makeCorrectFile($sslcertpath . '/' . ($parent_certificate ? $dom_certs['domain'] : $domain['domain']) . '.key')
]; ];
if (!$this->validateCertificate($dom_certs)) { if (!$this->validateCertificate($dom_certs)) {
@@ -93,19 +99,19 @@ class DomainSSL
$ssl_files['ssl_cert_chainfile'] = ''; $ssl_files['ssl_cert_chainfile'] = '';
// set them if they are != empty // set them if they are != empty
if ($dom_certs['ssl_ca_file'] != '') { if ($dom_certs['ssl_ca_file'] != '') {
$ssl_files['ssl_ca_file'] = FileDir::makeCorrectFile($sslcertpath . '/' . $domain['domain'] . '_CA.pem'); $ssl_files['ssl_ca_file'] = FileDir::makeCorrectFile($sslcertpath . '/' . ($parent_certificate ? $dom_certs['domain'] : $domain['domain']) . '_CA.pem');
} }
if ($dom_certs['ssl_cert_chainfile'] != '') { if ($dom_certs['ssl_cert_chainfile'] != '') {
if (Settings::Get('system.webserver') == 'nginx') { if (Settings::Get('system.webserver') == 'nginx') {
// put ca.crt in my.crt, as nginx does not support a separate chain file. // put ca.crt in my.crt, as nginx does not support a separate chain file.
$dom_certs['ssl_cert_file'] = trim($dom_certs['ssl_cert_file']) . "\n" . trim($dom_certs['ssl_cert_chainfile']) . "\n"; $dom_certs['ssl_cert_file'] = trim($dom_certs['ssl_cert_file']) . "\n" . trim($dom_certs['ssl_cert_chainfile']) . "\n";
} else { } else {
$ssl_files['ssl_cert_chainfile'] = FileDir::makeCorrectFile($sslcertpath . '/' . $domain['domain'] . '_chain.pem'); $ssl_files['ssl_cert_chainfile'] = FileDir::makeCorrectFile($sslcertpath . '/' . ($parent_certificate ? $dom_certs['domain'] : $domain['domain']) . '_chain.pem');
} }
} }
// will only be generated to be used externally, froxlor does not need this // will only be generated to be used externally, froxlor does not need this
if ($dom_certs['ssl_fullchain_file'] != '') { if ($dom_certs['ssl_fullchain_file'] != '') {
$ssl_files['ssl_fullchain_file'] = FileDir::makeCorrectFile($sslcertpath . '/' . $domain['domain'] . '_fullchain.pem'); $ssl_files['ssl_fullchain_file'] = FileDir::makeCorrectFile($sslcertpath . '/' . ($parent_certificate ? $dom_certs['domain'] : $domain['domain']) . '_fullchain.pem');
} }
// create them on the filesystem // create them on the filesystem
foreach ($ssl_files as $type => $filename) { foreach ($ssl_files as $type => $filename) {
@@ -131,7 +137,7 @@ class DomainSSL
return; return;
} }
private function validateCertificate($dom_certs = []) private function validateCertificate($dom_certs = []): bool
{ {
return openssl_x509_check_private_key($dom_certs['ssl_cert_file'], $dom_certs['ssl_key_file']); return openssl_x509_check_private_key($dom_certs['ssl_cert_file'], $dom_certs['ssl_key_file']);
} }

View File

@@ -321,6 +321,7 @@ EOC;
WHERE WHERE
dom.`customerid` = cust.`customerid` dom.`customerid` = cust.`customerid`
AND cust.deactivated = 0 AND cust.deactivated = 0
AND dom.`ssl_enabled` = 1
AND dom.`letsencrypt` = 1 AND dom.`letsencrypt` = 1
AND dom.`aliasdomain` IS NULL AND dom.`aliasdomain` IS NULL
AND dom.`iswildcarddomain` = 0 AND dom.`iswildcarddomain` = 0
@@ -382,6 +383,7 @@ EOC;
WHERE WHERE
dom.`customerid` = cust.`customerid` dom.`customerid` = cust.`customerid`
AND cust.deactivated = 0 AND cust.deactivated = 0
AND dom.`ssl_enabled` = 1
AND dom.`letsencrypt` = 1 AND dom.`letsencrypt` = 1
AND dom.`aliasdomain` IS NULL AND dom.`aliasdomain` IS NULL
AND dom.`iswildcarddomain` = 0 AND dom.`iswildcarddomain` = 0
@@ -518,9 +520,11 @@ EOC;
self::validateDns($domains, $certrow['domainid'], $cronlog); self::validateDns($domains, $certrow['domainid'], $cronlog);
self::runAcmeSh($certrow, $domains, $cronlog, $do_force); self::runAcmeSh($certrow, $domains, $cronlog, $do_force, $certrow['domainid'] == 0);
} else { } else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect");
// we need another reconfigure in order to get the certificate
Cronjob::inserttask(TaskId::REBUILD_VHOST);
} }
} }
} }
@@ -566,7 +570,7 @@ EOC;
} }
} }
private static function runAcmeSh(array $certrow, array $domains, &$cronlog = null, $force = false) private static function runAcmeSh(array $certrow, array $domains, &$cronlog = null, bool $force = false, bool $renew_hook = false)
{ {
if (!empty($domains)) { if (!empty($domains)) {
$acmesh_cmd = self::getAcmeSh() . " --server " . self::$apiserver . " --issue -d " . implode(" -d ", $domains); $acmesh_cmd = self::getAcmeSh() . " --server " . self::$apiserver . " --issue -d " . implode(" -d ", $domains);
@@ -587,6 +591,12 @@ EOC;
if ($force) { if ($force) {
$acmesh_cmd .= " --force"; $acmesh_cmd .= " --force";
} }
if ($renew_hook
&& !empty(trim(Settings::Get('system.le_renew_services') ?? ""))
&& !empty(trim(Settings::Get('system.le_renew_hook') ?? ""))
) {
$acmesh_cmd .= " --renew-hook '" . Settings::Get('system.le_renew_hook') . "'";
}
if (defined('CRON_DEBUG_FLAG')) { if (defined('CRON_DEBUG_FLAG')) {
$acmesh_cmd .= " --debug"; $acmesh_cmd .= " --debug";
} }
@@ -603,12 +613,67 @@ EOC;
} }
} else { } else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, "Successful exit-code returned - storing certificate"); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, "Successful exit-code returned - storing certificate");
self::certToDb($certrow, $cronlog, $acme_result); $cert_stored = self::certToDb($certrow, $cronlog, $acme_result);
if ($cert_stored
&& $renew_hook
&& !empty(trim(Settings::Get('system.le_renew_services') ?? ""))
&& !empty(trim(Settings::Get('system.le_renew_hook') ?? ""))
) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, "Renew-hook is enabled - adjusting configurations");
$certificate_folder = self::getCertificateFolder(strtolower(Settings::Get('system.hostname')));
$fullchain = FileDir::makeCorrectFile($certificate_folder . '/fullchain.cer');
$keyfile = FileDir::makeCorrectFile($certificate_folder . '/' . strtolower(Settings::Get('system.hostname')) . '.key');
$ca_file = FileDir::makeCorrectFile($certificate_folder . '/ca.cer');
if (Settings::IsInList('system.le_renew_services', 'postfix')) {
// "postconf -e" for postfix
FileDir::safe_exec('postconf -e smtpd_tls_cert_file=' . escapeshellarg($fullchain));
FileDir::safe_exec('postconf -e smtpd_tls_key_file=' . escapeshellarg($keyfile));
}
if (Settings::IsInList('system.le_renew_services', 'dovecot')) {
// custom config for dovecot
$dovecot_conf = '/etc/dovecot/conf.d/99-froxlor.ssl.conf'; // @fixme setting?
$ssl_content = <<<EOSSL
# Autogenerated configuration by froxlor.
# Do not manually edit this file as it will be overwritten.
ssl = yes
ssl_cert = <{$fullchain}
ssl_key = <{$keyfile}
EOSSL;
file_put_contents($dovecot_conf, $ssl_content);
}
if (Settings::IsInList('system.le_renew_services', 'proftpd')) {
$proftpd_conf = '/etc/proftpd/tls.conf'; // @fixme setting?
$rval = false;
// ECC certificate or not?
if (strpos($certificate_folder, '_ecc') === false) {
// comment out ECC related settings
FileDir::safe_exec("sed -i.bak 's|^TLSECCertificateFile|# TLSECCertificateFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
FileDir::safe_exec("sed -i.bak 's|^TLSECCertificateKeyFile|# TLSECCertificateKeyFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
// add RSA directives
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSRSACertificateFile.*|TLSRSACertificateFile " . $fullchain . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSRSACertificateKeyFile.*|TLSRSACertificateKeyFile " . $keyfile . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
} else {
// comment out RSA related settings
FileDir::safe_exec("sed -i.bak 's|^TLSRSACertificateFile|# TLSRSACertificateFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
FileDir::safe_exec("sed -i.bak 's|^TLSRSACertificateKeyFile|# TLSRSACertificateKeyFile|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
// add ECC directives
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSECCertificateFile.*|TLSECCertificateFile " . $fullchain . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSECCertificateKeyFile.*|TLSECCertificateKeyFile " . $keyfile . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
}
FileDir::safe_exec("sed -i.bak 's|^#\?\s\?TLSCACertificateFile.*|TLSCACertificateFile " . $ca_file . "|' " . escapeshellarg($proftpd_conf), $rval, ['|', '?']);
}
// reload the services
FileDir::safe_exec(Settings::Get('system.le_renew_hook'));
}
} }
} }
} }
private static function certToDb($certrow, &$cronlog, $acme_result) private static function certToDb($certrow, &$cronlog, $acme_result): bool
{ {
$return = []; $return = [];
self::readCertificateToVar(strtolower($certrow['domain']), $return, $cronlog); self::readCertificateToVar(strtolower($certrow['domain']), $return, $cronlog);
@@ -639,12 +704,14 @@ EOC;
} }
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']);
return true;
} else { } else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Got non-successful Let's Encrypt response for " . $certrow['domain'] . ":\n" . implode("\n", $acme_result)); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Got non-successful Let's Encrypt response for " . $certrow['domain'] . ":\n" . implode("\n", $acme_result));
} }
} else { } else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ":\n" . implode("\n", $acme_result)); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ":\n" . implode("\n", $acme_result));
} }
return false;
} }
/** /**

View File

@@ -406,6 +406,7 @@ class Lighttpd extends HttpConfigBase
// Get domain's redirect code // Get domain's redirect code
$code = Domain::getDomainRedirectCode($domain['id']); $code = Domain::getDomainRedirectCode($domain['id']);
$vhost_content .= $this->getLogFiles($domain);
$vhost_content .= ' url.redirect-code = ' . $code . "\n"; $vhost_content .= ' url.redirect-code = ' . $code . "\n";
$vhost_content .= ' url.redirect = (' . "\n"; $vhost_content .= ' url.redirect = (' . "\n";
$vhost_content .= ' "^/(.*)$" => "' . $uri . '$1"' . "\n"; $vhost_content .= ' "^/(.*)$" => "' . $uri . '$1"' . "\n";

View File

@@ -46,10 +46,9 @@ class Nginx extends HttpConfigBase
// protected // protected
protected $needed_htpasswds = []; protected $needed_htpasswds = [];
protected $auth_backend_loaded = false; protected $http2_on_directive = false;
protected $htpasswds_data = []; protected $htpasswds_data = [];
protected $known_htpasswdsfilenames = []; protected $known_htpasswdsfilenames = [];
protected $mod_accesslog_loaded = '0';
protected $vhost_root_autoindex = false; protected $vhost_root_autoindex = false;
/** /**
@@ -60,6 +59,18 @@ class Nginx extends HttpConfigBase
*/ */
private $deactivated = false; private $deactivated = false;
public function __construct()
{
$nores = false;
$res = FileDir::safe_exec('nginx -v 2>&1', $nores, ['>', '&']);
$ver_str = array_shift($res);
$cNginxVer = substr($ver_str, strrpos($ver_str, "/") + 1);
if (version_compare($cNginxVer, '1.25.1', '>=')) {
// at least 1.25.1
$this->http2_on_directive = true;
}
}
public function createVirtualHosts() public function createVirtualHosts()
{ {
return; return;
@@ -162,8 +173,10 @@ class Nginx extends HttpConfigBase
/** /**
* this HAS to be set for the default host in nginx or else no vhost will work * this HAS to be set for the default host in nginx or else no vhost will work
*/ */
$this->nginx_data[$vhost_filename] .= "\t" . 'listen ' . $ip . ':' . $port . ' default_server' . ($ssl_vhost == true ? ' ssl' : '') . ($http2 == true ? ' http2' : '') . ';' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . 'listen ' . $ip . ':' . $port . ' default_server' . ($ssl_vhost == true ? ' ssl' : '') . ($http2 && !$this->http2_on_directive ? ' http2' : '') . ';' . "\n";
if ($http2 && $this->http2_on_directive) {
$this->nginx_data[$vhost_filename] .= "\t" . 'http2 on;' . "\n";
}
$this->nginx_data[$vhost_filename] .= "\t" . '# Froxlor default vhost' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . '# Froxlor default vhost' . "\n";
$aliases = ""; $aliases = "";
@@ -481,6 +494,7 @@ class Nginx extends HttpConfigBase
$vhost_content = ''; $vhost_content = '';
$_vhost_content = ''; $_vhost_content = '';
$has_http2_on = false;
$query = "SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` `i`, `" . TABLE_DOMAINTOIP . "` `dip` $query = "SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` `i`, `" . TABLE_DOMAINTOIP . "` `dip`
WHERE dip.id_domain = :domainid AND i.id = dip.id_ipandports "; WHERE dip.id_domain = :domainid AND i.id = dip.id_ipandports ";
@@ -531,7 +545,11 @@ class Nginx extends HttpConfigBase
} }
$http2 = $ssl_vhost == true && (isset($domain['http2']) && $domain['http2'] == '1' && Settings::Get('system.http2_support') == '1'); $http2 = $ssl_vhost == true && (isset($domain['http2']) && $domain['http2'] == '1' && Settings::Get('system.http2_support') == '1');
$vhost_content .= "\t" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ($http2 == true ? ' http2' : '') . ';' . "\n"; $vhost_content .= "\t" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ($http2 && !$this->http2_on_directive ? ' http2' : '') . ';' . "\n";
if ($http2 && $this->http2_on_directive && !$has_http2_on) {
$vhost_content .= "\t" . 'http2 on;' . "\n";
$has_http2_on = true;
}
} }
// get all server-names // get all server-names
@@ -586,6 +604,7 @@ class Nginx extends HttpConfigBase
// Get domain's redirect code // Get domain's redirect code
$code = Domain::getDomainRedirectCode($domain['id']); $code = Domain::getDomainRedirectCode($domain['id']);
$vhost_content .= $this->getLogFiles($domain);
$vhost_content .= "\t" . 'location / {' . "\n"; $vhost_content .= "\t" . 'location / {' . "\n";
$vhost_content .= "\t\t" . 'return ' . $code . ' ' . $uri . '$request_uri;' . "\n"; $vhost_content .= "\t\t" . 'return ' . $code . ' ' . $uri . '$request_uri;' . "\n";
$vhost_content .= "\t" . '}' . "\n"; $vhost_content .= "\t" . '}' . "\n";

View File

@@ -0,0 +1,216 @@
<?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\Cron\Mail;
use Exception;
use Froxlor\Database\Database;
use Froxlor\FileDir;
use Froxlor\FroxlorLogger;
use Froxlor\Settings;
class Rspamd
{
const DEFAULT_MARK_LVL = 7.0;
const DEFAULT_REJECT_LVL = 14.0;
private string $frx_settings_file = "";
protected FroxlorLogger $logger;
public function __construct(FroxlorLogger $logger)
{
$this->logger = $logger;
}
/**
* @throws Exception
*/
public function writeConfigs()
{
// tell the world what we are doing
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Task9 started - Rebuilding antispam configuration');
// get all email addresses
$antispam_stmt = Database::prepare("
SELECT email, spam_tag_level, spam_kill_level, bypass_spam, policy_greylist, iscatchall
FROM `" . TABLE_MAIL_VIRTUAL . "`
ORDER BY email
");
Database::pexecute($antispam_stmt);
$this->frx_settings_file = "#\n# Automatically generated file by froxlor. DO NOT EDIT manually as it will be overwritten!\n# Generated: " . date('d.m.Y H:i') . "\n#\n\n";
while ($email = $antispam_stmt->fetch(\PDO::FETCH_ASSOC)) {
$this->generateEmailAddrConfig($email);
}
$antispam_cfg_file = FileDir::makeCorrectFile(Settings::Get('antispam.config_file'));
file_put_contents($antispam_cfg_file, $this->frx_settings_file);
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, $antispam_cfg_file . ' written');
$this->writeDkimConfigs();
$this->reloadDaemon();
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Task9 finished');
}
/**
* # local.d/dkim_signing.conf
* try_fallback = true;
* path = "/var/lib/rspamd/dkim/$domain.$selector.key";
* selector_map = "/etc/rspamd/dkim_selectors.map";
*
* @return void
* @throws Exception
*/
public function writeDkimConfigs()
{
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Writing DKIM key-pairs');
$dkim_selector_map = "";
$result_domains_stmt = Database::query("
SELECT `id`, `domain`, `dkim`, `dkim_id`, `dkim_pubkey`, `dkim_privkey`
FROM `" . TABLE_PANEL_DOMAINS . "`
WHERE `dkim` = '1'
ORDER BY `id` ASC
");
while ($domain = $result_domains_stmt->fetch(\PDO::FETCH_ASSOC)) {
if ($domain['dkim_privkey'] == '' || $domain['dkim_pubkey'] == '') {
$max_dkim_id_stmt = Database::query("SELECT MAX(`dkim_id`) as `max_dkim_id` FROM `" . TABLE_PANEL_DOMAINS . "`");
$max_dkim_id = $max_dkim_id_stmt->fetch(\PDO::FETCH_ASSOC);
$domain['dkim_id'] = (int)$max_dkim_id['max_dkim_id'] + 1;
$privkey_filename = FileDir::makeCorrectFile('/var/lib/rspamd/dkim/' . $domain['domain'] . '.dkim' . $domain['dkim_id'] . '.key');
$pubkey_filename = FileDir::makeCorrectFile('/var/lib/rspamd/dkim/' . $domain['domain'] . '.dkim' . $domain['dkim_id'] . '.txt');
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Generating DKIM keys for "' . $domain['domain'] . '"');
$rsret = [];
FileDir::safe_exec(
'rspamadm dkim_keygen -d ' . escapeshellarg($domain['domain']) . ' -k ' . $privkey_filename . ' -s dkim' . $domain['dkim_id'] . ' -b ' . Settings::Get('antispam.dkim_keylength') . ' -o plain > ' . escapeshellarg($pubkey_filename),
$rsret,
['>']
);
if (!file_exists($privkey_filename) || !file_exists($pubkey_filename)) {
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'DKIM Keypair for domain "' . $domain['domain'] . '" was not generated successfully.');
continue;
}
$domain['dkim_privkey'] = file_get_contents($privkey_filename);
FileDir::safe_exec("chmod 0640 " . escapeshellarg($privkey_filename));
FileDir::safe_exec("chown _rspamd:_rspamd " . escapeshellarg($privkey_filename));
$domain['dkim_pubkey'] = file_get_contents($pubkey_filename);
FileDir::safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename));
FileDir::safe_exec("chown _rspamd:_rspamd " . escapeshellarg($pubkey_filename));
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_DOMAINS . "` SET
`dkim_id` = :dkimid,
`dkim_privkey` = :privkey,
`dkim_pubkey` = :pubkey
WHERE `id` = :id
");
$upd_data = [
'dkimid' => $domain['dkim_id'],
'privkey' => $domain['dkim_privkey'],
'pubkey' => $domain['dkim_pubkey'],
'id' => $domain['id']
];
Database::pexecute($upd_stmt, $upd_data);
} else {
$privkey_filename = FileDir::makeCorrectFile('/var/lib/rspamd/dkim/' . $domain['domain'] . '.dkim' . $domain['dkim_id'] . '.key');
$pubkey_filename = FileDir::makeCorrectFile('/var/lib/rspamd/dkim/' . $domain['domain'] . '.dkim' . $domain['dkim_id'] . '.txt');
}
if (!file_exists($privkey_filename) && $domain['dkim_privkey'] != '') {
file_put_contents($privkey_filename, $domain['dkim_privkey']);
FileDir::safe_exec("chmod 0640 " . escapeshellarg($privkey_filename));
FileDir::safe_exec("chown _rspamd:_rspamd " . escapeshellarg($privkey_filename));
}
if (!file_exists($pubkey_filename) && $domain['dkim_pubkey'] != '') {
file_put_contents($pubkey_filename, $domain['dkim_pubkey']);
FileDir::safe_exec("chmod 0644 " . escapeshellarg($pubkey_filename));
FileDir::safe_exec("chown _rspamd:_rspamd " . escapeshellarg($pubkey_filename));
}
$dkim_selector_map .= $domain['domain'] . " dkim" . $domain['dkim_id'] . "\n";
}
$dkim_selector_file = FileDir::makeCorrectFile('/etc/rspamd/dkim_selectors.map');
file_put_contents($dkim_selector_file, $dkim_selector_map);
}
private function generateEmailAddrConfig(array $email): void
{
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'Generating antispam config for ' . $email['email']);
$email['spam_tag_level'] = floatval($email['spam_tag_level']);
$email['spam_kill_level'] = floatval($email['spam_kill_level']);
$email_id = md5($email['email']);
$this->frx_settings_file .= '# Email: ' . $email['email'] . "\n";
foreach (['rcpt', 'from'] as $type) {
$this->frx_settings_file .= 'frx_' . $email_id . '_' . $type . ' {' . "\n";
$this->frx_settings_file .= ' id = "frx_' . $email_id . '_' . $type . '";' . "\n";
if ($email['iscatchall']) {
$this->frx_settings_file .= ' priority = low;' . "\n";
$this->frx_settings_file .= ' ' . $type . ' = "' . substr($email['email'], strpos($email['email'], '@')) . '";' . "\n";
} else {
$this->frx_settings_file .= ' priority = medium;' . "\n";
$this->frx_settings_file .= ' ' . $type . ' = "' . $email['email'] . '";' . "\n";
}
if ((int)$email['bypass_spam'] == 1) {
$this->frx_settings_file .= ' want_spam = yes;' . "\n";
} else {
$this->frx_settings_file .= ' apply {' . "\n";
$this->frx_settings_file .= ' actions {' . "\n";
$this->frx_settings_file .= ' "add header" = ' . $email['spam_tag_level'] . ';' . "\n";
$this->frx_settings_file .= ' rewrite_subject = ' . $email['spam_tag_level'] . ';' . "\n";
$this->frx_settings_file .= ' reject = ' . $email['spam_kill_level'] . ';' . "\n";
if ($type == 'rcpt' && (int)$email['policy_greylist'] == 0) {
$this->frx_settings_file .= ' greylist = null;' . "\n";
}
$this->frx_settings_file .= ' }' . "\n";
$this->frx_settings_file .= ' }' . "\n";
if ($type == 'rcpt' && (int)$email['policy_greylist'] == 0) {
$this->frx_settings_file .= ' symbols [ "DONT_GREYLIST" ]' . "\n";
}
}
$this->frx_settings_file .= '}' . "\n";
}
$this->frx_settings_file .= "\n";
}
public function reloadDaemon()
{
// reload DNS daemon
$cmd = Settings::Get('antispam.reload_command');
$cmdStatus = 1;
FileDir::safe_exec(escapeshellcmd($cmd), $cmdStatus);
if ($cmdStatus === 0) {
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'Antispam daemon reloaded');
} else {
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'Error while running `' . $cmd . '`: exit code (' . $cmdStatus . ') - please check your system logs');
}
}
}

View File

@@ -115,30 +115,46 @@ class ExportCron extends FroxlorCron
$has_dbs = false; $has_dbs = false;
$current_dbserver = -1; $current_dbserver = -1;
while ($row = $sel_stmt->fetch()) {
// Get sql_root data for the specific database-server the database resides on // look for mysqldump
if ($current_dbserver != $row['dbserver']) { $section = 'mysqldump';
Database::needRoot(true, $row['dbserver']); if (file_exists("/usr/bin/mysqldump")) {
Database::needSqlData(); $mysql_dump = '/usr/bin/mysqldump';
$sql_root = Database::getSqlData(); } elseif (file_exists("/usr/local/bin/mysqldump")) {
Database::needRoot(false); $mysql_dump = '/usr/local/bin/mysqldump';
// create temporary mysql-defaults file for the connection-credentials/details } elseif (file_exists("/usr/bin/mariadb-dump")) {
$mysqlcnf_file = tempnam("/tmp", "frx"); $mysql_dump = '/usr/bin/mariadb-dump';
$mysqlcnf = "[mysqldump]\npassword=" . $sql_root['passwd'] . "\nhost=" . $sql_root['host'] . "\n"; $section = 'mariadb-dump';
if (!empty($sql_root['port'])) { }
$mysqlcnf .= "port=" . $sql_root['port'] . "\n"; if (!isset($mysql_dump)) {
} elseif (!empty($sql_root['socket'])) { $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'mysqldump/mariadb-dump executable could not be found. Please install mysql-client/mariadb-client package.');
$mysqlcnf .= "socket=" . $sql_root['socket'] . "\n"; } else {
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 = "[".$section."]\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);
} }
file_put_contents($mysqlcnf_file, $mysqlcnf); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> '.basename($mysql_dump) . ' -u ' . escapeshellarg($sql_root['user']) . ' -pXXXXX ' . $row['databasename'] . ' > ' . FileDir::makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'));
$bool_false = false;
FileDir::safe_exec($mysql_dump . ' --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'];
} }
$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, [
'>'
]);
$has_dbs = true;
$current_dbserver = $row['dbserver'];
} }
if ($has_dbs) { if ($has_dbs) {

View File

@@ -25,9 +25,11 @@
namespace Froxlor\Cron\System; namespace Froxlor\Cron\System;
use Exception;
use Froxlor\Cron\FroxlorCron; use Froxlor\Cron\FroxlorCron;
use Froxlor\Cron\Http\ConfigIO; use Froxlor\Cron\Http\ConfigIO;
use Froxlor\Cron\Http\HttpConfigBase; use Froxlor\Cron\Http\HttpConfigBase;
use Froxlor\Cron\Mail\Rspamd;
use Froxlor\Cron\TaskId; use Froxlor\Cron\TaskId;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Dns\PowerDNS; use Froxlor\Dns\PowerDNS;
@@ -40,6 +42,9 @@ use PDO;
class TasksCron extends FroxlorCron class TasksCron extends FroxlorCron
{ {
/**
* @throws Exception
*/
public static function run() public static function run()
{ {
/** /**
@@ -98,6 +103,11 @@ class TasksCron extends FroxlorCron
* refs #293 * refs #293
*/ */
self::deleteFtpData($row); self::deleteFtpData($row);
} elseif ($row['type'] == TaskId::REBUILD_RSPAMD && (int)Settings::Get('antispam.activated') != 0) {
/**
* TYPE=9 Rebuild antispam config
*/
self::rebuildAntiSpamConfigs();
} elseif ($row['type'] == TaskId::CREATE_QUOTA && (int)Settings::Get('system.diskquota_enabled') != 0) { } elseif ($row['type'] == TaskId::CREATE_QUOTA && (int)Settings::Get('system.diskquota_enabled') != 0) {
/** /**
* TYPE=10 Set the filesystem - quota * TYPE=10 Set the filesystem - quota
@@ -266,13 +276,7 @@ class TasksCron extends FroxlorCron
private static function rebuildDnsConfigs() private static function rebuildDnsConfigs()
{ {
$dnssrv = '\\Froxlor\\Cron\\Dns\\' . Settings::Get('system.dns_server'); $dnssrv = '\\Froxlor\\Cron\\Dns\\' . Settings::Get('system.dns_server');
$nameserver = new $dnssrv(FroxlorLogger::getInstanceOf()); $nameserver = new $dnssrv(FroxlorLogger::getInstanceOf());
if (Settings::Get('dkim.use_dkim') == '1') {
$nameserver->writeDKIMconfigs();
}
$nameserver->writeConfigs(); $nameserver->writeConfigs();
} }
@@ -344,24 +348,16 @@ class TasksCron extends FroxlorCron
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'TasksCron: Task7 started - deleting customer e-mail data'); FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, 'TasksCron: Task7 started - deleting customer e-mail data');
if (is_array($row['data'])) { if (is_array($row['data'])) {
if (isset($row['data']['loginname']) && isset($row['data']['email'])) { if (isset($row['data']['loginname']) && isset($row['data']['emailpath'])) {
// remove specific maildir // remove specific maildir
$email_full = $row['data']['email']; $email_full = $row['data']['emailpath'];
if (empty($email_full)) { if (empty($email_full)) {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'FATAL: Task7 asks to delete a email account but email field is empty!'); FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, 'FATAL: Task7 asks to delete a email account but emailpath field is empty!');
}
$email_user = substr($email_full, 0, strrpos($email_full, "@"));
$email_domain = substr($email_full, strrpos($email_full, "@") + 1);
$maildirname = trim(Settings::Get('system.vmail_maildirname'));
// Add trailing slash to Maildir if needed
$maildirpath = $maildirname;
if (!empty($maildirname) and substr($maildirname, -1) != "/") {
$maildirpath .= "/";
} }
$maildir = FileDir::makeCorrectDir(Settings::Get('system.vmail_homedir') . '/' . $row['data']['loginname'] . '/' . $email_domain . '/' . $email_user); $maildir = FileDir::makeCorrectDir($email_full);
if ($maildir != '/' && !empty($maildir) && !empty($email_full) && $maildir != Settings::Get('system.vmail_homedir') && substr($maildir, 0, strlen(Settings::Get('system.vmail_homedir'))) == Settings::Get('system.vmail_homedir') && is_dir($maildir) && is_dir(FileDir::makeCorrectDir($maildir . '/' . $maildirpath)) && fileowner($maildir) == Settings::Get('system.vmail_uid') && filegroup($maildir) == Settings::Get('system.vmail_gid')) { if ($maildir != '/' && !empty($maildir) && $maildir != Settings::Get('system.vmail_homedir') && substr($maildir, 0, strlen(Settings::Get('system.vmail_homedir'))) == Settings::Get('system.vmail_homedir') && is_dir($maildir) && fileowner($maildir) == Settings::Get('system.vmail_uid') && filegroup($maildir) == Settings::Get('system.vmail_gid')) {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: rm -rf ' . escapeshellarg($maildir)); FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: rm -rf ' . escapeshellarg($maildir));
// mail-address allows many special characters, see http://en.wikipedia.org/wiki/Email_address#Local_part // mail-address allows many special characters, see http://en.wikipedia.org/wiki/Email_address#Local_part
$return = false; $return = false;
@@ -373,23 +369,6 @@ class TasksCron extends FroxlorCron
'~', '~',
'?' '?'
]); ]);
} else {
// backward-compatibility for old folder-structure
$maildir_old = FileDir::makeCorrectDir(Settings::Get('system.vmail_homedir') . '/' . $row['data']['loginname'] . '/' . $row['data']['email']);
if ($maildir_old != '/' && !empty($maildir_old) && $maildir_old != Settings::Get('system.vmail_homedir') && substr($maildir_old, 0, strlen(Settings::Get('system.vmail_homedir'))) == Settings::Get('system.vmail_homedir') && is_dir($maildir_old) && fileowner($maildir_old) == Settings::Get('system.vmail_uid') && filegroup($maildir_old) == Settings::Get('system.vmail_gid')) {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Running: rm -rf ' . escapeshellarg($maildir_old));
// mail-address allows many special characters, see http://en.wikipedia.org/wiki/Email_address#Local_part
$return = false;
FileDir::safe_exec('rm -rf ' . escapeshellarg($maildir_old), $return, [
'|',
'&',
'`',
'$',
'~',
'?'
]);
}
} }
} }
} }
@@ -448,4 +427,13 @@ class TasksCron extends FroxlorCron
} }
} }
} }
/**
* @throws Exception
*/
private static function rebuildAntiSpamConfigs()
{
$antispam = new Rspamd(FroxlorLogger::getInstanceOf());
$antispam->writeConfigs();
}
} }

View File

@@ -66,6 +66,12 @@ final class TaskId
*/ */
const DELETE_FTP_DATA = 8; const DELETE_FTP_DATA = 8;
/**
* TYPE=9 MEANS THAT SOMETHING ANTISPAM RELATED HAS CHANGED.
* REBUILD froxlor_settings.conf IF ANTISPAM IS ENABLED
*/
const REBUILD_RSPAMD = 9;
/** /**
* TYPE=10 Set the filesystem - quota * TYPE=10 Set the filesystem - quota
*/ */

View File

@@ -70,7 +70,7 @@ class ReportsCron extends FroxlorCron
) as `traffic_used` ) as `traffic_used`
FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c` FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c`
LEFT JOIN `" . TABLE_PANEL_ADMINS . "` AS `a` LEFT JOIN `" . TABLE_PANEL_ADMINS . "` AS `a`
ON `a`.`adminid` = `c`.`adminid` WHERE `c`.`reportsent` <> '1' ON `a`.`adminid` = `c`.`adminid` WHERE `c`.`reportsent` & 1 = 0
"); ");
$result_data = [ $result_data = [
@@ -79,6 +79,11 @@ class ReportsCron extends FroxlorCron
]; ];
Database::pexecute($result_stmt, $result_data); Database::pexecute($result_stmt, $result_data);
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = `reportsent` + 1
WHERE `customerid` = :customerid
");
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$row['traffic'] *= 1024; $row['traffic'] *= 1024;
$row['traffic_used'] *= 1024; $row['traffic_used'] *= 1024;
@@ -148,10 +153,6 @@ class ReportsCron extends FroxlorCron
} }
$mail->ClearAddresses(); $mail->ClearAddresses();
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = '1'
WHERE `customerid` = :customerid
");
Database::pexecute($upd_stmt, [ Database::pexecute($upd_stmt, [
'customerid' => $row['customerid'] 'customerid' => $row['customerid']
]); ]);
@@ -165,7 +166,7 @@ class ReportsCron extends FroxlorCron
FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` `t` FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` `t`
WHERE `t`.`adminid` = `a`.`adminid` AND `t`.`year` = :year AND `t`.`month` = :month WHERE `t`.`adminid` = `a`.`adminid` AND `t`.`year` = :year AND `t`.`month` = :month
) as `traffic_used_total` ) as `traffic_used_total`
FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` = '0' FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` & 1 = 0
"); ");
$result_data = [ $result_data = [
@@ -174,6 +175,11 @@ class ReportsCron extends FroxlorCron
]; ];
Database::pexecute($result_stmt, $result_data); Database::pexecute($result_stmt, $result_data);
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = `reportsent` + 1
WHERE `adminid` = :adminid
");
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$row['traffic'] *= 1024; $row['traffic'] *= 1024;
$row['traffic_used_total'] *= 1024; $row['traffic_used_total'] *= 1024;
@@ -211,7 +217,7 @@ class ReportsCron extends FroxlorCron
$_mailerror = false; $_mailerror = false;
$mailerr_msg = ""; $mailerr_msg = "";
try { try {
$mail->SetFrom($row['email'], $row['name']); $mail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));
$mail->Subject = $mail_subject; $mail->Subject = $mail_subject;
$mail->AltBody = $mail_body; $mail->AltBody = $mail_body;
$mail->MsgHTML(nl2br($mail_body)); $mail->MsgHTML(nl2br($mail_body));
@@ -231,10 +237,6 @@ class ReportsCron extends FroxlorCron
} }
$mail->ClearAddresses(); $mail->ClearAddresses();
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = '1'
WHERE `adminid` = :adminid
");
Database::pexecute($upd_stmt, [ Database::pexecute($upd_stmt, [
'adminid' => $row['adminid'] 'adminid' => $row['adminid']
]); ]);
@@ -297,7 +299,7 @@ class ReportsCron extends FroxlorCron
$_mailerror = false; $_mailerror = false;
$mailerr_msg = ""; $mailerr_msg = "";
try { try {
$mail->SetFrom($row['email'], $row['name']); $mail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));
$mail->Subject = $mail_subject; $mail->Subject = $mail_subject;
$mail->Body = $mail_body; $mail->Body = $mail_body;
$mail->MsgHTML(nl2br($mail_body)); $mail->MsgHTML(nl2br($mail_body));
@@ -344,11 +346,16 @@ class ReportsCron extends FroxlorCron
FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c` FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c`
LEFT JOIN `" . TABLE_PANEL_ADMINS . "` AS `a` LEFT JOIN `" . TABLE_PANEL_ADMINS . "` AS `a`
ON `a`.`adminid` = `c`.`adminid` ON `a`.`adminid` = `c`.`adminid`
WHERE `c`.`diskspace` > '0' AND `c`.`reportsent` <> '2' WHERE `c`.`diskspace` > '0' AND `c`.`reportsent` & 2 = 0
"); ");
$mail = new Mailer(true); $mail = new Mailer(true);
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = `reportsent` + 2
WHERE `customerid` = :customerid
");
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$row['diskspace'] *= 1024; $row['diskspace'] *= 1024;
$row['diskspace_used'] *= 1024; $row['diskspace_used'] *= 1024;
@@ -418,10 +425,6 @@ class ReportsCron extends FroxlorCron
} }
$mail->ClearAddresses(); $mail->ClearAddresses();
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = '2'
WHERE `customerid` = :customerid
");
Database::pexecute($upd_stmt, [ Database::pexecute($upd_stmt, [
'customerid' => $row['customerid'] 'customerid' => $row['customerid']
]); ]);
@@ -432,7 +435,12 @@ class ReportsCron extends FroxlorCron
* report about diskusage for admins/reseller * report about diskusage for admins/reseller
*/ */
$result_stmt = Database::query(" $result_stmt = Database::query("
SELECT `a`.* FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` <> '2' SELECT `a`.* FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` & 2 = 0
");
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = `reportsent` + 2
WHERE `adminid` = :adminid
"); ");
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
@@ -472,7 +480,7 @@ class ReportsCron extends FroxlorCron
$_mailerror = false; $_mailerror = false;
$mailerr_msg = ""; $mailerr_msg = "";
try { try {
$mail->SetFrom($row['email'], $row['name']); $mail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname'));
$mail->Subject = $mail_subject; $mail->Subject = $mail_subject;
$mail->AltBody = $mail_body; $mail->AltBody = $mail_body;
$mail->MsgHTML(nl2br($mail_body)); $mail->MsgHTML(nl2br($mail_body));
@@ -492,10 +500,6 @@ class ReportsCron extends FroxlorCron
} }
$mail->ClearAddresses(); $mail->ClearAddresses();
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = '2'
WHERE `adminid` = :adminid
");
Database::pexecute($upd_stmt, [ Database::pexecute($upd_stmt, [
'adminid' => $row['adminid'] 'adminid' => $row['adminid']
]); ]);

View File

@@ -47,7 +47,7 @@ class TrafficCron extends FroxlorCron
public static function run() public static function run()
{ {
self::runFork([self::class, 'handle']); self::runFork([self::class, 'handle'], [true]);
} }
public static function handle() public static function handle()
@@ -122,7 +122,7 @@ class TrafficCron extends FroxlorCron
if ($mysql_usage_row) { if ($mysql_usage_row) {
$mysqlusage_all[$row_database['customerid']] += floatval($mysql_usage_row['customerusage']); $mysqlusage_all[$row_database['customerid']] += floatval($mysql_usage_row['customerusage']);
} else { } else {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Cannot get usage for database " . $row_database['databasename'] . "."); FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, "Cannot get usage for database " . $row_database['databasename'] . ".");
} }
} else { } else {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Seems like the database " . $row_database['databasename'] . " had been removed manually."); FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Seems like the database " . $row_database['databasename'] . " had been removed manually.");
@@ -163,14 +163,14 @@ class TrafficCron extends FroxlorCron
if (isset($domainlist[$row['customerid']]) && is_array($domainlist[$row['customerid']]) && count($domainlist[$row['customerid']]) != 0) { if (isset($domainlist[$row['customerid']]) && is_array($domainlist[$row['customerid']]) && count($domainlist[$row['customerid']]) != 0) {
// Examining which caption to use for default webalizer stats... // Examining which caption to use for default webalizer stats...
if ($row['standardsubdomain'] != '0') { if ($row['standardsubdomain'] != '0' && isset($domainlist[$row['customerid']][$row['standardsubdomain']])) {
// ... of course we'd prefer to use the standardsubdomain ... // ... of course we'd prefer to use the standardsubdomain ...
$caption = $domainlist[$row['customerid']][$row['standardsubdomain']]; $caption = $domainlist[$row['customerid']][$row['standardsubdomain']];
} else { } else {
// ... but if there is no standardsubdomain, we have to use the loginname ... // ... but if there is no standardsubdomain, we have to use the loginname ...
$caption = $row['loginname']; $caption = $row['loginname'];
// ... which results in non-usable links to files in the stats, so lets have a look if we find a domain which is not speciallogfiledomain // ... which results in non-usable links to files in the stats, so let's have a look if we find a domain which is not speciallogfiledomain
foreach ($domainlist[$row['customerid']] as $domainid => $domain) { foreach ($domainlist[$row['customerid']] as $domainid => $domain) {
if (!isset($speciallogfile_domainlist[$row['customerid']]) || !isset($speciallogfile_domainlist[$row['customerid']][$domainid])) { if (!isset($speciallogfile_domainlist[$row['customerid']]) || !isset($speciallogfile_domainlist[$row['customerid']][$domainid])) {
$caption = $domain; $caption = $domain;
@@ -193,6 +193,8 @@ class TrafficCron extends FroxlorCron
} else { } else {
$httptraffic += floatval(self::callWebalizerGetTraffic($row['loginname'] . '-' . $domain, $row['documentroot'] . '/webalizer/' . $domain . '/', $domain, $domainlist[$row['customerid']])); $httptraffic += floatval(self::callWebalizerGetTraffic($row['loginname'] . '-' . $domain, $row['documentroot'] . '/webalizer/' . $domain . '/', $domain, $domainlist[$row['customerid']]));
} }
// kind of a keep-alive-call as this unsets the link which leads to a new connection to the database
Database::needRoot();
} }
} }
} }
@@ -210,6 +212,8 @@ class TrafficCron extends FroxlorCron
} else { } else {
$httptraffic += floatval(self::callWebalizerGetTraffic($row['loginname'], $row['documentroot'] . '/webalizer/', $caption, $domainlist[$row['customerid']])); $httptraffic += floatval(self::callWebalizerGetTraffic($row['loginname'], $row['documentroot'] . '/webalizer/', $caption, $domainlist[$row['customerid']]));
} }
// kind of a keep-alive-call as this unsets the link which leads to a new connection to the database
Database::needRoot();
// make the stuff readable for the customer, #258 // make the stuff readable for the customer, #258
Statistics::makeChownWithNewStats($row); Statistics::makeChownWithNewStats($row);
@@ -618,7 +622,7 @@ class TrafficCron extends FroxlorCron
$format = Settings::Get('system.logfiles_type') == '2' ? 'VCOMBINED' : 'COMBINED'; $format = Settings::Get('system.logfiles_type') == '2' ? 'VCOMBINED' : 'COMBINED';
$monthyear = $monthyear_arr['month'] . '/' . $monthyear_arr['year']; $monthyear = $monthyear_arr['month'] . '/' . $monthyear_arr['year'];
$return_value = false; $return_value = false;
FileDir::safe_exec("grep '" . $monthyear . "' " . escapeshellarg($logfile) . " | goaccess " . $keep_params . " --db-path=" . escapeshellarg($outputdir) . " -o " . escapeshellarg($outputdir . '.tmp.json') . " -o " . escapeshellarg($outputdir . 'index.html') . " --html-report-title=" . escapeshellarg($caption) . " --log-format=" . $format . " - ", $return_value, ['|']); FileDir::safe_exec("grep '" . $monthyear . "' " . escapeshellarg($logfile) . " | goaccess " . $keep_params . " --db-path=" . escapeshellarg($outputdir) . " -o " . escapeshellarg($outputdir . '.tmp.json') . " -o " . escapeshellarg($outputdir . 'index.html') . " --html-report-title=" . escapeshellarg($caption) . " --log-format=" . $format . " --no-parsing-spinner --no-progress - ", $return_value, ['|']);
if (file_exists($outputdir . '.tmp.json')) { if (file_exists($outputdir . '.tmp.json')) {
// need jq here because of potentially LARGE json files // need jq here because of potentially LARGE json files
@@ -787,6 +791,8 @@ class TrafficCron extends FroxlorCron
// 'real' domains and no subdomains which are aliases in the // 'real' domains and no subdomains which are aliases in the
// model-config-file. // model-config-file.
$returnval += self::awstatsDoSingleDomain($singledomain, $outputdir, $current_stamp); $returnval += self::awstatsDoSingleDomain($singledomain, $outputdir, $current_stamp);
// kind of a keep-alive-call as this unsets the link which leads to a new connection to the database
Database::needRoot();
} }
/** /**

View File

@@ -46,7 +46,7 @@ class CurrentUser
*/ */
public static function hasSession(): bool public static function hasSession(): bool
{ {
return !empty($_SESSION) && isset($_SESSION['userinfo']) && !empty($_SESSION['userinfo']); return !empty($_SESSION) && !empty($_SESSION['userinfo']);
} }
/** /**

View File

@@ -32,7 +32,7 @@ class Customer
{ {
/** /**
* Get value of a a specific field from a given customer * Get value of a specific field from a given customer
* *
* @param int $customerid * @param int $customerid
* @param string $varname * @param string $varname

View File

@@ -25,6 +25,7 @@
namespace Froxlor\Database; namespace Froxlor\Database;
use Exception;
use Froxlor\Database\Manager\DbManagerMySQL; use Froxlor\Database\Manager\DbManagerMySQL;
use Froxlor\Froxlor; use Froxlor\Froxlor;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
@@ -83,7 +84,7 @@ class DbManager
* @param array $mysql_access_host_array * @param array $mysql_access_host_array
* *
* @return void * @return void
* @throws \Exception * @throws Exception
*/ */
public static function correctMysqlUsers(array $mysql_access_host_array) public static function correctMysqlUsers(array $mysql_access_host_array)
{ {
@@ -110,7 +111,7 @@ class DbManager
$users = $dbm->getManager()->getAllSqlUsers(false); $users = $dbm->getManager()->getAllSqlUsers(false);
foreach ($databases[$dbserver['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'])) { if (isset($users[$username]['hosts']) && is_array($users[$username]['hosts'])) {
$password = [ $password = [
'password' => $users[$username]['password'], 'password' => $users[$username]['password'],
@@ -150,6 +151,7 @@ class DbManager
* @param int $last_accnumber * @param int $last_accnumber
* *
* @return string|bool $username if successful or false of username is equal to the password * @return string|bool $username if successful or false of username is equal to the password
* @throws Exception
*/ */
public function createDatabase(string $loginname = null, string $password = null, int $dbserver = 0, int $last_accnumber = 0) public function createDatabase(string $loginname = null, string $password = null, int $dbserver = 0, int $last_accnumber = 0)
{ {

View File

@@ -76,11 +76,14 @@ class DbManagerMySQL
* optional, whether the password is encrypted or not, default false * optional, whether the password is encrypted or not, default false
* @param bool $update * @param bool $update
* optional, whether to update the password only (not create user) * optional, whether to update the password only (not create user)
* @param bool $grant_access_prefix
* optional, whether the given user will have access to all databases starting with the username, default false
* @throws \Exception * @throws \Exception
*/ */
public function grantPrivilegesTo(string $username, $password, string $access_host = null, bool $p_encrypted = false, bool $update = false) public function grantPrivilegesTo(string $username, $password, string $access_host = null, bool $p_encrypted = false, bool $update = false, bool $grant_access_prefix = false)
{ {
$pwd_plugin = 'mysql_native_password'; // this is required for mysql8
$pwd_plugin = 'caching_sha2_password';
if (is_array($password) && count($password) == 2) { if (is_array($password) && count($password) == 2) {
$pwd_plugin = $password['plugin']; $pwd_plugin = $password['plugin'];
$password = $password['password']; $password = $password['password'];
@@ -108,7 +111,7 @@ class DbManagerMySQL
]); ]);
// grant privileges // grant privileges
$stmt = Database::prepare(" $stmt = Database::prepare("
GRANT ALL ON `" . $username . "`.* TO :username@:host GRANT ALL ON `" . $username . ($grant_access_prefix ? '%' : '') . "`.* TO :username@:host
"); ");
Database::pexecute($stmt, [ Database::pexecute($stmt, [
"username" => $username, "username" => $username,
@@ -219,17 +222,31 @@ class DbManagerMySQL
* *
* @param string $username * @param string $username
* @param string $host * @param string $host
* @param bool $grant_access_prefix
* @throws \Exception * @throws \Exception
*/ */
public function enableUser(string $username, string $host) public function enableUser(string $username, string $host, bool $grant_access_prefix = false)
{ {
// check whether user exists to avoid errors // check whether user exists to avoid errors
if ($this->userExistsOnHost($username, $host)) {
Database::query('GRANT ALL PRIVILEGES ON `' . $username . ($grant_access_prefix ? '%' : '') . '`.* TO `' . $username . '`@`' . $host . '`');
Database::query('GRANT ALL PRIVILEGES ON `' . str_replace('_', '\_', $username) . ($grant_access_prefix ? '%' : '') . '` . * TO `' . $username . '`@`' . $host . '`');
}
}
/**
* Check whether a given username exists for the given host
*
* @param string $username
* @param string $host
* @return bool
* @throws \Exception
*/
public function userExistsOnHost(string $username, string $host): bool
{
$exist_check_stmt = Database::prepare("SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '" . $username . "' AND host = '" . $host . "')"); $exist_check_stmt = Database::prepare("SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '" . $username . "' AND host = '" . $host . "')");
$exist_check = Database::pexecute_first($exist_check_stmt); $exist_check = Database::pexecute_first($exist_check_stmt);
if ($exist_check && array_pop($exist_check) == '1') { return ($exist_check && array_pop($exist_check) == '1');
Database::query('GRANT ALL PRIVILEGES ON `' . $username . '`.* TO `' . $username . '`@`' . $host . '`');
Database::query('GRANT ALL PRIVILEGES ON `' . str_replace('_', '\_', $username) . '` . * TO `' . $username . '`@`' . $host . '`');
}
} }
/** /**
@@ -262,7 +279,7 @@ class DbManagerMySQL
if (!isset($allsqlusers[$row['User']]) || !is_array($allsqlusers[$row['User']])) { if (!isset($allsqlusers[$row['User']]) || !is_array($allsqlusers[$row['User']])) {
$allsqlusers[$row['User']] = [ $allsqlusers[$row['User']] = [
'password' => $row['Password'] ?? $row['authentication_string'], 'password' => $row['Password'] ?? $row['authentication_string'],
'plugin' => $row['plugin'] ?? 'mysql_native_password', 'plugin' => $row['plugin'] ?? 'caching_sha2_password',
'hosts' => [] 'hosts' => []
]; ];
} }

View File

@@ -54,7 +54,7 @@ class Dns
$dom_data['uid'] = $userinfo['userid']; $dom_data['uid'] = $userinfo['userid'];
} }
} else { } else {
$where_clause = '`customerid` = :uid AND '; $where_clause = '`customerid` = :uid AND `email_only` = "0" AND ';
$dom_data['uid'] = $userinfo['userid']; $dom_data['uid'] = $userinfo['userid'];
} }
@@ -120,20 +120,8 @@ class Dns
if ($domain['isemaildomain'] == '1') { if ($domain['isemaildomain'] == '1') {
self::addRequiredEntry('@', 'MX', $required_entries); self::addRequiredEntry('@', 'MX', $required_entries);
if (Settings::Get('system.dns_createmailentry')) { if (Settings::Get('system.dns_createmailentry')) {
foreach ( foreach (['imap', 'pop3', 'mail', 'smtp' ] as $record ) {
[ foreach (['AAAA', 'A' ] as $type ) {
'imap',
'pop3',
'mail',
'smtp'
] as $record
) {
foreach (
[
'AAAA',
'A'
] as $type
) {
self::addRequiredEntry($record, $type, $required_entries); self::addRequiredEntry($record, $type, $required_entries);
} }
} }
@@ -152,9 +140,9 @@ class Dns
if (!$froxlorhostname) { if (!$froxlorhostname) {
// additional required records for subdomains // additional required records for subdomains
$subdomains_stmt = Database::prepare(" $subdomains_stmt = Database::prepare("
SELECT `domain`, `iswildcarddomain`, `wwwserveralias`, `isemaildomain` FROM `" . TABLE_PANEL_DOMAINS . "` SELECT `domain`, `iswildcarddomain`, `wwwserveralias`, `isemaildomain` FROM `" . TABLE_PANEL_DOMAINS . "`
WHERE `parentdomainid` = :domainid WHERE `parentdomainid` = :domainid
"); ");
Database::pexecute($subdomains_stmt, [ Database::pexecute($subdomains_stmt, [
'domainid' => $domain_id 'domainid' => $domain_id
]); ]);
@@ -163,7 +151,7 @@ class Dns
$sub_record = str_replace('.' . $domain['domain'], '', $subdomain['domain']); $sub_record = str_replace('.' . $domain['domain'], '', $subdomain['domain']);
// Listing domains is enough as there currently is no support for choosing // Listing domains is enough as there currently is no support for choosing
// different ips for a subdomain => use same IPs as toplevel // different ips for a subdomain => use same IPs as toplevel
self::addRequiredEntry($sub_record, 'A',$required_entries); self::addRequiredEntry($sub_record, 'A', $required_entries);
self::addRequiredEntry($sub_record, 'AAAA', $required_entries); self::addRequiredEntry($sub_record, 'AAAA', $required_entries);
// Check whether to add a www.-prefix // Check whether to add a www.-prefix
@@ -181,7 +169,11 @@ class Dns
// check for SPF content later // check for SPF content later
self::addRequiredEntry('@SPF@.' . $sub_record, 'TXT', $required_entries); self::addRequiredEntry('@SPF@.' . $sub_record, 'TXT', $required_entries);
} }
if (Settings::Get('dkim.use_dkim') == '1') { if (Settings::Get('dmarc.use_dmarc') == '1') {
// check for DMARC content later
self::addRequiredEntry('@DMARC@.' . $sub_record, 'TXT', $required_entries);
}
if (Settings::Get('antispam.activated') == '1' && $domain['dkim'] == '1') {
// check for DKIM content later // check for DKIM content later
self::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey.' . $sub_record, 'TXT', $required_entries); self::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey.' . $sub_record, 'TXT', $required_entries);
} }
@@ -218,7 +210,11 @@ class Dns
// check for SPF content later // check for SPF content later
self::addRequiredEntry('@SPF@', 'TXT', $required_entries); self::addRequiredEntry('@SPF@', 'TXT', $required_entries);
} }
if (Settings::Get('dkim.use_dkim') == '1') { if (Settings::Get('dmarc.use_dmarc') == '1') {
// check for DMARC content later
self::addRequiredEntry('@DMARC@', 'TXT', $required_entries);
}
if (Settings::Get('antispam.activated') == '1' && $domain['dkim'] == '1') {
// check for DKIM content later // check for DKIM content later
self::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey', 'TXT', $required_entries); self::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey', 'TXT', $required_entries);
} }
@@ -229,51 +225,64 @@ class Dns
// now generate all records and unset the required entries we have // now generate all records and unset the required entries we have
foreach ($dom_entries as $entry) { foreach ($dom_entries as $entry) {
if (array_key_exists($entry['type'], $required_entries) && array_key_exists(md5($entry['record']), if (array_key_exists($entry['type'], $required_entries)
$required_entries[$entry['type']])) { && array_key_exists( md5($entry['record']), $required_entries[$entry['type']])
) {
unset($required_entries[$entry['type']][md5($entry['record'])]); unset($required_entries[$entry['type']][md5($entry['record'])]);
} }
if (Settings::Get('system.dns_createcaaentry') == '1' && $entry['type'] == 'CAA' && strtolower(substr($entry['content'], if (Settings::Get('system.dns_createcaaentry') == '1'
0, 7)) == '"v=caa1') { && $entry['type'] == 'CAA'
&& strtolower(substr($entry['content'], 0, 7 )) == '"v=caa1'
) {
// unset special CAA required-entry // unset special CAA required-entry
unset($required_entries[$entry['type']][md5("@CAA@")]); unset($required_entries[$entry['type']][md5("@CAA@")]);
} }
if (Settings::Get('spf.use_spf') == '1' && $entry['type'] == 'TXT' && $entry['record'] == '@' && (strtolower(substr($entry['content'], if (Settings::Get('spf.use_spf') == '1'
0, 7)) == '"v=spf1' || strtolower(substr($entry['content'], 0, 6)) == 'v=spf1')) { && $entry['type'] == 'TXT'
&& (strtolower(substr($entry['content'], 0, 7)) == '"v=spf1' || strtolower(substr($entry['content'], 0, 6)) == 'v=spf1')
) {
// unset special spf required-entry // unset special spf required-entry
unset($required_entries[$entry['type']][md5("@SPF@")]); if ($entry['record'] == '@') {
unset($required_entries[$entry['type']][md5("@SPF@")]);
} else {
// subdomain
unset($required_entries[$entry['type']][md5("@SPF@." . $entry['record'])]);
}
}
if (Settings::Get('dmarc.use_dmarc') == '1'
&& $entry['type'] == 'TXT'
&& ($entry['record'] == '_dmarc' || substr($entry['record'], 0, 7) == '_dmarc.')
&& (strtolower(substr($entry['content'], 0, 9)) == '"v=dmarc1' || strtolower(substr($entry['content'], 0, 8)) == 'v=dmarc1')
) {
// unset special dmarc required-entry
if ($entry['record'] == '_dmarc') {
unset($required_entries[$entry['type']][md5("@DMARC@")]);
} else {
// subdomain
unset($required_entries[$entry['type']][md5("@DMARC@" . substr($entry['record'], 6))]);
}
} }
if (empty($primary_ns) && $entry['record'] == '@' && $entry['type'] == 'NS') { if (empty($primary_ns) && $entry['record'] == '@' && $entry['type'] == 'NS') {
// use the first NS entry pertaining to the current domain as primary ns // use the first NS entry pertaining to the current domain as primary ns
$primary_ns = $entry['content']; $primary_ns = $entry['content'];
} }
// check for CNAME on @, www- or wildcard-Alias and remove A/AAAA record accordingly // check for CNAME on @, www- or wildcard-Alias and remove A/AAAA record accordingly
foreach ( foreach (['@', 'www', '*'] as $crecord) {
[ if ($entry['type'] == 'CNAME'
'@', && $entry['record'] == '@'
'www', && (array_key_exists(md5($crecord), $required_entries['A']) || array_key_exists(md5($crecord), $required_entries['AAAA']))
'*' ) {
] as $crecord
) {
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['A'][md5($crecord)]);
unset($required_entries['AAAA'][md5($crecord)]); unset($required_entries['AAAA'][md5($crecord)]);
} }
} }
// also allow overriding of auto-generated values (imap,pop3,mail,smtp) if enabled in the settings // also allow overriding of auto-generated values (imap,pop3,mail,smtp) if enabled in the settings
if (Settings::Get('system.dns_createmailentry')) { if (Settings::Get('system.dns_createmailentry')) {
foreach ( foreach (['imap', 'pop3', 'mail', 'smtp'] as $crecord) {
[ if ($entry['type'] == 'CNAME'
'imap', && $entry['record'] == $crecord
'pop3', && (array_key_exists(md5($crecord), $required_entries['A']) || array_key_exists(md5($crecord), $required_entries['AAAA']))
'mail', ) {
'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']))) {
unset($required_entries['A'][md5($crecord)]); unset($required_entries['A'][md5($crecord)]);
unset($required_entries['AAAA'][md5($crecord)]); unset($required_entries['AAAA'][md5($crecord)]);
} }
@@ -310,8 +319,7 @@ class Dns
foreach ($records as $record) { foreach ($records as $record) {
if ($type == 'A' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) { if ($type == 'A' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
$zonerecords[] = new DnsEntry($record, 'A', $ip['ip']); $zonerecords[] = new DnsEntry($record, 'A', $ip['ip']);
} elseif ($type == 'AAAA' && filter_var($ip['ip'], FILTER_VALIDATE_IP, } elseif ($type == 'AAAA' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) !== false) {
FILTER_FLAG_IPV6) !== false) {
$zonerecords[] = new DnsEntry($record, 'AAAA', $ip['ip']); $zonerecords[] = new DnsEntry($record, 'AAAA', $ip['ip']);
} }
} }
@@ -376,9 +384,7 @@ class Dns
// TXT (SPF and DKIM) // TXT (SPF and DKIM)
if (array_key_exists("TXT", $required_entries)) { if (array_key_exists("TXT", $required_entries)) {
if (Settings::Get('dkim.use_dkim') == '1') { $dkim_entries = self::generateDkimEntries($domain);
$dkim_entries = self::generateDkimEntries($domain);
}
foreach ($required_entries as $type => $records) { foreach ($required_entries as $type => $records) {
if ($type == 'TXT') { if ($type == 'TXT') {
@@ -392,6 +398,15 @@ class Dns
$txt_content = Settings::Get('spf.spf_entry'); $txt_content = Settings::Get('spf.spf_entry');
$sub_record = substr($record, 6); $sub_record = substr($record, 6);
$zonerecords[] = new DnsEntry($sub_record, 'TXT', self::encloseTXTContent($txt_content)); $zonerecords[] = new DnsEntry($sub_record, 'TXT', self::encloseTXTContent($txt_content));
} elseif ($record == '@DMARC@') {
// dmarc for main-domain
$txt_content = Settings::Get('dmarc.dmarc_entry');
$zonerecords[] = new DnsEntry('_dmarc', 'TXT', self::encloseTXTContent($txt_content));
} elseif (strlen($record) > 8 && substr($record, 0, 8) == '@DMARC@.') {
// dmarc for subdomain
$txt_content = Settings::Get('dmarc.dmarc_entry');
$sub_record = substr($record, 8);
$zonerecords[] = new DnsEntry('_dmarc.' . $sub_record, 'TXT', self::encloseTXTContent($txt_content));
} elseif (!empty($dkim_entries)) { } elseif (!empty($dkim_entries)) {
// DKIM entries // DKIM entries
$dkim_record = 'dkim' . $domain['dkim_id'] . '._domainkey'; $dkim_record = 'dkim' . $domain['dkim_id'] . '._domainkey';
@@ -403,7 +418,7 @@ class Dns
$multiline = true; $multiline = true;
} }
$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dkim_entries[0], $multiline)); $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 . '.') { } elseif (strlen($record) > strlen($dkim_record) && substr($record, 0, strlen($dkim_record) + 1) == $dkim_record . '.') {
// dkim for subdomain-domain // dkim for subdomain-domain
// check for multiline entry // check for multiline entry
$multiline = false; $multiline = false;
@@ -471,8 +486,7 @@ class Dns
if (!$isMainButSubTo) { if (!$isMainButSubTo) {
$date = date('Ymd'); $date = date('Ymd');
$domain['bindserial'] = (preg_match('/^' . $date . '/', $domain['bindserial'] = (preg_match('/^' . $date . '/', $domain['bindserial']) ? $domain['bindserial'] + 1 : $date . '00');
$domain['bindserial']) ? $domain['bindserial'] + 1 : $date . '00');
if (!$froxlorhostname) { if (!$froxlorhostname) {
$upd_stmt = Database::prepare(" $upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_DOMAINS . "` SET UPDATE `" . TABLE_PANEL_DOMAINS . "` SET
@@ -499,8 +513,12 @@ class Dns
array_unshift($zonerecords, $soa_record); array_unshift($zonerecords, $soa_record);
} }
$zone = new DnsZone((int)Settings::Get('system.defaultttl'), $domain['domain'], $domain['bindserial'], $zone = new DnsZone(
$zonerecords); (int)Settings::Get('system.defaultttl'),
$domain['domain'],
$domain['bindserial'],
$zonerecords
);
return $zone; return $zone;
} }
@@ -527,43 +545,11 @@ class Dns
{ {
$zone_dkim = []; $zone_dkim = [];
if (Settings::Get('dkim.use_dkim') == '1' && $domain['dkim'] == '1' && $domain['dkim_pubkey'] != '') { if (Settings::Get('antispam.activated') == '1' && $domain['dkim'] == '1' && $domain['dkim_pubkey'] != '') {
// start // start
$dkim_txt = 'v=DKIM1;'; $dkim_txt = 'v=DKIM1;';
// algorithm
$algorithm = explode(',', Settings::Get('dkim.dkim_algorithm'));
$alg = '';
foreach ($algorithm as $a) {
if ($a == 'all') {
break;
} else {
$alg .= $a . ':';
}
}
if ($alg != '') {
$alg = substr($alg, 0, -1);
$dkim_txt .= 'h=' . $alg . ';';
}
// notes
if (trim(Settings::Get('dkim.dkim_notes') != '')) {
$dkim_txt .= 'n=' . trim(Settings::Get('dkim.dkim_notes')) . ';';
}
// key // key
$dkim_txt .= 'k=rsa;p=' . trim(preg_replace('/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s', $dkim_txt .= 'k=rsa;p=' . trim($domain['dkim_pubkey']) . ';';
'$1', str_replace("\n", '', $domain['dkim_pubkey']))) . ';';
// service-type
if (Settings::Get('dkim.dkim_servicetype') == '1') {
$dkim_txt .= 's=email;';
}
// end-part
$dkim_txt .= 't=s';
// dkim-entry // dkim-entry
$zone_dkim[] = $dkim_txt; $zone_dkim[] = $dkim_txt;
} }

View File

@@ -43,9 +43,6 @@ class IpAddr
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
if (!isset($system_ipaddress_array[$row['ip']]) && !in_array($row['ip'], $system_ipaddress_array)) { if (!isset($system_ipaddress_array[$row['ip']]) && !in_array($row['ip'], $system_ipaddress_array)) {
if (filter_var($row['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$row['ip'] = '[' . $row['ip'] . ']';
}
$system_ipaddress_array[$row['ip']] = $row['ip']; $system_ipaddress_array[$row['ip']] = $row['ip'];
} }
} }

77
lib/Froxlor/ErrorBag.php Normal file
View File

@@ -0,0 +1,77 @@
<?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;
use Exception;
/**
* Class to manage the current user / session
*/
class ErrorBag
{
/**
* returns whether there are errors stored
*
* @return bool
*/
public static function hasErrors(): bool
{
return !empty($_SESSION) && !empty($_SESSION['_errors']);
}
/**
* add error
*
* @param string $data
*
* @return void
*/
public static function addError(string $data): void
{
if (!isset($_SESSION['_errors']) || !is_array($_SESSION['_errors'])) {
$_SESSION['_errors'] = [];
}
$_SESSION['_errors'][] = $data;
}
/**
* Return errors and clear session
*
* @return array
* @throws Exception
*/
public static function getErrors(): array
{
$errors = $_SESSION['_errors'] ?? [];
unset($_SESSION['_errors']);
if (Settings::Config('display_php_errors')) {
return $errors;
}
return [];
}
}

View File

@@ -140,6 +140,12 @@ class FileDir
if (is_link($check_dir)) { if (is_link($check_dir)) {
$original_target = $check_dir; $original_target = $check_dir;
$check_dir = readlink($check_dir); $check_dir = readlink($check_dir);
$link_dir = dirname($original_target);
// check whether the link is relative or absolute
if (substr($check_dir, 0, 1) != '/') {
// relative directory, prepend link_dir
$check_dir = $link_dir . '/' . $check_dir;
}
if (substr($check_dir, 0, strlen($fixed_homedir)) != $fixed_homedir) { if (substr($check_dir, 0, strlen($fixed_homedir)) != $fixed_homedir) {
throw new Exception("Found symlink pointing outside of customer home directory: " . substr($original_target, strlen($fixed_homedir))); throw new Exception("Found symlink pointing outside of customer home directory: " . substr($original_target, strlen($fixed_homedir)));
} }
@@ -287,7 +293,7 @@ class FileDir
$tpl_content = lng('admin.templates.unconfigured_content_fallback'); $tpl_content = lng('admin.templates.unconfigured_content_fallback');
} }
} }
$redirect_file = FileDir::makeCorrectFile(Froxlor::getInstallDir().'/notice.'.$tpl_ext); $redirect_file = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/notice.' . $tpl_ext);
file_put_contents($redirect_file, $tpl_content); file_put_contents($redirect_file, $tpl_content);
return basename($redirect_file); return basename($redirect_file);
} }

View File

@@ -31,15 +31,15 @@ final class Froxlor
{ {
// Main version variable // Main version variable
const VERSION = '2.1.2'; const VERSION = '2.2.0-rc3';
// Database version (YYYYMMDDC where C is a daily counter) // Database version (YYYYMMDDC where C is a daily counter)
const DBVERSION = '202312120'; const DBVERSION = '202407200';
// Distribution branding-tag (used for Debian etc.) // Distribution branding-tag (used for Debian etc.)
const BRANDING = ''; const BRANDING = '';
const DOCS_URL = 'https://docs.froxlor.org/v2.1/'; const DOCS_URL = 'https://docs.froxlor.org';
/** /**
* return path to where froxlor is installed, e.g. * return path to where froxlor is installed, e.g.
@@ -52,6 +52,14 @@ final class Froxlor
return dirname(__DIR__, 2) . '/'; return dirname(__DIR__, 2) . '/';
} }
public static function getDocsUrl(): string
{
if (preg_match('/(.+)-(dev|beta|rc)\d+$/', self::VERSION)) {
return self::DOCS_URL . '/dev/';
}
return self::DOCS_URL . '/v' . self::getShortVersion() . '/';
}
/** /**
* return basic version * return basic version
* *
@@ -62,6 +70,16 @@ final class Froxlor
return self::VERSION; return self::VERSION;
} }
/**
* return short basic version
*
* @return string
*/
public static function getShortVersion(): string
{
return explode(".", self::VERSION)[0] . '.' . explode(".", self::VERSION)[1];
}
/** /**
* return version + branding and database-version * return version + branding and database-version
* *

View File

@@ -175,6 +175,9 @@ class FroxlorLogger
$this->initMonolog(); $this->initMonolog();
} }
// clean log-text
$text = preg_replace("/[^\w @#\"':.,()\[\]+\-_\/\\\!]/i", "_", $text);
if (self::$crondebug_flag || ($action == FroxlorLogger::CRON_ACTION && $type <= LOG_WARNING)) { if (self::$crondebug_flag || ($action == FroxlorLogger::CRON_ACTION && $type <= LOG_WARNING)) {
echo "[" . $this->getLogLevelDesc($type) . "] " . $text . PHP_EOL; echo "[" . $this->getLogLevelDesc($type) . "] " . $text . PHP_EOL;
} }

View File

@@ -69,7 +69,7 @@ class AutoUpdate
if (Settings::Get('system.update_channel') == 'testing') { if (Settings::Get('system.update_channel') == 'testing') {
$channel = '/testing'; $channel = '/testing';
} elseif (Settings::Get('system.update_channel') == 'nightly') { } elseif (Settings::Get('system.update_channel') == 'nightly') {
if (empty(Froxlor::BRANDING)) { if (empty(Froxlor::BRANDING) || substr(Froxlor::BRANDING, 0, 1) == '-') {
$channel = '/nightly.0000000'; $channel = '/nightly.0000000';
} else { } else {
$channel = '/' . substr(Froxlor::BRANDING, 1); $channel = '/' . substr(Froxlor::BRANDING, 1);
@@ -151,6 +151,8 @@ class AutoUpdate
$zip->close(); $zip->close();
// success - remove unused archive // success - remove unused archive
@unlink($localArchive); @unlink($localArchive);
// reset cached version check
Settings::Set('system.updatecheck_data', '');
// wait a bit before we redirect to be sure // wait a bit before we redirect to be sure
sleep(3); sleep(3);
return 0; return 0;

View File

@@ -99,7 +99,7 @@ class Install
} }
// check for url manipulation or wrong step // check for url manipulation or wrong step
if ((isset($_SESSION['installation']['stepCompleted']) && ($this->currentStep + 1) > $_SESSION['installation']['stepCompleted']) if ((isset($_SESSION['installation']['stepCompleted']) && $this->currentStep > $_SESSION['installation']['stepCompleted'])
|| (!isset($_SESSION['installation']['stepCompleted']) && $this->currentStep > 0) || (!isset($_SESSION['installation']['stepCompleted']) && $this->currentStep > 0)
) { ) {
$this->currentStep = isset($_SESSION['installation']['stepCompleted']) ? $_SESSION['installation']['stepCompleted'] + 1 : 1; $this->currentStep = isset($_SESSION['installation']['stepCompleted']) ? $_SESSION['installation']['stepCompleted'] + 1 : 1;
@@ -322,13 +322,17 @@ class Install
$email = $validatedData['admin_email'] ?? ''; $email = $validatedData['admin_email'] ?? '';
$password = $validatedData['admin_pass'] ?? ''; $password = $validatedData['admin_pass'] ?? '';
$password_confirm = $validatedData['admin_pass_confirm'] ?? ''; $password_confirm = $validatedData['admin_pass_confirm'] ?? '';
$useadminmailassender = $validatedData['use_admin_email_as_sender'] ?? '1';
$senderemail = $validatedData['sender_email'] ?? '';
if (!preg_match('/^[^\r\n\t\f\0]*$/D', $name)) { if (!preg_match('/^[^\r\n\t\f\0]*$/D', $name)) {
throw new Exception(lng('error.stringformaterror', ['admin_name'])); throw new Exception(lng('error.stringformaterror', ['admin_name']));
} elseif (empty(trim($loginname)) || !preg_match('/^[a-z][a-z0-9]+$/', $loginname)) { } elseif (empty(trim($loginname)) || !preg_match('/^[a-z][a-z0-9]+$/Di', $loginname)) {
throw new Exception(lng('error.loginnameiswrong', [$loginname])); throw new Exception(lng('error.loginnameiswrong', [$loginname]));
} elseif (empty(trim($email)) || !Validate::validateEmail($email)) { } elseif (empty(trim($email)) || !Validate::validateEmail($email)) {
throw new Exception(lng('error.emailiswrong', [$email])); throw new Exception(lng('error.emailiswrong', [$email]));
} elseif ((int)$useadminmailassender == 0 && !empty(trim($senderemail)) && !Validate::validateEmail($senderemail)) {
throw new Exception(lng('error.emailiswrong', [$senderemail]));
} elseif (empty($password) || $password != $password_confirm) { } elseif (empty($password) || $password != $password_confirm) {
throw new Exception(lng('error.newpasswordconfirmerror')); throw new Exception(lng('error.newpasswordconfirmerror'));
} elseif ($password == $loginname) { } elseif ($password == $loginname) {

View File

@@ -115,7 +115,7 @@ class Core
// create entries // create entries
$this->doDataEntries($pdo); $this->doDataEntries($pdo);
// create JSON array for config-services // create JSON array for config-services
$this->createJsonArray(); $this->createJsonArray($pdo);
if ($create_ud_str) { if ($create_ud_str) {
$this->createUserdataParamStr(); $this->createUserdataParamStr();
} }
@@ -176,15 +176,19 @@ class Core
$filename = "/tmp/froxlor_backup_" . date('YmdHi') . ".sql"; $filename = "/tmp/froxlor_backup_" . date('YmdHi') . ".sql";
// look for mysqldump // look for mysqldump
$section = 'mysqldump';
if (file_exists("/usr/bin/mysqldump")) { if (file_exists("/usr/bin/mysqldump")) {
$mysql_dump = '/usr/bin/mysqldump'; $mysql_dump = '/usr/bin/mysqldump';
} elseif (file_exists("/usr/local/bin/mysqldump")) { } elseif (file_exists("/usr/local/bin/mysqldump")) {
$mysql_dump = '/usr/local/bin/mysqldump'; $mysql_dump = '/usr/local/bin/mysqldump';
} elseif (file_exists("/usr/bin/mariadb-dump")) {
$mysql_dump = '/usr/bin/mariadb-dump';
$section = 'mariadb-dump';
} }
// create temporary .cnf file // create temporary .cnf file
$cnffilename = "/tmp/froxlor_dump.cnf"; $cnffilename = "/tmp/froxlor_dump.cnf";
$dumpcnf = "[mysqldump]" . PHP_EOL . "password=\"" . $this->validatedData['mysql_root_pass'] . "\"" . PHP_EOL; $dumpcnf = "[".$section."]" . PHP_EOL . "password=\"" . $this->validatedData['mysql_root_pass'] . "\"" . PHP_EOL;
file_put_contents($cnffilename, $dumpcnf); file_put_contents($cnffilename, $dumpcnf);
// make the backup // make the backup
@@ -195,7 +199,7 @@ class Core
@unlink($cnffilename); @unlink($cnffilename);
if (stristr(implode(" ", $output), "error")) { if (stristr(implode(" ", $output), "error")) {
throw new Exception(lng('install.errors.mysqldump_backup_failed')); throw new Exception(lng('install.errors.mysqldump_backup_failed'));
} else if (!file_exists($filename)) { } elseif (!file_exists($filename)) {
throw new Exception(lng('install.errors.sql_backup_file_missing')); throw new Exception(lng('install.errors.sql_backup_file_missing'));
} }
} else { } else {
@@ -301,8 +305,8 @@ class Core
/* continue */ /* continue */
} }
} }
if (version_compare($db_root->getAttribute(PDO::ATTR_SERVER_VERSION), '10.0.0', '>=')) { if (version_compare($db_root->getAttribute(PDO::ATTR_SERVER_VERSION), '8.0.11', '>=')) {
// mariadb compatibility // mariadb & mysql8
// create user // create user
$stmt = $db_root->prepare("CREATE USER '" . $username . "'@'" . $access_host . "' IDENTIFIED BY :password"); $stmt = $db_root->prepare("CREATE USER '" . $username . "'@'" . $access_host . "' IDENTIFIED BY :password");
$stmt->execute([ $stmt->execute([
@@ -314,19 +318,6 @@ class Core
"username" => $username, "username" => $username,
"host" => $access_host "host" => $access_host
]); ]);
} elseif (version_compare($db_root->getAttribute(PDO::ATTR_SERVER_VERSION), '8.0.11', '>=')) {
// mysql8 compatibility
// create user
$stmt = $db_root->prepare("CREATE USER '" . $username . "'@'" . $access_host . "' IDENTIFIED WITH mysql_native_password BY :password");
$stmt->execute([
"password" => $password
]);
// grant privileges
$stmt = $db_root->prepare("GRANT ALL ON `" . $database . "`.* TO :username@:host");
$stmt->execute([
"username" => $username,
"host" => $access_host
]);
} else { } else {
// grant privileges // grant privileges
$stmt = $db_root->prepare("GRANT ALL PRIVILEGES ON `" . $database . "`.* TO :username@:host IDENTIFIED BY :password"); $stmt = $db_root->prepare("GRANT ALL PRIVILEGES ON `" . $database . "`.* TO :username@:host IDENTIFIED BY :password");
@@ -378,14 +369,21 @@ class Core
$mainip = !empty($this->validatedData['serveripv6']) ? $this->validatedData['serveripv6'] : $this->validatedData['serveripv4']; $mainip = !empty($this->validatedData['serveripv6']) ? $this->validatedData['serveripv6'] : $this->validatedData['serveripv4'];
$this->updateSetting($upd_stmt, 'admin@' . $this->validatedData['servername'], 'panel', 'adminmail'); if ($this->validatedData['use_admin_email_as_sender'] == '1') {
$adminmail_value = $this->validatedData['admin_email'];
} elseif ($this->validatedData['use_admin_email_as_sender'] == '0' && !empty($this->validatedData['sender_email'])) {
$adminmail_value = $this->validatedData['sender_email'];
} else {
$adminmail_value = 'admin@' . $this->validatedData['servername'];
}
$this->updateSetting($upd_stmt, $adminmail_value, 'panel', 'adminmail');
$this->updateSetting($upd_stmt, $mainip, 'system', 'ipaddress'); $this->updateSetting($upd_stmt, $mainip, 'system', 'ipaddress');
if ($this->validatedData['use_ssl']) { if ($this->validatedData['use_ssl']) {
$this->updateSetting($upd_stmt, 1, 'system', 'use_ssl'); $this->updateSetting($upd_stmt, 1, 'system', 'use_ssl');
$this->updateSetting($upd_stmt, 1, 'system', 'leenabled'); $this->updateSetting($upd_stmt, 1, 'system', 'leenabled');
$this->updateSetting($upd_stmt, 1, 'system', 'le_froxlor_enabled'); $this->updateSetting($upd_stmt, 1, 'system', 'le_froxlor_enabled');
} }
$this->updateSetting($upd_stmt, $this->validatedData['servername'], 'system', 'hostname'); $this->updateSetting($upd_stmt, strtolower($this->validatedData['servername']), 'system', 'hostname');
$this->updateSetting($upd_stmt, 'en', 'panel', 'standardlanguage'); // TODO: set language $this->updateSetting($upd_stmt, 'en', 'panel', 'standardlanguage'); // TODO: set language
$this->updateSetting($upd_stmt, $this->validatedData['mysql_access_host'], 'system', 'mysql_access_host'); $this->updateSetting($upd_stmt, $this->validatedData['mysql_access_host'], 'system', 'mysql_access_host');
$this->updateSetting($upd_stmt, $this->validatedData['webserver'], 'system', 'webserver'); $this->updateSetting($upd_stmt, $this->validatedData['webserver'], 'system', 'webserver');
@@ -437,23 +435,14 @@ class Core
// check currently used php version and set values of fpm/fcgid accordingly // check currently used php version and set values of fpm/fcgid accordingly
if (defined('PHP_MAJOR_VERSION') && defined('PHP_MINOR_VERSION')) { if (defined('PHP_MAJOR_VERSION') && defined('PHP_MINOR_VERSION')) {
// gentoo specific // php-fpm
if ($this->validatedData['distribution'] == 'gentoo') { $reload = "service php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "-fpm restart";
// php-fpm $config_dir = "/etc/php/" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "/fpm/pool.d/";
$reload = "/etc/init.d/php-fpm restart"; // fcgid
$config_dir = "/etc/php/fpm-php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "/fpm.d/"; if ($this->validatedData['distribution'] == 'bookworm') {
// fcgid $binary = "/usr/bin/php-cgi" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;
$binary = "/usr/bin/php-cgi";
} else { } else {
// php-fpm $binary = "/usr/bin/php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "-cgi";
$reload = "service php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "-fpm restart";
$config_dir = "/etc/php/" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "/fpm/pool.d/";
// fcgid
if ($this->validatedData['distribution'] == 'bookworm') {
$binary = "/usr/bin/php-cgi" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;
} else {
$binary = "/usr/bin/php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "-cgi";
}
} }
$db_user->query("UPDATE `" . TABLE_PANEL_FPMDAEMONS . "` SET `reload_cmd` = '" . $reload . "', `config_dir` = '" . $config_dir . "' WHERE `id` ='1';"); $db_user->query("UPDATE `" . TABLE_PANEL_FPMDAEMONS . "` SET `reload_cmd` = '" . $reload . "', `config_dir` = '" . $config_dir . "' WHERE `id` ='1';");
$db_user->query("UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET `binary` = '" . $binary . "';"); $db_user->query("UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET `binary` = '" . $binary . "';");
@@ -576,7 +565,7 @@ class Core
'password' => password_hash($this->validatedData['admin_pass'], PASSWORD_DEFAULT), 'password' => password_hash($this->validatedData['admin_pass'], PASSWORD_DEFAULT),
'adminname' => $this->validatedData['admin_name'], 'adminname' => $this->validatedData['admin_name'],
'email' => $this->validatedData['admin_email'], 'email' => $this->validatedData['admin_email'],
'deflang' => 'en' // TODO: set lanuage 'deflang' => 'en' // TODO: set language
]; ];
$ins_stmt = $db_user->prepare(" $ins_stmt = $db_user->prepare("
INSERT INTO `" . TABLE_PANEL_ADMINS . "` SET INSERT INTO `" . TABLE_PANEL_ADMINS . "` SET
@@ -666,9 +655,20 @@ class Core
@umask($umask); @umask($umask);
} }
private function createJsonArray() private function createJsonArray(&$db_user)
{ {
$system_params = ["cron", "libnssextrausers", "logrotate", "goaccess"]; // use traffic analyzer and ftpserver from settings as we could define defaults in the lib/configfiles/*.xml templates
// which can be useful for third-party package-maintainer (e.g. other distros) to have more control
// over the installation defaults (less hardcoded values)
$custom_dependency = $db_user->query("
SELECT `varname`, `value` FROM `" . TABLE_PANEL_SETTINGS . "`
WHERE `settinggroup` = 'system' AND (`varname` = 'traffictool' OR `varname` = 'ftpserver')
");
$cd_result = $custom_dependency->fetchAll(\PDO::FETCH_KEY_PAIR);
$system_params = ["cron", "libnssextrausers", "logrotate"];
if (isset($cd_result['traffictool'])) {
$system_params[] = $cd_result['traffictool'];
}
if ($this->validatedData['webserver_backend'] == 'php-fpm') { if ($this->validatedData['webserver_backend'] == 'php-fpm') {
$system_params[] = 'php-fpm'; $system_params[] = 'php-fpm';
} elseif ($this->validatedData['webserver_backend'] == 'fcgid') { } elseif ($this->validatedData['webserver_backend'] == 'fcgid') {
@@ -680,7 +680,8 @@ class Core
'http' => $this->validatedData['webserver'], 'http' => $this->validatedData['webserver'],
'smtp' => 'postfix_dovecot', 'smtp' => 'postfix_dovecot',
'mail' => 'dovecot_postfix2', 'mail' => 'dovecot_postfix2',
'ftp' => 'proftpd', 'antispam' => 'rspamd',
'ftp' => $cd_result['ftpserver'] ?? 'x',
'system' => $system_params 'system' => $system_params
]; ];
$_SESSION['installation']['json_params'] = json_encode($json_params); $_SESSION['installation']['json_params'] = json_encode($json_params);

View File

@@ -25,6 +25,7 @@
namespace Froxlor\Install; namespace Froxlor\Install;
use Froxlor\FileDir;
use Froxlor\Froxlor; use Froxlor\Froxlor;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\Settings; use Froxlor\Settings;
@@ -85,7 +86,7 @@ class Update
self::$update_tasks[self::$task_counter]['result'] = 1; self::$update_tasks[self::$task_counter]['result'] = 1;
break; break;
default: default:
self::$update_tasks[self::$task_counter]['result'] = -1; self::$update_tasks[self::$task_counter]['result'] = -1;
break; break;
} }
@@ -136,4 +137,36 @@ class Update
{ {
return self::$task_counter; return self::$task_counter;
} }
public static function cleanOldFiles(array $to_clean)
{
self::showUpdateStep("Cleaning up old files");
$disabled = explode(',', ini_get('disable_functions'));
$exec_allowed = !in_array('exec', $disabled);
$del_list = "";
foreach ($to_clean as $filedir) {
$complete_filedir = Froxlor::getInstallDir() . $filedir;
if (file_exists($complete_filedir)) {
if ($exec_allowed) {
FileDir::safe_exec("rm -rf " . escapeshellarg($complete_filedir));
} else {
$del_list .= "rm -rf " . escapeshellarg($complete_filedir) . PHP_EOL;
}
}
}
if ($exec_allowed) {
self::lastStepStatus(0);
} else {
if (empty($del_list)) {
// none of the files existed
self::lastStepStatus(0);
} else {
self::lastStepStatus(
1,
'manual commands needed',
'Please run the following commands manually:<br><pre>' . $del_list . '</pre>'
);
}
}
}
} }

View File

@@ -34,27 +34,6 @@ use voku\helper\AntiXSS;
class PhpHelper class PhpHelper
{ {
private static $sort_key = 'id';
private static $sort_type = SORT_STRING;
/**
* sort an array by either natural or string sort and a given index where the value for comparison is found
*
* @param array $list
* @param string $key
*
* @return bool
*/
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;
return usort($list, [
'self',
'sortListByGivenKey'
]);
}
/** /**
* Wrapper around htmlentities to handle arrays, with the advantage that you * Wrapper around htmlentities to handle arrays, with the advantage that you
* can select which fields should be handled by htmlentities * can select which fields should be handled by htmlentities
@@ -101,35 +80,6 @@ class PhpHelper
}); });
} }
/**
* 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 $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
*/
public static function strReplaceArray($search, $replace, $subject, $fields = '')
{
if (is_array($subject)) {
if (!is_array($fields)) {
$fields = self::arrayTrim(explode(' ', $fields));
}
foreach ($subject as $field => $value) {
if ((!is_array($fields) || empty($fields)) || (in_array($field, $fields))) {
$subject[$field] = str_replace($search, $replace, $value);
}
}
} else {
$subject = str_replace($search, $replace, $subject);
}
return $subject;
}
/** /**
* froxlor php error handler * froxlor php error handler
* *
@@ -170,9 +120,8 @@ class PhpHelper
$err_display .= '</pre></p>'; $err_display .= '</pre></p>';
// end later // end later
$err_display .= '</div>'; $err_display .= '</div>';
// check for more existing errors // set errors to session
$errors = isset(UI::twig()->getGlobals()['global_errors']) ? UI::twig()->getGlobals()['global_errors'] : ""; ErrorBag::addError($err_display);
UI::twig()->addGlobal('global_errors', $errors . $err_display);
// return true to ignore php standard error-handler // return true to ignore php standard error-handler
return true; return true;
} }
@@ -338,7 +287,8 @@ class PhpHelper
?string $max = '', ?string $max = '',
string $system = 'si', string $system = 'si',
string $retstring = '%01.2f %s' string $retstring = '%01.2f %s'
): string { ): string
{
// Pick units // Pick units
$systems = [ $systems = [
'si' => [ 'si' => [
@@ -421,8 +371,12 @@ class PhpHelper
array $haystack, array $haystack,
array &$keys = [], array &$keys = [],
string $currentKey = '' string $currentKey = ''
): bool { ): bool
{
foreach ($haystack as $key => $value) { foreach ($haystack as $key => $value) {
if (empty($value)) {
continue;
}
$pathkey = empty($currentKey) ? $key : $currentKey . '.' . $key; $pathkey = empty($currentKey) ? $key : $currentKey . '.' . $key;
if (is_array($value)) { if (is_array($value)) {
self::recursive_array_search($needle, $value, $keys, $pathkey); self::recursive_array_search($needle, $value, $keys, $pathkey);
@@ -465,6 +419,10 @@ class PhpHelper
'mysql_unprivileged_pass', 'mysql_unprivileged_pass',
'admin_pass', 'admin_pass',
'admin_pass_confirm', 'admin_pass_confirm',
'panel_password_special_char',
'old_password',
'new_password',
'new_password_confirm',
]; ];
if (!empty($global)) { if (!empty($global)) {
$tmp = $global; $tmp = $global;
@@ -476,19 +434,6 @@ class PhpHelper
} }
} }
/**
* @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]);
}
return strcasecmp($a[self::$sort_key], $b[self::$sort_key]);
}
/** /**
* Generate php file from array. * Generate php file from array.
* *

View File

@@ -25,6 +25,7 @@
namespace Froxlor; namespace Froxlor;
use Exception;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use PDO; use PDO;
use PDOStatement; use PDOStatement;
@@ -131,11 +132,12 @@ class Settings
self::$conf = [ self::$conf = [
'enable_webupdate' => false, 'enable_webupdate' => false,
'disable_otp_security_check' => false, 'disable_otp_security_check' => false,
'display_php_errors' => false,
]; ];
$configfile = Froxlor::getInstallDir() . '/lib/config.inc.php'; $configfile = Froxlor::getInstallDir() . '/lib/config.inc.php';
if (@file_exists($configfile) && is_readable($configfile)) { if (@file_exists($configfile) && is_readable($configfile)) {
self::$conf = include $configfile; self::$conf = array_merge(self::$conf, include $configfile);
} }
return true; return true;
} }
@@ -330,7 +332,7 @@ class Settings
} }
} }
public static function getAll() : array public static function getAll(): array
{ {
self::init(); self::init();
return self::$data; return self::$data;
@@ -338,17 +340,14 @@ class Settings
/** /**
* get value from config by identifier * get value from config by identifier
* @throws Exception
*/ */
public static function Config(string $config) public static function Config(string $config)
{ {
self::init(); self::init();
$sstr = explode(".", $config); $result = self::$conf[$config] ?? null;
$result = self::$conf; if (is_null($result)) {
foreach ($sstr as $key) { throw new Exception('Unknown local config name "' . $config . '"');
$result = $result[$key] ?? null;
if (empty($result)) {
break;
}
} }
return $result; return $result;
} }

View File

@@ -36,6 +36,7 @@ use Froxlor\PhpHelper;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\System\Cronjob; use Froxlor\System\Cronjob;
use Froxlor\System\IPTools; use Froxlor\System\IPTools;
use Froxlor\UI\Request;
use Froxlor\Validate\Validate; use Froxlor\Validate\Validate;
use PDO; use PDO;
@@ -225,6 +226,17 @@ class Store
return $returnvalue; return $returnvalue;
} }
public static function storeSettingFieldInsertAntispamTask($fieldname, $fielddata, $newfieldvalue)
{
// first save the setting itself
$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);
if ($returnvalue !== false) {
Cronjob::inserttask(TaskId::REBUILD_RSPAMD);
}
return $returnvalue;
}
public static function storeSettingHostname($fieldname, $fielddata, $newfieldvalue) public static function storeSettingHostname($fieldname, $fielddata, $newfieldvalue)
{ {
$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue); $returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);
@@ -454,7 +466,7 @@ class Store
} }
// Delete file? // Delete file?
if ($fielddata['value'] !== "" && array_key_exists($fieldname . '_delete', $_POST) && $_POST[$fieldname . '_delete']) { if ($fielddata['value'] !== "" && array_key_exists($fieldname . '_delete', $_POST) && Request::post($fieldname . '_delete')) {
@unlink(Froxlor::getInstallDir() . '/' . explode('?', $fielddata['value'], 2)[0]); @unlink(Froxlor::getInstallDir() . '/' . explode('?', $fielddata['value'], 2)[0]);
$save_to = ''; $save_to = '';
} }

View File

@@ -134,11 +134,15 @@ class Cronjob
INSERT INTO `" . TABLE_PANEL_TASKS . "` SET `type` = :type, `data` = :data INSERT INTO `" . TABLE_PANEL_TASKS . "` SET `type` = :type, `data` = :data
"); ");
if ($type == TaskId::REBUILD_VHOST || $type == TaskId::REBUILD_DNS || $type == TaskId::CREATE_FTP || $type == TaskId::CREATE_QUOTA || $type == TaskId::REBUILD_CRON) { if ($type == TaskId::REBUILD_VHOST || $type == TaskId::REBUILD_DNS || $type == TaskId::CREATE_FTP || $type == TaskId::REBUILD_RSPAMD || $type == TaskId::CREATE_QUOTA || $type == TaskId::REBUILD_CRON) {
// 4 = bind -> if bind disabled -> no task // 4 = bind -> if bind disabled -> no task
if ($type == TaskId::REBUILD_DNS && Settings::Get('system.bind_enable') == '0') { if ($type == TaskId::REBUILD_DNS && Settings::Get('system.bind_enable') == '0') {
return; return;
} }
// 9 = rspamd -> if antispam disabled -> no task
if ($type == TaskId::REBUILD_RSPAMD && Settings::Get('antispam.activated') == '0') {
return;
}
// 10 = quota -> if quota disabled -> no task // 10 = quota -> if quota disabled -> no task
if ($type == TaskId::CREATE_QUOTA && Settings::Get('system.diskquota_enabled') == '0') { if ($type == TaskId::CREATE_QUOTA && Settings::Get('system.diskquota_enabled') == '0') {
return; return;
@@ -179,7 +183,7 @@ class Cronjob
} elseif ($type == TaskId::DELETE_EMAIL_DATA && count($params) == 2 && $params[0] != '' && $params[1] != '') { } elseif ($type == TaskId::DELETE_EMAIL_DATA && count($params) == 2 && $params[0] != '' && $params[1] != '') {
$data = []; $data = [];
$data['loginname'] = $params[0]; $data['loginname'] = $params[0];
$data['email'] = $params[1]; $data['emailpath'] = $params[1];
$data = json_encode($data); $data = json_encode($data);
Database::pexecute($ins_stmt, [ Database::pexecute($ins_stmt, [
'type' => TaskId::DELETE_EMAIL_DATA, 'type' => TaskId::DELETE_EMAIL_DATA,

View File

@@ -42,7 +42,7 @@ class Traffic
{ {
$trafficCollectionObj = (new Collection(TrafficAPI::class, $userinfo, $trafficCollectionObj = (new Collection(TrafficAPI::class, $userinfo,
self::getParamsByRange($range, ['customer_traffic' => true]))); self::getParamsByRange($range, ['customer_traffic' => true])));
if ($userinfo['adminsession'] == 1) { if (($userinfo['adminsession'] ?? 0) == 1) {
$trafficCollectionObj->has('customer', Customers::class, 'customerid', 'customerid'); $trafficCollectionObj->has('customer', Customers::class, 'customerid', 'customerid');
} }
$trafficCollection = $trafficCollectionObj->get(); $trafficCollection = $trafficCollectionObj->get();
@@ -58,8 +58,17 @@ class Traffic
$mail = $item['mail']; $mail = $item['mail'];
$total = $http + $ftp + $mail; $total = $http + $ftp + $mail;
if (empty($users[$item['customerid']])) {
$users[$item['customerid']] = [
'total' => 0.00,
'http' => 0.00,
'ftp' => 0.00,
'mail' => 0.00,
];
}
// per user total // per user total
if ($userinfo['adminsession'] == 1) { if (($userinfo['adminsession'] ?? 0) == 1) {
$users[$item['customerid']]['loginname'] = $item['customer']['loginname']; $users[$item['customerid']]['loginname'] = $item['customer']['loginname'];
} }
$users[$item['customerid']]['total'] += $total; $users[$item['customerid']]['total'] += $total;
@@ -67,6 +76,30 @@ class Traffic
$users[$item['customerid']]['ftp'] += $ftp; $users[$item['customerid']]['ftp'] += $ftp;
$users[$item['customerid']]['mail'] += $mail; $users[$item['customerid']]['mail'] += $mail;
if (!$overview) { if (!$overview) {
if (empty($years[$item['year']])) {
$years[$item['year']] = [
'total' => 0.00,
'http' => 0.00,
'ftp' => 0.00,
'mail' => 0.00,
];
}
if (empty($months[$item['month'] . '/' . $item['year']])) {
$months[$item['month'] . '/' . $item['year']] = [
'total' => 0.00,
'http' => 0.00,
'ftp' => 0.00,
'mail' => 0.00,
];
}
if (empty($days[$item['day'] . '.' . $item['month'] . '.' . $item['year']])) {
$days[$item['day'] . '.' . $item['month'] . '.' . $item['year']] = [
'total' => 0.00,
'http' => 0.00,
'ftp' => 0.00,
'mail' => 0.00,
];
}
// per year // per year
$years[$item['year']]['total'] += $total; $years[$item['year']]['total'] += $total;
$years[$item['year']]['http'] += $http; $years[$item['year']]['http'] += $http;
@@ -86,7 +119,12 @@ class Traffic
} }
// calculate overview for given range from users // calculate overview for given range from users
$metrics = []; $metrics = [
'total' => 0.00,
'http' => 0.00,
'ftp' => 0.00,
'mail' => 0.00,
];
foreach ($users as $user) { foreach ($users as $user) {
$metrics['total'] += $user['total']; $metrics['total'] += $user['total'];
$metrics['http'] += $user['http']; $metrics['http'] += $user['http'];
@@ -102,6 +140,14 @@ class Traffic
$years_avail = $sel_stmt->fetchAll(\PDO::FETCH_ASSOC); $years_avail = $sel_stmt->fetchAll(\PDO::FETCH_ASSOC);
} }
// sort users by total traffic
uasort($users, function ($user_a, $user_b) {
if ($user_a['total'] == $user_b['total']) {
return 0;
}
return ($user_a['total'] < $user_b['total']) ? 1 : -1;
});
return [ return [
'metrics' => $metrics, 'metrics' => $metrics,
'users' => $users, 'users' => $users,

View File

@@ -25,6 +25,7 @@
namespace Froxlor\UI\Callbacks; namespace Froxlor\UI\Callbacks;
use Froxlor\CurrentUser;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Domain\Domain as DDomain; use Froxlor\Domain\Domain as DDomain;
use Froxlor\FileDir; use Froxlor\FileDir;
@@ -33,23 +34,36 @@ use Froxlor\UI\Panel\UI;
class Domain class Domain
{ {
public static function domainLink(array $attributes) public static function domainEditLink(array $attributes): array
{ {
return '<a href="https://' . $attributes['data'] . '" target="_blank">' . $attributes['data'] . '</a>'; $linker = UI::getLinker();
return [
'macro' => 'link',
'data' => [
'text' => $attributes['data'],
'href' => $linker->getLink([
'section' => 'domains',
'page' => 'domains',
'action' => 'edit',
'id' => $attributes['fields']['id'],
]),
'target' => '_blank'
]
];
} }
public static function domainWithCustomerLink(array $attributes) public static function domainWithCustomerLink(array $attributes): string
{ {
$linker = UI::getLinker(); $linker = UI::getLinker();
$result = '<a href="https://' . $attributes['data'] . '" target="_blank">' . $attributes['data'] . '</a>'; $result = '<a href="https://' . $attributes['data'] . '" target="_blank">' . $attributes['data'] . '</a>';
if ((int)UI::getCurrentUser()['adminsession'] == 1 && $attributes['fields']['customerid']) { if ((int)UI::getCurrentUser()['adminsession'] == 1 && $attributes['fields']['customerid']) {
$result .= ' (<a href="' . $linker->getLink([ $result .= ' (<a href="' . $linker->getLink([
'section' => 'customers', 'section' => 'customers',
'page' => 'customers', 'page' => 'customers',
'action' => 'su', 'action' => 'su',
'sort' => $attributes['fields']['loginname'], 'sort' => $attributes['fields']['loginname'],
'id' => $attributes['fields']['customerid'], 'id' => $attributes['fields']['customerid'],
]) . '">' . $attributes['fields']['loginname'] . '</a>)'; ]) . '">' . $attributes['fields']['loginname'] . '</a>)';
} }
return $result; return $result;
} }
@@ -60,6 +74,9 @@ class Domain
if ($attributes['fields']['deactivated']) { if ($attributes['fields']['deactivated']) {
return lng('admin.deactivated'); return lng('admin.deactivated');
} }
if ($attributes['fields']['email_only']) {
return lng('domains.email_only');
}
// path or redirect // path or redirect
if (preg_match('/^https?\:\/\//', $attributes['fields']['documentroot'])) { if (preg_match('/^https?\:\/\//', $attributes['fields']['documentroot'])) {
return [ return [
@@ -81,7 +98,7 @@ class Domain
return lng('domains.aliasdomain') . ' ' . $attributes['fields']['aliasdomain']; return lng('domains.aliasdomain') . ' ' . $attributes['fields']['aliasdomain'];
} }
public static function domainExternalLinkInfo(array $attributes) public static function domainExternalLinkInfo(array $attributes): string
{ {
$result = ''; $result = '';
if ($attributes['fields']['parentdomainid'] != 0) { if ($attributes['fields']['parentdomainid'] != 0) {
@@ -89,7 +106,11 @@ class Domain
} }
$result .= '<a href="http://' . $attributes['data'] . '" target="_blank">' . $attributes['data'] . '</a>'; $result .= '<a href="http://' . $attributes['data'] . '" target="_blank">' . $attributes['data'] . '</a>';
// check for statistics if parentdomainid==0 to show stats-link for customers // check for statistics if parentdomainid==0 to show stats-link for customers
if ((int)UI::getCurrentUser()['adminsession'] == 0 && $attributes['fields']['parentdomainid'] == 0 && $attributes['fields']['deactivated'] == 0) { if ((int)UI::getCurrentUser()['adminsession'] == 0
&& $attributes['fields']['parentdomainid'] == 0
&& $attributes['fields']['deactivated'] == 0
&& preg_match('/^https?:\/\/(.*)/i', $attributes['fields']['documentroot']) == false
) {
$statsapp = Settings::Get('system.traffictool'); $statsapp = Settings::Get('system.traffictool');
$result .= ' <a href="http://' . $attributes['data'] . '/' . $statsapp . '" rel="external" target="_blank" title="' . lng('domains.statstics') . '"><i class="fa-solid fa-chart-line text-secondary"></i></a>'; $result .= ' <a href="http://' . $attributes['data'] . '/' . $statsapp . '" rel="external" target="_blank" title="' . lng('domains.statstics') . '"><i class="fa-solid fa-chart-line text-secondary"></i></a>';
} }
@@ -104,7 +125,7 @@ class Domain
public static function canEdit(array $attributes): bool public static function canEdit(array $attributes): bool
{ {
return (bool)($attributes['fields']['caneditdomain'] && !$attributes['fields']['deactivated']); return $attributes['fields']['caneditdomain'] && !$attributes['fields']['deactivated'];
} }
public static function canViewLogs(array $attributes): bool public static function canViewLogs(array $attributes): bool
@@ -139,6 +160,7 @@ class Domain
&& $attributes['fields']['caneditdomain'] == '1' && $attributes['fields']['caneditdomain'] == '1'
&& Settings::Get('system.bind_enable') == '1' && Settings::Get('system.bind_enable') == '1'
&& Settings::Get('system.dnsenabled') == '1' && Settings::Get('system.dnsenabled') == '1'
&& !$attributes['fields']['email_only']
&& !$attributes['fields']['deactivated']; && !$attributes['fields']['deactivated'];
} }
@@ -154,14 +176,16 @@ class Domain
return ((bool)$attributes['fields']['letsencrypt'] && (int)$attributes['fields']['email_only'] == 0); return ((bool)$attributes['fields']['letsencrypt'] && (int)$attributes['fields']['email_only'] == 0);
} }
/**
* @throws \Exception
*/
public static function canEditSSL(array $attributes): bool public static function canEditSSL(array $attributes): bool
{ {
if ( if (Settings::Get('system.use_ssl') == '1'
Settings::Get('system.use_ssl') == '1'
&& DDomain::domainHasSslIpPort($attributes['fields']['id']) && DDomain::domainHasSslIpPort($attributes['fields']['id'])
&& (int)$attributes['fields']['caneditdomain'] == 1 && (CurrentUser::isAdmin() || (!CurrentUser::isAdmin() && (int)$attributes['fields']['caneditdomain'] == 1))
&& (int)$attributes['fields']['letsencrypt'] == 0 && (int)$attributes['fields']['letsencrypt'] == 0
&& (int)$attributes['fields']['email_only'] == 0 && !(int)$attributes['fields']['email_only']
&& !$attributes['fields']['deactivated'] && !$attributes['fields']['deactivated']
) { ) {
return true; return true;
@@ -192,15 +216,15 @@ class Domain
], ],
]; ];
// specified certificate for domain
if ($attributes['fields']['domain_hascert'] == 1) { if ($attributes['fields']['domain_hascert'] == 1) {
// specified certificate for domain
$result['icon'] .= ' text-success'; $result['icon'] .= ' text-success';
} // shared certificates (e.g. subdomain of domain where certificate is specified) } elseif ($attributes['fields']['domain_hascert'] == 2) {
elseif ($attributes['fields']['domain_hascert'] == 2) { // shared certificates (e.g. subdomain of domain where certificate is specified)
$result['icon'] .= ' text-warning'; $result['icon'] .= ' text-warning';
$result['title'] .= "\n" . lng('panel.ssleditor_infoshared'); $result['title'] .= "\n" . lng('panel.ssleditor_infoshared');
} // no certificate specified, using global fallbacks (IPs and Ports or if empty SSL settings) } elseif ($attributes['fields']['domain_hascert'] == 0) {
elseif ($attributes['fields']['domain_hascert'] == 0) { // no certificate specified, using global fallbacks (IPs and Ports or if empty SSL settings)
$result['icon'] .= ' text-danger'; $result['icon'] .= ' text-danger';
$result['title'] .= "\n" . lng('panel.ssleditor_infoglobal'); $result['title'] .= "\n" . lng('panel.ssleditor_infoglobal');
} }
@@ -212,16 +236,19 @@ class Domain
public static function listIPs(array $attributes): string public static function listIPs(array $attributes): string
{ {
if (isset($attributes['fields']['ipsandports']) && !empty($attributes['fields']['ipsandports'])) { if (!empty($attributes['fields']['ipsandports'])) {
$iplist = ""; $iplist = "";
foreach ($attributes['fields']['ipsandports'] as $ipport) { foreach ($attributes['fields']['ipsandports'] as $ipport) {
$iplist .= $ipport['ip'] . ':' . $ipport['port'] . '<br>'; $iplist .= $ipport['ip'] . ':' . $ipport['port'] . '<br>';
} }
return $iplist; return $iplist;
} }
return lng('panel.empty'); return lng('panel.listing_empty');
} }
/**
* @throws \Exception
*/
public static function getPhpConfigName(array $attributes): string public static function getPhpConfigName(array $attributes): string
{ {
$sel_stmt = Database::prepare("SELECT `description` FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id"); $sel_stmt = Database::prepare("SELECT `description` FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id");
@@ -229,11 +256,11 @@ class Domain
if ((int)UI::getCurrentUser()['adminsession'] == 1) { if ((int)UI::getCurrentUser()['adminsession'] == 1) {
$linker = UI::getLinker(); $linker = UI::getLinker();
$result = '<a href="' . $linker->getLink([ $result = '<a href="' . $linker->getLink([
'section' => 'phpsettings', 'section' => 'phpsettings',
'page' => 'overview', 'page' => 'overview',
'searchfield' => 'c.id', 'searchfield' => 'c.id',
'searchtext' => $attributes['data'], 'searchtext' => $attributes['data'],
]) . '">' . $phpconfig['description'] . '</a>'; ]) . '">' . $phpconfig['description'] . '</a>';
} else { } else {
$result = $phpconfig['description']; $result = $phpconfig['description'];
} }

Some files were not shown because too many files have changed in this diff Show More