Compare commits

...

132 Commits

Author SHA1 Message Date
Michael Kaufmann
10555bff76 set version to 2.0.23 for upcoming bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-09-03 20:16:18 +02:00
Michael Kaufmann
37aa7af4da check for existing userinfo if settings are being imported via cli
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-09-02 17:11:06 +02:00
Michael Kaufmann
4b75369597 only check non-admin resources if user is not an admin in navigation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-09-02 15:53:15 +02:00
Michael Kaufmann
9d0e463906 set version to 2.0.22 for upcoming maintenance release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-09-01 09:58:33 +02:00
Daniel
a7198f58ce Fix"Add" shortcut link in email address navigation (#1169)
Seems to have changed when adding the domain-filter overview for email addresses, but not updated in the navigation.
2023-08-13 08:19:32 +02:00
Michael Kaufmann
47be4b2847 remove shortcode for --diff-params in configdiff command
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-08-12 09:04:58 +02:00
Daniel
b0fae4bd14 Add config-diff CLI Command (#1168)
---------

Co-authored-by: Michael Kaufmann <d00p@froxlor.org>
2023-08-12 09:03:16 +02:00
Michael Kaufmann
4711a41436 correct validation of hostingplan name and description
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-08-11 13:57:21 +02:00
Michael Kaufmann
faa71ceaef forgot to save one file for the last commit
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-08-11 12:13:33 +02:00
Michael Kaufmann
2d30394150 correctly redirect to last-page if session is timed out and remove passing script/qrystr url parameters
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-08-11 12:09:23 +02:00
Michael Kaufmann
99c1182af8 adjustments in installation for debian 12 and fcgid / disabling mod_php; thx to Konstantin
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-07-27 11:25:03 +02:00
Michael Kaufmann
d9abe58dd2 adjust proftpd config for debian 12 bookworm
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-07-22 13:00:11 +02:00
Michael Kaufmann
23034b8ad2 rework path to certificates non-ecc/ecc, regardless of current setting
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-07-21 08:54:29 +02:00
Michael Kaufmann
1cae5638d3 fix optional-flag for IpsAndPorts.add() and IpsAndPorts.update()
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-07-16 17:09:45 +02:00
Michael Kaufmann
ce9a5f97a3 validate non-empy admin-name in Admins.update()
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-07-16 16:44:46 +02:00
Michael Kaufmann
c38b90deef Merge branch 'main' of github.com:Froxlor/Froxlor 2023-07-07 09:52:37 +02:00
Michael Kaufmann
13daa7d6fa set version to 2.0.21 for upcoming maintenance release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-07-07 09:50:50 +02:00
Michael Kaufmann
b0e43d332d validate generated config-json parameter string
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-07-07 09:47:17 +02:00
jabertwo
75c8754fb4 Fix typo in pathDescriptionSubdomain (#1156) 2023-06-26 11:03:48 +02:00
Michael Kaufmann
e0fa64f897 fix update-check unit-tests now that the current testing version is at 2.1.0-dev1
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-06-08 12:33:48 +02:00
Michael Kaufmann
ed72fd1766 exclude password fields from being filtered/escaped by AntiXSS, fixes #1150
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-06-08 12:14:13 +02:00
Michael Kaufmann
826ae36647 adjust log-levels in API methods
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-06-05 12:13:38 +02:00
Michael Kaufmann
9ddf24539e remove hidden fields from login/passwd-reset; refs #1102
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-06-05 12:10:39 +02:00
Grigory Morozov
3940c1429d Correcting Nginx location match, fixes #1153 2023-06-05 08:06:44 +02:00
Michael Kaufmann
c236d9eaab set version to 2.0.20 for upcoming release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-06-02 20:13:36 +02:00
Michael Kaufmann
688994e40c idna encode umlaut-emailaddresses when adding email-forwarder
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-29 20:52:57 +02:00
Michael Kaufmann
9facaee809 re-enable fcgid/php-fpm activation-validate-check
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-28 15:49:06 +02:00
Michael Kaufmann
a7dd5f4685 show 0 value of resource-fields if value is empty, fixes #1149
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-28 10:46:28 +02:00
Michael Kaufmann
da810ea953 secure filename of local-archive in webupdate
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-25 09:51:55 +02:00
Michael Kaufmann
51b6e067e8 idna encode umlaut-emailaddresses when adding/editing email-account; use correct password-suggestion-layout in change-email-account formfield
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-25 08:26:39 +02:00
Michael Kaufmann
34cf6698bc remove superfluous try_files in nginx config if php-backend (non-fastcgi) is used
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-15 20:14:26 +02:00
Michael Kaufmann
4642160724 add same loginfail restrictions for entering 2fa code as for user/pwd login
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-12 10:36:27 +02:00
Nicolas Thumann
78a259ef3b Fix IPv6 address in cookie domain (#1137)
* Implement getCookieHost to extract cookie host from HTTP_HOST
2023-05-10 08:26:08 +02:00
Nicolas Thumann
68cf4ab69a Fix typo in English privileged_passwd (#1136) 2023-05-09 18:52:43 +02:00
Michael Kaufmann
d5661d492d set version to 2.0.19 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-07 11:07:31 +02:00
Michael Kaufmann
6900898ae1 typo in updater
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-07 11:03:21 +02:00
Michael Kaufmann
d90fb7fa68 fix mysql-pdo check on installation, set version to 2.0.18 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-07 10:54:47 +02:00
Michael Kaufmann
4ea8629fcc set version to 2.0.17 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-06 22:08:43 +02:00
Michael Kaufmann
9d4ff8698d fix ratelimiting when settings do not exist (yet)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-06 22:00:19 +02:00
Michael Kaufmann
b164038846 set version to 2.0.16 for upcoming maintenance release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-05 21:10:01 +02:00
Michael Kaufmann
5c46960734 fix language mixup for rate-limit-interval setting
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-05 13:21:12 +02:00
Michael Kaufmann
a7f4f0c737 output nicer message when hitting rate limit
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-04 10:55:34 +02:00
Michael Kaufmann
b64dd501dd fix missing use-statement
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-02 10:27:28 +02:00
Michael Kaufmann
1679675aa1 introduce http-request rate-limit; smaller fixes
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-05-02 10:19:53 +02:00
sro0
640466f301 Disable autocomplete on 2FA input element (#1133)
2FA codes change every login. So there is no need to save entered values in browser and suggest them again during future logins.

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 13:21:01 +01:00
Michael Kaufmann
64d8bf4fba avoid socket length limitations leading to cut-off/invalid filename for very long domain and/or loginnames, fixes #1108
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-15 13:18:43 +01:00
Michael Kaufmann
ae6ee95973 avoid using posix-extension function before requirement-check can test for it and inform user
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-15 08:54:50 +01:00
Michael Kaufmann
e9051dc30a add spanish language translation reference to german language file
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-08 09:48:13 +01:00
scramatte
b6c7c53c3a Add Spanish language (#1105)
* Add Spanish localization
* add spanish to languages list
2023-03-08 09:43:35 +01:00
Michael Kaufmann
f36bc61fc7 better validation for uploaded/imported image files
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-08 09:33:30 +01:00
Michael Kaufmann
c56e0b9dac add 'Passing HTTP AUTH BASIC' header option when using FCGID; fix typeerror in parameter for Froxlor\Dns\Dns; require php-gd extension for validating uploaded images
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-08 09:33:02 +01:00
Michael Kaufmann
1deb08bf75 use correct parameter in PowerDNS::cleanDomainZone(), fixes #1104
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-04 14:42:31 +01:00
Michael Kaufmann
b30d7a8252 set version to 2.0.13 for maintenance release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-03 13:05:09 +01:00
Michael Kaufmann
b03e11c18d fix email-domain navigation and descriptions
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-03-03 13:03:12 +01:00
Michael Kaufmann
bf7d22a794 typecast parameter values for sizeReadable(), fixes #1103
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-28 08:15:31 +01:00
Michael Kaufmann
fb57a8a3b5 update dependencies
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-26 10:24:44 +01:00
Mickey
0d625797b0 Add command to remove debians prerotate script (#1101)
Co-authored-by: Mickey Knox <mickey@netfreaks.org>
2023-02-22 10:01:25 +01:00
Michael Kaufmann
6777fbf229 type-safe comparsion of md5-compatibility hash-validation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-22 09:35:27 +01:00
Michael Kaufmann
23f1f79eff specify clearly which tls settings are being overwritten/ignored depending on the 'Override system TLS settings' flag when adding/updating Domains
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-19 12:27:56 +01:00
Michael Kaufmann
a5af104d53 keep search-fields/text in pagination links of displaying a search-result
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-19 12:06:47 +01:00
Michael Kaufmann
38d94698ce set version to 2.0.12 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-17 20:47:17 +01:00
Michael Kaufmann
5ba28ef599 fix wrong request-parameter reading for table-column mangement
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-17 19:32:27 +01:00
Michael Kaufmann
a3486cc5b3 updated workflow for building/deploying documentation; added missing api-method-description for EmailDomains-API
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-17 17:53:32 +01:00
Michael Kaufmann
5ab322ab1d remove unused required function parameter in nginx cron; set default value for function parameter in lighttpd cron
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-17 13:19:48 +01:00
Michael Kaufmann
4f26bdd535 set version to 2.0.11 for upcoming release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-17 10:26:32 +01:00
Michael Kaufmann
88f76e4355 use bcrypt hash algorithms for htpasswd password hashing instead of the old SHA1
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-17 10:00:08 +01:00
Oliver Rahner
a464d8cb19 fixed duplicated column heading (#1100) 2023-02-15 20:44:57 +01:00
Michael Kaufmann
0f596dce8b fix api parameter issue when empty values are passed
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-15 08:33:49 +01:00
Michael Kaufmann
60270b20b3 backup possible remote-db-server databases in backup-cron
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-14 11:36:39 +01:00
Michael Kaufmann
4003a8d2b6 check for existing fields when setting/updating tablelisting-columns
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-14 11:36:11 +01:00
Michael Kaufmann
89843d6f37 fix referenced quota field for searching/sorting, fixes #1099
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-12 10:08:41 +01:00
Michael Kaufmann
256a52a5da fix setting incorrect acme-challenge path on installation; fixes #1097
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-11 08:54:09 +01:00
Michael Kaufmann
c9b2bfe53c fix pagination for entity-listings with extra parameters
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-11 08:52:43 +01:00
Michael Kaufmann
98cb36327e add SPF/DKIM to Subdomain DNS Zone separately if isemaildomain = 1
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-08 12:37:20 +01:00
Michael Kaufmann
7d23e4882d fix '0 illegal offset type' when changing mysql-access-host setting
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-08 12:36:38 +01:00
Michael Kaufmann
1cc3a1d066 re-add special image_data import for exported custom-logos
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-07 17:21:39 +01:00
Michael Kaufmann
de0f7d2f01 generalize array-index name of settings to be settingsgroup_varname
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-07 13:39:09 +01:00
Michael Kaufmann
aa48ffca2b run Form::processForm() when importing settings so the same validations apply if the import file has malicious content
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-07 13:02:11 +01:00
Michael Kaufmann
802168cb5b forgot to add Validate/Validate to the last commit
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-07 11:45:57 +01:00
Michael Kaufmann
6ace2e9f3d corrected call to Domain::triggerLetsEncryptCSRForAliasDestinationDomain only if aliasdomain is a valid id; validate registration-date and termination-date only if given
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-07 11:44:07 +01:00
Michael Kaufmann
0bff360d22 another type fix
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-07 11:26:13 +01:00
Michael Kaufmann
e300acf109 corrected return type of pexecute_first
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-07 11:23:21 +01:00
Michael Kaufmann
14d8e12cdc honor deactivated flag for redirects and prepare to use domain.deactivated for domain-specific deactivation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-07 11:19:31 +01:00
Michael Kaufmann
d29411dba6 backup nginx.conf when configuring service
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-07 11:15:56 +01:00
Michael Kaufmann
464663877c cleanup function/parameters and add type declarations where possible
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-07 11:15:19 +01:00
Michael Kaufmann
c3f769d48b remove robots.txt to actually make meta-tag robots work; fixes #1096
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-06 14:27:23 +01:00
Michael Kaufmann
f97536ed02 minor adjustments in customer-email-domain-overview
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-05 18:30:29 +01:00
Michael Kaufmann
7686effc8c new setting to select default value of 'allow api access' for new customers; fixes #1087
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-03 14:16:37 +01:00
Michael Kaufmann
ee8385467b add fallback to system-hostname for faulty http-clients not setting 'Host' in the request
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-03 13:59:12 +01:00
Michael Kaufmann
0a51d97684 add translation for new email domain overview
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-03 13:57:28 +01:00
Michael Kaufmann
67fc762eef fix let's encrypt dns validation check caused by issue in PhpHelper::gethostbynamel6()
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-01-31 17:32:56 +01:00
Michael Kaufmann
8378795f5d Merge branch 'main' of github.com:Froxlor/Froxlor 2023-01-31 09:33:02 +01:00
Michael Kaufmann
98e6f1df4a Merge branch 'main' of github.com:Froxlor/Froxlor 2023-01-31 09:32:21 +01:00
Michael Kaufmann
674e35e5c5 add new EmailDomains API Commands for listing domain/email-usage information; show email-domain overview when customer has >1 domains with email addresses; add EmailDomains to GlobalSearch
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-01-31 09:31:58 +01:00
Michael Kaufmann
b24ca44e6f fix typos
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-01-30 08:47:11 +01:00
Michael Kaufmann
e0f7fcd2ef fix awstats path in generated vhost config if speciallogfile=0
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-01-29 15:58:49 +01:00
Michael Kaufmann
97c4c9a366 Merge branch 'main' of github.com:Froxlor/Froxlor 2023-01-23 09:00:21 +01:00
Michael Kaufmann
ed50e03957 Merge remote-tracking branch 'origin/main' into customeremail-overview 2023-01-22 14:03:07 +01:00
Michael Kaufmann
ea88d53e39 Merge remote-tracking branch 'origin/main' into customeremail-overview 2023-01-12 09:59:22 +01:00
Michael Kaufmann
61f6a474e4 add emails-overview tablelisting
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-01-12 09:59:19 +01:00
Michael Kaufmann
82af9af1e1 group email-domains in overview if there are email addresses for multiple domains
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-01-11 22:22:39 +01:00
166 changed files with 6358 additions and 9339 deletions

View File

@@ -1,4 +1,4 @@
name: build-docs name: build-documentation
on: on:
release: release:
@@ -11,4 +11,4 @@ jobs:
- env: - env:
GITHUB_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN }}
run: | run: |
gh workflow run --repo Froxlor/Documentation build-docs -f ref=${{github.ref_name}} gh workflow run --repo Froxlor/Documentation build-and-deploy.yml -f type=tags ref=${{github.ref_name}}

View File

@@ -138,6 +138,26 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'system_req_limit_per_interval' => [
'label' => lng('serversettings.req_limit_per_interval'),
'settinggroup' => 'system',
'varname' => 'req_limit_per_interval',
'type' => 'number',
'min' => 30,
'default' => 60,
'save_method' => 'storeSettingField',
'advanced_mode' => true
],
'system_req_limit_interval' => [
'label' => lng('serversettings.req_limit_interval'),
'settinggroup' => 'system',
'varname' => 'req_limit_interval',
'type' => 'number',
'min' => 5,
'default' => 60,
'save_method' => 'storeSettingField',
'advanced_mode' => true
],
'customer_accountprefix' => [ 'customer_accountprefix' => [
'label' => lng('serversettings.accountprefix'), 'label' => lng('serversettings.accountprefix'),
'settinggroup' => 'customer', 'settinggroup' => 'customer',

View File

@@ -109,7 +109,19 @@ return [
'default' => false, 'default' => false,
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],
'update_channel' => [ 'api_customer_default' => [
'label' => lng('serversettings.api_customer_default'),
'settinggroup' => 'api',
'varname' => 'customer_default',
'type' => 'select',
'default' => 1,
'select_var' => [
1 => lng('panel.yes'),
0 => lng('panel.no')
],
'save_method' => 'storeSettingField'
],
'system_update_channel' => [
'label' => lng('serversettings.update_channel'), 'label' => lng('serversettings.update_channel'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'update_channel', 'varname' => 'update_channel',
@@ -122,7 +134,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'system_validatedomain' => [ 'system_validate_domain' => [
'label' => lng('serversettings.validate_domain'), 'label' => lng('serversettings.validate_domain'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'validate_domain', 'varname' => 'validate_domain',
@@ -307,7 +319,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'hide_incompatible_settings' => [ 'system_hide_incompatible_settings' => [
'label' => lng('serversettings.hide_incompatible_settings'), 'label' => lng('serversettings.hide_incompatible_settings'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'hide_incompatible_settings', 'varname' => 'hide_incompatible_settings',

View File

@@ -53,7 +53,7 @@ return [
'string_regexp' => '/^(([a-z0-9\-\._]+, ?)*[a-z0-9\-\._]+)?$/i', 'string_regexp' => '/^(([a-z0-9\-\._]+, ?)*[a-z0-9\-\._]+)?$/i',
'string_emptyallowed' => true, 'string_emptyallowed' => true,
'default' => '', 'default' => '',
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingClearCertificates',
'advanced_mode' => true 'advanced_mode' => true
], ],
/** /**
@@ -154,7 +154,7 @@ return [
/** /**
* FCGID * FCGID
*/ */
'system_mod_fcgid_enabled_ownvhost' => [ 'system_mod_fcgid_ownvhost' => [
'label' => lng('serversettings.mod_fcgid_ownvhost'), 'label' => lng('serversettings.mod_fcgid_ownvhost'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'mod_fcgid_ownvhost', 'varname' => 'mod_fcgid_ownvhost',
@@ -224,7 +224,7 @@ return [
/** /**
* php-fpm * php-fpm
*/ */
'system_phpfpm_enabled_ownvhost' => [ 'phpfpm_enabled_ownvhost' => [
'label' => lng('phpfpm.ownvhost'), 'label' => lng('phpfpm.ownvhost'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'enabled_ownvhost', 'varname' => 'enabled_ownvhost',
@@ -237,7 +237,7 @@ return [
]), ]),
'requires_reconf' => ['system:php-fpm'] 'requires_reconf' => ['system:php-fpm']
], ],
'system_phpfpm_httpuser' => [ 'phpfpm_vhost_httpuser' => [
'label' => lng('phpfpm.vhost_httpuser'), 'label' => lng('phpfpm.vhost_httpuser'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'vhost_httpuser', 'varname' => 'vhost_httpuser',
@@ -250,7 +250,7 @@ return [
]), ]),
'requires_reconf' => ['system:php-fpm'] 'requires_reconf' => ['system:php-fpm']
], ],
'system_phpfpm_httpgroup' => [ 'phpfpm_vhost_httpgroup' => [
'label' => lng('phpfpm.vhost_httpgroup'), 'label' => lng('phpfpm.vhost_httpgroup'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'vhost_httpgroup', 'varname' => 'vhost_httpgroup',
@@ -263,7 +263,7 @@ return [
]), ]),
'requires_reconf' => ['system:php-fpm'] 'requires_reconf' => ['system:php-fpm']
], ],
'system_phpfpm_defaultini_ownvhost' => [ 'phpfpm_vhost_defaultini' => [
'label' => lng('serversettings.mod_fcgid.defaultini_ownvhost'), 'label' => lng('serversettings.mod_fcgid.defaultini_ownvhost'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'vhost_defaultini', 'varname' => 'vhost_defaultini',

View File

@@ -60,7 +60,7 @@ return [
'apache2' 'apache2'
] ]
], ],
'system_apache_itksupport' => [ 'system_apacheitksupport' => [
'label' => lng('serversettings.apache_itksupport'), 'label' => lng('serversettings.apache_itksupport'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'apacheitksupport', 'varname' => 'apacheitksupport',
@@ -229,7 +229,7 @@ return [
'nginx' 'nginx'
] ]
], ],
'system_customersslpath' => [ 'system_customer_ssl_path' => [
'label' => lng('serversettings.customerssl_directory'), 'label' => lng('serversettings.customerssl_directory'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'customer_ssl_path', 'varname' => 'customer_ssl_path',
@@ -287,7 +287,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'system_apache_globaldiropt' => [ 'system_apacheglobaldiropt' => [
'label' => lng('serversettings.apache_globaldiropt'), 'label' => lng('serversettings.apache_globaldiropt'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'apacheglobaldiropt', 'varname' => 'apacheglobaldiropt',

View File

@@ -32,7 +32,7 @@ return [
'title' => lng('admin.sslsettings'), 'title' => lng('admin.sslsettings'),
'icon' => 'fa-solid fa-shield', 'icon' => 'fa-solid fa-shield',
'fields' => [ 'fields' => [
'system_ssl_enabled' => [ 'system_use_ssl' => [
'label' => lng('serversettings.ssl.use_ssl'), 'label' => lng('serversettings.ssl.use_ssl'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'use_ssl', 'varname' => 'use_ssl',

View File

@@ -33,7 +33,7 @@ return [
'lighttpd' 'lighttpd'
], ],
'fields' => [ 'fields' => [
'system_mod_fcgid_enabled' => [ 'system_mod_fcgid' => [
'label' => lng('serversettings.mod_fcgid'), 'label' => lng('serversettings.mod_fcgid'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'mod_fcgid', 'varname' => 'mod_fcgid',

View File

@@ -31,7 +31,7 @@ return [
'title' => lng('admin.phpfpm_settings'), 'title' => lng('admin.phpfpm_settings'),
'icon' => 'fa-brands fa-php', 'icon' => 'fa-brands fa-php',
'fields' => [ 'fields' => [
'system_phpfpm_enabled' => [ 'phpfpm_enabled' => [
'label' => lng('serversettings.phpfpm'), 'label' => lng('serversettings.phpfpm'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'enabled', 'varname' => 'enabled',
@@ -45,7 +45,7 @@ return [
'overview_option' => true, 'overview_option' => true,
'requires_reconf' => ['http', 'system:php-fpm'] 'requires_reconf' => ['http', 'system:php-fpm']
], ],
'system_phpfpm_defaultini' => [ 'phpfpm_defaultini' => [
'label' => lng('serversettings.mod_fcgid.defaultini'), 'label' => lng('serversettings.mod_fcgid.defaultini'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'defaultini', 'varname' => 'defaultini',
@@ -57,7 +57,7 @@ return [
], ],
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],
'system_phpfpm_aliasconfigdir' => [ 'phpfpm_aliasconfigdir' => [
'label' => lng('serversettings.phpfpm_settings.aliasconfigdir'), 'label' => lng('serversettings.phpfpm_settings.aliasconfigdir'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'aliasconfigdir', 'varname' => 'aliasconfigdir',
@@ -67,7 +67,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'system_phpfpm_tmpdir' => [ 'phpfpm_tmpdir' => [
'label' => lng('serversettings.mod_fcgid.tmpdir'), 'label' => lng('serversettings.mod_fcgid.tmpdir'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'tmpdir', 'varname' => 'tmpdir',
@@ -76,7 +76,7 @@ return [
'default' => '/var/customers/tmp/', 'default' => '/var/customers/tmp/',
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],
'system_phpfpm_peardir' => [ 'phpfpm_peardir' => [
'label' => lng('serversettings.mod_fcgid.peardir'), 'label' => lng('serversettings.mod_fcgid.peardir'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'peardir', 'varname' => 'peardir',
@@ -88,7 +88,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'system_phpfpm_envpath' => [ 'phpfpm_envpath' => [
'label' => lng('serversettings.phpfpm_settings.envpath'), 'label' => lng('serversettings.phpfpm_settings.envpath'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'envpath', 'varname' => 'envpath',
@@ -100,7 +100,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'system_phpfpm_fastcgi_ipcdir' => [ 'phpfpm_fastcgi_ipcdir' => [
'label' => lng('serversettings.phpfpm_settings.ipcdir'), 'label' => lng('serversettings.phpfpm_settings.ipcdir'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'fastcgi_ipcdir', 'varname' => 'fastcgi_ipcdir',
@@ -110,7 +110,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'system_phpfpm_use_mod_proxy' => [ 'phpfpm_use_mod_proxy' => [
'label' => lng('phpfpm.use_mod_proxy'), 'label' => lng('phpfpm.use_mod_proxy'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'use_mod_proxy', 'varname' => 'use_mod_proxy',
@@ -119,7 +119,7 @@ return [
'visible' => Settings::Get('system.apache24'), 'visible' => Settings::Get('system.apache24'),
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],
'system_phpfpm_ini_flags' => [ 'phpfpm_ini_flags' => [
'label' => lng('phpfpm.ini_flags'), 'label' => lng('phpfpm.ini_flags'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'ini_flags', 'varname' => 'ini_flags',
@@ -128,7 +128,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'system_phpfpm_ini_values' => [ 'phpfpm_ini_values' => [
'label' => lng('phpfpm.ini_values'), 'label' => lng('phpfpm.ini_values'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'ini_values', 'varname' => 'ini_values',
@@ -137,7 +137,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'system_phpfpm_ini_admin_flags' => [ 'phpfpm_ini_admin_flags' => [
'label' => lng('phpfpm.ini_admin_flags'), 'label' => lng('phpfpm.ini_admin_flags'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'ini_admin_flags', 'varname' => 'ini_admin_flags',
@@ -146,7 +146,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'system_phpfpm_ini_admin_values' => [ 'phpfpm_ini_admin_values' => [
'label' => lng('phpfpm.ini_admin_values'), 'label' => lng('phpfpm.ini_admin_values'),
'settinggroup' => 'phpfpm', 'settinggroup' => 'phpfpm',
'varname' => 'ini_admin_values', 'varname' => 'ini_admin_values',

View File

@@ -29,7 +29,7 @@ return [
'title' => lng('admin.perl_settings'), 'title' => lng('admin.perl_settings'),
'icon' => 'fa-solid fa-code', 'icon' => 'fa-solid fa-code',
'fields' => [ 'fields' => [
'perl_path' => [ 'system_perl_path' => [
'label' => lng('serversettings.perl_path'), 'label' => lng('serversettings.perl_path'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'perl_path', 'varname' => 'perl_path',
@@ -40,7 +40,7 @@ return [
'lighttpd' 'lighttpd'
] ]
], ],
'system_perl_suexecworkaround' => [ 'perl_suexecworkaround' => [
'label' => lng('serversettings.perl.suexecworkaround'), 'label' => lng('serversettings.perl.suexecworkaround'),
'settinggroup' => 'perl', 'settinggroup' => 'perl',
'varname' => 'suexecworkaround', 'varname' => 'suexecworkaround',
@@ -51,7 +51,7 @@ return [
'apache2' 'apache2'
] ]
], ],
'system_perl_suexeccgipath' => [ 'perl_suexecpath' => [
'label' => lng('serversettings.perl.suexeccgipath'), 'label' => lng('serversettings.perl.suexeccgipath'),
'settinggroup' => 'perl', 'settinggroup' => 'perl',
'varname' => 'suexecpath', 'varname' => 'suexecpath',
@@ -63,7 +63,7 @@ return [
'apache2' 'apache2'
] ]
], ],
'perl_server' => [ 'serversettings_perl_server' => [
'label' => lng('serversettings.perl_server'), 'label' => lng('serversettings.perl_server'),
'settinggroup' => 'serversettings', 'settinggroup' => 'serversettings',
'varname' => 'perl_server', 'varname' => 'perl_server',

View File

@@ -98,7 +98,7 @@ return [
'default' => 100, 'default' => 100,
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],
'system_catchall_enabled' => [ 'catchall_catchall_enabled' => [
'label' => lng('serversettings.catchall_enabled'), 'label' => lng('serversettings.catchall_enabled'),
'settinggroup' => 'catchall', 'settinggroup' => 'catchall',
'varname' => 'catchall_enabled', 'varname' => 'catchall_enabled',

View File

@@ -29,7 +29,7 @@ return [
'title' => lng('admin.ftpserversettings'), 'title' => lng('admin.ftpserversettings'),
'icon' => 'fa-solid fa-arrow-right-arrow-left', 'icon' => 'fa-solid fa-arrow-right-arrow-left',
'fields' => [ 'fields' => [
'ftpserver' => [ 'system_ftpserver' => [
'label' => lng('admin.ftpserver'), 'label' => lng('admin.ftpserver'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'ftpserver', 'varname' => 'ftpserver',

View File

@@ -31,7 +31,7 @@ return [
'title' => lng('admin.nameserversettings'), 'title' => lng('admin.nameserversettings'),
'icon' => 'fa-solid fa-globe', 'icon' => 'fa-solid fa-globe',
'fields' => [ 'fields' => [
'nameserver_enable' => [ 'system_bind_enable' => [
'label' => lng('serversettings.bindenable'), 'label' => lng('serversettings.bindenable'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'bind_enable', 'varname' => 'bind_enable',

View File

@@ -31,7 +31,7 @@ return [
'title' => lng('admin.dkimsettings'), 'title' => lng('admin.dkimsettings'),
'icon' => 'fa-solid fa-fingerprint', 'icon' => 'fa-solid fa-fingerprint',
'fields' => [ 'fields' => [
'dkim_enabled' => [ 'dkim_use_dkim' => [
'label' => lng('dkim.use_dkim'), 'label' => lng('dkim.use_dkim'),
'settinggroup' => 'dkim', 'settinggroup' => 'dkim',
'varname' => 'use_dkim', 'varname' => 'use_dkim',
@@ -40,7 +40,7 @@ return [
'save_method' => 'storeSettingFieldInsertBindTask', 'save_method' => 'storeSettingFieldInsertBindTask',
'overview_option' => true 'overview_option' => true
], ],
'dkim_prefix' => [ 'dkim_dkim_prefix' => [
'label' => lng('dkim.dkim_prefix'), 'label' => lng('dkim.dkim_prefix'),
'settinggroup' => 'dkim', 'settinggroup' => 'dkim',
'varname' => 'dkim_prefix', 'varname' => 'dkim_prefix',
@@ -59,7 +59,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'advanced_mode' => true 'advanced_mode' => true
], ],
'dkim_domains' => [ 'dkim_dkim_domains' => [
'label' => lng('dkim.dkim_domains'), 'label' => lng('dkim.dkim_domains'),
'settinggroup' => 'dkim', 'settinggroup' => 'dkim',
'varname' => 'dkim_domains', 'varname' => 'dkim_domains',
@@ -68,7 +68,7 @@ return [
'default' => 'domains', 'default' => 'domains',
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],
'dkim_dkimkeys' => [ 'dkim_dkim_dkimkeys' => [
'label' => lng('dkim.dkim_dkimkeys'), 'label' => lng('dkim.dkim_dkimkeys'),
'settinggroup' => 'dkim', 'settinggroup' => 'dkim',
'varname' => 'dkim_dkimkeys', 'varname' => 'dkim_dkimkeys',
@@ -77,7 +77,7 @@ return [
'default' => 'dkim-keys.conf', 'default' => 'dkim-keys.conf',
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],
'dkim_algorithm' => [ 'dkim_dkim_algorithm' => [
'label' => lng('dkim.dkim_algorithm'), 'label' => lng('dkim.dkim_algorithm'),
'settinggroup' => 'dkim', 'settinggroup' => 'dkim',
'varname' => 'dkim_algorithm', 'varname' => 'dkim_algorithm',
@@ -92,7 +92,7 @@ return [
'save_method' => 'storeSettingFieldInsertBindTask', 'save_method' => 'storeSettingFieldInsertBindTask',
'advanced_mode' => true 'advanced_mode' => true
], ],
'dkim_servicetype' => [ 'dkim_dkim_servicetype' => [
'label' => lng('dkim.dkim_servicetype'), 'label' => lng('dkim.dkim_servicetype'),
'settinggroup' => 'dkim', 'settinggroup' => 'dkim',
'varname' => 'dkim_servicetype', 'varname' => 'dkim_servicetype',
@@ -105,7 +105,7 @@ return [
'save_method' => 'storeSettingFieldInsertBindTask', 'save_method' => 'storeSettingFieldInsertBindTask',
'advanced_mode' => true 'advanced_mode' => true
], ],
'dkim_keylength' => [ 'dkim_dkim_keylength' => [
'label' => [ 'label' => [
'title' => lng('dkim.dkim_keylength.title'), 'title' => lng('dkim.dkim_keylength.title'),
'description' => lng('dkim.dkim_keylength.description', [Settings::Get('dkim.dkim_prefix')]) 'description' => lng('dkim.dkim_keylength.description', [Settings::Get('dkim.dkim_prefix')])
@@ -120,7 +120,7 @@ return [
], ],
'save_method' => 'storeSettingFieldInsertBindTask' 'save_method' => 'storeSettingFieldInsertBindTask'
], ],
'dkim_notes' => [ 'dkim_dkim_notes' => [
'label' => lng('dkim.dkim_notes'), 'label' => lng('dkim.dkim_notes'),
'settinggroup' => 'dkim', 'settinggroup' => 'dkim',
'varname' => 'dkim_notes', 'varname' => 'dkim_notes',
@@ -130,7 +130,7 @@ return [
'save_method' => 'storeSettingFieldInsertBindTask', 'save_method' => 'storeSettingFieldInsertBindTask',
'advanced_mode' => true 'advanced_mode' => true
], ],
'dkimrestart_command' => [ 'dkim_dkimrestart_command' => [
'label' => lng('dkim.dkimrestart_command'), 'label' => lng('dkim.dkimrestart_command'),
'settinggroup' => 'dkim', 'settinggroup' => 'dkim',
'varname' => 'dkimrestart_command', 'varname' => 'dkimrestart_command',

View File

@@ -29,7 +29,7 @@ return [
'title' => lng('admin.spfsettings'), 'title' => lng('admin.spfsettings'),
'icon' => 'fa-solid fa-clipboard-check', 'icon' => 'fa-solid fa-clipboard-check',
'fields' => [ 'fields' => [
'use_spf' => [ 'spf_use_spf' => [
'label' => lng('spf.use_spf'), 'label' => lng('spf.use_spf'),
'settinggroup' => 'spf', 'settinggroup' => 'spf',
'varname' => 'use_spf', 'varname' => 'use_spf',
@@ -38,7 +38,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'overview_option' => true 'overview_option' => true
], ],
'spf_entry' => [ 'spf_spf_entry' => [
'label' => lng('spf.spf_entry'), 'label' => lng('spf.spf_entry'),
'settinggroup' => 'spf', 'settinggroup' => 'spf',
'varname' => 'spf_entry', 'varname' => 'spf_entry',

View File

@@ -30,7 +30,7 @@ return [
'icon' => 'fa-solid fa-sliders', 'icon' => 'fa-solid fa-sliders',
'advanced_mode' => true, 'advanced_mode' => true,
'fields' => [ 'fields' => [
'diskquota_enabled' => [ 'system_diskquota_enabled' => [
'label' => lng('serversettings.diskquota_enabled'), 'label' => lng('serversettings.diskquota_enabled'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'diskquota_enabled', 'varname' => 'diskquota_enabled',
@@ -39,7 +39,7 @@ return [
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'overview_option' => true 'overview_option' => true
], ],
'diskquota_repquota_path' => [ 'system_diskquota_repquota_path' => [
'label' => lng('serversettings.diskquota_repquota_path.description'), 'label' => lng('serversettings.diskquota_repquota_path.description'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'diskquota_repquota_path', 'varname' => 'diskquota_repquota_path',
@@ -47,7 +47,7 @@ return [
'default' => '/usr/sbin/repquota', 'default' => '/usr/sbin/repquota',
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],
'diskquota_quotatool_path' => [ 'system_diskquota_quotatool_path' => [
'label' => lng('serversettings.diskquota_quotatool_path.description'), 'label' => lng('serversettings.diskquota_quotatool_path.description'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'diskquota_quotatool_path', 'varname' => 'diskquota_quotatool_path',
@@ -55,7 +55,7 @@ return [
'default' => '/usr/bin/quotatool', 'default' => '/usr/bin/quotatool',
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
], ],
'diskquota_customer_partition' => [ 'system_diskquota_customer_partition' => [
'label' => lng('serversettings.diskquota_customer_partition.description'), 'label' => lng('serversettings.diskquota_customer_partition.description'),
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'diskquota_customer_partition', 'varname' => 'diskquota_customer_partition',

View File

@@ -77,6 +77,7 @@ if (($page == 'admins' || $page == 'overview') && $userinfo['change_serversettin
$result['switched_user'] = CurrentUser::getData(); $result['switched_user'] = CurrentUser::getData();
$result['adminsession'] = 1; $result['adminsession'] = 1;
$result['userid'] = $result['adminid']; $result['userid'] = $result['adminid'];
session_regenerate_id(true);
CurrentUser::setData($result); CurrentUser::setData($result);
$log->logAction( $log->logAction(

View File

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

View File

@@ -33,6 +33,7 @@ use Froxlor\Settings;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request; use Froxlor\UI\Request;
use Froxlor\UI\Response; use Froxlor\UI\Response;
use Froxlor\Validate\Validate;
if ($userinfo['change_serversettings'] == '1') { if ($userinfo['change_serversettings'] == '1') {
if ($action == 'setconfigured') { if ($action == 'setconfigured') {
@@ -91,6 +92,7 @@ if ($userinfo['change_serversettings'] == '1') {
} }
if ($distribution != "" && isset($_POST['finish'])) { if ($distribution != "" && isset($_POST['finish'])) {
$valid_keys = ['http', 'dns', 'smtp', 'mail', 'ftp', 'system', 'distro'];
unset($_POST['finish']); unset($_POST['finish']);
unset($_POST['csrf_token']); unset($_POST['csrf_token']);
$params = $_POST; $params = $_POST;
@@ -99,6 +101,20 @@ if ($userinfo['change_serversettings'] == '1') {
foreach ($_POST['system'] as $sysdaemon) { foreach ($_POST['system'] as $sysdaemon) {
$params['system'][] = $sysdaemon; $params['system'][] = $sysdaemon;
} }
// validate params
foreach ($params as $key => $value) {
if (!in_array($key, $valid_keys)) {
unset($params[$key]);
continue;
}
if (!is_array($value)) {
$params[$key] = Validate::validate($value, $key);
} else {
foreach ($value as $subkey => $subvalue) {
$params[$key][$subkey] = Validate::validate($subvalue, $key.'.'.$subkey);
}
}
}
$params_content = json_encode($params); $params_content = json_encode($params);
$params_filename = FileDir::makeCorrectFile(Froxlor::getInstallDir() . 'install/' . Froxlor::genSessionId() . '.json'); $params_filename = FileDir::makeCorrectFile(Froxlor::getInstallDir() . 'install/' . Froxlor::genSessionId() . '.json');
file_put_contents($params_filename, $params_content); file_put_contents($params_filename, $params_content);

View File

@@ -93,6 +93,7 @@ if (($page == 'customers' || $page == 'overview') && $userinfo['customers'] != '
$result['switched_user'] = CurrentUser::getData(); $result['switched_user'] = CurrentUser::getData();
$result['adminsession'] = 0; $result['adminsession'] = 0;
$result['userid'] = $result['customerid']; $result['userid'] = $result['customerid'];
session_regenerate_id(true);
CurrentUser::setData($result); CurrentUser::setData($result);
$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 . "'");

View File

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

View File

@@ -53,6 +53,7 @@ if ($action == 'logout') {
if (is_array(CurrentUser::getField('switched_user'))) { if (is_array(CurrentUser::getField('switched_user'))) {
$result = CurrentUser::getData(); $result = CurrentUser::getData();
$result = $result['switched_user']; $result = $result['switched_user'];
session_regenerate_id(true);
CurrentUser::setData($result); CurrentUser::setData($result);
$target = (isset($_GET['target']) ? $_GET['target'] : 'index'); $target = (isset($_GET['target']) ? $_GET['target'] : 'index');
$redirect = "admin_" . $target . ".php"; $redirect = "admin_" . $target . ".php";

View File

@@ -26,6 +26,7 @@
declare(strict_types=1); declare(strict_types=1);
use Froxlor\Cli\ConfigDiff;
use Symfony\Component\Console\Application; use Symfony\Component\Console\Application;
use Froxlor\Cli\RunApiCommand; use Froxlor\Cli\RunApiCommand;
use Froxlor\Cli\ConfigServices; use Froxlor\Cli\ConfigServices;
@@ -61,4 +62,5 @@ $application->add(new InstallCommand());
$application->add(new MasterCron()); $application->add(new MasterCron());
$application->add(new UserCommand()); $application->add(new UserCommand());
$application->add(new ValidateAcmeWebroot()); $application->add(new ValidateAcmeWebroot());
$application->add(new ConfigDiff());
$application->run(); $application->run();

View File

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

781
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -26,9 +26,10 @@
const AREA = 'customer'; const AREA = 'customer';
require __DIR__ . '/lib/init.php'; require __DIR__ . '/lib/init.php';
use Froxlor\Api\Commands\EmailAccounts as EmailAccounts; use Froxlor\Api\Commands\EmailAccounts;
use Froxlor\Api\Commands\EmailForwarders as EmailForwarders; use Froxlor\Api\Commands\EmailForwarders;
use Froxlor\Api\Commands\Emails as Emails; use Froxlor\Api\Commands\Emails;
use Froxlor\Api\Commands\EmailDomains;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\PhpHelper; use Froxlor\PhpHelper;
@@ -50,13 +51,50 @@ if (Settings::IsInList('panel.customer_hide_options', 'email') || $userinfo['ema
$id = (int)Request::any('id'); $id = (int)Request::any('id');
if ($page == 'overview' || $page == 'emails') { if ($page == 'overview' || $page == 'emails') {
$result_stmt = Database::prepare("
SELECT COUNT(DISTINCT `domainid`) as maildomains FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid`= :cid
");
$domain_count = Database::pexecute_first($result_stmt, [
"cid" => $userinfo['customerid']
]);
if ($domain_count['maildomains'] && $domain_count['maildomains'] > 1) {
try {
$emaildomain_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.emails_overview.php';
$collection = (new Collection(EmailDomains::class, $userinfo))
->withPagination($emaildomain_list_data['emaildomain_list']['columns'],
$emaildomain_list_data['emaildomain_list']['default_sorting']);
} catch (Exception $e) {
Response::dynamicError($e->getMessage());
}
UI::view('user/table.html.twig', [
'listing' => Listing::format($collection, $emaildomain_list_data, 'emaildomain_list'),
'actions_links' => CurrentUser::canAddResource('emails') ? [
[
'href' => $linker->getLink(['section' => 'email', 'page' => 'email_domain', 'action' => 'add']),
'label' => lng('emails.emails_add')
]
] : null,
]);
} else {
// only emails for one domain -> show email address listing directly
$page = 'email_domain';
}
}
if ($page == 'email_domain') {
$email_domainid = Request::any('domainid', 0);
if ($action == '') { if ($action == '') {
$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed customer_email::emails"); $log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed customer_email::emails");
$sql_search = [];
if ($email_domainid > 0) {
$sql_search = ['sql_search' => ['m.domainid' => ['op' => '=', 'value' => $email_domainid]]];
}
try { try {
$email_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.emails.php'; $email_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/customer/tablelisting.emails.php';
$collection = (new Collection(Emails::class, $userinfo)) $collection = (new Collection(Emails::class, $userinfo, $sql_search))
->withPagination($email_list_data['email_list']['columns'], $email_list_data['email_list']['default_sorting']); ->withPagination($email_list_data['email_list']['columns'],
$email_list_data['email_list']['default_sorting']);
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
@@ -71,13 +109,22 @@ if ($page == 'overview' || $page == 'emails') {
]); ]);
$emaildomains_count = $result2['emaildomains']; $emaildomains_count = $result2['emaildomains'];
$actions_links = false; $actions_links = [];
if ($email_domainid > 0) {
$actions_links[] = [
'class' => 'btn-outline-primary',
'href' => $linker->getLink([
'section' => 'email',
'page' => 'emails',
]),
'label' => lng('emails.back_to_overview'),
'icon' => 'fa-solid fa-reply'
];
}
if (CurrentUser::canAddResource('emails')) { if (CurrentUser::canAddResource('emails')) {
$actions_links = [ $actions_links[] = [
[ 'href' => $linker->getLink(['section' => 'email', 'page' => 'email_domain', 'action' => 'add', 'domainid' => $email_domainid]),
'href' => $linker->getLink(['section' => 'email', 'page' => $page, 'action' => 'add']), 'label' => lng('emails.emails_add')
'label' => lng('emails.emails_add')
]
]; ];
} }
@@ -145,7 +192,11 @@ if ($page == 'overview' || $page == 'emails') {
"cid" => $userinfo['customerid'] "cid" => $userinfo['customerid']
]); ]);
$domains = []; $domains = [];
$selected_domain = "";
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
if ($email_domainid == $row['id']) {
$selected_domain = $row['domain'];
}
$domains[$row['domain']] = $idna_convert->decode($row['domain']); $domains[$row['domain']] = $idna_convert->decode($row['domain']);
} }
@@ -244,11 +295,13 @@ if ($page == 'overview' || $page == 'emails') {
} }
Response::redirectTo($filename, [ Response::redirectTo($filename, [
'page' => $page, 'page' => $page,
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id,
]); ]);
} }
} elseif ($page == 'accounts') { } elseif ($page == 'accounts') {
$email_domainid = Request::any('domainid', 0);
if ($action == 'add' && $id != 0) { if ($action == 'add' && $id != 0) {
if ($userinfo['email_accounts'] == '-1' || ($userinfo['email_accounts_used'] < $userinfo['email_accounts'])) { if ($userinfo['email_accounts'] == '-1' || ($userinfo['email_accounts_used'] < $userinfo['email_accounts'])) {
try { try {
@@ -267,7 +320,8 @@ if ($page == 'overview' || $page == 'emails') {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
Response::redirectTo($filename, [ Response::redirectTo($filename, [
'page' => 'emails', 'page' => 'email_domain',
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id
]); ]);
@@ -292,7 +346,8 @@ if ($page == 'overview' || $page == 'emails') {
'class' => 'btn-secondary', 'class' => 'btn-secondary',
'href' => $linker->getLink([ 'href' => $linker->getLink([
'section' => 'email', 'section' => 'email',
'page' => 'emails', 'page' => 'email_domain',
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id
]), ]),
@@ -301,7 +356,11 @@ if ($page == 'overview' || $page == 'emails') {
], ],
[ [
'class' => 'btn-secondary', 'class' => 'btn-secondary',
'href' => $linker->getLink(['section' => 'email', 'page' => 'emails']), 'href' => $linker->getLink([
'section' => 'email',
'page' => 'email_domain',
'domainid' => $email_domainid
]),
'label' => lng('menue.email.emails'), 'label' => lng('menue.email.emails'),
'icon' => 'fa-solid fa-envelope' 'icon' => 'fa-solid fa-envelope'
] ]
@@ -332,7 +391,8 @@ if ($page == 'overview' || $page == 'emails') {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
Response::redirectTo($filename, [ Response::redirectTo($filename, [
'page' => 'emails', 'page' => 'email_domain',
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id
]); ]);
@@ -350,7 +410,8 @@ if ($page == 'overview' || $page == 'emails') {
'class' => 'btn-secondary', 'class' => 'btn-secondary',
'href' => $linker->getLink([ 'href' => $linker->getLink([
'section' => 'email', 'section' => 'email',
'page' => 'emails', 'page' => 'email_domain',
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id
]), ]),
@@ -359,7 +420,11 @@ if ($page == 'overview' || $page == 'emails') {
], ],
[ [
'class' => 'btn-secondary', 'class' => 'btn-secondary',
'href' => $linker->getLink(['section' => 'email', 'page' => 'emails']), 'href' => $linker->getLink([
'section' => 'email',
'page' => 'email_domain',
'domainid' => $email_domainid
]),
'label' => lng('menue.email.emails'), 'label' => lng('menue.email.emails'),
'icon' => 'fa-solid fa-envelope' 'icon' => 'fa-solid fa-envelope'
] ]
@@ -385,7 +450,8 @@ if ($page == 'overview' || $page == 'emails') {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
Response::redirectTo($filename, [ Response::redirectTo($filename, [
'page' => 'emails', 'page' => 'email_domain',
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id
]); ]);
@@ -403,7 +469,8 @@ if ($page == 'overview' || $page == 'emails') {
'class' => 'btn-secondary', 'class' => 'btn-secondary',
'href' => $linker->getLink([ 'href' => $linker->getLink([
'section' => 'email', 'section' => 'email',
'page' => 'emails', 'page' => 'email_domain',
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id
]), ]),
@@ -412,7 +479,11 @@ if ($page == 'overview' || $page == 'emails') {
], ],
[ [
'class' => 'btn-secondary', 'class' => 'btn-secondary',
'href' => $linker->getLink(['section' => 'email', 'page' => 'emails']), 'href' => $linker->getLink([
'section' => 'email',
'page' => 'email_domain',
'domainid' => $email_domainid
]),
'label' => lng('menue.email.emails'), 'label' => lng('menue.email.emails'),
'icon' => 'fa-solid fa-envelope' 'icon' => 'fa-solid fa-envelope'
] ]
@@ -438,7 +509,8 @@ if ($page == 'overview' || $page == 'emails') {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
Response::redirectTo($filename, [ Response::redirectTo($filename, [
'page' => 'emails', 'page' => 'email_domain',
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id
]); ]);
@@ -446,12 +518,14 @@ if ($page == 'overview' || $page == 'emails') {
HTML::askYesNoWithCheckbox('email_reallydelete_account', 'admin_customer_alsoremovemail', $filename, [ HTML::askYesNoWithCheckbox('email_reallydelete_account', 'admin_customer_alsoremovemail', $filename, [
'id' => $id, 'id' => $id,
'page' => $page, 'page' => $page,
'domainid' => $email_domainid,
'action' => $action 'action' => $action
], $idna_convert->decode($result['email_full'])); ], $idna_convert->decode($result['email_full']));
} }
} }
} }
} elseif ($page == 'forwarders') { } elseif ($page == 'forwarders') {
$email_domainid = Request::any('domainid', 0);
if ($action == 'add' && $id != 0) { if ($action == 'add' && $id != 0) {
if ($userinfo['email_forwarders_used'] < $userinfo['email_forwarders'] || $userinfo['email_forwarders'] == '-1') { if ($userinfo['email_forwarders_used'] < $userinfo['email_forwarders'] || $userinfo['email_forwarders'] == '-1') {
try { try {
@@ -471,7 +545,8 @@ if ($page == 'overview' || $page == 'emails') {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
Response::redirectTo($filename, [ Response::redirectTo($filename, [
'page' => 'emails', 'page' => 'email_domain',
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id
]); ]);
@@ -489,7 +564,8 @@ if ($page == 'overview' || $page == 'emails') {
'class' => 'btn-secondary', 'class' => 'btn-secondary',
'href' => $linker->getLink([ 'href' => $linker->getLink([
'section' => 'email', 'section' => 'email',
'page' => 'emails', 'page' => 'email_domain',
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id
]), ]),
@@ -498,7 +574,11 @@ if ($page == 'overview' || $page == 'emails') {
], ],
[ [
'class' => 'btn-secondary', 'class' => 'btn-secondary',
'href' => $linker->getLink(['section' => 'email', 'page' => 'emails']), 'href' => $linker->getLink([
'section' => 'email',
'page' => 'email_domain',
'domainid' => $email_domainid
]),
'label' => lng('menue.email.emails'), 'label' => lng('menue.email.emails'),
'icon' => 'fa-solid fa-envelope' 'icon' => 'fa-solid fa-envelope'
] ]
@@ -540,7 +620,8 @@ if ($page == 'overview' || $page == 'emails') {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }
Response::redirectTo($filename, [ Response::redirectTo($filename, [
'page' => 'emails', 'page' => 'email_domain',
'domainid' => $email_domainid,
'action' => 'edit', 'action' => 'edit',
'id' => $id 'id' => $id
]); ]);
@@ -549,6 +630,7 @@ if ($page == 'overview' || $page == 'emails') {
'id' => $id, 'id' => $id,
'forwarderid' => $forwarderid, 'forwarderid' => $forwarderid,
'page' => $page, 'page' => $page,
'domainid' => $email_domainid,
'action' => $action 'action' => $action
], $idna_convert->decode($result['email_full']) . ' -> ' . $idna_convert->decode($forwarder)); ], $idna_convert->decode($result['email_full']) . ' -> ' . $idna_convert->decode($forwarder));
} }

View File

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

View File

@@ -52,6 +52,7 @@ if ($action == 'logout') {
if (is_array(CurrentUser::getField('switched_user'))) { if (is_array(CurrentUser::getField('switched_user'))) {
$result = CurrentUser::getData(); $result = CurrentUser::getData();
$result = $result['switched_user']; $result = $result['switched_user'];
session_regenerate_id(true);
CurrentUser::setData($result); CurrentUser::setData($result);
$target = (isset($_GET['target']) ? $_GET['target'] : 'index'); $target = (isset($_GET['target']) ? $_GET['target'] : 'index');
$redirect = "admin_" . $target . ".php"; $redirect = "admin_" . $target . ".php";

View File

@@ -104,7 +104,7 @@ if ($action == 'add_record' && !empty($_POST)) {
try { try {
$dns_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/tablelisting.dns.php'; $dns_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/tablelisting.dns.php';
$collection = (new Collection(DomainZones::class, $userinfo, ['id' => $domain_id])) $collection = (new Collection(DomainZones::class, $userinfo, ['id' => $domain_id]))
->withPagination($dns_list_data['dns_list']['columns'], $dns_list_data['dns_list']['default_sorting']); ->withPagination($dns_list_data['dns_list']['columns'], $dns_list_data['dns_list']['default_sorting'], ['domain_id='.$domain_id]);
} catch (Exception $e) { } catch (Exception $e) {
Response::dynamicError($e->getMessage()); Response::dynamicError($e->getMessage());
} }

101
index.php
View File

@@ -40,7 +40,6 @@ use Froxlor\UI\Panel\UI;
use Froxlor\UI\Response; use Froxlor\UI\Response;
use Froxlor\User; use Froxlor\User;
use Froxlor\Validate\Validate; use Froxlor\Validate\Validate;
use Froxlor\Language;
if ($action == '') { if ($action == '') {
$action = 'login'; $action = 'login';
@@ -53,9 +52,15 @@ if ($action == '2fa_entercode') {
Response::redirectTo('index.php'); Response::redirectTo('index.php');
exit(); exit();
} }
$smessage = isset($_GET['showmessage']) ? (int)$_GET['showmessage'] : 0;
$message = "";
if ($smessage > 0) {
$message = lng('error.2fa_wrongcode');
}
// show template to enter code // show template to enter code
UI::view('login/enter2fa.html.twig', [ UI::view('login/enter2fa.html.twig', [
'pagetitle' => lng('login.2fa') 'pagetitle' => lng('login.2fa'),
'message' => $message
]); ]);
} elseif ($action == '2fa_verify') { } elseif ($action == '2fa_verify') {
// verify code from 2fa code-enter form // verify code from 2fa code-enter form
@@ -68,25 +73,25 @@ if ($action == '2fa_entercode') {
// verify entered code // verify entered code
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname')); $tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
$result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3)); $result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3));
// get user-data
$table = $_SESSION['uidtable_2fa'];
$field = $_SESSION['uidfield_2fa'];
$uid = $_SESSION['uid_2fa'];
$isadmin = $_SESSION['unfo_2fa'];
// either the code is valid when using authenticator-app, or we will select userdata by id and entered code // either the code is valid when using authenticator-app, or we will select userdata by id and entered code
// which is temporarily stored for the customer when using email-2fa // which is temporarily stored for the customer when using email-2fa
if ($result) { if ($result) {
// get user-data
$table = $_SESSION['uidtable_2fa'];
$field = $_SESSION['uidfield_2fa'];
$uid = $_SESSION['uid_2fa'];
$isadmin = $_SESSION['unfo_2fa'];
$sel_param = [ $sel_param = [
'uid' => $uid 'uid' => $uid
]; ];
if ($_SESSION['secret_2fa'] == 'email') { if ($_SESSION['secret_2fa'] == 'email') {
// verify code by selecting user by id and the temp. stored code, // verify code by selecting user by id and the temp. stored code,
// so only if it's the correct code, we get the user-data // so only if it's the correct code, we get the user-data
$sel_stmt = Database::prepare("SELECT * FROM $table WHERE `" . $field . "` = :uid AND `data_2fa` = :code"); $sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
$sel_param['code'] = $code; $sel_param['code'] = $code;
} else { } else {
// Authenticator-verification has already happened at this point, so just get the user-data // Authenticator-verification has already happened at this point, so just get the user-data
$sel_stmt = Database::prepare("SELECT * FROM $table WHERE `" . $field . "` = :uid"); $sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid");
} }
$userinfo = Database::pexecute_first($sel_stmt, $sel_param); $userinfo = Database::pexecute_first($sel_stmt, $sel_param);
// whoops, no (valid) user? Start again // whoops, no (valid) user? Start again
@@ -108,19 +113,54 @@ if ($action == '2fa_entercode') {
// when using email-2fa, remove the one-time-code // when using email-2fa, remove the one-time-code
if ($userinfo['type_2fa'] == '1') { if ($userinfo['type_2fa'] == '1') {
$del_stmt = Database::prepare("UPDATE $table SET `data_2fa` = '' WHERE `" . $field . "` = :uid"); $del_stmt = Database::prepare("UPDATE " . $table . " SET `data_2fa` = '' WHERE `" . $field . "` = :uid");
$userinfo = Database::pexecute_first($del_stmt, [ $userinfo = Database::pexecute_first($del_stmt, [
'uid' => $uid 'uid' => $uid
]); ]);
} }
exit(); exit();
} }
// wrong 2fa code - treat like "wrong password"
$stmt = Database::prepare("
UPDATE " . $table . "
SET `lastlogin_fail`= :lastlogin_fail, `loginfail_count`=`loginfail_count`+1
WHERE `" . $field . "`= :uid
");
Database::pexecute($stmt, [
"lastlogin_fail" => time(),
"uid" => $uid
]);
// get data for processing further
$stmt = Database::prepare("
SELECT `loginname`, `loginfail_count`, `lastlogin_fail` FROM " . $table . "
WHERE `" . $field . "`= :uid
");
$fail_user = Database::pexecute_first($stmt, [
"uid" => $uid
]);
if ($fail_user['loginfail_count'] >= Settings::Get('login.maxloginattempts') && $fail_user['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))) {
// Log failed login
$rstlog = FroxlorLogger::getInstanceOf([
'loginname' => $_SERVER['REMOTE_ADDR']
]);
$rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, "User '" . $fail_user['loginname'] . "' entered wrong 2fa code too often.");
unset($fail_user);
Response::redirectTo('index.php', [
'showmessage' => '3'
]);
exit();
}
unset($fail_user);
// back to form
Response::redirectTo('index.php', [ Response::redirectTo('index.php', [
'showmessage' => '2' 'action' => '2fa_entercode',
'showmessage' => '1'
]); ]);
exit(); exit();
} elseif ($action == 'login') { } elseif ($action == 'login') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (!empty($_POST)) {
$loginname = Validate::validate($_POST['loginname'], 'loginname'); $loginname = Validate::validate($_POST['loginname'], 'loginname');
$password = Validate::validate($_POST['password'], 'password'); $password = Validate::validate($_POST['password'], 'password');
@@ -390,13 +430,18 @@ if ($action == '2fa_entercode') {
} }
$lastqrystr = ""; $lastqrystr = "";
if (isset($_REQUEST['qrystr']) && $_REQUEST['qrystr'] != "") { if (isset($_REQUEST['qrystr']) && $_REQUEST['qrystr'] != "") {
$lastqrystr = htmlspecialchars($_REQUEST['qrystr'], ENT_QUOTES); $lastqrystr = urlencode($_REQUEST['qrystr']);
}
if (!empty($lastscript)) {
$_SESSION['lastscript'] = $lastscript;
}
if (!empty($lastqrystr)) {
$_SESSION['lastqrystr'] = $lastqrystr;
} }
UI::view('login/login.html.twig', [ UI::view('login/login.html.twig', [
'pagetitle' => 'Login', 'pagetitle' => 'Login',
'lastscript' => $lastscript,
'lastqrystr' => $lastqrystr,
'upd_in_progress' => $update_in_progress, 'upd_in_progress' => $update_in_progress,
'message' => $message, 'message' => $message,
'successmsg' => $successmessage 'successmsg' => $successmessage
@@ -408,7 +453,7 @@ if ($action == 'forgotpwd') {
$adminchecked = false; $adminchecked = false;
$message = ''; $message = '';
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (!empty($_POST)) {
$loginname = Validate::validate($_POST['loginname'], 'loginname'); $loginname = Validate::validate($_POST['loginname'], 'loginname');
$email = Validate::validateEmail($_POST['loginemail']); $email = Validate::validateEmail($_POST['loginemail']);
$result_stmt = Database::prepare("SELECT `adminid`, `customerid`, `customernumber`, `firstname`, `name`, `company`, `email`, `loginname`, `def_language`, `deactivated` FROM `" . TABLE_PANEL_CUSTOMERS . "` $result_stmt = Database::prepare("SELECT `adminid`, `customerid`, `customernumber`, `firstname`, `name`, `company`, `email`, `loginname`, `def_language`, `deactivated` FROM `" . TABLE_PANEL_CUSTOMERS . "`
@@ -592,7 +637,7 @@ if ($action == 'forgotpwd') {
UI::view('login/fpwd.html.twig', [ UI::view('login/fpwd.html.twig', [
'pagetitle' => lng('login.presend'), 'pagetitle' => lng('login.presend'),
'action' => $action, 'formaction' => 'index.php?action=' . $action,
'message' => $message, 'message' => $message,
]); ]);
} }
@@ -615,7 +660,7 @@ if ($action == 'resetpwd') {
$check = substr($activationcode, 40, 10); $check = substr($activationcode, 40, 10);
if (substr(md5($third . $timestamp), 0, 10) == $check && $timestamp >= time() - 86400) { if (substr(md5($third . $timestamp), 0, 10) == $check && $timestamp >= time() - 86400) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (!empty($_POST)) {
$stmt = Database::prepare("SELECT `userid`, `admin` FROM `" . TABLE_PANEL_ACTIVATION . "` $stmt = Database::prepare("SELECT `userid`, `admin` FROM `" . TABLE_PANEL_ACTIVATION . "`
WHERE `activationcode` = :activationcode"); WHERE `activationcode` = :activationcode");
$result = Database::pexecute_first($stmt, [ $result = Database::pexecute_first($stmt, [
@@ -692,6 +737,7 @@ if ($action == 'resetpwd') {
function finishLogin($userinfo) function finishLogin($userinfo)
{ {
if (isset($userinfo['userid']) && $userinfo['userid'] != '') { if (isset($userinfo['userid']) && $userinfo['userid'] != '') {
session_regenerate_id(true);
CurrentUser::setData($userinfo); CurrentUser::setData($userinfo);
$language = $userinfo['def_language'] ?? Settings::Get('panel.standardlanguage'); $language = $userinfo['def_language'] ?? Settings::Get('panel.standardlanguage');
@@ -705,29 +751,34 @@ function finishLogin($userinfo)
} }
$qryparams = []; $qryparams = [];
if (isset($_POST['qrystr']) && $_POST['qrystr'] != "") { if (!empty($_SESSION['lastqrystr'])) {
parse_str(urldecode($_POST['qrystr']), $qryparams); parse_str(urldecode($_SESSION['lastqrystr']), $qryparams);
unset($_SESSION['lastqrystr']);
} }
if ($userinfo['adminsession'] == '1') { if ($userinfo['adminsession'] == '1') {
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) { if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
Response::redirectTo('admin_updates.php?page=overview'); Response::redirectTo('admin_updates.php?page=overview');
} else { } else {
if (isset($_POST['script']) && $_POST['script'] != "") { if (!empty($_SESSION['lastscript'])) {
if (preg_match("/customer\_/", $_POST['script']) === 1) { $lastscript = $_SESSION['lastscript'];
unset($_SESSION['lastscript']);
if (preg_match("/customer\_/", $lastscript) === 1) {
Response::redirectTo('admin_customers.php', [ Response::redirectTo('admin_customers.php', [
"page" => "customers" "page" => "customers"
]); ]);
} else { } else {
Response::redirectTo($_POST['script'], $qryparams); Response::redirectTo($lastscript, $qryparams);
} }
} else { } else {
Response::redirectTo('admin_index.php', $qryparams); Response::redirectTo('admin_index.php', $qryparams);
} }
} }
} else { } else {
if (isset($_POST['script']) && $_POST['script'] != "") { if (!empty($_SESSION['lastscript'])) {
Response::redirectTo($_POST['script'], $qryparams); $lastscript = $_SESSION['lastscript'];
unset($_SESSION['lastscript']);
Response::redirectTo($lastscript, $qryparams);
} else { } else {
Response::redirectTo('customer_index.php', $qryparams); Response::redirectTo('customer_index.php', $qryparams);
} }

View File

@@ -697,9 +697,12 @@ opcache.validate_timestamps'),
('system', 'distribution', ''), ('system', 'distribution', ''),
('system', 'update_channel', 'stable'), ('system', 'update_channel', 'stable'),
('system', 'updatecheck_data', ''), ('system', 'updatecheck_data', ''),
('system', 'update_notify_last', '2.0.10'), ('system', 'update_notify_last', '2.0.23'),
('system', 'traffictool', 'goaccess'), ('system', 'traffictool', 'goaccess'),
('system', 'req_limit_per_interval', 60),
('system', 'req_limit_interval', 60),
('api', 'enabled', '0'), ('api', 'enabled', '0'),
('api', 'customer_default', '1'),
('2fa', 'enabled', '1'), ('2fa', 'enabled', '1'),
('panel', 'decimal_places', '4'), ('panel', 'decimal_places', '4'),
('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'adminmail', 'admin@SERVERNAME'),
@@ -741,8 +744,8 @@ opcache.validate_timestamps'),
('panel', 'logo_overridetheme', '0'), ('panel', 'logo_overridetheme', '0'),
('panel', 'logo_overridecustom', '0'), ('panel', 'logo_overridecustom', '0'),
('panel', 'settings_mode', '0'), ('panel', 'settings_mode', '0'),
('panel', 'version', '2.0.10'), ('panel', 'version', '2.0.23'),
('panel', 'db_version', '202301180'); ('panel', 'db_version', '202304260');
DROP TABLE IF EXISTS `panel_tasks`; DROP TABLE IF EXISTS `panel_tasks`;
@@ -982,7 +985,9 @@ CREATE TABLE IF NOT EXISTS `domain_ssl_settings` (
`ssl_cert_chainfile` mediumtext, `ssl_cert_chainfile` mediumtext,
`ssl_csr_file` mediumtext, `ssl_csr_file` mediumtext,
`ssl_fullchain_file` mediumtext, `ssl_fullchain_file` mediumtext,
`expirationdate` datetime DEFAULT NULL, `validfromdate` datetime DEFAULT NULL,
`validtodate` datetime DEFAULT NULL,
`issuer` varchar(255) NOT NULL default '',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY (`domainid`) UNIQUE KEY (`domainid`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci; ) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;

View File

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

View File

@@ -23,11 +23,11 @@
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2 * @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/ */
use Froxlor\Froxlor;
use Froxlor\FileDir;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Settings; use Froxlor\FileDir;
use Froxlor\Froxlor;
use Froxlor\Install\Update; use Froxlor\Install\Update;
use Froxlor\Settings;
if (!defined('_CRON_UPDATE')) { if (!defined('_CRON_UPDATE')) {
if (!defined('AREA') || (defined('AREA') && AREA != 'admin') || !isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) { if (!defined('AREA') || (defined('AREA') && AREA != 'admin') || !isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) {
@@ -82,7 +82,7 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP COLUMN `domains_see_all`;"); Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP COLUMN `domains_see_all`;");
Update::lastStepStatus(0); Update::lastStepStatus(0);
Update::showUpdateStep("Checking for multiple mysql-servers to allow acccess to customers for existing databases"); Update::showUpdateStep("Checking for multiple mysql-servers to allow access to customers for existing databases");
$dbservers_stmt = Database::query(" $dbservers_stmt = Database::query("
SELECT `customerid`, SELECT `customerid`,
GROUP_CONCAT(DISTINCT `dbserver` SEPARATOR ',') as allowed_mysqlserver GROUP_CONCAT(DISTINCT `dbserver` SEPARATOR ',') as allowed_mysqlserver
@@ -93,7 +93,8 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
while ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) { while ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {
if (isset($dbserver['allowed_mysqlserver']) && !empty($dbserver['allowed_mysqlserver'])) { if (isset($dbserver['allowed_mysqlserver']) && !empty($dbserver['allowed_mysqlserver'])) {
$allowed_mysqlserver = json_encode(explode(",", $dbserver['allowed_mysqlserver'])); $allowed_mysqlserver = json_encode(explode(",", $dbserver['allowed_mysqlserver']));
Database::pexecute($upd_stmt, ['allowed_mysql_server' => $allowed_mysqlserver, 'customerid' => $dbserver['customerid']]); Database::pexecute($upd_stmt,
['allowed_mysql_server' => $allowed_mysqlserver, 'customerid' => $dbserver['customerid']]);
} }
} }
Update::lastStepStatus(0); Update::lastStepStatus(0);
@@ -140,14 +141,15 @@ if (Froxlor::isFroxlorVersion('0.10.38.3')) {
// none of the files existed // none of the files existed
Update::lastStepStatus(0); Update::lastStepStatus(0);
} else { } else {
Update::lastStepStatus(1, 'manual commands needed', 'Please run the following commands manually:<br><pre>' . $del_list . '</pre>'); Update::lastStepStatus(1, 'manual commands needed',
'Please run the following commands manually:<br><pre>' . $del_list . '</pre>');
} }
} }
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;
Settings::AddNew("panel.settings_mode", $panel_settings_mode); Settings::AddNew("panel.settings_mode", $panel_settings_mode);
$system_distribution = isset($_POST['system_distribution']) ? $_POST['system_distribution'] : ''; $system_distribution = isset($_POST['system_distribution']) ? $_POST['system_distribution'] : 'bullseye';
Settings::AddNew("system.distribution", $system_distribution); Settings::AddNew("system.distribution", $system_distribution);
Settings::AddNew("system.update_channel", 'stable'); Settings::AddNew("system.update_channel", 'stable');
Settings::AddNew("system.updatecheck_data", ''); Settings::AddNew("system.updatecheck_data", '');
@@ -224,7 +226,8 @@ EOF;
} else { } else {
$cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL; $cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL;
$cron_run_cmd .= FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -r 99'; $cron_run_cmd .= FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -r 99';
Update::lastStepStatus(1, 'manual commands needed', 'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>'); Update::lastStepStatus(1, 'manual commands needed',
'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>');
} }
Froxlor::updateToDbVersion('202212060'); Froxlor::updateToDbVersion('202212060');
@@ -283,7 +286,8 @@ EOF;
} else { } else {
$cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL; $cron_run_cmd = 'chmod +x ' . FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . PHP_EOL;
$cron_run_cmd .= FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -r 99'; $cron_run_cmd .= FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -r 99';
Update::lastStepStatus(1, 'manual commands needed', 'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>'); Update::lastStepStatus(1, 'manual commands needed',
'Please run the following commands manually:<br><pre>' . $cron_run_cmd . '</pre>');
} }
} }
Froxlor::updateToVersion('2.0.4'); Froxlor::updateToVersion('2.0.4');
@@ -323,7 +327,7 @@ if (Froxlor::isDatabaseVersion('202212060')) {
$system_letsencryptchallengepath_upd = isset($_POST['system_letsencryptchallengepath_upd']) ? $_POST['system_letsencryptchallengepath_upd'] : $acmesh_challenge_dir; $system_letsencryptchallengepath_upd = isset($_POST['system_letsencryptchallengepath_upd']) ? $_POST['system_letsencryptchallengepath_upd'] : $acmesh_challenge_dir;
if ($acmesh_challenge_dir != $system_letsencryptchallengepath_upd) { if ($acmesh_challenge_dir != $system_letsencryptchallengepath_upd) {
Settings::Set('system.letsencryptchallengepath', $system_letsencryptchallengepath_upd); Settings::Set('system.letsencryptchallengepath', $system_letsencryptchallengepath_upd);
if ((int) Settings::Get('system.leenabled') == 1) { if ((int)Settings::Get('system.leenabled') == 1) {
// create JSON string for --apply // create JSON string for --apply
$dist = Settings::Get('system.distribution'); $dist = Settings::Get('system.distribution');
$webserver = Settings::Get('system.webserver'); $webserver = Settings::Get('system.webserver');
@@ -382,3 +386,129 @@ if (Froxlor::isFroxlorVersion('2.0.9')) {
Update::showUpdateStep("Updating from 2.0.9 to 2.0.10", false); Update::showUpdateStep("Updating from 2.0.9 to 2.0.10", false);
Froxlor::updateToVersion('2.0.10'); Froxlor::updateToVersion('2.0.10');
} }
if (Froxlor::isDatabaseVersion('202301180')) {
Update::showUpdateStep("Adding new setting for 'Allow API access' default value for new customers");
Settings::AddNew("api.customer_default", "1");
Update::lastStepStatus(0);
Froxlor::updateToDbVersion('202302030');
}
if (Froxlor::isFroxlorVersion('2.0.10')) {
Update::showUpdateStep("Updating from 2.0.10 to 2.0.11", false);
Froxlor::updateToVersion('2.0.11');
}
if (Froxlor::isFroxlorVersion('2.0.11')) {
Update::showUpdateStep("Updating from 2.0.11 to 2.0.12", false);
Froxlor::updateToVersion('2.0.12');
}
if (Froxlor::isFroxlorVersion('2.0.12')) {
Update::showUpdateStep("Updating from 2.0.12 to 2.0.13", false);
Froxlor::updateToVersion('2.0.13');
}
if (Froxlor::isDatabaseVersion('202302030')) {
Update::showUpdateStep("Correcting language mapping of templates created pre 2.0.x");
// languages from 0.10.x
$language_mapping_comp = [
'de' => 'Deutsch',
'en' => 'English',
'fr' => 'Fran&ccedil;ais',
'pt' => 'Portugu&ecirc;s',
'it' => 'Italiano',
'nl' => 'Nederlands',
'se' => 'Svenska',
'cz' => '&#268;esk&aacute; republika'
];
$upd_tpl_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET `language` = :iso WHERE `language` = :lng");
foreach ($language_mapping_comp as $iso => $lang) {
Database::pexecute($upd_tpl_stmt, ['iso' => $iso, 'lng' => $lang]);
}
Update::lastStepStatus(0);
Update::showUpdateStep("Enhancing ssl data table");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` CHANGE `expirationdate` `validtodate` datetime DEFAULT NULL;");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `validfromdate` datetime DEFAULT NULL AFTER `ssl_fullchain_file`;");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `issuer` varchar(255) NOT NULL default '' AFTER `validtodate`;");
Update::lastStepStatus(0);
Update::showUpdateStep("Filling new ssl data fields with existing certificate data");
$crt_upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SET `validfromdate` = :validfromdate, `issuer` = :issuer WHERE `id` = :id");
$crt_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`");
Database::pexecute($crt_stmt);
while ($cert = $crt_stmt->fetch(\PDO::FETCH_ASSOC)) {
$cert_content = openssl_x509_parse($cert['ssl_cert_file']);
if (is_array($cert_content)) {
$validfromdate = empty($cert_content['validFrom_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validFrom_time_t']);
$issuer = $cert_content['issuer']['O'] ?? "";
Database::pexecute($crt_upd_stmt, ['validfromdate' => $validfromdate, 'issuer' => $issuer, 'id' => $cert['id']]);
}
}
// clear possible user customized columns
Database::query("DELETE FROM `" . TABLE_PANEL_USERCOLUMNS . "` WHERE `section` = 'sslcertificates_list'");
Update::lastStepStatus(0);
Froxlor::updateToDbVersion('202303150');
}
if (Froxlor::isFroxlorVersion('2.0.13')) {
Update::showUpdateStep("Updating from 2.0.13 to 2.0.14", false);
Froxlor::updateToVersion('2.0.14');
}
if (Froxlor::isFroxlorVersion('2.0.14')) {
Update::showUpdateStep("Updating from 2.0.14 to 2.0.15", false);
Froxlor::updateToVersion('2.0.15');
}
if (Froxlor::isDatabaseVersion('202303150')) {
Update::showUpdateStep("Adding new request rate limit settings");
Settings::AddNew("system.req_limit_per_interval", "60");
Settings::AddNew("system.req_limit_interval", "60");
Update::lastStepStatus(0);
Froxlor::updateToDbVersion('202304260');
}
if (Froxlor::isFroxlorVersion('2.0.15')) {
Update::showUpdateStep("Updating from 2.0.15 to 2.0.16", false);
Froxlor::updateToVersion('2.0.16');
}
if (Froxlor::isFroxlorVersion('2.0.16')) {
Update::showUpdateStep("Updating from 2.0.16 to 2.0.17", false);
Froxlor::updateToVersion('2.0.17');
}
if (Froxlor::isFroxlorVersion('2.0.17')) {
Update::showUpdateStep("Updating from 2.0.17 to 2.0.18", false);
Froxlor::updateToVersion('2.0.18');
}
if (Froxlor::isFroxlorVersion('2.0.18')) {
Update::showUpdateStep("Updating from 2.0.18 to 2.0.19", false);
Froxlor::updateToVersion('2.0.19');
}
if (Froxlor::isFroxlorVersion('2.0.19')) {
Update::showUpdateStep("Updating from 2.0.19 to 2.0.20", false);
Froxlor::updateToVersion('2.0.20');
}
if (Froxlor::isFroxlorVersion('2.0.20')) {
Update::showUpdateStep("Updating from 2.0.20 to 2.0.21", false);
Froxlor::updateToVersion('2.0.21');
}
if (Froxlor::isFroxlorVersion('2.0.21')) {
Update::showUpdateStep("Updating from 2.0.21 to 2.0.22", false);
Froxlor::updateToVersion('2.0.22');
}
if (Froxlor::isFroxlorVersion('2.0.22')) {
Update::showUpdateStep("Updating from 2.0.22 to 2.0.23", false);
Froxlor::updateToVersion('2.0.23');
}

View File

@@ -54,7 +54,7 @@ if (Update::versionInUpdate($current_version, '2.0.0-beta1')) {
$config_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/lib/configfiles/'); $config_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/lib/configfiles/');
// show list of available distro's // show list of available distro's
$distros = glob($config_dir . '*.xml'); $distros = glob($config_dir . '*.xml');
$distributions_select[''] = '-'; // selection is required $distributions_select[''] = '-';
// read in all the distros // read in all the distros
foreach ($distros as $_distribution) { foreach ($distros as $_distribution) {
// get configparser object // get configparser object

View File

@@ -237,11 +237,11 @@ class Ajax
private function updateTablelisting() private function updateTablelisting()
{ {
$columns = []; $columns = [];
foreach ((Request::any('columns') ?? []) as $value) { foreach ((Request::post('columns') ?? []) as $value) {
$columns[] = $value; $columns[] = $value;
} }
if (!empty($columns)) { if (!empty($columns)) {
Listing::storeColumnListingForUser([Request::any('listing') => $columns]); $columns = Listing::storeColumnListingForUser([Request::get('listing') => $columns]);
return $this->jsonResponse($columns); return $this->jsonResponse($columns);
} }
return $this->errorResponse('At least one column must be selected', 406); return $this->errorResponse('At least one column must be selected', 406);
@@ -249,7 +249,7 @@ class Ajax
private function resetTablelisting() private function resetTablelisting()
{ {
Listing::deleteColumnListingForUser([Request::any('listing') => []]); Listing::deleteColumnListingForUser([Request::get('listing') => []]);
return $this->jsonResponse([]); return $this->jsonResponse([]);
} }

View File

@@ -28,6 +28,7 @@ namespace Froxlor\Ajax;
use Froxlor\Api\Commands\Admins; use Froxlor\Api\Commands\Admins;
use Froxlor\Api\Commands\Customers; use Froxlor\Api\Commands\Customers;
use Froxlor\Api\Commands\Domains; use Froxlor\Api\Commands\Domains;
use Froxlor\Api\Commands\EmailDomains;
use Froxlor\Api\Commands\Emails; use Froxlor\Api\Commands\Emails;
use Froxlor\Api\Commands\FpmDaemons; use Froxlor\Api\Commands\FpmDaemons;
use Froxlor\Api\Commands\Ftps; use Froxlor\Api\Commands\Ftps;
@@ -267,7 +268,20 @@ class GlobalSearch
'result_format' => [ 'result_format' => [
'title' => ['self', 'getFieldFromResult'], 'title' => ['self', 'getFieldFromResult'],
'title_args' => 'email', 'title_args' => 'email',
'href' => 'customer_email.php?page=emails&searchfield=m.email&searchtext=' 'href' => 'customer_email.php?page=email_domain&domainid={domainid}&searchfield=m.email&searchtext='
]
],
// email-domains
'email_domains' => [
'class' => EmailDomains::class,
'searchfields' => [
'd.domain',
],
'result_key' => 'domain',
'result_format' => [
'title' => ['self', 'getFieldFromResult'],
'title_args' => 'domain',
'href' => 'customer_email.php?page=emails&searchfield=d.domain&searchtext='
] ]
], ],
// databases // databases
@@ -326,6 +340,14 @@ class GlobalSearch
if (!isset($result[$entity])) { if (!isset($result[$entity])) {
$result[$entity] = []; $result[$entity] = [];
} }
// replacer from result in href
$href_replacer = [];
if (preg_match_all('/\{([a-z]+)\}/', $edata['result_format']['href'], $href_replacer) !== false) {
foreach ($href_replacer[1] as $href_field) {
$href_field_value = self::getFieldFromResult($cresult, $href_field);
$edata['result_format']['href'] = str_replace('{'.$href_field.'}', $href_field_value, $edata['result_format']['href']);
}
}
$result[$entity][] = [ $result[$entity][] = [
'title' => call_user_func($edata['result_format']['title'], $cresult, ($edata['result_format']['title_args'] ?? null)), 'title' => call_user_func($edata['result_format']['title'], $cresult, ($edata['result_format']['title_args'] ?? null)),
'href' => $edata['result_format']['href'] . $cresult[$edata['result_key']] 'href' => $edata['result_format']['href'] . $cresult[$edata['result_key']]
@@ -335,7 +357,7 @@ class GlobalSearch
} }
} // foreach entity } // foreach entity
} // foreach splitted search-term } // foreach split search-term
} }
return $result; return $result;
} }

View File

@@ -26,6 +26,7 @@
namespace Froxlor\Api; namespace Froxlor\Api;
use Exception; use Exception;
use Froxlor\Http\RateLimiter;
use Froxlor\Settings; use Froxlor\Settings;
use voku\helper\AntiXSS; use voku\helper\AntiXSS;
@@ -52,6 +53,8 @@ class Api
if (Settings::Get('api.enabled') != 1) { if (Settings::Get('api.enabled') != 1) {
throw new Exception('API is not enabled. Please contact the administrator if you think this is wrong.', 400); throw new Exception('API is not enabled. Please contact the administrator if you think this is wrong.', 400);
} }
RateLimiter::run();
} }
/** /**
@@ -117,6 +120,6 @@ class Api
private function stripcslashesDeep($value) private function stripcslashesDeep($value)
{ {
return is_array($value) ? array_map([$this, 'stripcslashesDeep'], $value) : (!empty($value) ? stripcslashes($value) : null); return is_array($value) ? array_map([$this, 'stripcslashesDeep'], $value) : (!empty($value) ? stripcslashes($value) : $value);
} }
} }

View File

@@ -39,12 +39,12 @@ abstract class ApiParameter
/** /**
* *
* @param array $params * @param array|null $params
* optional, array of parameters (var=>value) for the command * optional, array of parameters (var=>value) for the command
* *
* @throws Exception * @throws Exception
*/ */
public function __construct($params = null) public function __construct(array $params = null)
{ {
if (!is_null($params)) { if (!is_null($params)) {
$params = $this->trimArray($params); $params = $this->trimArray($params);
@@ -57,7 +57,7 @@ abstract class ApiParameter
* *
* @param array $input * @param array $input
* *
* @return array * @return string|array
*/ */
private function trimArray($input) private function trimArray($input)
{ {
@@ -79,9 +79,9 @@ abstract class ApiParameter
/** /**
* get specific parameter which also has and unlimited-field * get specific parameter which also has and unlimited-field
* *
* @param string $param * @param string|null $param
* parameter to get out of the request-parameter list * parameter to get out of the request-parameter list
* @param string $ul_field * @param string|null $ul_field
* parameter to get out of the request-parameter list * parameter to get out of the request-parameter list
* @param bool $optional * @param bool $optional
* default: false * default: false
@@ -91,7 +91,7 @@ abstract class ApiParameter
* @return mixed * @return mixed
* @throws Exception * @throws Exception
*/ */
protected function getUlParam($param = null, $ul_field = null, $optional = false, $default = 0) protected function getUlParam(string $param = null, string $ul_field = null, bool $optional = false, $default = 0)
{ {
$param_value = (int)$this->getParam($param, $optional, $default); $param_value = (int)$this->getParam($param, $optional, $default);
$ul_field_value = $this->getBoolParam($ul_field, true, 0); $ul_field_value = $this->getBoolParam($ul_field, true, 0);
@@ -102,11 +102,11 @@ abstract class ApiParameter
} }
/** /**
* get specific parameter from the parameterlist; * get specific parameter from the parameter list;
* check for existence and != empty if needed. * check for existence and != empty if needed.
* Maybe more in the future * Maybe more in the future
* *
* @param string $param * @param string|null $param
* parameter to get out of the request-parameter list * parameter to get out of the request-parameter list
* @param bool $optional * @param bool $optional
* default: false * default: false
@@ -116,7 +116,7 @@ abstract class ApiParameter
* @return mixed * @return mixed
* @throws Exception * @throws Exception
*/ */
protected function getParam($param = null, $optional = false, $default = '') protected function getParam(string $param = null, bool $optional = false, $default = '')
{ {
// does it exist? // does it exist?
if (!isset($this->cmd_params[$param])) { if (!isset($this->cmd_params[$param])) {
@@ -128,7 +128,7 @@ abstract class ApiParameter
return $default; return $default;
} }
// is it empty? - test really on string, as value 0 is being seen as empty by php // is it empty? - test really on string, as value 0 is being seen as empty by php
if ($this->cmd_params[$param] === "") { if (!is_array($this->cmd_params[$param]) && trim($this->cmd_params[$param]) === "") {
if ($optional === false) { if ($optional === false) {
// get module + function for better error-messages // get module + function for better error-messages
$inmod = $this->getModFunctionString(); $inmod = $this->getModFunctionString();
@@ -142,7 +142,7 @@ abstract class ApiParameter
/** /**
* returns "module::function()" for better error-messages (missing parameter etc.) * returns "module::function()" for better error-messages (missing parameter etc.)
* makes debugging a whole lot more comfortable * makes debugging a lot more comfortable
* *
* @param int $level * @param int $level
* depth of backtrace, default 2 * depth of backtrace, default 2
@@ -152,7 +152,7 @@ abstract class ApiParameter
* *
* @return string * @return string
*/ */
private function getModFunctionString($level = 1, $max_level = 5, $trace = null) private function getModFunctionString(int $level = 1, int $max_level = 5, $trace = null)
{ {
// which class called us // which class called us
$_class = get_called_class(); $_class = get_called_class();
@@ -174,7 +174,7 @@ abstract class ApiParameter
/** /**
* getParam wrapper for boolean parameter * getParam wrapper for boolean parameter
* *
* @param string $param * @param string|null $param
* parameter to get out of the request-parameter list * parameter to get out of the request-parameter list
* @param bool $optional * @param bool $optional
* default: false * default: false
@@ -183,7 +183,7 @@ abstract class ApiParameter
* *
* @return string * @return string
*/ */
protected function getBoolParam($param = null, $optional = false, $default = false) protected function getBoolParam(string $param = null, bool $optional = false, $default = false)
{ {
$_default = '0'; $_default = '0';
if ($default) { if ($default) {

View File

@@ -95,7 +95,7 @@ class Admins extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list admins"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list admins");
$query_fields = []; $query_fields = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT * SELECT *
@@ -407,7 +407,7 @@ class Admins extends ApiCommand implements ResourceEntity
]; ];
$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(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get admin '" . $result['loginname'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get admin '" . $result['loginname'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'"); $key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'");
@@ -584,6 +584,18 @@ class Admins extends ApiCommand implements ResourceEntity
$theme = Settings::Get('panel.default_theme'); $theme = Settings::Get('panel.default_theme');
} }
if (empty(trim($name))) {
Response::standardError([
'stringisempty',
'admin.name'
], '', true);
}
if (empty(trim($email))) {
Response::standardError([
'stringisempty',
'admin.email'
], '', true);
}
if (!Validate::validateEmail($email)) { if (!Validate::validateEmail($email)) {
Response::standardError('emailiswrong', $email, true); Response::standardError('emailiswrong', $email, true);
} else { } else {
@@ -705,7 +717,7 @@ class Admins extends ApiCommand implements ResourceEntity
WHERE `adminid` = :adminid WHERE `adminid` = :adminid
"); ");
Database::pexecute($upd_stmt, $upd_data, true, true); Database::pexecute($upd_stmt, $upd_data, true, true);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] edited admin '" . $result['loginname'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] edited admin '" . $result['loginname'] . "'");
// get all admin-data for return-array // get all admin-data for return-array
$result = $this->apiCall('Admins.get', [ $result = $this->apiCall('Admins.get', [

View File

@@ -97,7 +97,7 @@ class Certificates extends ApiCommand implements ResourceEntity
} }
if (!$has_cert) { if (!$has_cert) {
$this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true); $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added ssl-certificate for '" . $domain['domain'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ssl-certificate for '" . $domain['domain'] . "'");
$result = $this->apiCall('Certificates.get', [ $result = $this->apiCall('Certificates.get', [
'id' => $domain['id'] 'id' => $domain['id']
]); ]);
@@ -127,7 +127,9 @@ class Certificates extends ApiCommand implements ResourceEntity
} }
$do_verify = true; $do_verify = true;
$expirationdate = null; $validtodate = null;
$validtodate = null;
$issuer = "";
// no cert-file given -> forget everything // no cert-file given -> forget everything
if ($ssl_cert_file == '') { if ($ssl_cert_file == '') {
$ssl_key_file = ''; $ssl_key_file = '';
@@ -168,7 +170,10 @@ class Certificates extends ApiCommand implements ResourceEntity
} else { } else {
Response::standardError('sslcertificateinvalidcert', '', true); Response::standardError('sslcertificateinvalidcert', '', true);
} }
$expirationdate = empty($cert_content['validTo_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validTo_time_t']); // get data from certificate to store in the table
$validfromdate = empty($cert_content['validFrom_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validFrom_time_t']);
$validtodate = empty($cert_content['validTo_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validTo_time_t']);
$issuer = $cert_content['issuer']['O'] ?? "";
} }
// Add/Update database entry // Add/Update database entry
@@ -183,7 +188,9 @@ class Certificates extends ApiCommand implements ResourceEntity
`ssl_key_file` = :ssl_key_file, `ssl_key_file` = :ssl_key_file,
`ssl_ca_file` = :ssl_ca_file, `ssl_ca_file` = :ssl_ca_file,
`ssl_cert_chainfile` = :ssl_cert_chainfile, `ssl_cert_chainfile` = :ssl_cert_chainfile,
`expirationdate` = :expirationdate `validfromdate` = :validfromdate,
`validtodate` = :validtodate,
`issuer` = :issuer
" . $qrywhere . " `domainid`= :domainid " . $qrywhere . " `domainid`= :domainid
"); ");
$params = [ $params = [
@@ -191,7 +198,9 @@ class Certificates extends ApiCommand implements ResourceEntity
"ssl_key_file" => $ssl_key_file, "ssl_key_file" => $ssl_key_file,
"ssl_ca_file" => $ssl_ca_file, "ssl_ca_file" => $ssl_ca_file,
"ssl_cert_chainfile" => $ssl_cert_chainfile, "ssl_cert_chainfile" => $ssl_cert_chainfile,
"expirationdate" => $expirationdate, "validfromdate" => $validfromdate,
"validtodate" => $validtodate,
"issuer" => $issuer,
"domainid" => $domainid "domainid" => $domainid
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
@@ -239,7 +248,7 @@ class Certificates extends ApiCommand implements ResourceEntity
$ssl_ca_file = $this->getParam('ssl_ca_file', true, ''); $ssl_ca_file = $this->getParam('ssl_ca_file', true, '');
$ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, ''); $ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, '');
$this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, false); $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, false);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ssl-certificate for '" . $domain['domain'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ssl-certificate for '" . $domain['domain'] . "'");
$result = $this->apiCall('Certificates.get', [ $result = $this->apiCall('Certificates.get', [
'id' => $domain['id'] 'id' => $domain['id']
]); ]);
@@ -299,27 +308,23 @@ class Certificates extends ApiCommand implements ResourceEntity
} }
// Set data from certificate // Set data from certificate
$cert['isvalid'] = false;
$cert['san'] = null;
$cert_data = openssl_x509_parse($cert['ssl_cert_file']); $cert_data = openssl_x509_parse($cert['ssl_cert_file']);
if ($cert_data) { if ($cert_data) {
$cert['validfromdate'] = date('Y-m-d H:i:s', $cert_data['validFrom_time_t']);
$cert['validtodate'] = date('Y-m-d H:i:s', $cert_data['validTo_time_t']);
$cert['isvalid'] = (bool)$cert_data['validTo_time_t'] > time(); $cert['isvalid'] = (bool)$cert_data['validTo_time_t'] > time();
$cert['issuer'] = $cert_data['issuer']['O'] ?? null; // Set subject alt names from certificate
} if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) {
$SANs = explode(",", $cert_data['extensions']['subjectAltName']);
// Set subject alt names from certificate $SANs = array_map('trim', $SANs);
$cert['san'] = null; foreach ($SANs as $san) {
if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) { $san = str_replace("DNS:", "", $san);
$SANs = explode(",", $cert_data['extensions']['subjectAltName']); if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) {
$SANs = array_map('trim', $SANs); $cert['san'][] = $san;
foreach ($SANs as $san) { }
$san = str_replace("DNS:", "", $san);
if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) {
$cert['san'][] = $san;
} }
} }
} }
$result[] = $cert; $result[] = $cert;
} }
return $this->response([ return $this->response([
@@ -465,7 +470,7 @@ class Certificates extends ApiCommand implements ResourceEntity
if ($chk['letsencrypt'] == '1') { if ($chk['letsencrypt'] == '1') {
Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $chk['domain']); Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $chk['domain']);
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] removed ssl-certificate for '" . $chk['domain'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] removed ssl-certificate for '" . $chk['domain'] . "'");
return $this->response($result); return $this->response($result);
} }
throw new Exception("Unable to determine SSL certificate. Maybe no access?", 406); throw new Exception("Unable to determine SSL certificate. Maybe no access?", 406);

View File

@@ -147,7 +147,7 @@ class Cronjobs extends ApiCommand implements ResourceEntity
// insert task to re-generate the cron.d-file // insert task to re-generate the cron.d-file
Cronjob::inserttask(TaskId::REBUILD_CRON); Cronjob::inserttask(TaskId::REBUILD_CRON);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] cronjob with description '" . $result['module'] . '/' . $result['cronfile'] . "' has been updated by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] cronjob with description '" . $result['module'] . '/' . $result['cronfile'] . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
$result = $this->apiCall('Cronjobs.get', [ $result = $this->apiCall('Cronjobs.get', [
'id' => $id 'id' => $id
]); ]);
@@ -177,7 +177,7 @@ class Cronjobs extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin()) { if ($this->isAdmin()) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list cronjobs"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list cronjobs");
$query_fields = []; $query_fields = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT `c`.* FROM `" . TABLE_PANEL_CRONRUNS . "` `c` " . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit()); SELECT `c`.* FROM `" . TABLE_PANEL_CRONRUNS . "` `c` " . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());

View File

@@ -194,7 +194,7 @@ class CustomerBackups extends ApiCommand implements ResourceEntity
$result[] = $entry; $result[] = $entry;
} }
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list customer-backups"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list customer-backups");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result

View File

@@ -298,7 +298,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'));
$api_allowed = $this->getBoolParam('api_allowed', true, Settings::Get('api.enabled')); $api_allowed = $this->getBoolParam('api_allowed', true, (Settings::Get('api.enabled') && Settings::Get('api.customer_default')));
$gender = (int)$this->getParam('gender', true, 0); $gender = (int)$this->getParam('gender', true, 0);
$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);
@@ -895,7 +895,7 @@ class Customers extends ApiCommand implements ResourceEntity
$result['dbspace_used'] = 0; $result['dbspace_used'] = 0;
} }
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get customer '" . $result['loginname'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get customer '" . $result['loginname'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'"); $key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'");
@@ -1327,7 +1327,7 @@ class Customers extends ApiCommand implements ResourceEntity
'vu' => $valid_until 'vu' => $valid_until
], true, true); ], true, true);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
} }
@@ -1538,7 +1538,7 @@ class Customers extends ApiCommand implements ResourceEntity
Database::query($admin_update_query); Database::query($admin_update_query);
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited user '" . $result['loginname'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited user '" . $result['loginname'] . "'");
/* /*
* move customer to another admin/reseller; #1166 * move customer to another admin/reseller; #1166
@@ -1911,7 +1911,7 @@ class Customers extends ApiCommand implements ResourceEntity
// now, recalculate the resource-usage for the old and the new admin // now, recalculate the resource-usage for the old and the new admin
User::updateCounters(false); User::updateCounters(false);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'");
$result = $this->apiCall('Customers.get', [ $result = $this->apiCall('Customers.get', [
'id' => $c_result['customerid'] 'id' => $c_result['customerid']

View File

@@ -144,7 +144,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
$id = Database::lastInsertId(); $id = Database::lastInsertId();
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added directory-option for '" . $userpath . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added directory-option for '" . $userpath . "'");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$result = $this->apiCall('DirOptions.get', [ $result = $this->apiCall('DirOptions.get', [
@@ -247,7 +247,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
$params['id'] = $id; $params['id'] = $id;
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get directory options for '" . $result['path'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get directory options for '" . $result['path'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = "id #" . $id; $key = "id #" . $id;
@@ -331,7 +331,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
"id" => $id "id" => $id
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited directory options for '" . str_replace($customer['documentroot'], '/', $result['path']) . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited directory options for '" . str_replace($customer['documentroot'], '/', $result['path']) . "'");
} }
$result = $this->apiCall('DirOptions.get', [ $result = $this->apiCall('DirOptions.get', [
@@ -379,7 +379,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list directory-options"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list directory-options");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result
@@ -478,7 +478,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
"customerid" => $customer_data['customerid'], "customerid" => $customer_data['customerid'],
"id" => $id "id" => $id
], true, true); ], true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted directory-option for '" . str_replace($customer_data['documentroot'], '/', $result['path']) . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] deleted directory-option for '" . str_replace($customer_data['documentroot'], '/', $result['path']) . "'");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
return $this->response($result); return $this->response($result);
} }

View File

@@ -129,7 +129,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
$id = Database::lastInsertId(); $id = Database::lastInsertId();
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added directory-protection for '" . $username . " (" . $path . ")'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added directory-protection for '" . $username . " (" . $path . ")'");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$result = $this->apiCall('DirProtections.get', [ $result = $this->apiCall('DirProtections.get', [
@@ -196,7 +196,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
$params['idun'] = ($id <= 0 ? $username : $id); $params['idun'] = ($id <= 0 ? $username : $id);
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get directory protection for '" . $result['path'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get directory protection for '" . $result['path'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "username '" . $username . "'"); $key = ($id > 0 ? "id #" . $id : "username '" . $username . "'");
@@ -279,7 +279,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated directory-protection '" . $result['username'] . " (" . $result['path'] . ")'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated directory-protection '" . $result['username'] . " (" . $result['path'] . ")'");
$result = $this->apiCall('DirProtections.get', [ $result = $this->apiCall('DirProtections.get', [
'id' => $result['id'] 'id' => $result['id']
]); ]);
@@ -325,7 +325,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list directory-protections"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list directory-protections");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result
@@ -413,7 +413,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
"id" => $id "id" => $id
]); ]);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
return $this->response($result); return $this->response($result);
} }

View File

@@ -413,7 +413,7 @@ class DomainZones extends ApiCommand implements ResourceEntity
$zone = Dns::createDomainZone($id); $zone = Dns::createDomainZone($id);
$zonefile = (string)$zone; $zonefile = (string)$zone;
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get dns-zone for '" . $result['domain'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get dns-zone for '" . $result['domain'] . "'");
return $this->response(explode("\n", $zonefile)); return $this->response(explode("\n", $zonefile));
} }

View File

@@ -225,6 +225,8 @@ class Domains extends ApiCommand implements ResourceEntity
* optional, whether php is enabled for this domain, default 0 (false) * optional, whether php is enabled for this domain, default 0 (false)
* @param bool $openbasedir * @param bool $openbasedir
* optional, whether to activate openbasedir restriction for this domain, default 0 (false) * optional, whether to activate openbasedir restriction for this domain, default 0 (false)
* @param int $openbasedir_path
* optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot
* @param int $phpsettingid * @param int $phpsettingid
* optional, specify php-configuration that is being used by id, default 1 (system-default) * optional, specify php-configuration that is being used by id, default 1 (system-default)
* @param int $mod_fcgid_starter * @param int $mod_fcgid_starter
@@ -312,6 +314,7 @@ class Domains extends ApiCommand implements ResourceEntity
$documentroot = $this->getParam('documentroot', true, ''); $documentroot = $this->getParam('documentroot', true, '');
$phpenabled = $this->getBoolParam('phpenabled', true, 0); $phpenabled = $this->getBoolParam('phpenabled', true, 0);
$openbasedir = $this->getBoolParam('openbasedir', true, 0); $openbasedir = $this->getBoolParam('openbasedir', true, 0);
$openbasedir_path = $this->getParam('openbasedir_path', true, 0);
$phpsettingid = $this->getParam('phpsettingid', true, 1); $phpsettingid = $this->getParam('phpsettingid', true, 1);
$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, -1); $mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, -1);
$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, -1); $mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, -1);
@@ -404,20 +407,25 @@ class Domains extends ApiCommand implements ResourceEntity
$documentroot = $_documentroot; $documentroot = $_documentroot;
} }
$registration_date = Validate::validate($registration_date, 'registration_date', Validate::REGEX_YYYY_MM_DD, '', [ if (!is_null($registration_date)) {
'0000-00-00', $registration_date = Validate::validate($registration_date, 'registration_date',
'0', Validate::REGEX_YYYY_MM_DD, '', [
'' '0000-00-00',
], true); '0',
''
], true);
}
if ($registration_date == '0000-00-00' || empty($registration_date)) { if ($registration_date == '0000-00-00' || empty($registration_date)) {
$registration_date = null; $registration_date = null;
} }
if (!is_null($termination_date)) {
$termination_date = Validate::validate($termination_date, 'termination_date', Validate::REGEX_YYYY_MM_DD, '', [ $termination_date = Validate::validate($termination_date, 'termination_date',
'0000-00-00', Validate::REGEX_YYYY_MM_DD, '', [
'0', '0000-00-00',
'' '0',
], true); ''
], true);
}
if ($termination_date == '0000-00-00' || empty($termination_date)) { if ($termination_date == '0000-00-00' || empty($termination_date)) {
$termination_date = null; $termination_date = null;
} }
@@ -525,6 +533,10 @@ class Domains extends ApiCommand implements ResourceEntity
$mod_fcgid_maxrequests = '-1'; $mod_fcgid_maxrequests = '-1';
} }
if ($openbasedir_path > 2 && $openbasedir_path < 0) {
$openbasedir_path = 0;
}
// check non-ssl IP // check non-ssl IP
$ipandports = $this->validateIpAddresses($p_ipandports); $ipandports = $this->validateIpAddresses($p_ipandports);
// check ssl IP // check ssl IP
@@ -696,6 +708,7 @@ class Domains extends ApiCommand implements ResourceEntity
'caneditdomain' => $caneditdomain, 'caneditdomain' => $caneditdomain,
'phpenabled' => $phpenabled, 'phpenabled' => $phpenabled,
'openbasedir' => $openbasedir, 'openbasedir' => $openbasedir,
'openbasedir_path' => $openbasedir_path,
'speciallogfile' => $speciallogfile, 'speciallogfile' => $speciallogfile,
'specialsettings' => $specialsettings, 'specialsettings' => $specialsettings,
'ssl_specialsettings' => $ssl_specialsettings, 'ssl_specialsettings' => $ssl_specialsettings,
@@ -749,6 +762,7 @@ class Domains extends ApiCommand implements ResourceEntity
`caneditdomain` = :caneditdomain, `caneditdomain` = :caneditdomain,
`phpenabled` = :phpenabled, `phpenabled` = :phpenabled,
`openbasedir` = :openbasedir, `openbasedir` = :openbasedir,
`openbasedir_path` = :openbasedir_path,
`speciallogfile` = :speciallogfile, `speciallogfile` = :speciallogfile,
`specialsettings` = :specialsettings, `specialsettings` = :specialsettings,
`ssl_specialsettings` = :ssl_specialsettings, `ssl_specialsettings` = :ssl_specialsettings,
@@ -884,7 +898,7 @@ class Domains extends ApiCommand implements ResourceEntity
$result['ipsandports'] = $this->getIpsForDomain($result['id']); $result['ipsandports'] = $this->getIpsForDomain($result['id']);
} }
$result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']); $result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get domain '" . $result['domain'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get domain '" . $result['domain'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'"); $key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'");
@@ -1096,6 +1110,8 @@ class Domains extends ApiCommand implements ResourceEntity
* from setting system.apply_phpconfigs_default * from setting system.apply_phpconfigs_default
* @param bool $openbasedir * @param bool $openbasedir
* optional, whether to activate openbasedir restriction for this domain, default 0 (false) * optional, whether to activate openbasedir restriction for this domain, default 0 (false)
* @param int $openbasedir_path
* optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot
* @param int $phpsettingid * @param int $phpsettingid
* optional, specify php-configuration that is being used by id, default 1 (system-default) * optional, specify php-configuration that is being used by id, default 1 (system-default)
* @param int $mod_fcgid_starter * @param int $mod_fcgid_starter
@@ -1193,6 +1209,7 @@ class Domains extends ApiCommand implements ResourceEntity
$phpenabled = $this->getBoolParam('phpenabled', true, $result['phpenabled']); $phpenabled = $this->getBoolParam('phpenabled', true, $result['phpenabled']);
$phpfs = $this->getBoolParam('phpsettingsforsubdomains', true, Settings::Get('system.apply_phpconfigs_default')); $phpfs = $this->getBoolParam('phpsettingsforsubdomains', true, Settings::Get('system.apply_phpconfigs_default'));
$openbasedir = $this->getBoolParam('openbasedir', true, $result['openbasedir']); $openbasedir = $this->getBoolParam('openbasedir', true, $result['openbasedir']);
$openbasedir_path = $this->getParam('openbasedir_path', true, $result['openbasedir_path']);
$phpsettingid = $this->getParam('phpsettingid', true, $result['phpsettingid']); $phpsettingid = $this->getParam('phpsettingid', true, $result['phpsettingid']);
$mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, $result['mod_fcgid_starter']); $mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, $result['mod_fcgid_starter']);
$mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, $result['mod_fcgid_maxrequests']); $mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, $result['mod_fcgid_maxrequests']);
@@ -1322,19 +1339,25 @@ class Domains extends ApiCommand implements ResourceEntity
$adminid = $result['adminid']; $adminid = $result['adminid'];
} }
$registration_date = Validate::validate($registration_date, 'registration_date', Validate::REGEX_YYYY_MM_DD, '', [ if (!is_null($registration_date)) {
'0000-00-00', $registration_date = Validate::validate($registration_date, 'registration_date',
'0', Validate::REGEX_YYYY_MM_DD, '', [
'' '0000-00-00',
], true); '0',
''
], true);
}
if ($registration_date == '0000-00-00' || empty($registration_date)) { if ($registration_date == '0000-00-00' || empty($registration_date)) {
$registration_date = null; $registration_date = null;
} }
$termination_date = Validate::validate($termination_date, 'termination_date', Validate::REGEX_YYYY_MM_DD, '', [ if (!is_null($termination_date)) {
'0000-00-00', $termination_date = Validate::validate($termination_date, 'termination_date',
'0', Validate::REGEX_YYYY_MM_DD, '', [
'' '0000-00-00',
], true); '0',
''
], true);
}
if ($termination_date == '0000-00-00' || empty($termination_date)) { if ($termination_date == '0000-00-00' || empty($termination_date)) {
$termination_date = null; $termination_date = null;
} }
@@ -1478,6 +1501,11 @@ class Domains extends ApiCommand implements ResourceEntity
$mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests'];
} }
// check changes of openbasedir-path variable
if ($openbasedir_path > 2 && $openbasedir_path < 0) {
$openbasedir_path = 0;
}
// check non-ssl IP // check non-ssl IP
$ipandports = $this->validateIpAddresses($p_ipandports, false, $result['id']); $ipandports = $this->validateIpAddresses($p_ipandports, false, $result['id']);
// check ssl IP // check ssl IP
@@ -1623,7 +1651,31 @@ class Domains extends ApiCommand implements ResourceEntity
$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';
$iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0';
if ($documentroot != $result['documentroot'] || $ssl_redirect != $result['ssl_redirect'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $phpenabled != $result['phpenabled'] || $openbasedir != $result['openbasedir'] || $phpsettingid != $result['phpsettingid'] || $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || $ssl_specialsettings != $result['ssl_specialsettings'] || $notryfiles != $result['notryfiles'] || $writeaccesslog != $result['writeaccesslog'] || $writeerrorlog != $result['writeerrorlog'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt'] || $http2 != $result['http2'] || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload'] || $ocsp_stapling != $result['ocsp_stapling']) { if ($documentroot != $result['documentroot']
|| $ssl_redirect != $result['ssl_redirect']
|| $wwwserveralias != $result['wwwserveralias']
|| $iswildcarddomain != $result['iswildcarddomain']
|| $phpenabled != $result['phpenabled']
|| $openbasedir != $result['openbasedir']
|| $phpsettingid != $result['phpsettingid']
|| $mod_fcgid_starter != $result['mod_fcgid_starter']
|| $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests']
|| $specialsettings != $result['specialsettings']
|| $ssl_specialsettings != $result['ssl_specialsettings']
|| $notryfiles != $result['notryfiles']
|| $writeaccesslog != $result['writeaccesslog']
|| $writeerrorlog != $result['writeerrorlog']
|| $aliasdomain != $result['aliasdomain']
|| $issubof != $result['ismainbutsubto']
|| $email_only != $result['email_only']
|| ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1')
|| $letsencrypt != $result['letsencrypt']
|| $http2 != $result['http2']
|| $hsts_maxage != $result['hsts']
|| $hsts_sub != $result['hsts_sub']
|| $hsts_preload != $result['hsts_preload']
|| $ocsp_stapling != $result['ocsp_stapling']
) {
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
} }
@@ -1749,7 +1801,7 @@ class Domains extends ApiCommand implements ResourceEntity
Database::pexecute($upd_stmt, [ Database::pexecute($upd_stmt, [
'id' => $id 'id' => $id
], true, true); ], true, true);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] removed specialsettings on all subdomains of domain #" . $id); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] removed specialsettings on all subdomains of domain #" . $id);
} }
$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';
@@ -1771,7 +1823,8 @@ class Domains extends ApiCommand implements ResourceEntity
$update_data['wwwserveralias'] = $wwwserveralias; $update_data['wwwserveralias'] = $wwwserveralias;
$update_data['iswildcarddomain'] = $iswildcarddomain; $update_data['iswildcarddomain'] = $iswildcarddomain;
$update_data['phpenabled'] = $phpenabled; $update_data['phpenabled'] = $phpenabled;
$update_data['openbasedir'] = $openbasedir; $update_data['openbasedir'] = $openbasedir;;
$update_data['openbasedir_path'] = $openbasedir_path;
$update_data['speciallogfile'] = $speciallogfile; $update_data['speciallogfile'] = $speciallogfile;
$update_data['phpsettingid'] = $phpsettingid; $update_data['phpsettingid'] = $phpsettingid;
$update_data['mod_fcgid_starter'] = $mod_fcgid_starter; $update_data['mod_fcgid_starter'] = $mod_fcgid_starter;
@@ -1819,6 +1872,7 @@ class Domains extends ApiCommand implements ResourceEntity
`iswildcarddomain` = :iswildcarddomain, `iswildcarddomain` = :iswildcarddomain,
`phpenabled` = :phpenabled, `phpenabled` = :phpenabled,
`openbasedir` = :openbasedir, `openbasedir` = :openbasedir,
`openbasedir_path` = :openbasedir_path,
`speciallogfile` = :speciallogfile, `speciallogfile` = :speciallogfile,
`phpsettingid` = :phpsettingid, `phpsettingid` = :phpsettingid,
`mod_fcgid_starter` = :mod_fcgid_starter, `mod_fcgid_starter` = :mod_fcgid_starter,
@@ -1854,6 +1908,7 @@ class Domains extends ApiCommand implements ResourceEntity
$_update_data['adminid'] = $adminid; $_update_data['adminid'] = $adminid;
$_update_data['phpenabled'] = $phpenabled; $_update_data['phpenabled'] = $phpenabled;
$_update_data['openbasedir'] = $openbasedir; $_update_data['openbasedir'] = $openbasedir;
$_update_data['openbasedir_path'] = $openbasedir_path;
$_update_data['mod_fcgid_starter'] = $mod_fcgid_starter; $_update_data['mod_fcgid_starter'] = $mod_fcgid_starter;
$_update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests; $_update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests;
$_update_data['notryfiles'] = $notryfiles; $_update_data['notryfiles'] = $notryfiles;
@@ -1887,6 +1942,7 @@ class Domains extends ApiCommand implements ResourceEntity
`adminid` = :adminid, `adminid` = :adminid,
`phpenabled` = :phpenabled, `phpenabled` = :phpenabled,
`openbasedir` = :openbasedir, `openbasedir` = :openbasedir,
`openbasedir_path` = :openbasedir_path,
`mod_fcgid_starter` = :mod_fcgid_starter, `mod_fcgid_starter` = :mod_fcgid_starter,
`mod_fcgid_maxrequests` = :mod_fcgid_maxrequests, `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests,
`notryfiles` = :notryfiles, `notryfiles` = :notryfiles,
@@ -1903,6 +1959,18 @@ class Domains extends ApiCommand implements ResourceEntity
"); ");
Database::pexecute($_update_stmt, $_update_data, true, true); Database::pexecute($_update_stmt, $_update_data, true, true);
// get current ip<>domain entries
$ip_sel_stmt = Database::prepare("
SELECT id_ipandports FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id
");
Database::pexecute($ip_sel_stmt, [
'id' => $id
], true, true);
$current_ips = [];
while ($cIP = $ip_sel_stmt->fetch(\PDO::FETCH_ASSOC)) {
$current_ips[] = $cIP['id_ipandports'];
}
// Cleanup domain <-> ip mapping // Cleanup domain <-> ip mapping
$del_stmt = Database::prepare(" $del_stmt = Database::prepare("
DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id
@@ -1930,6 +1998,12 @@ class Domains extends ApiCommand implements ResourceEntity
} }
} }
// check ip changes
$all_new_ips = array_merge($ipandports, $ssl_ipandports);
if (count(array_diff($current_ips, $all_new_ips)) != 0 || count(array_diff($all_new_ips, $current_ips)) != 0) {
Cronjob::inserttask(TaskId::REBUILD_VHOST);
}
// Cleanup domain <-> ip mapping for subdomains // Cleanup domain <-> ip mapping for subdomains
$domainidsresult_stmt = Database::prepare(" $domainidsresult_stmt = Database::prepare("
SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :id SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :id
@@ -1974,12 +2048,11 @@ class Domains extends ApiCommand implements ResourceEntity
} }
if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) {
// or when wwwserveralias or letsencrypt was changed // or when wwwserveralias or letsencrypt was changed
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());
if ((int)$aliasdomain === 0) { if ((int)$aliasdomain === 0) {
// in case the wwwserveralias is set on a main domain, $aliasdomain is 0 // in case the wwwserveralias is set on a main domain, $aliasdomain is 0
// --> the call just above to triggerLetsEncryptCSRForAliasDestinationDomain
// is a noop...let's repeat it with the domain id of the main domain
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($id, $this->logger()); Domain::triggerLetsEncryptCSRForAliasDestinationDomain($id, $this->logger());
} else {
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger());
} }
} }
@@ -2138,7 +2211,9 @@ class Domains extends ApiCommand implements ResourceEntity
'domainid' => $id 'domainid' => $id
], true, true); ], true, true);
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger()); if ((int)$result['aliasdomain'] !== 0) {
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger());
}
// remove domains DNS from powerDNS if used, #581 // remove domains DNS from powerDNS if used, #581
Cronjob::inserttask(TaskId::DELETE_DOMAIN_PDNS, $result['domain']); Cronjob::inserttask(TaskId::DELETE_DOMAIN_PDNS, $result['domain']);
@@ -2146,7 +2221,7 @@ class Domains extends ApiCommand implements ResourceEntity
// remove domain from acme.sh / lets encrypt if used // remove domain from acme.sh / lets encrypt if used
Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $result['domain']); Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $result['domain']);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] deleted domain/subdomains (#" . $result['id'] . ")"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] deleted domain/subdomains (#" . $result['id'] . ")");
User::updateCounters(); User::updateCounters();
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

View File

@@ -63,7 +63,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
* @param string $alternative_email * @param string $alternative_email
* optional email address to send account information to, default is the account that is being created * optional email address to send account information to, default is the account that is being created
* @param int $email_quota * @param int $email_quota
* optional quota if enabled in MB, default 0 * optional quota if enabled in MB, default setting: system.mail_quota
* @param bool $sendinfomail * @param bool $sendinfomail
* optional, sends the welcome message to the new account (needed for creation, without the user won't * optional, sends the welcome message to the new account (needed for creation, without the user won't
* be able to login before any mail is received), default 1 (true) * be able to login before any mail is received), default 1 (true)
@@ -85,7 +85,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
$emailaddr = $this->getParam('emailaddr', $ea_optional, ''); $emailaddr = $this->getParam('emailaddr', $ea_optional, '');
$email_password = $this->getParam('email_password'); $email_password = $this->getParam('email_password');
$alternative_email = $this->getParam('alternative_email', true, ''); $alternative_email = $this->getParam('alternative_email', true, '');
$quota = $this->getParam('email_quota', true, 0); $quota = $this->getParam('email_quota', true, Settings::Get('system.mail_quota') ?? 0);
$sendinfomail = $this->getBoolParam('sendinfomail', true, 1); $sendinfomail = $this->getBoolParam('sendinfomail', true, 1);
// validation // validation
@@ -99,6 +99,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
Response::standardError('notallowedtouseaccounts', '', true); Response::standardError('notallowedtouseaccounts', '', true);
} }
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
// get email address // get email address
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,
@@ -306,7 +311,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
} }
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email account for '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email account for '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $result['email_full'] 'emailaddr' => $result['email_full']
]); ]);
@@ -357,6 +362,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
$ea_optional = $id > 0; $ea_optional = $id > 0;
$emailaddr = $this->getParam('emailaddr', $ea_optional, ''); $emailaddr = $this->getParam('emailaddr', $ea_optional, '');
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
// validation // validation
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,
@@ -450,7 +460,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
Admins::increaseUsage($customer['adminid'], 'email_quota_used', '', ($quota - $result['quota'])); Admins::increaseUsage($customer['adminid'], 'email_quota_used', '', ($quota - $result['quota']));
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated email account '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated email account '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $result['email_full'] 'emailaddr' => $result['email_full']
]); ]);
@@ -556,7 +566,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
Customers::decreaseUsage($customer['customerid'], 'email_accounts_used'); Customers::decreaseUsage($customer['customerid'], 'email_accounts_used');
Customers::decreaseUsage($customer['customerid'], 'email_quota_used', '', $quota); Customers::decreaseUsage($customer['customerid'], 'email_quota_used', '', $quota);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email account for '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted email account for '" . $result['email_full'] . "'");
return $this->response($result); return $this->response($result);
} }
} }

View File

@@ -0,0 +1,188 @@
<?php
/**
* This file is part of the Froxlor project.
* Copyright (c) 2010 the Froxlor Team (see authors).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can also view it online at
* https://files.froxlor.org/misc/COPYING.txt
*
* @copyright the authors
* @author Froxlor team <team@froxlor.org>
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/
namespace Froxlor\Api\Commands;
use Exception;
use Froxlor\Api\ApiCommand;
use Froxlor\Api\ResourceEntity;
use Froxlor\Database\Database;
use Froxlor\FroxlorLogger;
use Froxlor\Settings;
use PDO;
/**
* @since 2.0
*/
class EmailDomains extends ApiCommand implements ResourceEntity
{
/**
* list all domains with email addresses connected to it.
* If called from an admin, list all domains with email addresses
* connected to it from all customers you are allowed to view, or
* specify id or loginname for one specific customer
*
* @param int $customerid
* optional, admin-only, select email addresses of a specific customer by id
* @param string $loginname
* optional, admin-only, select email addresses of a specific customer by loginname
* @param array $sql_search
* optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),
* LIKE is used if left empty and 'value' => searchvalue
* @param int $sql_limit
* optional specify number of results to be returned
* @param int $sql_offset
* optional specify offset for resultset
* @param array $sql_orderby
* optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more
* fields
*
* @access admin, customer
* @return string json-encoded array count|list
* @throws Exception
*/
public function listing()
{
$customer_ids = $this->getAllowedCustomerIds('email');
$result = [];
$query_fields = [];
$result_stmt = Database::prepare("
SELECT DISTINCT d.domain, e.domainid,
COUNT(e.email) as addresses,
IFNULL(SUM(CASE WHEN e.popaccountid > 0 THEN 1 ELSE 0 END), 0) as accounts,
IFNULL(SUM(
CASE
WHEN LENGTH(REPLACE(e.destination, CONCAT(e.email_full, ' '), '')) - LENGTH(REPLACE(REPLACE(e.destination, CONCAT(e.email_full, ' '), ''), ' ', '')) > 0
THEN LENGTH(REPLACE(e.destination, CONCAT(e.email_full, ' '), '')) - LENGTH(REPLACE(REPLACE(e.destination, CONCAT(e.email_full, ' '), ''), ' ', ''))
WHEN e.destination <> e.email_full THEN 1
ELSE 0
END
), 0) as forwarder
FROM `" . TABLE_MAIL_VIRTUAL . "` e
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON d.id = e.domainid
WHERE e.customerid IN (" . implode(", ", $customer_ids) . ") AND d.domain IS NOT NULL " .
$this->getSearchWhere($query_fields,
true) . " GROUP BY e.domainid " . $this->getOrderBy() . $this->getLimit());
Database::pexecute($result_stmt, $query_fields, true, true);
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row;
}
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO,
"[API] list email-domains");
return $this->response([
'count' => count($result),
'list' => $result
]);
}
/**
* returns the total number of accessible domains with email addresses connected to
*
* @param int $customerid
* optional, admin-only, select email addresses of a specific customer by id
* @param string $loginname
* optional, admin-only, select email addresses of a specific customer by loginname
*
* @access admin, customer
* @return string json-encoded response message
* @throws Exception
*/
public function listingCount()
{
$customer_ids = $this->getAllowedCustomerIds('email');
$result_stmt = Database::prepare("
SELECT COUNT(DISTINCT d.domain) as num_emaildomains
FROM `" . TABLE_MAIL_VIRTUAL . "` e
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON d.id = e.domainid
WHERE e.customerid IN (" . implode(", ", $customer_ids) . ") AND d.domain IS NOT NULL
");
$result = Database::pexecute_first($result_stmt, null, true, true);
if ($result) {
return $this->response($result['num_emaildomains']);
}
return $this->response(0);
}
/**
* You cannot directly access email-domains
*
* @access admin, customer
* @return string json-encoded array
* @throws Exception
*/
public function get()
{
if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {
throw new Exception("You cannot access this resource", 405);
}
throw new Exception('You cannot directly access this resource.', 303);
}
/**
* You cannot directly add email-domains
*
* @access admin, customer
* @return string json-encoded array
* @throws Exception
*/
public function add()
{
if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {
throw new Exception("You cannot access this resource", 405);
}
throw new Exception('You cannot directly add this resource.', 303);
}
/**
* toggle catchall flag of given email address either by id or email-address
*
* @access admin, customer
* @return string json-encoded array
* @throws Exception
*/
public function update()
{
if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {
throw new Exception("You cannot access this resource", 405);
}
throw new Exception('You cannot directly update this resource.', 303);
}
/**
* You cannot directly delete email-domains
*
* @access admin, customer
* @return string json-encoded array
* @throws Exception
*/
public function delete()
{
if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {
throw new Exception("You cannot access this resource", 405);
}
throw new Exception('You cannot directly delete this resource.', 303);
}
}

View File

@@ -77,6 +77,11 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
$idna_convert = new IdnaWrapper(); $idna_convert = new IdnaWrapper();
$destination = $idna_convert->encode($destination); $destination = $idna_convert->encode($destination);
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,
'emailaddr' => $emailaddr 'emailaddr' => $emailaddr
@@ -116,7 +121,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
// update customer usage // update customer usage
Customers::increaseUsage($customer['customerid'], 'email_forwarders_used'); Customers::increaseUsage($customer['customerid'], 'email_forwarders_used');
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email forwarder for '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email forwarder for '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $result['email_full'] 'emailaddr' => $result['email_full']
@@ -293,7 +298,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
// update customer usage // update customer usage
Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used'); Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used');
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email forwarder for '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] deleted email forwarder for '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $result['email_full'] 'emailaddr' => $result['email_full']

View File

@@ -159,7 +159,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');
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email address '" . $email_full . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email address '" . $email_full . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $email_full 'emailaddr' => $email_full
@@ -195,11 +195,11 @@ class Emails extends ApiCommand implements ResourceEntity
FROM `" . TABLE_MAIL_VIRTUAL . "` v FROM `" . TABLE_MAIL_VIRTUAL . "` v
LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id` LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id`
WHERE v.`customerid` IN (" . implode(", ", $customer_ids) . ") WHERE v.`customerid` IN (" . implode(", ", $customer_ids) . ")
AND (v.`id`= :idea OR (v.`email` = :idea OR v.`email_full` = :idea)) AND " . (is_numeric($params['idea']) ? "v.`id`= :idea" : "(v.`email` = :idea OR v.`email_full` = :idea)")
"); );
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get email address '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get email address '" . $result['email_full'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "emailaddr '" . $emailaddr . "'"); $key = ($id > 0 ? "id #" . $id : "emailaddr '" . $emailaddr . "'");
@@ -294,7 +294,7 @@ class Emails extends ApiCommand implements ResourceEntity
"id" => $id "id" => $id
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $result['email_full'] 'emailaddr' => $result['email_full']
@@ -340,7 +340,7 @@ class Emails extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list email-addresses"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list email-addresses");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result
@@ -445,7 +445,7 @@ class Emails extends ApiCommand implements ResourceEntity
], true, true); ], true, true);
Customers::decreaseUsage($customer['customerid'], 'emails_used'); Customers::decreaseUsage($customer['customerid'], 'emails_used');
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email address '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted email address '" . $result['email_full'] . "'");
return $this->response($result); return $this->response($result);
} }
} }

View File

@@ -64,7 +64,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin()) { if ($this->isAdmin()) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list fpm-daemons"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list fpm-daemons");
$query_fields = []; $query_fields = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "`" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit()); SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "`" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());
@@ -258,7 +258,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
$id = Database::lastInsertId(); $id = Database::lastInsertId();
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
$result = $this->apiCall('FpmDaemons.get', [ $result = $this->apiCall('FpmDaemons.get', [
'id' => $id 'id' => $id
]); ]);
@@ -384,7 +384,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
Database::pexecute($upd_stmt, $upd_data, true, true); Database::pexecute($upd_stmt, $upd_data, true, true);
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
$result = $this->apiCall('FpmDaemons.get', [ $result = $this->apiCall('FpmDaemons.get', [
'id' => $id 'id' => $id
]); ]);
@@ -433,7 +433,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
], true, true); ], true, true);
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
return $this->response($result); return $this->response($result);
} }
throw new Exception("Not allowed to execute given command.", 403); throw new Exception("Not allowed to execute given command.", 403);

View File

@@ -72,7 +72,7 @@ class Froxlor extends ApiCommand
if (empty($uc_data) || empty($response) || $uc_data['ts'] + self::UPDATE_CHECK_INTERVAL < time() || $uc_data['channel'] != Settings::Get('system.update_channel') || $force_ucheck) { if (empty($uc_data) || empty($response) || $uc_data['ts'] + self::UPDATE_CHECK_INTERVAL < time() || $uc_data['channel'] != Settings::Get('system.update_channel') || $force_ucheck) {
// log our actions // log our actions
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] checking for updates"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] checking for updates");
// check for new version // check for new version
$aucheck = AutoUpdate::checkVersion(); $aucheck = AutoUpdate::checkVersion();
@@ -142,7 +142,7 @@ class Froxlor extends ApiCommand
{ {
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
$json_str = $this->getParam('json_str'); $json_str = $this->getParam('json_str');
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "User " . $this->getUserDetail('loginname') . " imported settings"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "User " . $this->getUserDetail('loginname') . " imported settings");
try { try {
SImExporter::import($json_str); SImExporter::import($json_str);
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);

View File

@@ -257,7 +257,7 @@ class Ftps extends ApiCommand implements ResourceEntity
Customers::increaseUsage($customer['customerid'], 'ftp_lastaccountnumber'); Customers::increaseUsage($customer['customerid'], 'ftp_lastaccountnumber');
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added ftp-account '" . $username . " (" . $path . ")'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ftp-account '" . $username . " (" . $path . ")'");
Cronjob::inserttask(TaskId::CREATE_FTP); Cronjob::inserttask(TaskId::CREATE_FTP);
if ($sendinfomail == 1) { if ($sendinfomail == 1) {
@@ -302,7 +302,7 @@ class Ftps extends ApiCommand implements ResourceEntity
$this->mailer()->clearAddresses(); $this->mailer()->clearAddresses();
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] added ftp-user '" . $username . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ftp-user '" . $username . "'");
$result = $this->apiCall('Ftps.get', [ $result = $this->apiCall('Ftps.get', [
'username' => $username 'username' => $username
@@ -367,7 +367,7 @@ class Ftps extends ApiCommand implements ResourceEntity
$params['idun'] = ($id <= 0 ? $username : $id); $params['idun'] = ($id <= 0 ? $username : $id);
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get ftp-user '" . $result['username'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get ftp-user '" . $result['username'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "username '" . $username . "'"); $key = ($id > 0 ? "id #" . $id : "username '" . $username . "'");
@@ -453,7 +453,7 @@ class Ftps extends ApiCommand implements ResourceEntity
"id" => $id, "id" => $id,
"password" => $cryptPassword "password" => $cryptPassword
], true, true); ], true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ftp-account password for '" . $result['username'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ftp-account password for '" . $result['username'] . "'");
} }
// path update? // path update?
@@ -471,7 +471,7 @@ class Ftps extends ApiCommand implements ResourceEntity
"customerid" => $customer['customerid'], "customerid" => $customer['customerid'],
"id" => $id "id" => $id
], true, true); ], true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ftp-account homdir for '" . $result['username'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ftp-account homdir for '" . $result['username'] . "'");
} }
} }
// it's the task for "new ftp" but that will // it's the task for "new ftp" but that will
@@ -533,7 +533,7 @@ class Ftps extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list ftp-users"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list ftp-users");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result

View File

@@ -61,7 +61,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin()) { if ($this->isAdmin()) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list hosting-plans"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list hosting-plans");
$query_fields = []; $query_fields = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT p.*, a.loginname as adminname SELECT p.*, a.loginname as adminname
@@ -200,8 +200,8 @@ class HostingPlans extends ApiCommand implements ResourceEntity
$value_arr['logviewenabled'] = $this->getBoolParam('logviewenabled', true, 0); $value_arr['logviewenabled'] = $this->getBoolParam('logviewenabled', true, 0);
// validation // validation
$name = Validate::validate(trim($name), 'name', '', '', [], true); $name = Validate::validate(trim($name), 'name', Validate::REGEX_DESC_TEXT, '', [], true);
$description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_DESC_TEXT); $description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_CONF_TEXT);
if (Settings::Get('system.mail_quota_enabled') != '1') { if (Settings::Get('system.mail_quota_enabled') != '1') {
$value_arr['email_quota'] = -1; $value_arr['email_quota'] = -1;
@@ -227,7 +227,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
'valuearr' => json_encode($value_arr) 'valuearr' => json_encode($value_arr)
]; ];
Database::pexecute($ins_stmt, $ins_data, true, true); Database::pexecute($ins_stmt, $ins_data, true, true);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] added hosting-plan '" . $name . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] added hosting-plan '" . $name . "'");
$result = $this->apiCall('HostingPlans.get', [ $result = $this->apiCall('HostingPlans.get', [
'planname' => $name 'planname' => $name
]); ]);
@@ -264,7 +264,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
} }
$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(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get hosting-plan '" . $result['name'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get hosting-plan '" . $result['name'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "planname '" . $planname . "'"); $key = ($id > 0 ? "id #" . $id : "planname '" . $planname . "'");
@@ -382,8 +382,8 @@ class HostingPlans extends ApiCommand implements ResourceEntity
$value_arr['logviewenabled'] = $this->getBoolParam('logviewenabled', true, $result['logviewenabled']); $value_arr['logviewenabled'] = $this->getBoolParam('logviewenabled', true, $result['logviewenabled']);
// validation // validation
$name = Validate::validate(trim($name), 'name', '', '', [], true); $name = Validate::validate(trim($name), 'name', Validate::REGEX_DESC_TEXT, '', [], true);
$description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_DESC_TEXT); $description = Validate::validate(str_replace("\r\n", "\n", $description), 'description', Validate::REGEX_CONF_TEXT);
if (Settings::Get('system.mail_quota_enabled') != '1') { if (Settings::Get('system.mail_quota_enabled') != '1') {
$value_arr['email_quota'] = -1; $value_arr['email_quota'] = -1;
@@ -414,7 +414,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
'id' => $id 'id' => $id
]; ];
Database::pexecute($upd_stmt, $update_data, true, true); Database::pexecute($upd_stmt, $update_data, true, true);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] updated hosting-plan '" . $result['name'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] updated hosting-plan '" . $result['name'] . "'");
return $this->response($update_data); return $this->response($update_data);
} }
throw new Exception("Not allowed to execute given command.", 403); throw new Exception("Not allowed to execute given command.", 403);

View File

@@ -65,7 +65,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || !empty($this->getUserDetail('ip')))) { if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || !empty($this->getUserDetail('ip')))) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list ips and ports"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list ips and ports");
$ip_where = ""; $ip_where = "";
$append_where = false; $append_where = false;
if (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) { if (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) {
@@ -175,9 +175,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
$docroot = Validate::validate($this->getParam('docroot', true, ''), 'docroot', Validate::REGEX_DIR, '', [], true); $docroot = Validate::validate($this->getParam('docroot', true, ''), 'docroot', Validate::REGEX_DIR, '', [], true);
if ((int)Settings::Get('system.use_ssl') == 1) { if ((int)Settings::Get('system.use_ssl') == 1) {
$ssl = !empty($this->getBoolParam('ssl', true, 0)) ? intval($this->getBoolParam('ssl', true, 0)) : 0; $ssl = (bool)$this->getBoolParam('ssl', true, 0);
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $ssl, ''), 'ssl_cert_file', '', '', [], true); $ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', !$ssl, ''), 'ssl_cert_file', '', '', [], true);
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $ssl, ''), 'ssl_key_file', '', '', [], true); $ssl_key_file = Validate::validate($this->getParam('ssl_key_file', !$ssl, ''), 'ssl_key_file', '', '', [], true);
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, ''), 'ssl_ca_file', '', '', [], true); $ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, ''), 'ssl_ca_file', '', '', [], true);
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, ''), 'ssl_cert_chainfile', '', '', [], true); $ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, ''), 'ssl_cert_chainfile', '', '', [], true);
$sslss = $this->getParam('ssl_specialsettings', true, ''); $sslss = $this->getParam('ssl_specialsettings', true, '');
@@ -335,7 +335,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
'id' => $id 'id' => $id
], true, true); ], true, true);
if ($result) { if ($result) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get ip " . $result['ip'] . " " . $result['port']); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get ip " . $result['ip'] . " " . $result['port']);
return $this->response($result); return $this->response($result);
} }
throw new Exception("IP/port with id #" . $id . " could not be found", 404); throw new Exception("IP/port with id #" . $id . " could not be found", 404);
@@ -414,9 +414,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
$docroot = Validate::validate($this->getParam('docroot', true, $result['docroot']), 'docroot', Validate::REGEX_DIR, '', [], true); $docroot = Validate::validate($this->getParam('docroot', true, $result['docroot']), 'docroot', Validate::REGEX_DIR, '', [], true);
if ((int)Settings::Get('system.use_ssl') == 1) { if ((int)Settings::Get('system.use_ssl') == 1) {
$ssl = $this->getBoolParam('ssl', true, $result['ssl']); $ssl = (bool)$this->getBoolParam('ssl', true, $result['ssl']);
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $ssl, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true); $ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', !$ssl, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true);
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $ssl, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true); $ssl_key_file = Validate::validate($this->getParam('ssl_key_file', !$ssl, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, $result['ssl_ca_file']), 'ssl_ca_file', '', '', [], true); $ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, $result['ssl_ca_file']), 'ssl_ca_file', '', '', [], true);
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', [], true); $ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', [], true);
$sslss = $this->getParam('ssl_specialsettings', true, $result['ssl_specialsettings']); $sslss = $this->getParam('ssl_specialsettings', true, $result['ssl_specialsettings']);

View File

@@ -26,14 +26,15 @@
namespace Froxlor\Api\Commands; namespace Froxlor\Api\Commands;
use Exception; use Exception;
use PDO;
use PDOException;
use Froxlor\Froxlor;
use Froxlor\PhpHelper;
use Froxlor\Api\ApiCommand; use Froxlor\Api\ApiCommand;
use Froxlor\Api\ResourceEntity; use Froxlor\Api\ResourceEntity;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Froxlor;
use Froxlor\FroxlorLogger;
use Froxlor\PhpHelper;
use Froxlor\Validate\Validate; use Froxlor\Validate\Validate;
use PDO;
use PDOException;
class MysqlServer extends ApiCommand implements ResourceEntity class MysqlServer extends ApiCommand implements ResourceEntity
{ {
@@ -73,8 +74,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
* optional, test connection with given credentials, default is true (yes) * optional, test connection with given credentials, default is true (yes)
* *
* @access admin * @access admin
* @throws Exception
* @return string json-encoded array * @return string json-encoded array
* @throws Exception
*/ */
public function add() public function add()
{ {
@@ -112,7 +113,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
); );
if (!empty($mysql_ca)) { if (!empty($mysql_ca)) {
$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca; $options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca;
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool) $mysql_verifycert; $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_verifycert;
} }
$dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";"; $dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";";
@@ -167,6 +168,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
$this->addDatabaseFromCustomerAllowedList($newdbserver); $this->addDatabaseFromCustomerAllowedList($newdbserver);
} }
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] added new database server '" . $description . "' (" . $mysql_host . ")");
return $this->response(['dbserver' => $newdbserver]); return $this->response(['dbserver' => $newdbserver]);
} }
@@ -179,16 +182,16 @@ class MysqlServer extends ApiCommand implements ResourceEntity
* optional the number of the mysql server (either id or dbserver must be set) * optional the number of the mysql server (either id or dbserver must be set)
* *
* @access admin * @access admin
* @throws Exception
* @return string json-encoded array * @return string json-encoded array
* @throws Exception
*/ */
public function delete() public function delete()
{ {
$this->validateAccess(); $this->validateAccess();
$id = (int) $this->getParam('id', true, -1); $id = (int)$this->getParam('id', true, -1);
$dn_optional = $id >= 0; $dn_optional = $id >= 0;
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1); $dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
$dbserver = $id >= 0 ? $id : $dbserver; $dbserver = $id >= 0 ? $id : $dbserver;
if ($dbserver == 0) { if ($dbserver == 0) {
@@ -212,8 +215,12 @@ class MysqlServer extends ApiCommand implements ResourceEntity
// when removing, remove from list of allowed_mysqlservers from any customers // when removing, remove from list of allowed_mysqlservers from any customers
$this->removeDatabaseFromCustomerAllowedList($dbserver); $this->removeDatabaseFromCustomerAllowedList($dbserver);
$description = $sql_root[$dbserver]['caption'] ?? "unknown";
$mysql_host = $sql_root[$dbserver]['host'] ?? "unknown";
unset($sql_root[$dbserver]); unset($sql_root[$dbserver]);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] removed database server '" . $description . "' (" . $mysql_host . ")");
$this->generateNewUserData($sql, $sql_root); $this->generateNewUserData($sql, $sql_root);
return $this->response(['true']); return $this->response(['true']);
} }
@@ -287,14 +294,14 @@ class MysqlServer extends ApiCommand implements ResourceEntity
* optional the number of the mysql server (either id or dbserver must be set) * optional the number of the mysql server (either id or dbserver must be set)
* *
* @access admin, customer * @access admin, customer
* @throws Exception
* @return string json-encoded array * @return string json-encoded array
* @throws Exception
*/ */
public function get() public function get()
{ {
$id = (int) $this->getParam('id', true, -1); $id = (int)$this->getParam('id', true, -1);
$dn_optional = $id >= 0; $dn_optional = $id >= 0;
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1); $dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
$dbserver = $id >= 0 ? $id : $dbserver; $dbserver = $id >= 0 ? $id : $dbserver;
$sql_root = []; $sql_root = [];
@@ -317,6 +324,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
unset($sql_root[$dbserver]['password']); unset($sql_root[$dbserver]['password']);
$sql_root[$dbserver]['id'] = $dbserver; $sql_root[$dbserver]['id'] = $dbserver;
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get database-server '" . $sql_root[$dbserver]['caption'] . "'");
return $this->response($sql_root[$dbserver]); return $this->response($sql_root[$dbserver]);
} }
@@ -347,16 +355,16 @@ class MysqlServer extends ApiCommand implements ResourceEntity
* optional, test connection with given credentials, default is true (yes) * optional, test connection with given credentials, default is true (yes)
* *
* @access admin * @access admin
* @throws Exception
* @return string json-encoded array * @return string json-encoded array
* @throws Exception
*/ */
public function update() public function update()
{ {
$this->validateAccess(); $this->validateAccess();
$id = (int) $this->getParam('id', true, -1); $id = (int)$this->getParam('id', true, -1);
$dn_optional = $id >= 0; $dn_optional = $id >= 0;
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1); $dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
$dbserver = $id >= 0 ? $id : $dbserver; $dbserver = $id >= 0 ? $id : $dbserver;
$sql_root = []; $sql_root = [];
@@ -417,7 +425,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
); );
if (!empty($mysql_ca)) { if (!empty($mysql_ca)) {
$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca; $options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca;
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool) $mysql_verifycert; $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_verifycert;
} }
$dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";"; $dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";";
@@ -448,6 +456,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
$this->addDatabaseFromCustomerAllowedList($dbserver); $this->addDatabaseFromCustomerAllowedList($dbserver);
} }
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] edited database server '" . $description . "' (" . $mysql_host . ")");
return $this->response(['true']); return $this->response(['true']);
} }
@@ -472,7 +482,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
WHERE `dbserver` = :dbserver WHERE `dbserver` = :dbserver
"); ");
$result = Database::pexecute_first($result_stmt, ['dbserver' => $dbserver], true, true); $result = Database::pexecute_first($result_stmt, ['dbserver' => $dbserver], true, true);
return (int) $result['num_dbs']; return (int)$result['num_dbs'];
} else { } else {
$dbserver = $this->getParam('mysql_server'); $dbserver = $this->getParam('mysql_server');
$customer_ids = $this->getAllowedCustomerIds(); $customer_ids = $this->getAllowedCustomerIds();
@@ -516,7 +526,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
`allowed_mysqlserver` = :am WHERE `customerid` = :cid `allowed_mysqlserver` = :am WHERE `customerid` = :cid
"); ");
while ($customer = $sel_stmt->fetch(PDO::FETCH_ASSOC)) { while ($customer = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {
$allowed_mysqls = json_decode(($customer['allowed_mysqlserver'] ?? '[]'), true); $allowed_mysqls = json_decode(($customer['allowed_mysqlserver'] ?: '[]'), true);
if (!in_array($dbserver, $allowed_mysqls)) { if (!in_array($dbserver, $allowed_mysqls)) {
$allowed_mysqls[] = $dbserver; $allowed_mysqls[] = $dbserver;
$allowed_mysqls = json_encode($allowed_mysqls); $allowed_mysqls = json_encode($allowed_mysqls);

View File

@@ -199,7 +199,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
$this->mailer()->clearAddresses(); $this->mailer()->clearAddresses();
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] added mysql-database '" . $username . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added mysql-database '" . $username . "'");
$result = $this->apiCall('Mysqls.get', [ $result = $this->apiCall('Mysqls.get', [
'dbname' => $username, 'dbname' => $username,
@@ -299,7 +299,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
$mbdata = $mbdata_stmt->fetch(PDO::FETCH_ASSOC); $mbdata = $mbdata_stmt->fetch(PDO::FETCH_ASSOC);
Database::needRoot(false); Database::needRoot(false);
$result['size'] = $mbdata['MB'] ?? 0; $result['size'] = $mbdata['MB'] ?? 0;
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get database '" . $result['databasename'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get database '" . $result['databasename'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "dbname '" . $dbname . "'"); $key = ($id > 0 ? "id #" . $id : "dbname '" . $dbname . "'");
@@ -388,7 +388,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] updated mysql-database '" . $result['databasename'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated mysql-database '" . $result['databasename'] . "'");
$result = $this->apiCall('Mysqls.get', [ $result = $this->apiCall('Mysqls.get', [
'dbname' => $result['databasename'] 'dbname' => $result['databasename']
]); ]);

View File

@@ -67,7 +67,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin()) { if ($this->isAdmin()) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list php-configs"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list php-configs");
$with_subdomains = $this->getBoolParam('with_subdomains', true, false); $with_subdomains = $this->getBoolParam('with_subdomains', true, false);
$query_fields = []; $query_fields = [];
@@ -392,7 +392,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
$ins_data['id'] = Database::lastInsertId(); $ins_data['id'] = Database::lastInsertId();
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] php setting with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
$result = $this->apiCall('PhpSettings.get', [ $result = $this->apiCall('PhpSettings.get', [
'id' => $ins_data['id'] 'id' => $ins_data['id']
@@ -629,7 +629,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
Database::pexecute($upd_stmt, $upd_data, true, true); Database::pexecute($upd_stmt, $upd_data, true, true);
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] php setting with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
$result = $this->apiCall('PhpSettings.get', [ $result = $this->apiCall('PhpSettings.get', [
'id' => $id 'id' => $id
@@ -686,7 +686,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
], true, true); ], true, true);
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] php setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
return $this->response($result); return $this->response($result);
} }
throw new Exception("Not allowed to execute given command.", 403); throw new Exception("Not allowed to execute given command.", 403);

View File

@@ -62,7 +62,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
* optional, overwrites path value with an URL to generate a redirect, alternatively use the path * optional, overwrites path value with an URL to generate a redirect, alternatively use the path
* parameter also for URLs * parameter also for URLs
* @param int $openbasedir_path * @param int $openbasedir_path
* optional, either 0 for domains-docroot, 1 for customers-homedir or 2 for parent-directory of domains-docroot * optional, either 0 for domains-docroot [default], 1 for customers-homedir or 2 for parent-directory of domains-docroot
* @param int $phpsettingid * @param int $phpsettingid
* optional, php-settings-id, if empty the $domain value is used * optional, php-settings-id, if empty the $domain value is used
* @param int $redirectcode * @param int $redirectcode
@@ -104,7 +104,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
$aliasdomain = $this->getParam('alias', true, 0); $aliasdomain = $this->getParam('alias', true, 0);
$path = $this->getParam('path', true, ''); $path = $this->getParam('path', true, '');
$url = $this->getParam('url', true, ''); $url = $this->getParam('url', true, '');
$openbasedir_path = $this->getParam('openbasedir_path', true, 1); $openbasedir_path = $this->getParam('openbasedir_path', true, 0);
$phpsettingid = $this->getParam('phpsettingid', true, 0); $phpsettingid = $this->getParam('phpsettingid', true, 0);
$redirectcode = $this->getParam('redirectcode', true, Settings::Get('customredirect.default')); $redirectcode = $this->getParam('redirectcode', true, Settings::Get('customredirect.default'));
$isemaildomain = $this->getParam('isemaildomain', true, 0); $isemaildomain = $this->getParam('isemaildomain', true, 0);
@@ -486,7 +486,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
$result['ipsandports'] = $this->getIpsForDomain($result['id']); $result['ipsandports'] = $this->getIpsForDomain($result['id']);
} }
$result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']); $result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get subdomain '" . $result['domain'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get subdomain '" . $result['domain'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'"); $key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'");
@@ -856,7 +856,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
Cronjob::inserttask(TaskId::REBUILD_DNS); Cronjob::inserttask(TaskId::REBUILD_DNS);
$idna_convert = new IdnaWrapper(); $idna_convert = new IdnaWrapper();
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited domain '" . $idna_convert->decode($result['domain']) . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited domain '" . $idna_convert->decode($result['domain']) . "'");
} }
$result = $this->apiCall('SubDomains.get', [ $result = $this->apiCall('SubDomains.get', [
'id' => $id 'id' => $id
@@ -1133,7 +1133,9 @@ class SubDomains extends ApiCommand implements ResourceEntity
} }
} }
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger()); if ((int)$result['aliasdomain'] !== 0) {
Domain::triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger());
}
// delete domain from table // delete domain from table
$stmt = Database::prepare(" $stmt = Database::prepare("

View File

@@ -92,7 +92,7 @@ class SysLog extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list log-entries"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list log-entries");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result

View File

@@ -166,7 +166,7 @@ class Traffic extends ApiCommand implements ResourceEntity
$row['mail'] *= 1024; $row['mail'] *= 1024;
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list traffic"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list traffic");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result

View File

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

View File

@@ -0,0 +1,178 @@
<?php
/**
* This file is part of the Froxlor project.
* Copyright (c) 2010 the Froxlor Team (see authors).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can also view it online at
* https://files.froxlor.org/misc/COPYING.txt
*
* @copyright the authors
* @author Froxlor team <team@froxlor.org>
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/
namespace Froxlor\Cli;
use Froxlor\Config\ConfigParser;
use Froxlor\FileDir;
use Froxlor\Froxlor;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
final class ConfigDiff extends CliCommand
{
protected function configure(): void
{
$this->setName('froxlor:config-diff')
->setDescription('Shows differences in config templates between OS versions')
->addArgument('from', InputArgument::OPTIONAL, 'OS version to compare against')
->addArgument('to', InputArgument::OPTIONAL, 'OS version to compare from')
->addOption('list', 'l', InputOption::VALUE_NONE, 'List all possible OS versions')
->addOption('diff-params', '', InputOption::VALUE_REQUIRED, 'Additional parameters for `diff`, e.g. --diff-params="--color=always"');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
require Froxlor::getInstallDir() . '/lib/functions.php';
$parsers = $versions = [];
foreach (glob(Froxlor::getInstallDir() . '/lib/configfiles/*.xml') as $config) {
$name = str_replace(".xml", "", strtolower(basename($config)));
$parser = new ConfigParser($config);
$versions[$name] = $parser->getCompleteDistroName();
$parsers[$name] = $parser;
}
asort($versions);
if ($input->getOption('list') === true) {
$output->writeln('The following OS version templates are available:');
foreach ($versions as $k => $v) {
$output->writeln(str_pad($k, 20) . $v);
}
return self::SUCCESS;
}
if (!$input->hasArgument('from') || !array_key_exists($input->getArgument('from'), $versions)) {
$output->writeln('<error>Missing or invalid "from" argument.</error>');
$output->writeln('Available versions: ' . implode(', ', array_keys($versions)));
return self::INVALID;
}
if (!$input->hasArgument('to') || !array_key_exists($input->getArgument('to'), $versions)) {
$output->writeln('<error>Missing or invalid "to" argument.</error>');
$output->writeln('Available versions: ' . implode(', ', array_keys($versions)));
return self::INVALID;
}
// Make sure diff is installed
$check_diff_installed = FileDir::safe_exec('which diff');
if (count($check_diff_installed) === 0) {
$output->writeln('<error>Unable to find "diff" installation on your system.</error>');
return self::INVALID;
}
$parser_from = $parsers[$input->getArgument('from')];
$parser_to = $parsers[$input->getArgument('to')];
$tmp_from = tempnam(sys_get_temp_dir(), 'froxlor_config_diff_from');
$tmp_to = tempnam(sys_get_temp_dir(), 'froxlor_config_diff_to');
$files = [];
$titles_by_key = [];
// Aggregate content for each config file
foreach ([[$parser_from, 'from'], [$parser_to, 'to']] as $todo) {
foreach ($todo[0]->getServices() as $service_type => $service) {
foreach ($service->getDaemons() as $daemon_name => $daemon) {
foreach ($daemon->getConfig() as $instruction) {
if ($instruction['type'] !== 'file') {
continue;
}
if (isset($instruction['subcommands'])) {
foreach ($instruction['subcommands'] as $subinstruction) {
if ($subinstruction['type'] !== 'file') {
continue;
}
$content = $subinstruction['content'];
}
} else {
$content = $instruction['content'];
}
if (!isset($content)) {
throw new \Exception("Cannot find content for {$instruction['name']}");
}
$key = "{$service_type}_{$daemon_name}_{$instruction['name']}";
$titles_by_key[$key] = "{$service->title} : {$daemon->title} : {$instruction['name']}";
if (!isset($files[$key])) {
$files[$key] = ['from' => '', 'to' => ''];
}
$files[$key][$todo[1]] = $this->filterContent($content);
}
}
}
}
ksort($files);
$diff_params = '';
if ($input->hasOption('diff-params') && trim($input->getOption('diff-params')) !== '') {
$diff_params = trim($input->getOption('diff-params'));
}
// Run diff on each file and output, if anything changed
foreach ($files as $file_key => $content) {
file_put_contents($tmp_from, $content['from']);
file_put_contents($tmp_to, $content['to']);
$diff_output = FileDir::safe_exec("{$check_diff_installed[0]} {$diff_params} {$tmp_from} {$tmp_to}");
if (count($diff_output) === 0) {
continue;
}
$output->writeln('<info># ' . $titles_by_key[$file_key] . '</info>');
$output->writeln(implode("\n", $diff_output) . "\n");
unset($diff_output);
}
// Remove tmp files again
unlink($tmp_from);
unlink($tmp_to);
return self::SUCCESS;
}
private function filterContent(string $content): string
{
$new_content = '';
foreach (explode("\n", $content) as $n) {
$n = trim($n);
if (!$n) {
continue;
}
if (str_starts_with($n, '#')) {
continue;
}
$new_content .= $n . "\n";
}
return $new_content;
}
}

View File

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

View File

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

View File

@@ -76,7 +76,7 @@ final class ValidateAcmeWebroot extends CliCommand
'domain' => Settings::Get('system.hostname') 'domain' => Settings::Get('system.hostname')
]; ];
} }
$upd_stmt = Database::prepare("UPDATE domain_ssl_settings SET expirationdate=NULL WHERE `domainid` = :did"); $upd_stmt = Database::prepare("UPDATE domain_ssl_settings SET `validtodate`=NULL WHERE `domainid` = :did");
$acmesh_dir = dirname(Settings::Get('system.acmeshpath')); $acmesh_dir = dirname(Settings::Get('system.acmeshpath'));
$acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/"); $acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/");
$recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), "/"); $recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), "/");

View File

@@ -613,7 +613,10 @@ class Apache extends HttpConfigBase
// Apply header // Apply header
$this->virtualhosts_data[$vhosts_filename] = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; $this->virtualhosts_data[$vhosts_filename] = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n";
if ($domain['deactivated'] != '1' || Settings::Get('system.deactivateddocroot') != '') { $ddr = Settings::Get('system.deactivateddocroot');
if (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && empty($ddr)) {
$this->virtualhosts_data[$vhosts_filename] .= '# Customer/domain deactivated and a docroot for deactivated users hasn\'t been set.' . "\n";
} else {
// Create vhost without ssl // Create vhost without ssl
$this->virtualhosts_data[$vhosts_filename] .= $this->getVhostContent($domain, false); $this->virtualhosts_data[$vhosts_filename] .= $this->getVhostContent($domain, false);
@@ -623,8 +626,6 @@ class Apache extends HttpConfigBase
$this->virtualhosts_data[$vhosts_filename_ssl] = '# Domain ID: ' . $domain['id'] . ' (SSL) - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; $this->virtualhosts_data[$vhosts_filename_ssl] = '# Domain ID: ' . $domain['id'] . ' (SSL) - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n";
$this->virtualhosts_data[$vhosts_filename_ssl] .= $this->getVhostContent($domain, true); $this->virtualhosts_data[$vhosts_filename_ssl] .= $this->getVhostContent($domain, true);
} }
} else {
$this->virtualhosts_data[$vhosts_filename] .= '# Customer deactivated and a docroot for deactivated users hasn\'t been set.' . "\n";
} }
} }
} }
@@ -840,29 +841,34 @@ class Apache extends HttpConfigBase
$domain['documentroot'] = trim($domain['documentroot']); $domain['documentroot'] = trim($domain['documentroot']);
if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { if (preg_match('/^https?\:\/\//', $domain['documentroot'])) {
$corrected_docroot = $domain['documentroot']; $possible_deactivated_webroot = $this->getWebroot($domain);
if ($this->deactivated == false) {
$corrected_docroot = $domain['documentroot'];
// Get domain's redirect code // Get domain's redirect code
$code = Domain::getDomainRedirectCode($domain['id']); $code = Domain::getDomainRedirectCode($domain['id']);
$modrew_red = ''; $modrew_red = '';
if ($code != '') { if ($code != '') {
$modrew_red = ' [R=' . $code . ';L,NE]'; $modrew_red = ' [R=' . $code . ';L,NE]';
} }
// 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";
if (!$ssl_vhost) { if (!$ssl_vhost) {
$vhost_content .= ' RewriteCond %{HTTPS} off' . "\n"; $vhost_content .= ' RewriteCond %{HTTPS} off' . "\n";
}
if ($domain['letsencrypt'] == '1') {
$vhost_content .= ' RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge' . "\n";
}
$vhost_content .= ' RewriteRule ^/(.*) ' . $corrected_docroot . '$1' . $modrew_red . "\n";
$vhost_content .= ' </IfModule>' . "\n";
$vhost_content .= ' <IfModule !mod_rewrite.c>' . "\n";
$vhost_content .= ' Redirect ' . $code . ' / ' . $domain['documentroot_norewrite'] . "\n";
$vhost_content .= ' </IfModule>' . "\n";
} elseif (Settings::Get('system.deactivateddocroot') != '') {
$vhost_content .= $possible_deactivated_webroot;
} }
if ($domain['letsencrypt'] == '1') {
$vhost_content .= ' RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge' . "\n";
}
$vhost_content .= ' RewriteRule ^/(.*) ' . $corrected_docroot . '$1' . $modrew_red . "\n";
$vhost_content .= ' </IfModule>' . "\n";
$vhost_content .= ' <IfModule !mod_rewrite.c>' . "\n";
$vhost_content .= ' Redirect ' . $code . ' / ' . $domain['documentroot_norewrite'] . "\n";
$vhost_content .= ' </IfModule>' . "\n";
} else { } else {
FileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true); FileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true);
$vhost_content .= $this->getWebroot($domain); $vhost_content .= $this->getWebroot($domain);
@@ -952,8 +958,8 @@ class Apache extends HttpConfigBase
$domain['customerroot'] = FileDir::makeCorrectDir($domain['customerroot']); $domain['customerroot'] = FileDir::makeCorrectDir($domain['customerroot']);
$domain['documentroot'] = FileDir::makeCorrectDir($domain['documentroot']); $domain['documentroot'] = FileDir::makeCorrectDir($domain['documentroot']);
if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') { if (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && Settings::Get('system.deactivateddocroot') != '') {
$webroot_text .= ' # Using docroot for deactivated users...' . "\n"; $webroot_text .= ' # Using docroot for deactivated users/domains...' . "\n";
$webroot_text .= ' DocumentRoot "' . rtrim(FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')), "/") . "\"\n"; $webroot_text .= ' DocumentRoot "' . rtrim(FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')), "/") . "\"\n";
$webroot_text .= ' <Directory "' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . '">' . "\n"; $webroot_text .= ' <Directory "' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . '">' . "\n";
// >=apache-2.4 enabled? // >=apache-2.4 enabled?
@@ -1034,6 +1040,10 @@ class Apache extends HttpConfigBase
$statTool = Settings::Get('system.traffictool'); $statTool = Settings::Get('system.traffictool');
$statDomain = ""; $statDomain = "";
if ($statTool == 'awstats') {
// awstats generates for each domain regardless of speciallogfile
$statDomain = "/" . $domain['domain'];
}
if ($domain['speciallogfile'] == '1') { if ($domain['speciallogfile'] == '1') {
$statDomain = "/" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']); $statDomain = "/" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']);
} }

View File

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

View File

@@ -179,7 +179,7 @@ class HttpConfigBase
$froxlor_ssl_settings_stmt = Database::prepare(" $froxlor_ssl_settings_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
WHERE `domainid` = '0' AND WHERE `domainid` = '0' AND
(`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `expirationdate` IS NULL) (`validtodate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `validtodate` IS NULL)
"); ");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt); $froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
if ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) { if ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) {

View File

@@ -114,7 +114,9 @@ class AcmeSh extends FroxlorCron
`ssl_cert_chainfile` = :chain, `ssl_cert_chainfile` = :chain,
`ssl_csr_file` = :csr, `ssl_csr_file` = :csr,
`ssl_fullchain_file` = :fullchain, `ssl_fullchain_file` = :fullchain,
`expirationdate` = :expirationdate `validfromdate` = :validfromdate,
`validtodate` = :validtodate,
`issuer` = :issuer
"); ");
// prepare domain update sql // prepare domain update sql
@@ -136,7 +138,9 @@ class AcmeSh extends FroxlorCron
'lepublickey' => Settings::Get('system.lepublickey'), 'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'), 'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'), 'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
'expirationdate' => null, 'validfromdate' => null,
'validtodate' => null,
'issuer' => "",
'ssl_cert_file' => null, 'ssl_cert_file' => null,
'ssl_key_file' => null, 'ssl_key_file' => null,
'ssl_ca_file' => null, 'ssl_ca_file' => null,
@@ -171,7 +175,9 @@ class AcmeSh extends FroxlorCron
'lepublickey' => Settings::Get('system.lepublickey'), 'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'), 'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'), 'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
'expirationdate' => is_array($renew_froxlor) ? $renew_froxlor['expirationdate'] : date('Y-m-d H:i:s', 0), 'validfromdate' => is_array($renew_froxlor) ? $renew_froxlor['validfromdate'] : date('Y-m-d H:i:s', 0),
'validtodate' => is_array($renew_froxlor) ? $renew_froxlor['validtodate'] : date('Y-m-d H:i:s', 0),
'issuer' => is_array($renew_froxlor) ? $renew_froxlor['issuer'] : "",
'ssl_cert_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_cert_file'] : null, 'ssl_cert_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_cert_file'] : null,
'ssl_key_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_key_file'] : null, 'ssl_key_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_key_file'] : null,
'ssl_ca_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_ca_file'] : null, 'ssl_ca_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_ca_file'] : null,
@@ -187,7 +193,7 @@ class AcmeSh extends FroxlorCron
'loginname' => $domain['loginname'], 'loginname' => $domain['loginname'],
'adminsession' => 0 'adminsession' => 0
]); ]);
if (defined('CRON_IS_FORCED') || self::checkFsFilesAreNewer($domain['domain'], $domain['expirationdate'])) { if (defined('CRON_IS_FORCED') || self::checkFsFilesAreNewer($domain['domain'], $domain['validtodate'])) {
self::certToDb($domain, $cronlog, []); self::certToDb($domain, $cronlog, []);
$changedetected = 1; $changedetected = 1;
} }
@@ -221,16 +227,21 @@ class AcmeSh extends FroxlorCron
"); ");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt); $froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
// also check for possible existing certificate // also check for possible existing certificate
if (!$froxlor_ssl && !self::checkFsFilesAreNewer(Settings::Get('system.hostname'), date('Y-m-d H:i:s'))) { if (($froxlor_ssl && empty($froxlor_ssl['validtodate']))
|| (!$froxlor_ssl && !self::checkFsFilesAreNewer(Settings::Get('system.hostname'), date('Y-m-d H:i:s')))
) {
return true; return true;
} }
} }
return false; return false;
} }
private static function checkFsFilesAreNewer($domain, $cert_date = 0) private static function checkFsFilesAreNewer($domain, $cert_date = 0): bool
{ {
$certificate_folder = self::getWorkingDirFromEnv(strtolower($domain)); $certificate_folder = self::getCertificateFolder(strtolower($domain));
if (empty($certificate_folder)) {
return false;
}
$ssl_file = FileDir::makeCorrectFile($certificate_folder . '/' . strtolower($domain) . '.cer'); $ssl_file = FileDir::makeCorrectFile($certificate_folder . '/' . strtolower($domain) . '.cer');
if (is_dir($certificate_folder) && file_exists($ssl_file) && is_readable($ssl_file)) { if (is_dir($certificate_folder) && file_exists($ssl_file) && is_readable($ssl_file)) {
@@ -242,9 +253,13 @@ class AcmeSh extends FroxlorCron
return false; return false;
} }
public static function getWorkingDirFromEnv($domain = "", $forced_noecc = false) public static function getWorkingDirFromEnv($domain = "", $forced_ecc = false): string
{ {
if (Settings::Get('system.leecc') > 0 && !$forced_noecc) { // first try without _ecc either if it's enabled currently or not as
// it might have been at some point so there is a chance we have certificates
// with and without _ecc - the method getCertificateFolder() will check both
// possibilities
if ($forced_ecc) {
$domain .= "_ecc"; $domain .= "_ecc";
} }
$env_file = FileDir::makeCorrectFile(dirname(self::getAcmeSh()) . '/acme.sh.env'); $env_file = FileDir::makeCorrectFile(dirname(self::getAcmeSh()) . '/acme.sh.env');
@@ -254,7 +269,7 @@ class AcmeSh extends FroxlorCron
cut -d'"' -f2 cut -d'"' -f2
EOC; EOC;
exec('grep "LE_WORKING_DIR" ' . escapeshellarg($env_file) . ' | ' . $cut, $output); exec('grep "LE_WORKING_DIR" ' . escapeshellarg($env_file) . ' | ' . $cut, $output);
if (is_array($output) && !empty($output) && isset($output[0]) && !empty($output[0])) { if (is_array($output) && !empty($output) && !empty($output[0])) {
return FileDir::makeCorrectDir($output[0] . "/" . $domain); return FileDir::makeCorrectDir($output[0] . "/" . $domain);
} }
} }
@@ -279,7 +294,9 @@ EOC;
SELECT SELECT
domssl.`id`, domssl.`id`,
domssl.`domainid`, domssl.`domainid`,
domssl.`expirationdate`, domssl.`validfromdate`,
domssl.`validtodate`,
domssl.`issuer`,
domssl.`ssl_cert_file`, domssl.`ssl_cert_file`,
domssl.`ssl_key_file`, domssl.`ssl_key_file`,
domssl.`ssl_ca_file`, domssl.`ssl_ca_file`,
@@ -306,7 +323,7 @@ EOC;
AND dom.`letsencrypt` = 1 AND dom.`letsencrypt` = 1
AND dom.`aliasdomain` IS NULL AND dom.`aliasdomain` IS NULL
AND dom.`iswildcarddomain` = 0 AND dom.`iswildcarddomain` = 0
AND domssl.`expirationdate` IS NULL AND domssl.`validtodate` IS NULL
"); ");
$customer_ssl = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC); $customer_ssl = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC);
if ($customer_ssl) { if ($customer_ssl) {
@@ -330,7 +347,7 @@ EOC;
"); ");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt); $froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
// also check for possible existing certificate // also check for possible existing certificate
if ($froxlor_ssl && self::checkFsFilesAreNewer(Settings::Get('system.hostname'), $froxlor_ssl['expirationdate'])) { if ($froxlor_ssl && self::checkFsFilesAreNewer(Settings::Get('system.hostname'), $froxlor_ssl['validtodate'])) {
return $froxlor_ssl; return $froxlor_ssl;
} }
} }
@@ -346,7 +363,9 @@ EOC;
SELECT SELECT
domssl.`id`, domssl.`id`,
domssl.`domainid`, domssl.`domainid`,
domssl.`expirationdate`, domssl.`validfromdate`,
domssl.`validtodate`,
domssl.`issuer`,
domssl.`ssl_cert_file`, domssl.`ssl_cert_file`,
domssl.`ssl_key_file`, domssl.`ssl_key_file`,
dom.`domain`, dom.`domain`,
@@ -370,7 +389,7 @@ EOC;
if ($renew_certs) { if ($renew_certs) {
if ($check) { if ($check) {
foreach ($renew_certs as $cert) { foreach ($renew_certs as $cert) {
if (self::checkFsFilesAreNewer($cert['domain'], $cert['expirationdate'])) { if (self::checkFsFilesAreNewer($cert['domain'], $cert['validtodate'])) {
return true; return true;
} }
} }
@@ -453,7 +472,7 @@ EOC;
// Only issue let's encrypt certificate if no broken ssl_redirect is enabled // Only issue let's encrypt certificate if no broken ssl_redirect is enabled
if ($certrow['ssl_redirect'] != 2) { if ($certrow['ssl_redirect'] != 2) {
$do_force = false; $do_force = false;
if (!empty($certrow['ssl_cert_file']) && empty($certrow['expirationdate'])) { if (!empty($certrow['ssl_cert_file']) && empty($certrow['validtodate'])) {
// domain changed (SAN or similar) // domain changed (SAN or similar)
$do_force = true; $do_force = true;
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']);
@@ -594,7 +613,9 @@ EOC;
'chain' => $return['chain'], 'chain' => $return['chain'],
'csr' => $return['csr'], 'csr' => $return['csr'],
'fullchain' => $return['fullchain'], 'fullchain' => $return['fullchain'],
'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) 'validfromdate' => date('Y-m-d H:i:s', $newcert['validFrom_time_t']),
'validtodate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']),
'issuer' => $newcert['issuer']['O'] ?? ""
]); ]);
if ($certrow['ssl_redirect'] == 3) { if ($certrow['ssl_redirect'] == 3) {
@@ -621,35 +642,21 @@ EOC;
*/ */
private static function readCertificateToVar($domain, &$return, &$cronlog) private static function readCertificateToVar($domain, &$return, &$cronlog)
{ {
$certificate_folder = self::getWorkingDirFromEnv($domain); $certificate_folder = self::getCertificateFolder($domain);
$certificate_folder_noecc = null;
if (Settings::Get('system.leecc') > 0) {
$certificate_folder_noecc = self::getWorkingDirFromEnv($domain, true);
}
$certificate_folder = FileDir::makeCorrectDir($certificate_folder);
if (is_dir($certificate_folder) || is_dir($certificate_folder_noecc)) { if (!empty($certificate_folder)) {
foreach ( $certificate_files = [
[ 'crt' => $domain . '.cer',
'crt' => $domain . '.cer', 'key' => $domain . '.key',
'key' => $domain . '.key', 'chain' => 'ca.cer',
'chain' => 'ca.cer', 'fullchain' => 'fullchain.cer',
'fullchain' => 'fullchain.cer', 'csr' => $domain . '.csr'
'csr' => $domain . '.csr' ];
] as $index => $sslfile foreach ($certificate_files as $index => $sslfile) {
) {
$ssl_file = FileDir::makeCorrectFile($certificate_folder . '/' . $sslfile); $ssl_file = FileDir::makeCorrectFile($certificate_folder . '/' . $sslfile);
if (file_exists($ssl_file)) { if (file_exists($ssl_file)) {
$return[$index] = file_get_contents($ssl_file); $return[$index] = file_get_contents($ssl_file);
} else { } else {
if (!empty($certificate_folder_noecc)) {
$ssl_file_fb = FileDir::makeCorrectFile($certificate_folder_noecc . '/' . $sslfile);
if (file_exists($ssl_file_fb)) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "ECC certificates activated but found only non-ecc file");
$return[$index] = file_get_contents($ssl_file_fb);
continue;
}
}
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find file '" . $sslfile . "' in '" . $certificate_folder . "'"); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find file '" . $sslfile . "' in '" . $certificate_folder . "'");
$return[$index] = null; $return[$index] = null;
} }
@@ -658,4 +665,18 @@ EOC;
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find certificate-folder '" . $certificate_folder . "'"); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find certificate-folder '" . $certificate_folder . "'");
} }
} }
private static function getCertificateFolder(string $domain): string
{
$certificate_folder = self::getWorkingDirFromEnv(strtolower($domain));
if (file_exists($certificate_folder)) {
return $certificate_folder;
}
$certificate_folder_ecc = self::getWorkingDirFromEnv($domain, true);
if (file_exists($certificate_folder_ecc)) {
return $certificate_folder_ecc;
}
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find certificate-folder for domain '" . $domain . "'");
return "";
}
} }

View File

@@ -414,15 +414,20 @@ class Lighttpd extends HttpConfigBase
$domain['documentroot'] = trim($domain['documentroot']); $domain['documentroot'] = trim($domain['documentroot']);
if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { if (preg_match('/^https?\:\/\//', $domain['documentroot'])) {
$uri = $domain['documentroot']; $possible_deactivated_webroot = $this->getWebroot($domain);
if ($this->deactivated == false) {
$uri = $domain['documentroot'];
// Get domain's redirect code // Get domain's redirect code
$code = Domain::getDomainRedirectCode($domain['id']); $code = Domain::getDomainRedirectCode($domain['id']);
$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";
$vhost_content .= ' )' . "\n"; $vhost_content .= ' )' . "\n";
} elseif (Settings::Get('system.deactivateddocroot') != '') {
$vhost_content .= $possible_deactivated_webroot;
}
} else { } else {
FileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true); FileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true);
@@ -562,12 +567,12 @@ class Lighttpd extends HttpConfigBase
return $servernames_text; return $servernames_text;
} }
protected function getWebroot($domain, $ssl) protected function getWebroot($domain, bool $ssl = false)
{ {
$webroot_text = ''; $webroot_text = '';
if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') { if (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && Settings::Get('system.deactivateddocroot') != '') {
$webroot_text .= ' # Using docroot for deactivated users...' . "\n"; $webroot_text .= ' # Using docroot for deactivated users/domains...' . "\n";
$webroot_text .= ' server.document-root = "' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n"; $webroot_text .= ' server.document-root = "' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n";
$this->deactivated = true; $this->deactivated = true;
} else { } else {
@@ -719,6 +724,10 @@ class Lighttpd extends HttpConfigBase
$statTool = Settings::Get('system.traffictool'); $statTool = Settings::Get('system.traffictool');
$statDomain = ""; $statDomain = "";
if ($statTool == 'awstats') {
// awstats generates for each domain regardless of speciallogfile
$statDomain = "/" . $domain['domain'];
}
if ($domain['speciallogfile'] == '1') { if ($domain['speciallogfile'] == '1') {
$statDomain = "/" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']); $statDomain = "/" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']);
} }

View File

@@ -225,7 +225,7 @@ class Nginx extends HttpConfigBase
$this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n";
// protect bin/ // protect bin/
$this->nginx_data[$vhost_filename] .= "\t" . 'location ~ ' . rtrim($relpath, "/") . '/(bin|cache|logs|tests|vendor) {' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . 'location ~ ^' . rtrim($relpath, "/") . '/(bin|cache|logs|tests|vendor) {' . "\n";
$this->nginx_data[$vhost_filename] .= "\t" . ' deny all;' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . ' deny all;' . "\n";
$this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n";
} }
@@ -493,10 +493,10 @@ class Nginx extends HttpConfigBase
return ''; return '';
} }
// check whether the customer is deactivated and NO docroot for deactivated users has been set# // check whether the customer/domain is deactivated and NO docroot for deactivated users has been set#
$ddr = Settings::Get('system.deactivateddocroot'); $ddr = Settings::Get('system.deactivateddocroot');
if ($domain['deactivated'] == '1' && empty($ddr)) { if (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1') && empty($ddr)) {
return '# Customer deactivated and a docroot for deactivated users hasn\'t been set.' . "\n"; return '# Customer deactivated and a docroot for deactivated users/domains hasn\'t been set.' . "\n";
} }
$vhost_content = ''; $vhost_content = '';
@@ -596,22 +596,27 @@ class Nginx extends HttpConfigBase
// if the documentroot is an URL we just redirect // if the documentroot is an URL we just redirect
if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { if (preg_match('/^https?\:\/\//', $domain['documentroot'])) {
$uri = $domain['documentroot']; $possible_deactivated_webroot = $this->getWebroot($domain);
if (substr($uri, -1) == '/') { if ($this->deactivated == false) {
$uri = substr($uri, 0, -1); $uri = $domain['documentroot'];
if (substr($uri, -1) == '/') {
$uri = substr($uri, 0, -1);
}
// Get domain's redirect code
$code = Domain::getDomainRedirectCode($domain['id']);
$vhost_content .= "\t" . 'location / {' . "\n";
$vhost_content .= "\t\t" . 'return ' . $code . ' ' . $uri . '$request_uri;' . "\n";
$vhost_content .= "\t" . '}' . "\n";
} elseif (Settings::Get('system.deactivateddocroot') != '') {
$vhost_content .= $possible_deactivated_webroot;
} }
// Get domain's redirect code
$code = Domain::getDomainRedirectCode($domain['id']);
$vhost_content .= "\t" . 'location / {' . "\n";
$vhost_content .= "\t\t" . 'return ' . $code . ' ' . $uri . '$request_uri;' . "\n";
$vhost_content .= "\t" . '}' . "\n";
} else { } else {
FileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true); FileDir::mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true);
$vhost_content .= $this->getLogFiles($domain); $vhost_content .= $this->getLogFiles($domain);
$vhost_content .= $this->getWebroot($domain, $ssl_vhost); $vhost_content .= $this->getWebroot($domain);
if ($this->deactivated == false) { if ($this->deactivated == false) {
$vhost_content = $this->mergeVhostCustom($vhost_content, $this->createPathOptions($domain)) . "\n"; $vhost_content = $this->mergeVhostCustom($vhost_content, $this->createPathOptions($domain)) . "\n";
@@ -770,12 +775,12 @@ class Nginx extends HttpConfigBase
return $logfiles_text; return $logfiles_text;
} }
protected function getWebroot($domain, $ssl) protected function getWebroot($domain)
{ {
$webroot_text = ''; $webroot_text = '';
if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') { if (($domain['deactivated'] == '1' || $domain['customer_deactivated'] == '1' ) && Settings::Get('system.deactivateddocroot') != '') {
$webroot_text .= "\t" . '# Using docroot for deactivated users...' . "\n"; $webroot_text .= "\t" . '# Using docroot for deactivated users/domains...' . "\n";
$webroot_text .= "\t" . 'root ' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . ';' . "\n"; $webroot_text .= "\t" . 'root ' . FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . ';' . "\n";
$this->deactivated = true; $this->deactivated = true;
} else { } else {
@@ -878,13 +883,7 @@ class Nginx extends HttpConfigBase
// remove comments // remove comments
$vhost = implode("\n", preg_replace('/^(\s+)?#(.*)$/', '', explode("\n", $vhost))); $vhost = implode("\n", preg_replace('/^(\s+)?#(.*)$/', '', explode("\n", $vhost)));
// Break blocks into lines // Break blocks into lines
$vhost = str_replace([ $vhost = preg_replace("/^(\s+)location(.+)\{(.+)\}$/misU", "location $2 {\n $3 \n}", $vhost);
"{",
"}"
], [
" {\n",
"\n}"
], $vhost);
// Break into array items // Break into array items
$vhost = explode("\n", preg_replace('/[ \t]+/', ' ', trim(preg_replace('/\t+/', '', $vhost)))); $vhost = explode("\n", preg_replace('/[ \t]+/', ' ', trim(preg_replace('/\t+/', '', $vhost))));
// Remove empty lines // Remove empty lines
@@ -1035,6 +1034,11 @@ class Nginx extends HttpConfigBase
$path_options .= "\t\t" . 'auth_basic_user_file ' . FileDir::makeCorrectFile($single['usrf']) . ';' . "\n"; $path_options .= "\t\t" . 'auth_basic_user_file ' . FileDir::makeCorrectFile($single['usrf']) . ';' . "\n";
if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') {
$path_options .= "\t\t" . 'index index.php index.html index.htm;' . "\n"; $path_options .= "\t\t" . 'index index.php index.html index.htm;' . "\n";
if ($domain['notryfiles'] != 1) {
$path_options .= "\t\t" . 'location ~ ^(.+?\.php)(/.*)?$ {' . "\n";
$path_options .= "\t\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n";
$path_options .= "\t\t" . '}' . "\n\n";
}
} else { } else {
$path_options .= "\t\t" . 'index index.html index.htm;' . "\n"; $path_options .= "\t\t" . 'index index.html index.htm;' . "\n";
} }
@@ -1120,6 +1124,10 @@ class Nginx extends HttpConfigBase
$statTool = Settings::Get('system.traffictool'); $statTool = Settings::Get('system.traffictool');
$statDomain = ""; $statDomain = "";
if ($statTool == 'awstats') {
// awstats generates for each domain regardless of speciallogfile
$statDomain = "/" . $domain['domain'];
}
if ($domain['speciallogfile'] == '1') { if ($domain['speciallogfile'] == '1') {
$statDomain = "/" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']); $statDomain = "/" . (($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']);
} }
@@ -1157,7 +1165,6 @@ class Nginx extends HttpConfigBase
$phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n"; $phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n";
$phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n"; $phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n";
$phpopts .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n"; $phpopts .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n";
$phpopts .= "\t\ttry_files \$fastcgi_script_name =404;\n";
$phpopts .= "\t\tfastcgi_pass " . Settings::Get('system.nginx_php_backend') . ";\n"; $phpopts .= "\t\tfastcgi_pass " . Settings::Get('system.nginx_php_backend') . ";\n";
$phpopts .= "\t\tfastcgi_index index.php;\n"; $phpopts .= "\t\tfastcgi_index index.php;\n";
if ($domain['ssl'] == '1' && $ssl_vhost) { if ($domain['ssl'] == '1' && $ssl_vhost) {

View File

@@ -342,8 +342,17 @@ pm.max_children = 1
public function getSocketFile($createifnotexists = true) public function getSocketFile($createifnotexists = true)
{ {
$socketdir = FileDir::makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir')); $socketdir = FileDir::makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir'));
// add fpm-config-id to filename so it's unique for the fpm-daemon and doesn't interfere with running configs when reuilding // add fpm-config-id to filename, so it's unique for the fpm-daemon and doesn't interfere with running configs when reuilding
$socket = strtolower(FileDir::makeCorrectFile($socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['domain'] . '-php-fpm.socket')); $socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['domain'] . '-php-fpm.socket';
if (strlen($socket_filename) > 100) {
// respect the unix socket-length limitation
$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['loginname'] . '-' . $this->domain['id'] . '-php-fpm.socket';
if (strlen($socket_filename) > 100) {
// even a long loginname it seems
$socket_filename = $socketdir . '/' . $this->domain['fpm_config_id'] . '-' . $this->domain['guid'] . '-' . $this->domain['id'] . '-php-fpm.socket';
}
}
$socket = strtolower(FileDir::makeCorrectFile($socket_filename));
if (!is_dir($socketdir) && $createifnotexists) { if (!is_dir($socketdir) && $createifnotexists) {
FileDir::safe_exec('mkdir -p ' . escapeshellarg($socketdir)); FileDir::safe_exec('mkdir -p ' . escapeshellarg($socketdir));

View File

@@ -43,7 +43,7 @@ class WebserverBase
{ {
$query = "SELECT `d`.*, `pd`.`domain` AS `parentdomain`, `c`.`loginname`, $query = "SELECT `d`.*, `pd`.`domain` AS `parentdomain`, `c`.`loginname`,
`d`.`phpsettingid`, `c`.`adminid`, `c`.`guid`, `c`.`email`, `d`.`phpsettingid`, `c`.`adminid`, `c`.`guid`, `c`.`email`,
`c`.`documentroot` AS `customerroot`, `c`.`deactivated`, `c`.`documentroot` AS `customerroot`, `c`.`deactivated` as `customer_deactivated`,
`c`.`phpenabled` AS `phpenabled_customer`, `c`.`phpenabled` AS `phpenabled_customer`,
`d`.`phpenabled` AS `phpenabled_vhost` `d`.`phpenabled` AS `phpenabled_vhost`
FROM `" . TABLE_PANEL_DOMAINS . "` `d` FROM `" . TABLE_PANEL_DOMAINS . "` `d`

View File

@@ -146,29 +146,37 @@ class BackupCron extends FroxlorCron
FileDir::safe_exec('mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir($tmpdir . '/mysql'))); FileDir::safe_exec('mkdir -p ' . escapeshellarg(FileDir::makeCorrectDir($tmpdir . '/mysql')));
// get all customer database-names // get all customer database-names
// @fixme respect multiple dbservers $sel_stmt = Database::prepare("SELECT `databasename`, `dbserver` FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid` = :cid ORDER BY `dbserver`");
$sel_stmt = Database::prepare("SELECT `databasename` FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid` = :cid");
Database::pexecute($sel_stmt, [ Database::pexecute($sel_stmt, [
'cid' => $data['customerid'] 'cid' => $data['customerid']
]); ]);
Database::needRoot(true);
Database::needSqlData();
$sql_root = Database::getSqlData();
Database::needRoot(false);
$mysqlcnf_file = tempnam("/tmp", "frx");
$mysqlcnf = "[mysqldump]\npassword=".$sql_root['passwd']."\n";
file_put_contents($mysqlcnf_file, $mysqlcnf);
$has_dbs = false; $has_dbs = false;
$current_dbserver = null;
while ($row = $sel_stmt->fetch()) { while ($row = $sel_stmt->fetch()) {
// Get sql_root data for the specific database-server the database resides on
if ($current_dbserver != $row['dbserver']) {
Database::needRoot(true, $row['dbserver']);
Database::needSqlData();
$sql_root = Database::getSqlData();
Database::needRoot(false);
// create temporary mysql-defaults file for the connection-credentials/details
$mysqlcnf_file = tempnam("/tmp", "frx");
$mysqlcnf = "[mysqldump]\npassword=" . $sql_root['passwd'] . "\nhost=" . $sql_root['host'] . "\n";
if (!empty($sql_root['port'])) {
$mysqlcnf .= "port=" . $sql_root['port'] . "\n";
} elseif (!empty($sql_root['socket'])) {
$mysqlcnf .= "socket=" . $sql_root['socket'] . "\n";
}
file_put_contents($mysqlcnf_file, $mysqlcnf);
}
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'shell> mysqldump -u ' . escapeshellarg($sql_root['user']) . ' -pXXXXX ' . $row['databasename'] . ' > ' . FileDir::makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql')); $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; $bool_false = false;
FileDir::safe_exec('mysqldump --defaults-file=' . escapeshellarg($mysqlcnf_file) .' -u ' . escapeshellarg($sql_root['user']) . ' ' . $row['databasename'] . ' > ' . FileDir::makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'), $bool_false, [ FileDir::safe_exec('mysqldump --defaults-file=' . escapeshellarg($mysqlcnf_file) . ' -u ' . escapeshellarg($sql_root['user']) . ' ' . $row['databasename'] . ' > ' . FileDir::makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'), $bool_false, [
'>' '>'
]); ]);
$has_dbs = true; $has_dbs = true;
$current_dbserver = $row['dbserver'];
} }
if ($has_dbs) { if ($has_dbs) {

View File

@@ -33,8 +33,6 @@ namespace Froxlor\Cron\Traffic;
use Exception; use Exception;
use Froxlor\Cron\FroxlorCron; use Froxlor\Cron\FroxlorCron;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\FileDir;
use Froxlor\Froxlor;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\PhpHelper; use Froxlor\PhpHelper;
use Froxlor\Settings; use Froxlor\Settings;
@@ -99,8 +97,8 @@ class ReportsCron extends FroxlorCron
'COMPANY' => $rep_userinfo['company'], 'COMPANY' => $rep_userinfo['company'],
'USERNAME' => $rep_userinfo['loginname'], 'USERNAME' => $rep_userinfo['loginname'],
'CUSTOMER_NO' => $rep_userinfo['customernumber'], 'CUSTOMER_NO' => $rep_userinfo['customernumber'],
'TRAFFIC' => PhpHelper::sizeReadable($row['traffic'], null, 'bi'), 'TRAFFIC' => PhpHelper::sizeReadable((int)$row['traffic'], null, 'bi'),
'TRAFFICUSED' => PhpHelper::sizeReadable($row['traffic_used'], null, 'bi'), 'TRAFFICUSED' => PhpHelper::sizeReadable((int)$row['traffic_used'], null, 'bi'),
'USAGE_PERCENT' => round(($row['traffic_used'] * 100) / $row['traffic'], 2), 'USAGE_PERCENT' => round(($row['traffic_used'] * 100) / $row['traffic'], 2),
'MAX_PERCENT' => Settings::Get('system.report_trafficmax') 'MAX_PERCENT' => Settings::Get('system.report_trafficmax')
]; ];
@@ -182,8 +180,8 @@ class ReportsCron extends FroxlorCron
if (isset($row['traffic']) && $row['traffic'] > 0 && (($row['traffic_used_total'] * 100) / ($row['traffic'])) >= (int)Settings::Get('system.report_trafficmax')) { if (isset($row['traffic']) && $row['traffic'] > 0 && (($row['traffic_used_total'] * 100) / ($row['traffic'])) >= (int)Settings::Get('system.report_trafficmax')) {
$replace_arr = [ $replace_arr = [
'NAME' => $row['name'], 'NAME' => $row['name'],
'TRAFFIC' => PhpHelper::sizeReadable($row['traffic'], null, 'bi'), 'TRAFFIC' => PhpHelper::sizeReadable((int)$row['traffic'], null, 'bi'),
'TRAFFICUSED' => PhpHelper::sizeReadable($row['traffic_used_total'], null, 'bi'), 'TRAFFICUSED' => PhpHelper::sizeReadable((int)$row['traffic_used_total'], null, 'bi'),
'USAGE_PERCENT' => round(($row['traffic_used_total'] * 100) / $row['traffic'], 2), 'USAGE_PERCENT' => round(($row['traffic_used_total'] * 100) / $row['traffic'], 2),
'MAX_PERCENT' => Settings::Get('system.report_trafficmax') 'MAX_PERCENT' => Settings::Get('system.report_trafficmax')
]; ];
@@ -265,10 +263,10 @@ class ReportsCron extends FroxlorCron
while ($customer = $customers_stmt->fetch(PDO::FETCH_ASSOC)) { while ($customer = $customers_stmt->fetch(PDO::FETCH_ASSOC)) {
$customer['traffic'] *= 1024; $customer['traffic'] *= 1024;
$t = $customer['traffic_used_total'] * 1024; $t = (int) $customer['traffic_used_total'] * 1024;
if ($customer['traffic'] > 0) { if ($customer['traffic'] > 0) {
$p = (($t * 100) / $customer['traffic']); $p = (($t * 100) / $customer['traffic']);
$tg = $customer['traffic']; $tg = (int) $customer['traffic'];
$str = sprintf('%s ( %00.1f %% )', PhpHelper::sizeReadable($t, null, 'bi'), $p); $str = sprintf('%s ( %00.1f %% )', PhpHelper::sizeReadable($t, null, 'bi'), $p);
$mail_body .= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%s', PhpHelper::sizeReadable($tg, null, 'bi')) . "\n"; $mail_body .= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%s', PhpHelper::sizeReadable($tg, null, 'bi')) . "\n";
} elseif ($customer['traffic'] == 0) { } elseif ($customer['traffic'] == 0) {
@@ -282,10 +280,10 @@ class ReportsCron extends FroxlorCron
$mail_body .= '---------------------------------------------------------------' . "\n"; $mail_body .= '---------------------------------------------------------------' . "\n";
$t = $row['traffic_used_total']; $t = (int) $row['traffic_used_total'];
if ($row['traffic'] > 0) { if ($row['traffic'] > 0) {
$p = (($t * 100) / $row['traffic']); $p = (($t * 100) / $row['traffic']);
$tg = $row['traffic']; $tg = (int) $row['traffic'];
$str = sprintf('%s ( %00.1f %% )', PhpHelper::sizeReadable($t, null, 'bi'), $p); $str = sprintf('%s ( %00.1f %% )', PhpHelper::sizeReadable($t, null, 'bi'), $p);
$mail_body .= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%s', PhpHelper::sizeReadable($tg, null, 'bi')) . "\n"; $mail_body .= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%s', PhpHelper::sizeReadable($tg, null, 'bi')) . "\n";
} elseif ($row['traffic'] == 0) { } elseif ($row['traffic'] == 0) {
@@ -369,8 +367,8 @@ class ReportsCron extends FroxlorCron
'COMPANY' => $rep_userinfo['company'], 'COMPANY' => $rep_userinfo['company'],
'USERNAME' => $rep_userinfo['loginname'], 'USERNAME' => $rep_userinfo['loginname'],
'CUSTOMER_NO' => $rep_userinfo['customernumber'], 'CUSTOMER_NO' => $rep_userinfo['customernumber'],
'DISKAVAILABLE' => PhpHelper::sizeReadable($row['diskspace'], null, 'bi'), 'DISKAVAILABLE' => PhpHelper::sizeReadable((int)$row['diskspace'], null, 'bi'),
'DISKUSED' => PhpHelper::sizeReadable($row['diskspace_used'], null, 'bi'), 'DISKUSED' => PhpHelper::sizeReadable((int)$row['diskspace_used'], null, 'bi'),
'USAGE_PERCENT' => round(($row['diskspace_used'] * 100) / $row['diskspace'], 2), 'USAGE_PERCENT' => round(($row['diskspace_used'] * 100) / $row['diskspace'], 2),
'MAX_PERCENT' => Settings::Get('system.report_webmax') 'MAX_PERCENT' => Settings::Get('system.report_webmax')
]; ];
@@ -443,8 +441,8 @@ class ReportsCron extends FroxlorCron
if (isset($row['diskspace']) && $row['diskspace_used'] != null && $row['diskspace_used'] > 0 && (($row['diskspace_used'] * 100) / $row['diskspace']) >= (int)Settings::Get('system.report_webmax')) { if (isset($row['diskspace']) && $row['diskspace_used'] != null && $row['diskspace_used'] > 0 && (($row['diskspace_used'] * 100) / $row['diskspace']) >= (int)Settings::Get('system.report_webmax')) {
$replace_arr = [ $replace_arr = [
'NAME' => $row['name'], 'NAME' => $row['name'],
'DISKAVAILABLE' => PhpHelper::sizeReadable($row['diskspace'], null, 'bi'), 'DISKAVAILABLE' => PhpHelper::sizeReadable((int)$row['diskspace'], null, 'bi'),
'DISKUSED' => PhpHelper::sizeReadable($row['diskspace_used'], null, 'bi'), 'DISKUSED' => PhpHelper::sizeReadable((int)$row['diskspace_used'], null, 'bi'),
'USAGE_PERCENT' => ($row['diskspace_used'] * 100) / $row['diskspace'], 'USAGE_PERCENT' => ($row['diskspace_used'] * 100) / $row['diskspace'],
'MAX_PERCENT' => Settings::Get('system.report_webmax') 'MAX_PERCENT' => Settings::Get('system.report_webmax')
]; ];

View File

@@ -78,7 +78,7 @@ class TrafficCron extends FroxlorCron
// Fork failed // Fork failed
return 1; return 1;
} }
} else if (!defined('CRON_NOFORK_FLAG')) { } elseif (!defined('CRON_NOFORK_FLAG')) {
if (extension_loaded('pcntl')) { if (extension_loaded('pcntl')) {
$msg = "PHP compiled with pcntl but pcntl_fork function is not available."; $msg = "PHP compiled with pcntl but pcntl_fork function is not available.";
} else { } else {
@@ -406,7 +406,7 @@ class TrafficCron extends FroxlorCron
} else { } else {
// Use the old fashioned way with "du" // Use the old fashioned way with "du"
if (file_exists($row['documentroot']) && is_dir($row['documentroot'])) { if (file_exists($row['documentroot']) && is_dir($row['documentroot'])) {
$back = FileDir::safe_exec('du -sk ' . escapeshellarg($row['documentroot']) . ''); $back = FileDir::safe_exec('du -sk ' . escapeshellarg($row['documentroot']));
foreach ($back as $backrow) { foreach ($back as $backrow) {
$webspaceusage = explode(' ', $backrow); $webspaceusage = explode(' ', $backrow);
} }
@@ -426,7 +426,7 @@ class TrafficCron extends FroxlorCron
$maildir = FileDir::makeCorrectDir(Settings::Get('system.vmail_homedir') . $row['loginname']); $maildir = FileDir::makeCorrectDir(Settings::Get('system.vmail_homedir') . $row['loginname']);
if (file_exists($maildir) && is_dir($maildir)) { if (file_exists($maildir) && is_dir($maildir)) {
$back = FileDir::safe_exec('du -sk ' . escapeshellarg($maildir) . ''); $back = FileDir::safe_exec('du -sk ' . escapeshellarg($maildir));
foreach ($back as $backrow) { foreach ($back as $backrow) {
$emailusage = explode(' ', $backrow); $emailusage = explode(' ', $backrow);
} }

View File

@@ -25,10 +25,10 @@
namespace Froxlor; namespace Froxlor;
use Froxlor\Database\Database;
use Froxlor\UI\Collection;
use Froxlor\Api\Commands\Customers; use Froxlor\Api\Commands\Customers;
use Froxlor\Api\Commands\SubDomains; use Froxlor\Api\Commands\SubDomains;
use Froxlor\Database\Database;
use Froxlor\UI\Collection;
/** /**
* Class to manage the current user / session * Class to manage the current user / session
@@ -63,9 +63,10 @@ class CurrentUser
/** /**
* re-read in the user data if a valid session exists * re-read in the user data if a valid session exists
* *
* @return boolean * @return bool
* @throws \Exception
*/ */
public static function reReadUserData() public static function reReadUserData(): bool
{ {
$table = self::isAdmin() ? TABLE_PANEL_ADMINS : TABLE_PANEL_CUSTOMERS; $table = self::isAdmin() ? TABLE_PANEL_ADMINS : TABLE_PANEL_CUSTOMERS;
$userinfo_stmt = Database::prepare(" $userinfo_stmt = Database::prepare("
@@ -75,7 +76,7 @@ class CurrentUser
"loginname" => self::getField('loginname') "loginname" => self::getField('loginname')
]); ]);
if ($userinfo) { if ($userinfo) {
// dont just set the data, we need to merge with current data // don't just set the data, we need to merge with current data
// array_merge is a right-reduction - value existing in getData() will be overwritten with $userinfo, // array_merge is a right-reduction - value existing in getData() will be overwritten with $userinfo,
// other than the union-operator (+) which would keep the values already existing from getData() // other than the union-operator (+) which would keep the values already existing from getData()
$newuserinfo = array_merge(self::getData(), $userinfo); $newuserinfo = array_merge(self::getData(), $userinfo);
@@ -107,7 +108,7 @@ class CurrentUser
*/ */
public static function getField(string $index) public static function getField(string $index)
{ {
return isset($_SESSION['userinfo'][$index]) ? $_SESSION['userinfo'][$index] : ""; return $_SESSION['userinfo'][$index] ?? "";
} }
/** /**
@@ -130,6 +131,11 @@ class CurrentUser
$_SESSION['userinfo'] = $data; $_SESSION['userinfo'] = $data;
} }
/**
* @param string $resource
* @return bool
* @throws \Exception
*/
public static function canAddResource(string $resource): bool public static function canAddResource(string $resource): bool
{ {
$addition = true; $addition = true;
@@ -145,14 +151,15 @@ class CurrentUser
]); ]);
$addition = $result['emaildomains'] != 0; $addition = $result['emaildomains'] != 0;
} elseif ($resource == 'subdomains') { } elseif ($resource == 'subdomains') {
$parentDomainCollection = (new Collection(SubDomains::class, $_SESSION['userinfo'], ['sql_search' => ['d.parentdomainid' => 0]])); $parentDomainCollection = (new Collection(SubDomains::class, $_SESSION['userinfo'],
$addition = $parentDomainCollection != 0; ['sql_search' => ['d.parentdomainid' => 0]]));
$addition = $parentDomainCollection->count() != 0;
} elseif ($resource == 'domains') { } elseif ($resource == 'domains') {
$customerCollection = (new Collection(Customers::class, $_SESSION['userinfo'])); $customerCollection = (new Collection(Customers::class, $_SESSION['userinfo']));
$addition = $customerCollection != 0; $addition = $customerCollection != 0;
} }
return ($_SESSION['userinfo'][$resource.'_used'] < $_SESSION['userinfo'][$resource] || $_SESSION['userinfo'][$resource] == '-1') && $addition; return ($_SESSION['userinfo'][$resource . '_used'] < $_SESSION['userinfo'][$resource] || $_SESSION['userinfo'][$resource] == '-1') && $addition;
} }
} }

View File

@@ -31,7 +31,15 @@ use PDO;
class Customer class Customer
{ {
public static function getCustomerDetail($customerid, $varname) /**
* Get value of a a specific field from a given customer
*
* @param int $customerid
* @param string $varname
* @return false|mixed
* @throws \Exception
*/
public static function getCustomerDetail(int $customerid, string $varname)
{ {
$customer_stmt = Database::prepare(" $customer_stmt = Database::prepare("
SELECT `" . $varname . "` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :customerid SELECT `" . $varname . "` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :customerid
@@ -42,20 +50,19 @@ class Customer
if (isset($customer[$varname])) { if (isset($customer[$varname])) {
return $customer[$varname]; return $customer[$varname];
} else {
return false;
} }
return false;
} }
/** /**
* returns the loginname of a customer by given uid * returns the loginname of a customer by given uid
* *
* @param int $uid * @param int $uid uid of customer
* uid of customer
* *
* @return string customers loginname * @return string customers loginname
* @throws \Exception
*/ */
public static function getLoginNameByUid($uid = null) public static function getLoginNameByUid(int $uid)
{ {
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `guid` = :guid SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `guid` = :guid
@@ -64,7 +71,7 @@ class Customer
'guid' => $uid 'guid' => $uid
]); ]);
if (is_array($result) && isset($result['loginname'])) { if ($result && isset($result['loginname'])) {
return $result['loginname']; return $result['loginname'];
} }
return false; return false;
@@ -76,23 +83,22 @@ class Customer
* returns true or false whether perl is * returns true or false whether perl is
* enabled for the given customer * enabled for the given customer
* *
* @param * @param int $cid customer-id
* int customer-id
* *
* @return boolean * @return bool
* @throws \Exception
*/ */
public static function customerHasPerlEnabled($cid = 0) public static function customerHasPerlEnabled(int $cid = 0)
{ {
if ($cid > 0) { if ($cid > 0) {
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT `perlenabled` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :cid"); SELECT `perlenabled` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :cid");
Database::pexecute($result_stmt, [ $result = Database::pexecute_first($result_stmt, [
'cid' => $cid 'cid' => $cid
]); ]);
$result = $result_stmt->fetch(PDO::FETCH_ASSOC);
if (is_array($result) && isset($result['perlenabled'])) { if ($result && isset($result['perlenabled'])) {
return $result['perlenabled'] == '1'; return (bool)$result['perlenabled'];
} }
} }
return false; return false;

View File

@@ -61,41 +61,43 @@ class Database
/** /**
* indicator whether to use root-connection or not * indicator whether to use root-connection or not
*/ */
private static $needroot = false; private static bool $needroot = false;
/** /**
* indicator which database-server we're on (not really used) * indicator which database-server we're on (not really used)
*/ */
private static $dbserver = 0; private static int $dbserver = 0;
/** /**
* used database-name * used database-name
*/ */
private static $dbname = null; private static ?string $dbname = null;
/** /**
* sql-access data * sql-access data
*/ */
private static $needsqldata = false; private static bool $needsqldata = false;
private static $sqldata = null; private static $sqldata = null;
private static $need_dbname = true; private static bool $need_dbname = true;
/** /**
* Wrapper for PDOStatement::execute so we can catch the PDOException * Wrapper for PDOStatement::execute, so we can catch the PDOException
* and display the error nicely on the panel - also fetches the * and display the error nicely on the panel - also fetches the
* result from the statement and returns the resulting array * result from the statement and returns the resulting array
* *
* @param PDOStatement $stmt * @param PDOStatement $stmt
* @param array $params * @param array|null $params
* (optional) * (optional)
* @param bool $showerror * @param bool $showerror
* suppress errordisplay (default true) * suppress error display (default true)
* @param bool $json_response
* *
* @return array * @return mixed
* @throws Exception
*/ */
public static function pexecute_first(&$stmt, $params = null, $showerror = true, $json_response = false) public static function pexecute_first(PDOStatement &$stmt, $params = null, bool $showerror = true, bool $json_response = false)
{ {
self::pexecute($stmt, $params, $showerror, $json_response); self::pexecute($stmt, $params, $showerror, $json_response);
return $stmt->fetch(PDO::FETCH_ASSOC); return $stmt->fetch(PDO::FETCH_ASSOC);
@@ -106,12 +108,15 @@ class Database
* and display the error nicely on the panel * and display the error nicely on the panel
* *
* @param PDOStatement $stmt * @param PDOStatement $stmt
* @param array $params * @param array|null $params
* (optional) * (optional)
* @param bool $showerror * @param bool $showerror
* suppress errordisplay (default true) * suppress error display (default true)
* @param bool $json_response
*
* @throws Exception
*/ */
public static function pexecute(&$stmt, $params = null, $showerror = true, $json_response = false) public static function pexecute(PDOStatement &$stmt, $params = null, bool $showerror = true, bool $json_response = false)
{ {
try { try {
$stmt->execute($params); $stmt->execute($params);
@@ -125,9 +130,10 @@ class Database
* *
* @param PDOException $error * @param PDOException $error
* @param bool $showerror * @param bool $showerror
* if set to false, the error will be logged but we go on * if set to false, the error will be logged, but we go on
* @throws Exception
*/ */
private static function showerror($error, $showerror = true, $json_response = false, PDOStatement $stmt = null) private static function showerror(Exception $error, bool $showerror = true, bool $json_response = false, PDOStatement $stmt = null)
{ {
global $userinfo, $theme, $linker; global $userinfo, $theme, $linker;
@@ -143,7 +149,7 @@ class Database
0 => [ 0 => [
'caption' => 'Default', 'caption' => 'Default',
'host' => $sql['host'], 'host' => $sql['host'],
'socket' => (isset($sql['socket']) ? $sql['socket'] : null), 'socket' => ($sql['socket'] ?? null),
'user' => $sql['root_user'], 'user' => $sql['root_user'],
'password' => $sql['root_password'] 'password' => $sql['root_password']
] ]
@@ -159,8 +165,8 @@ class Database
$substitutions = [ $substitutions = [
$sql['password'] => 'DB_UNPRIV_PWD', $sql['password'] => 'DB_UNPRIV_PWD',
]; ];
foreach ($sql_root as $dbserver => $sql_root_data) { foreach ($sql_root as $sql_root_data) {
$substitutions[$sql_root_data[$dbserver]]['password'] = 'DB_ROOT_PWD'; $substitutions[$sql_root_data['password']] = 'DB_ROOT_PWD';
} }
// hide username/password in messages // hide username/password in messages
@@ -254,7 +260,7 @@ class Database
* @param int $minLength * @param int $minLength
* @return string * @return string
*/ */
private static function substitute($content, array $substitutions, $minLength = 6) private static function substitute(string $content, array $substitutions, int $minLength = 6): string
{ {
$replacements = []; $replacements = [];
@@ -262,9 +268,7 @@ class Database
$replacements += self::createShiftedSubstitutions($search, $replace, $minLength); $replacements += self::createShiftedSubstitutions($search, $replace, $minLength);
} }
$content = str_replace(array_keys($replacements), array_values($replacements), $content); return str_replace(array_keys($replacements), array_values($replacements), $content);
return $content;
} }
/** /**
@@ -284,7 +288,7 @@ class Database
* @param int $minLength * @param int $minLength
* @return array * @return array
*/ */
private static function createShiftedSubstitutions($search, $replace, $minLength) private static function createShiftedSubstitutions(string $search, string $replace, int $minLength): array
{ {
$substitutions = []; $substitutions = [];
$length = strlen($search); $length = strlen($search);
@@ -303,8 +307,9 @@ class Database
* *
* @param int $length * @param int $length
* @return string * @return string
* @throws Exception
*/ */
private static function genUniqueToken(int $length = 16) private static function genUniqueToken(int $length = 16): string
{ {
if (intval($length) <= 8) { if (intval($length) <= 8) {
$length = 16; $length = 16;
@@ -327,7 +332,7 @@ class Database
* *
* @return int * @return int
*/ */
public static function num_rows() public static function num_rows(): int
{ {
return Database::query("SELECT FOUND_ROWS()")->fetchColumn(); return Database::query("SELECT FOUND_ROWS()")->fetchColumn();
} }
@@ -337,7 +342,7 @@ class Database
* *
* @return string * @return string
*/ */
public static function getDbName() public static function getDbName(): ?string
{ {
return self::$dbname; return self::$dbname;
} }
@@ -349,8 +354,8 @@ class Database
* the 'normal' database-connection * the 'normal' database-connection
* *
* @param bool $needroot * @param bool $needroot
* @param int $dbserver * @param int $dbserver optional
* optional * @param bool $need_db
*/ */
public static function needRoot(bool $needroot = false, int $dbserver = 0, bool $need_db = true) public static function needRoot(bool $needroot = false, int $dbserver = 0, bool $need_db = true)
{ {
@@ -366,7 +371,7 @@ class Database
* *
* @param int $dbserver * @param int $dbserver
*/ */
private static function setServer($dbserver = 0) private static function setServer(int $dbserver = 0)
{ {
self::$dbserver = $dbserver; self::$dbserver = $dbserver;
self::$link = null; self::$link = null;
@@ -397,17 +402,16 @@ class Database
* function that will be called on every static call * function that will be called on every static call
* which connects to the database if necessary * which connects to the database if necessary
* *
* @param bool $root
*
* @return object * @return object
* @throws Exception
*/ */
private static function getDB() private static function getDB()
{ {
if (!extension_loaded('pdo') || in_array("mysql", PDO::getAvailableDrivers()) == false) { if (!extension_loaded('pdo') || !in_array("mysql", PDO::getAvailableDrivers())) {
self::showerror(new Exception("The php PDO extension or PDO-MySQL driver is not available")); self::showerror(new Exception("The php PDO extension or PDO-MySQL driver is not available"));
} }
// do we got a connection already? // do we have a connection already?
if (self::$link) { if (self::$link) {
// return it // return it
return self::$link; return self::$link;
@@ -422,7 +426,7 @@ class Database
0 => [ 0 => [
'caption' => 'Default', 'caption' => 'Default',
'host' => $sql['host'], 'host' => $sql['host'],
'socket' => (isset($sql['socket']) ? $sql['socket'] : null), 'socket' => ($sql['socket'] ?? null),
'user' => $sql['root_user'], 'user' => $sql['root_user'],
'password' => $sql['root_password'] 'password' => $sql['root_password']
] ]
@@ -441,8 +445,8 @@ class Database
$user = $sql_root[self::$dbserver]['user']; $user = $sql_root[self::$dbserver]['user'];
$password = $sql_root[self::$dbserver]['password']; $password = $sql_root[self::$dbserver]['password'];
$host = $sql_root[self::$dbserver]['host']; $host = $sql_root[self::$dbserver]['host'];
$socket = isset($sql_root[self::$dbserver]['socket']) ? $sql_root[self::$dbserver]['socket'] : null; $socket = $sql_root[self::$dbserver]['socket'] ?? null;
$port = isset($sql_root[self::$dbserver]['port']) ? $sql_root[self::$dbserver]['port'] : '3306'; $port = $sql_root[self::$dbserver]['port'] ?? '3306';
$sslCAFile = $sql_root[self::$dbserver]['ssl']['caFile'] ?? ""; $sslCAFile = $sql_root[self::$dbserver]['ssl']['caFile'] ?? "";
$sslVerifyServerCertificate = $sql_root[self::$dbserver]['ssl']['verifyServerCertificate'] ?? false; $sslVerifyServerCertificate = $sql_root[self::$dbserver]['ssl']['verifyServerCertificate'] ?? false;
} else { } else {
@@ -450,8 +454,8 @@ class Database
$user = $sql["user"]; $user = $sql["user"];
$password = $sql["password"]; $password = $sql["password"];
$host = $sql["host"]; $host = $sql["host"];
$socket = isset($sql['socket']) ? $sql['socket'] : null; $socket = $sql['socket'] ?? null;
$port = isset($sql['port']) ? $sql['port'] : '3306'; $port = $sql['port'] ?? '3306';
$sslCAFile = $sql['ssl']['caFile'] ?? ""; $sslCAFile = $sql['ssl']['caFile'] ?? "";
$sslVerifyServerCertificate = $sql['ssl']['verifyServerCertificate'] ?? false; $sslVerifyServerCertificate = $sql['ssl']['verifyServerCertificate'] ?? false;
} }
@@ -556,14 +560,14 @@ class Database
* *
* @return int * @return int
*/ */
public static function getSqlUsernameLength() public static function getSqlUsernameLength(): int
{ {
// MariaDB supports up to 80 characters but only 64 for databases and as we use the loginname also for // MariaDB supports up to 80 characters but only 64 for databases and as we use the login-name also for
// database names, we set the limit to 64 here // database names, we set the limit to 64 here
if (strpos(strtolower(Database::getAttribute(\PDO::ATTR_SERVER_VERSION)), "mariadb") !== false) { if (strpos(strtolower(Database::getAttribute(\PDO::ATTR_SERVER_VERSION)), "mariadb") !== false) {
$mysql_max = 64; $mysql_max = 64;
} else { } else {
// MySQL user names can be up to 32 characters long (16 characters before MySQL 5.7.8). // MySQL user-names can be up to 32 characters long (16 characters before MySQL 5.7.8).
$mysql_max = 32; $mysql_max = 32;
if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.8', '<')) { if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.8', '<')) {
$mysql_max = 16; $mysql_max = 16;
@@ -573,15 +577,16 @@ class Database
} }
/** /**
* let's us interact with the PDO-Object by using static * Lets us interact with the PDO-Object by using static
* call like "Database::function()" * call like "Database::function()"
* *
* @param string $name * @param string $name
* @param mixed $args * @param mixed $args
* *
* @return mixed * @return mixed
* @throws Exception
*/ */
public static function __callStatic($name, $args) public static function __callStatic(string $name, $args)
{ {
$callback = [ $callback = [
self::getDB(), self::getDB(),

View File

@@ -77,7 +77,15 @@ class DbManager
$this->manager = new DbManagerMySQL($this->log); $this->manager = new DbManagerMySQL($this->log);
} }
public static function correctMysqlUsers($mysql_access_host_array) /**
* function called when the mysql-access-host setting changes
*
* @param array $mysql_access_host_array
*
* @return void
* @throws \Exception
*/
public static function correctMysqlUsers(array $mysql_access_host_array)
{ {
// get all databases for all dbservers // get all databases for all dbservers
$databases = []; $databases = [];
@@ -101,7 +109,7 @@ class DbManager
$dbm = new DbManager(FroxlorLogger::getInstanceOf()); $dbm = new DbManager(FroxlorLogger::getInstanceOf());
$users = $dbm->getManager()->getAllSqlUsers(false); $users = $dbm->getManager()->getAllSqlUsers(false);
foreach ($databases[$dbserver] as $username) { foreach ($databases[$dbserver['dbserver']] as $username) {
if (isset($users[$username]) && is_array($users[$username]) && isset($users[$username]['hosts']) && is_array($users[$username]['hosts'])) { if (isset($users[$username]) && is_array($users[$username]) && isset($users[$username]['hosts']) && is_array($users[$username]['hosts'])) {
$password = [ $password = [
@@ -136,13 +144,14 @@ class DbManager
* DB-name and user-name are being generated and * DB-name and user-name are being generated and
* the password for the user will be set * the password for the user will be set
* *
* @param string $loginname * @param ?string $loginname
* @param string $password * @param ?string $password
* @param int $dbserver
* @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
*/ */
public function createDatabase($loginname = null, $password = null, int $dbserver = 0, $last_accnumber = 0) public function createDatabase(string $loginname = null, string $password = null, int $dbserver = 0, int $last_accnumber = 0)
{ {
Database::needRoot(true, $dbserver, false); Database::needRoot(true, $dbserver, false);

View File

@@ -85,21 +85,22 @@ class IntegrityCheck
/** /**
* check whether the froxlor database and its tables are in utf-8 character-set * check whether the froxlor database and its tables are in utf-8 character-set
* *
* @param bool $fix * @param bool $fix fix db charset/collation if not utf8
* fix db charset/collation if not utf8
* *
* @return boolean * @return bool
* @throws \Exception
*/ */
public function databaseCharset($fix = false) public function databaseCharset(bool $fix = false): bool
{ {
// get characterset // get character-set
$cs_stmt = Database::prepare('SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = :dbname'); $cs_stmt = Database::prepare('SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = :dbname');
$resp = Database::pexecute_first($cs_stmt, [ $resp = Database::pexecute_first($cs_stmt, [
'dbname' => Database::getDbName() 'dbname' => Database::getDbName()
]); ]);
$charset = isset($resp['default_character_set_name']) ? $resp['default_character_set_name'] : null; $charset = $resp['default_character_set_name'] ?? null;
if (!empty($charset) && substr(strtolower($charset), 0, 4) != 'utf8') { if (!empty($charset) && substr(strtolower($charset), 0, 4) != 'utf8') {
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "database charset seems to be different from UTF-8, integrity-check can fix that"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
"database charset seems to be different from UTF-8, integrity-check can fix that");
if ($fix) { if ($fix) {
// fix database // fix database
Database::query('ALTER DATABASE `' . Database::getDbName() . '` CHARACTER SET utf8 COLLATE utf8_general_ci'); Database::query('ALTER DATABASE `' . Database::getDbName() . '` CHARACTER SET utf8 COLLATE utf8_general_ci');
@@ -109,7 +110,8 @@ class IntegrityCheck
$table = $row[0]; $table = $row[0];
Database::query('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;'); Database::query('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;');
} }
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "database charset was different from UTF-8, integrity-check fixed that"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
"database charset was different from UTF-8, integrity-check fixed that");
} else { } else {
return false; return false;
} }
@@ -124,10 +126,12 @@ class IntegrityCheck
/** /**
* Check the integrity of the domain to ip/port - association * Check the integrity of the domain to ip/port - association
* *
* @param bool $fix * @param bool $fix fix everything found directly
* Fix everything found directly *
* @return bool
* @throws \Exception
*/ */
public function domainIpTable($fix = false) public function domainIpTable(bool $fix = false): bool
{ {
$ips = []; $ips = [];
$domains = []; $domains = [];
@@ -184,9 +188,11 @@ class IntegrityCheck
'domainid' => $row['id_domain'], 'domainid' => $row['id_domain'],
'ipandportid' => $row['id_ipandports'] 'ipandportid' => $row['id_ipandports']
]); ]);
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found an ip/port-id in domain <> ip table which does not exist, integrity check fixed this"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
"found an ip/port-id in domain <> ip table which does not exist, integrity check fixed this");
} else { } else {
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found an ip/port-id in domain <> ip table which does not exist, integrity check can fix this"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
"found an ip/port-id in domain <> ip table which does not exist, integrity check can fix this");
return false; return false;
} }
} }
@@ -196,9 +202,11 @@ class IntegrityCheck
'domainid' => $row['id_domain'], 'domainid' => $row['id_domain'],
'ipandportid' => $row['id_ipandports'] 'ipandportid' => $row['id_ipandports']
]); ]);
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a domain-id in domain <> ip table which does not exist, integrity check fixed this"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
"found a domain-id in domain <> ip table which does not exist, integrity check fixed this");
} else { } else {
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a domain-id in domain <> ip table which does not exist, integrity check can fix this"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
"found a domain-id in domain <> ip table which does not exist, integrity check can fix this");
return false; return false;
} }
} }
@@ -216,9 +224,11 @@ class IntegrityCheck
'ipandportid' => $defaultip 'ipandportid' => $defaultip
]); ]);
} }
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a domain-id with no entry in domain <> ip table, integrity check fixed this"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
"found a domain-id with no entry in domain <> ip table, integrity check fixed this");
} else { } else {
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a domain-id with no entry in domain <> ip table, integrity check can fix this"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
"found a domain-id with no entry in domain <> ip table, integrity check can fix this");
return false; return false;
} }
} }
@@ -226,18 +236,19 @@ class IntegrityCheck
if ($fix) { if ($fix) {
return $this->domainIpTable(); return $this->domainIpTable();
} else {
return true;
} }
return true;
} }
/** /**
* Check if all subdomains have ssl-redirect = 0 if domain has no ssl-port * Check if all subdomains have ssl-redirect = 0 if domain has no ssl-port
* *
* @param bool $fix * @param bool $fix fix everything found directly
* Fix everything found directly *
* @return bool
* @throws \Exception
*/ */
public function subdomainSslRedirect($fix = false) public function subdomainSslRedirect(bool $fix = false): bool
{ {
$ips = []; $ips = [];
$parentdomains = []; $parentdomains = [];
@@ -300,28 +311,31 @@ class IntegrityCheck
Database::pexecute($upd_stmt, [ Database::pexecute($upd_stmt, [
'domainid' => $id 'domainid' => $id
]); ]);
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check fixed this"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
"found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check fixed this");
} else { } else {
// It's just the check, let the function fail // It's just the check, let the function fail
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check can fix this"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
"found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check can fix this");
return false; return false;
} }
} }
if ($fix) { if ($fix) {
return $this->subdomainSslRedirect(); return $this->subdomainSslRedirect();
} else {
return true;
} }
return true;
} }
/** /**
* Check if all subdomain have letsencrypt = 0 if domain has no ssl-port * Check if all subdomain have letsencrypt = 0 if domain has no ssl-port
* *
* @param bool $fix * @param bool $fix fix everything found directly
* Fix everything found directly *
* @return bool
* @throws \Exception
*/ */
public function subdomainLetsencrypt($fix = false) public function subdomainLetsencrypt(bool $fix = false): bool
{ {
$ips = []; $ips = [];
$parentdomains = []; $parentdomains = [];
@@ -384,31 +398,32 @@ class IntegrityCheck
Database::pexecute($upd_stmt, [ Database::pexecute($upd_stmt, [
'domainid' => $id 'domainid' => $id
]); ]);
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check fixed this"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING,
"found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check fixed this");
} else { } else {
// It's just the check, let the function fail // It's just the check, let the function fail
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check can fix this"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
"found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check can fix this");
return false; return false;
} }
} }
if ($fix) { if ($fix) {
return $this->subdomainLetsencrypt(); return $this->subdomainLetsencrypt();
} else {
return true;
} }
return true;
} }
/** /**
* check whether the webserveruser is in * check whether the webserveruser is in
* the customers groups when fcgid / php-fpm is used * the customers groups when fcgid / php-fpm is used
* *
* @param bool $fix * @param bool $fix fix member/groups
* fix member/groups
* *
* @return boolean * @return bool
* @throws \Exception
*/ */
public function webserverGroupMemberForFcgidPhpFpm($fix = false) public function webserverGroupMemberForFcgidPhpFpm(bool $fix = false): bool
{ {
if (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0) { if (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0) {
return true; return true;
@@ -423,7 +438,8 @@ class IntegrityCheck
]); ]);
if ($cwg_stmt->rowCount() > 0) { if ($cwg_stmt->rowCount() > 0) {
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers are missing the webserver-user as group-member, integrity-check can fix that"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
"Customers are missing the webserver-user as group-member, integrity-check can fix that");
if ($fix) { if ($fix) {
// prepare update statement // prepare update statement
$upd_stmt = Database::prepare(" $upd_stmt = Database::prepare("
@@ -438,7 +454,8 @@ class IntegrityCheck
$upd_data['id'] = $cwg_row['id']; $upd_data['id'] = $cwg_row['id'];
Database::pexecute($upd_stmt, $upd_data); Database::pexecute($upd_stmt, $upd_data);
} }
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers were missing the webserver-user as group-member, integrity-check fixed that"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
"Customers were missing the webserver-user as group-member, integrity-check fixed that");
} else { } else {
return false; return false;
} }
@@ -455,12 +472,12 @@ class IntegrityCheck
* the customers groups when fcgid / php-fpm and * the customers groups when fcgid / php-fpm and
* fcgid/fpm in froxlor vhost is used * fcgid/fpm in froxlor vhost is used
* *
* @param bool $fix * @param bool $fix fix member/groups
* fix member/groups
* *
* @return boolean * @return bool
* @throws \Exception
*/ */
public function froxlorLocalGroupMemberForFcgidPhpFpm($fix = false) public function froxlorLocalGroupMemberForFcgidPhpFpm(bool $fix = false): bool
{ {
if (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0) { if (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0) {
return true; return true;
@@ -491,7 +508,8 @@ class IntegrityCheck
]); ]);
if ($cwg_stmt->rowCount() > 0) { if ($cwg_stmt->rowCount() > 0) {
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers are missing the local froxlor-user as group-member, integrity-check can fix that"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
"Customers are missing the local froxlor-user as group-member, integrity-check can fix that");
if ($fix) { if ($fix) {
// prepare update statement // prepare update statement
$upd_stmt = Database::prepare(" $upd_stmt = Database::prepare("
@@ -506,7 +524,8 @@ class IntegrityCheck
$upd_data['id'] = $cwg_row['id']; $upd_data['id'] = $cwg_row['id'];
Database::pexecute($upd_stmt, $upd_data); Database::pexecute($upd_stmt, $upd_data);
} }
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers were missing the local froxlor-user as group-member, integrity-check fixed that"); $this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE,
"Customers were missing the local froxlor-user as group-member, integrity-check fixed that");
} else { } else {
return false; return false;
} }

View File

@@ -48,7 +48,7 @@ class DbManagerMySQL
/** /**
* main constructor * main constructor
* *
* @param FroxlorLogger $log * @param FroxlorLogger|null $log
*/ */
public function __construct(&$log = null) public function __construct(&$log = null)
{ {
@@ -58,9 +58,9 @@ class DbManagerMySQL
/** /**
* creates a database * creates a database
* *
* @param string $dbname * @param string|null $dbname
*/ */
public function createDatabase($dbname = null) public function createDatabase(string $dbname = null)
{ {
Database::query("CREATE DATABASE `" . $dbname . "`"); Database::query("CREATE DATABASE `" . $dbname . "`");
} }
@@ -71,13 +71,14 @@ class DbManagerMySQL
* *
* @param string $username * @param string $username
* @param string|array $password * @param string|array $password
* @param string $access_host * @param ?string $access_host
* @param bool $p_encrypted * @param bool $p_encrypted
* 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)
* @throws \Exception
*/ */
public function grantPrivilegesTo($username = null, $password = null, $access_host = null, $p_encrypted = false, $update = false) public function grantPrivilegesTo(string $username, $password, string $access_host = null, bool $p_encrypted = false, bool $update = false)
{ {
$pwd_plugin = 'mysql_native_password'; $pwd_plugin = 'mysql_native_password';
if (is_array($password) && count($password) == 2) { if (is_array($password) && count($password) == 2) {
@@ -141,8 +142,9 @@ class DbManagerMySQL
* takes away any privileges from a user to that db * takes away any privileges from a user to that db
* *
* @param string $dbname * @param string $dbname
* @throws \Exception
*/ */
public function deleteDatabase($dbname = null) public function deleteDatabase(string $dbname)
{ {
if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.0.2', '<')) { if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.0.2', '<')) {
// failsafe if user has been deleted manually (requires MySQL 4.1.2+) // failsafe if user has been deleted manually (requires MySQL 4.1.2+)
@@ -178,8 +180,9 @@ class DbManagerMySQL
* *
* @param string $username * @param string $username
* @param string $host * @param string $host
* @throws \Exception
*/ */
public function deleteUser($username = null, $host = null) public function deleteUser(string $username, string $host)
{ {
if (Database::getAttribute(PDO::ATTR_SERVER_VERSION) < '5.0.2') { if (Database::getAttribute(PDO::ATTR_SERVER_VERSION) < '5.0.2') {
// Revoke privileges (only required for MySQL 4.1.2 - 5.0.1) // Revoke privileges (only required for MySQL 4.1.2 - 5.0.1)
@@ -203,9 +206,9 @@ class DbManagerMySQL
* *
* @param string $username * @param string $username
* @param string $host * @param string $host
* (unused in mysql) * @throws \Exception
*/ */
public function disableUser($username = null, $host = null) public function disableUser(string $username, string $host)
{ {
$stmt = Database::prepare('REVOKE ALL PRIVILEGES, GRANT OPTION FROM `' . $username . '`@`' . $host . '`'); $stmt = Database::prepare('REVOKE ALL PRIVILEGES, GRANT OPTION FROM `' . $username . '`@`' . $host . '`');
Database::pexecute($stmt, [], false); Database::pexecute($stmt, [], false);
@@ -216,8 +219,9 @@ class DbManagerMySQL
* *
* @param string $username * @param string $username
* @param string $host * @param string $host
* @throws \Exception
*/ */
public function enableUser($username = null, $host = null) public function enableUser(string $username, string $host)
{ {
// check whether user exists to avoid errors // check whether user exists to avoid errors
$exist_check_stmt = Database::prepare("SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '" . $username . "' AND host = '" . $host . "')"); $exist_check_stmt = Database::prepare("SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '" . $username . "' AND host = '" . $host . "')");
@@ -239,14 +243,14 @@ class DbManagerMySQL
/** /**
* return an array of all usernames used in that DBMS * return an array of all usernames used in that DBMS
* *
* @param bool $user_only * @param bool $user_only if false, will be selected from mysql.user and slightly different array will be generated
* if false, * will be selected from mysql.user and slightly different array will be generated
* *
* @return array * @return array
* @throws \Exception
*/ */
public function getAllSqlUsers($user_only = true) public function getAllSqlUsers(bool $user_only = true): array
{ {
if ($user_only == false) { if (!$user_only) {
$result_stmt = Database::prepare('SELECT * FROM mysql.user'); $result_stmt = Database::prepare('SELECT * FROM mysql.user');
} else { } else {
$result_stmt = Database::prepare('SELECT `User` FROM mysql.user'); $result_stmt = Database::prepare('SELECT `User` FROM mysql.user');

View File

@@ -33,7 +33,15 @@ use PDO;
class Dns class Dns
{ {
public static function getAllowedDomainEntry($domain_id, $area = 'customer', $userinfo = []) /**
* @param int $domain_id
* @param string $area
* @param array $userinfo
*
* @return string|void
* @throws \Exception
*/
public static function getAllowedDomainEntry(int $domain_id, string $area = 'customer', array $userinfo = [])
{ {
$dom_data = [ $dom_data = [
'did' => $domain_id 'did' => $domain_id
@@ -67,7 +75,15 @@ class Dns
Response::standardError('dns_notfoundorallowed'); Response::standardError('dns_notfoundorallowed');
} }
public static function createDomainZone($domain_id, $froxlorhostname = false, $isMainButSubTo = false) /**
* @param int|array $domain_id id of domain or in case of froxlorhostname, a domain-array with the needed data
* @param bool $froxlorhostname
* @param bool $isMainButSubTo
*
* @return DnsZone|void
* @throws \Exception
*/
public static function createDomainZone($domain_id, bool $froxlorhostname = false, bool $isMainButSubTo = false)
{ {
if (!$froxlorhostname) { if (!$froxlorhostname) {
// get domain-name // get domain-name
@@ -136,7 +152,7 @@ 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` 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, [
@@ -144,18 +160,31 @@ class Dns
]); ]);
while ($subdomain = $subdomains_stmt->fetch(PDO::FETCH_ASSOC)) { while ($subdomain = $subdomains_stmt->fetch(PDO::FETCH_ASSOC)) {
$sub_record = str_replace('.' . $domain['domain'], '', $subdomain['domain']);
// Listing domains is enough as there currently is no support for choosing // 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(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); self::addRequiredEntry($sub_record, 'A',$required_entries);
self::addRequiredEntry(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); self::addRequiredEntry($sub_record, 'AAAA', $required_entries);
// Check whether to add a www.-prefix // Check whether to add a www.-prefix
if ($subdomain['iswildcarddomain'] == '1') { if ($subdomain['iswildcarddomain'] == '1') {
self::addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); self::addRequiredEntry('*.' . $sub_record, 'A', $required_entries);
self::addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); self::addRequiredEntry('*.' . $sub_record, 'AAAA', $required_entries);
} elseif ($subdomain['wwwserveralias'] == '1') { } elseif ($subdomain['wwwserveralias'] == '1') {
self::addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); self::addRequiredEntry('www.' . $sub_record, 'A', $required_entries);
self::addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); self::addRequiredEntry('www.' . $sub_record, 'AAAA', $required_entries);
}
// check for email ability
if ($subdomain['isemaildomain'] == '1') {
if (Settings::Get('spf.use_spf') == '1') {
// check for SPF content later
self::addRequiredEntry('@SPF@.' . $sub_record, 'TXT', $required_entries);
}
if (Settings::Get('dkim.use_dkim') == '1') {
// check for DKIM content later
self::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey.' . $sub_record, 'TXT', $required_entries);
}
} }
} }
} }
@@ -200,14 +229,17 @@ class Dns
// now generate all records and unset the required entries we have // 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']), $required_entries[$entry['type']])) { if (array_key_exists($entry['type'], $required_entries) && array_key_exists(md5($entry['record']),
$required_entries[$entry['type']])) {
unset($required_entries[$entry['type']][md5($entry['record'])]); unset($required_entries[$entry['type']][md5($entry['record'])]);
} }
if (Settings::Get('system.dns_createcaaentry') == '1' && $entry['type'] == 'CAA' && strtolower(substr($entry['content'], 0, 7)) == '"v=caa1') { if (Settings::Get('system.dns_createcaaentry') == '1' && $entry['type'] == 'CAA' && strtolower(substr($entry['content'],
0, 7)) == '"v=caa1') {
// unset special CAA required-entry // unset 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'], 0, 7)) == '"v=spf1' || strtolower(substr($entry['content'], 0, 6)) == 'v=spf1')) { if (Settings::Get('spf.use_spf') == '1' && $entry['type'] == 'TXT' && $entry['record'] == '@' && (strtolower(substr($entry['content'],
0, 7)) == '"v=spf1' || strtolower(substr($entry['content'], 0, 6)) == 'v=spf1')) {
// unset special spf required-entry // unset special spf required-entry
unset($required_entries[$entry['type']][md5("@SPF@")]); unset($required_entries[$entry['type']][md5("@SPF@")]);
} }
@@ -223,7 +255,8 @@ class Dns
'*' '*'
] as $crecord ] as $crecord
) { ) {
if ($entry['type'] == 'CNAME' && $entry['record'] == '@' && (array_key_exists(md5($crecord), $required_entries['A']) || array_key_exists(md5($crecord), $required_entries['AAAA']))) { if ($entry['type'] == 'CNAME' && $entry['record'] == '@' && (array_key_exists(md5($crecord),
$required_entries['A']) || array_key_exists(md5($crecord), $required_entries['AAAA']))) {
unset($required_entries['A'][md5($crecord)]); unset($required_entries['A'][md5($crecord)]);
unset($required_entries['AAAA'][md5($crecord)]); unset($required_entries['AAAA'][md5($crecord)]);
} }
@@ -238,13 +271,15 @@ class Dns
'smtp' 'smtp'
] as $crecord ] as $crecord
) { ) {
if ($entry['type'] == 'CNAME' && $entry['record'] == $crecord && (array_key_exists(md5($crecord), $required_entries['A']) || array_key_exists(md5($crecord), $required_entries['AAAA']))) { if ($entry['type'] == 'CNAME' && $entry['record'] == $crecord && (array_key_exists(md5($crecord),
$required_entries['A']) || array_key_exists(md5($crecord),
$required_entries['AAAA']))) {
unset($required_entries['A'][md5($crecord)]); unset($required_entries['A'][md5($crecord)]);
unset($required_entries['AAAA'][md5($crecord)]); unset($required_entries['AAAA'][md5($crecord)]);
} }
} }
} }
$zonerecords[] = new DnsEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'], $entry['ttl']); $zonerecords[] = new DnsEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'] ?? 0, $entry['ttl']);
} }
// add missing required entries // add missing required entries
@@ -275,7 +310,8 @@ 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, FILTER_FLAG_IPV6) !== false) { } elseif ($type == 'AAAA' && filter_var($ip['ip'], FILTER_VALIDATE_IP,
FILTER_FLAG_IPV6) !== false) {
$zonerecords[] = new DnsEntry($record, 'AAAA', $ip['ip']); $zonerecords[] = new DnsEntry($record, 'AAAA', $ip['ip']);
} }
} }
@@ -348,15 +384,34 @@ class Dns
if ($type == 'TXT') { if ($type == 'TXT') {
foreach ($records as $record) { foreach ($records as $record) {
if ($record == '@SPF@') { if ($record == '@SPF@') {
// spf for main-domain
$txt_content = Settings::Get('spf.spf_entry'); $txt_content = Settings::Get('spf.spf_entry');
$zonerecords[] = new DnsEntry('@', 'TXT', self::encloseTXTContent($txt_content)); $zonerecords[] = new DnsEntry('@', 'TXT', self::encloseTXTContent($txt_content));
} elseif ($record == 'dkim' . $domain['dkim_id'] . '._domainkey' && !empty($dkim_entries)) { } elseif (strlen($record) > 6 && substr($record, 0, 6) == '@SPF@.') {
// check for multiline entry // spf for subdomain
$multiline = false; $txt_content = Settings::Get('spf.spf_entry');
if (substr($dkim_entries[0], 0, 1) == '(') { $sub_record = substr($record, 6);
$multiline = true; $zonerecords[] = new DnsEntry($sub_record, 'TXT', self::encloseTXTContent($txt_content));
} elseif (!empty($dkim_entries)) {
// DKIM entries
$dkim_record = 'dkim' . $domain['dkim_id'] . '._domainkey';
if ($record == $dkim_record) {
// dkim for main-domain
// check for multiline entry
$multiline = false;
if (substr($dkim_entries[0], 0, 1) == '(') {
$multiline = true;
}
$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dkim_entries[0], $multiline));
} elseif (strlen($record) > strlen($dkim_record) && substr($record, 0, strlen($dkim_record)+1) == $dkim_record . '.') {
// dkim for subdomain-domain
// check for multiline entry
$multiline = false;
if (substr($dkim_entries[0], 0, 1) == '(') {
$multiline = true;
}
$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dkim_entries[0], $multiline));
} }
$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dkim_entries[0], $multiline));
} }
} }
} }
@@ -416,7 +471,8 @@ class Dns
if (!$isMainButSubTo) { if (!$isMainButSubTo) {
$date = date('Ymd'); $date = date('Ymd');
$domain['bindserial'] = (preg_match('/^' . $date . '/', $domain['bindserial']) ? $domain['bindserial'] + 1 : $date . '00'); $domain['bindserial'] = (preg_match('/^' . $date . '/',
$domain['bindserial']) ? $domain['bindserial'] + 1 : $date . '00');
if (!$froxlorhostname) { if (!$froxlorhostname) {
$upd_stmt = Database::prepare(" $upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_DOMAINS . "` SET UPDATE `" . TABLE_PANEL_DOMAINS . "` SET
@@ -443,12 +499,19 @@ class Dns
array_unshift($zonerecords, $soa_record); array_unshift($zonerecords, $soa_record);
} }
$zone = new DnsZone((int)Settings::Get('system.defaultttl'), $domain['domain'], $domain['bindserial'], $zonerecords); $zone = new DnsZone((int)Settings::Get('system.defaultttl'), $domain['domain'], $domain['bindserial'],
$zonerecords);
return $zone; return $zone;
} }
private static function addRequiredEntry($record = '@', $type = 'A', &$required = []) /**
* @param string $record
* @param string $type
* @param array $required
* @return void
*/
private static function addRequiredEntry(string $record = '@', string $type = 'A', array &$required = [])
{ {
if (!isset($required[$type])) { if (!isset($required[$type])) {
$required[$type] = []; $required[$type] = [];
@@ -456,7 +519,11 @@ class Dns
$required[$type][md5($record)] = $record; $required[$type][md5($record)] = $record;
} }
private static function generateDkimEntries($domain) /**
* @param array $domain
* @return array
*/
private static function generateDkimEntries(array $domain): array
{ {
$zone_dkim = []; $zone_dkim = [];
@@ -486,7 +553,8 @@ class Dns
} }
// key // key
$dkim_txt .= 'k=rsa;p=' . trim(preg_replace('/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s', '$1', str_replace("\n", '', $domain['dkim_pubkey']))) . ';'; $dkim_txt .= 'k=rsa;p=' . trim(preg_replace('/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s',
'$1', str_replace("\n", '', $domain['dkim_pubkey']))) . ';';
// service-type // service-type
if (Settings::Get('dkim.dkim_servicetype') == '1') { if (Settings::Get('dkim.dkim_servicetype') == '1') {
@@ -503,10 +571,15 @@ class Dns
return $zone_dkim; return $zone_dkim;
} }
public static function encloseTXTContent($txt_content, $isMultiLine = false) /**
* @param string $txt_content
* @param bool $isMultiLine
* @return string
*/
public static function encloseTXTContent(string $txt_content, bool $isMultiLine = false): string
{ {
// check that TXT content is enclosed in " " // check that TXT content is enclosed in " "
if ($isMultiLine == false && Settings::Get('system.dns_server') != 'PowerDNS') { if (!$isMultiLine && Settings::Get('system.dns_server') != 'PowerDNS') {
if (substr($txt_content, 0, 1) != '"') { if (substr($txt_content, 0, 1) != '"') {
$txt_content = '"' . $txt_content; $txt_content = '"' . $txt_content;
} }
@@ -526,10 +599,13 @@ class Dns
return $txt_content; return $txt_content;
} }
private static function escapeSoaAdminMail($email) /**
* @param string $email
* @return string
*/
private static function escapeSoaAdminMail(string $email): string
{ {
$mail_parts = explode("@", $email); $mail_parts = explode("@", $email);
$escpd_mail = str_replace(".", "\.", $mail_parts[0]) . "." . $mail_parts[1] . "."; return str_replace(".", "\.", $mail_parts[0]) . "." . $mail_parts[1] . ".";
return $escpd_mail;
} }
} }

View File

@@ -29,14 +29,22 @@ use Froxlor\Settings;
class DnsEntry class DnsEntry
{ {
public $record; public string $record;
public $ttl; public int $ttl;
public $class = 'IN'; public string $class = 'IN';
public $type; public string $type;
public $priority; public int $priority;
public $content; public ?string $content;
public function __construct($record = '', $type = 'A', $content = null, $prio = 0, $ttl = 0, $class = 'IN') /**
* @param string $record
* @param string $type
* @param string|null $content
* @param int $prio
* @param int $ttl
* @param string $class
*/
public function __construct(string $record = '', string $type = 'A', string $content = null, int $prio = 0, int $ttl = 0, string $class = 'IN')
{ {
$this->record = $record; $this->record = $record;
$this->type = $type; $this->type = $type;
@@ -72,7 +80,6 @@ class DnsEntry
// last line // last line
$_content .= "\t\t\t\t" . '"' . $_l . '")'; $_content .= "\t\t\t\t" . '"' . $_l . '")';
} }
$result = $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "") . $_content . PHP_EOL; return $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "") . $_content . PHP_EOL;
return $result;
} }
} }

View File

@@ -29,12 +29,18 @@ use Froxlor\Settings;
class DnsZone class DnsZone
{ {
public $ttl; public int $ttl;
public $origin; public string $origin;
public $serial; public string $serial;
public $records; public ?array $records;
public function __construct($ttl = 0, $origin = '', $serial = '', $records = null) /**
* @param int $ttl
* @param string $origin
* @param string $serial
* @param array|null $records
*/
public function __construct(int $ttl = 0, string $origin = '', string $serial = '', array $records = null)
{ {
$this->ttl = ($ttl <= 0 ? Settings::Get('system.defaultttl') : $ttl); $this->ttl = ($ttl <= 0 ? Settings::Get('system.defaultttl') : $ttl);
$this->origin = $origin; $this->origin = $origin;
@@ -44,13 +50,13 @@ class DnsZone
public function __toString() public function __toString()
{ {
$_zonefile = "\$TTL " . $this->ttl . PHP_EOL; $zone_file = "\$TTL " . $this->ttl . PHP_EOL;
$_zonefile .= "\$ORIGIN " . $this->origin . "." . PHP_EOL; $zone_file .= "\$ORIGIN " . $this->origin . "." . PHP_EOL;
if (!empty($this->records)) { if (!empty($this->records)) {
foreach ($this->records as $record) { foreach ($this->records as $record) {
$_zonefile .= (string)$record; $zone_file .= (string)$record;
} }
} }
return $_zonefile; return $zone_file;
} }
} }

View File

@@ -37,18 +37,18 @@ class PowerDNS
/** /**
* remove all records and entries of a given domain * remove all records and entries of a given domain
* *
* @param array $domain * @param string|null $domain
*/ */
public static function cleanDomainZone($domain = null) public static function cleanDomainZone(string $domain = null)
{ {
if (is_array($domain) && isset($domain['domain'])) { if (!empty($domain)) {
$pdns_domains_stmt = self::getDB()->prepare("SELECT `id`, `name` FROM `domains` WHERE `name` = :domain"); $pdns_domains_stmt = self::getDB()->prepare("SELECT `id`, `name` FROM `domains` WHERE `name` = :domain");
$del_rec_stmt = self::getDB()->prepare("DELETE FROM `records` WHERE `domain_id` = :did"); $del_rec_stmt = self::getDB()->prepare("DELETE FROM `records` WHERE `domain_id` = :did");
$del_meta_stmt = self::getDB()->prepare("DELETE FROM `domainmetadata` WHERE `domain_id` = :did"); $del_meta_stmt = self::getDB()->prepare("DELETE FROM `domainmetadata` WHERE `domain_id` = :did");
$del_dom_stmt = self::getDB()->prepare("DELETE FROM `domains` WHERE `id` = :did"); $del_dom_stmt = self::getDB()->prepare("DELETE FROM `domains` WHERE `id` = :did");
$pdns_domains_stmt->execute([ $pdns_domains_stmt->execute([
'domain' => $domain['domain'] 'domain' => $domain
]); ]);
$pdns_domain = $pdns_domains_stmt->fetch(PDO::FETCH_ASSOC); $pdns_domain = $pdns_domains_stmt->fetch(PDO::FETCH_ASSOC);
@@ -67,16 +67,19 @@ class PowerDNS
/** /**
* get pdo database connection to powerdns database * get pdo database connection to powerdns database
* *
* @return PDO * @return \PDO
*/ */
public static function getDB() public static function getDB(): \PDO
{ {
if (!isset(self::$pdns_db) || (self::$pdns_db instanceof PDO) == false) { if (!isset(self::$pdns_db) || !(self::$pdns_db instanceof PDO)) {
self::connectToPdnsDb(); self::connectToPdnsDb();
} }
return self::$pdns_db; return self::$pdns_db;
} }
/**
* @return void
*/
private static function connectToPdnsDb() private static function connectToPdnsDb()
{ {
// get froxlor pdns config // get froxlor pdns config

View File

@@ -41,8 +41,9 @@ class Domain
* *
* @param int $domain_id * @param int $domain_id
* @return array * @return array
* @throws \Exception
*/ */
public static function getIpsOfDomain($domain_id) public static function getIpsOfDomain(int $domain_id = 0): array
{ {
if ($domain_id > 0) { if ($domain_id > 0) {
$sel_stmt = Database::prepare(" $sel_stmt = Database::prepare("
@@ -75,7 +76,7 @@ class Domain
* *
* @return array array of enabled redirect-codes * @return array array of enabled redirect-codes
*/ */
public static function getRedirectCodesArray() public static function getRedirectCodesArray(): array
{ {
$sql = "SELECT * FROM `" . TABLE_PANEL_REDIRECTCODES . "` WHERE `enabled` = '1' ORDER BY `id` ASC"; $sql = "SELECT * FROM `" . TABLE_PANEL_REDIRECTCODES . "` WHERE `enabled` = '1' ORDER BY `id` ASC";
$result_stmt = Database::query($sql); $result_stmt = Database::query($sql);
@@ -92,12 +93,12 @@ class Domain
* returns the redirect-code for a given * returns the redirect-code for a given
* domain-id * domain-id
* *
* @param integer $domainid * @param int $domainid id of the domain
* id of the domain
* *
* @return string redirect-code * @return string redirect-code
* @throws \Exception
*/ */
public static function getDomainRedirectCode($domainid = 0) public static function getDomainRedirectCode(int $domainid = 0): string
{ {
// get system default // get system default
$default = '301'; $default = '301';
@@ -128,12 +129,11 @@ class Domain
* return an array of all enabled redirect-codes * return an array of all enabled redirect-codes
* for the settings form * for the settings form
* *
* @param bool $add_desc * @param bool $add_desc optional, default true, add the code-description
* optional, default true, add the code-description
* *
* @return array array of enabled redirect-codes * @return array array of enabled redirect-codes
*/ */
public static function getRedirectCodes($add_desc = true) public static function getRedirectCodes(bool $add_desc = true): array
{ {
$sql = "SELECT * FROM `" . TABLE_PANEL_REDIRECTCODES . "` WHERE `enabled` = '1' ORDER BY `id` ASC"; $sql = "SELECT * FROM `" . TABLE_PANEL_REDIRECTCODES . "` WHERE `enabled` = '1' ORDER BY `id` ASC";
$result_stmt = Database::query($sql); $result_stmt = Database::query($sql);
@@ -153,12 +153,12 @@ class Domain
* returns the redirect-id for a given * returns the redirect-id for a given
* domain-id * domain-id
* *
* @param integer $domainid * @param int $domainid id of the domain
* id of the domain
* *
* @return integer redirect-code-id * @return int redirect-code-id
* @throws \Exception
*/ */
public static function getDomainRedirectId($domainid = 0) public static function getDomainRedirectId(int $domainid = 0): int
{ {
$code = 1; $code = 1;
if ($domainid > 0) { if ($domainid > 0) {
@@ -171,7 +171,7 @@ class Domain
'domainid' => $domainid 'domainid' => $domainid
]); ]);
if (is_array($result) && isset($result['redirect'])) { if ($result && isset($result['redirect'])) {
$code = (int)$result['redirect']; $code = (int)$result['redirect'];
} }
} }
@@ -179,16 +179,15 @@ class Domain
} }
/** /**
* adds a redirectcode for a domain * adds a redirect-code for a domain
* *
* @param integer $domainid * @param int $domainid id of the domain to add the code for
* id of the domain to add the code for * @param int $redirect selected redirect-id
* @param integer $redirect
* selected redirect-id
* *
* @return null * @return null
* @throws \Exception
*/ */
public static function addRedirectToDomain($domainid = 0, $redirect = 1) public static function addRedirectToDomain(int $domainid = 0, int $redirect = 1)
{ {
if ($domainid > 0) { if ($domainid > 0) {
$ins_stmt = Database::prepare(" $ins_stmt = Database::prepare("
@@ -202,19 +201,18 @@ class Domain
} }
/** /**
* updates the redirectcode of a domain * updates the redirect-code of a domain
* if redirect-code is false, nothing happens * if redirect-code is false, nothing happens
* *
* @param integer $domainid * @param int $domainid id of the domain to update
* id of the domain to update * @param int $redirect selected redirect-id
* @param integer $redirect
* selected redirect-id or false
* *
* @return null * @return null
* @throws \Exception
*/ */
public static function updateRedirectOfDomain($domainid = 0, $redirect = false) public static function updateRedirectOfDomain(int $domainid = 0, int $redirect = 0)
{ {
if ($redirect == false) { if (!$redirect) {
return; return;
} }
@@ -240,12 +238,12 @@ class Domain
* check whether a domain has subdomains added as full-domains * check whether a domain has subdomains added as full-domains
* #329 * #329
* *
* @param int $id * @param int $id domain-id
* domain-id
* *
* @return boolean * @return bool
* @throws \Exception
*/ */
public static function domainHasMainSubDomains($id = 0) public static function domainHasMainSubDomains(int $id): bool
{ {
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT COUNT(`id`) as `mainsubs` FROM `" . TABLE_PANEL_DOMAINS . "` SELECT COUNT(`id`) as `mainsubs` FROM `" . TABLE_PANEL_DOMAINS . "`
@@ -254,8 +252,8 @@ class Domain
'id' => $id 'id' => $id
]); ]);
if (isset($result['mainsubs']) && $result['mainsubs'] > 0) { if ($result && isset($result['mainsubs'])) {
return true; return $result['mainsubs'] > 0;
} }
return false; return false;
} }
@@ -264,12 +262,12 @@ class Domain
* check whether a subof-domain exists * check whether a subof-domain exists
* #329 * #329
* *
* @param int $id * @param int $id subof-domain-id
* subof-domain-id
* *
* @return boolean * @return bool
* @throws \Exception
*/ */
public static function domainMainToSubExists($id = 0) public static function domainMainToSubExists(int $id): bool
{ {
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `id` = :id"); SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `id` = :id");
@@ -278,8 +276,8 @@ class Domain
]); ]);
$result = $result_stmt->fetch(PDO::FETCH_ASSOC); $result = $result_stmt->fetch(PDO::FETCH_ASSOC);
if (isset($result['id']) && $result['id'] > 0) { if ($result && isset($result['id'])) {
return true; return $result['id'] > 0;
} }
return false; return false;
} }
@@ -289,9 +287,10 @@ class Domain
* *
* @param int $domainid * @param int $domainid
* *
* @return boolean * @return bool
* @throws \Exception
*/ */
public static function domainHasSslIpPort($domainid = 0) public static function domainHasSslIpPort(int $domainid): bool
{ {
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT `dt`.* FROM `" . TABLE_DOMAINTOIP . "` `dt`, `" . TABLE_PANEL_IPSANDPORTS . "` `iap` SELECT `dt`.* FROM `" . TABLE_DOMAINTOIP . "` `dt`, `" . TABLE_PANEL_IPSANDPORTS . "` `iap`
@@ -301,7 +300,7 @@ class Domain
]); ]);
$result = $result_stmt->fetch(PDO::FETCH_ASSOC); $result = $result_stmt->fetch(PDO::FETCH_ASSOC);
if (is_array($result) && isset($result['id_ipandports'])) { if ($result && isset($result['id_ipandports'])) {
return true; return true;
} }
return false; return false;
@@ -311,12 +310,12 @@ class Domain
* returns true or false whether a given domain id * returns true or false whether a given domain id
* is the std-subdomain of a customer * is the std-subdomain of a customer
* *
* @param * @param int $did domain-id
* int domain-id
* *
* @return boolean * @return bool
* @throws \Exception
*/ */
public static function isCustomerStdSubdomain($did = 0) public static function isCustomerStdSubdomain(int $did): bool
{ {
if ($did > 0) { if ($did > 0) {
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
@@ -327,21 +326,31 @@ class Domain
'did' => $did 'did' => $did
]); ]);
if (is_array($result) && isset($result['customerid']) && $result['customerid'] > 0) { if ($result && isset($result['customerid'])) {
return true; return $result['customerid'] > 0;
} }
} }
return false; return false;
} }
public static function triggerLetsEncryptCSRForAliasDestinationDomain($aliasDestinationDomainID, $log) /**
{ * @param int $aliasDestinationDomainID
if (isset($aliasDestinationDomainID) && $aliasDestinationDomainID > 0) { * @param FroxlorLogger $log
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "LetsEncrypt CSR triggered for domain ID " . $aliasDestinationDomainID); *
* @return void
* @throws \Exception
*/
public static function triggerLetsEncryptCSRForAliasDestinationDomain(
int $aliasDestinationDomainID,
FroxlorLogger $log
) {
if ($aliasDestinationDomainID > 0) {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO,
"LetsEncrypt CSR triggered for domain ID " . $aliasDestinationDomainID);
$upd_stmt = Database::prepare("UPDATE $upd_stmt = Database::prepare("UPDATE
`" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
SET SET
`expirationdate` = null `validtodate` = null
WHERE WHERE
domainid = :domainid domainid = :domainid
"); ");
@@ -351,7 +360,11 @@ class Domain
} }
} }
public static function doLetsEncryptCleanUp($domainname = null) /**
* @param string $domainname
* @return true
*/
public static function doLetsEncryptCleanUp(string $domainname): bool
{ {
// @ see \Froxlor\Cron\Http\LetsEncrypt\AcmeSh.php // @ see \Froxlor\Cron\Http\LetsEncrypt\AcmeSh.php
$acmesh = AcmeSh::getAcmeSh(); $acmesh = AcmeSh::getAcmeSh();
@@ -374,18 +387,19 @@ class Domain
/** /**
* checks give path for security issues * checks give path for security issues
* and returns a string that can be appended * and returns a string that can be appended
* to a line for a open_basedir directive * to a line for an open_basedir directive
* *
* @param string $path * @param string $path the path to check and append
* the path to check and append * @param bool $first if true, no ':' will be prefixed to the path
* @param boolean $first
* if true, no ':' will be prefixed to the path
* *
* @return string * @return string
* @throws \Exception
*/ */
public static function appendOpenBasedirPath($path = '', $first = false) public static function appendOpenBasedirPath(string $path = '', bool $first = false): string
{ {
if ($path != '' && $path != '/' && (!preg_match("#^/dev#i", $path) || preg_match("#^/dev/urandom#i", $path)) && !preg_match("#^/proc#i", $path) && !preg_match("#^/etc#i", $path) && !preg_match("#^/sys#i", $path) && !preg_match("#:#", $path)) { if ($path != '' && $path != '/' && (!preg_match("#^/dev#i", $path) || preg_match("#^/dev/urandom#i",
$path)) && !preg_match("#^/proc#i", $path) && !preg_match("#^/etc#i",
$path) && !preg_match("#^/sys#i", $path) && !preg_match("#:#", $path)) {
if (preg_match("#^/dev/urandom#i", $path)) { if (preg_match("#^/dev/urandom#i", $path)) {
$path = FileDir::makeCorrectFile($path); $path = FileDir::makeCorrectFile($path);
} else { } else {
@@ -394,7 +408,7 @@ class Domain
// check for php-version that requires the trailing // check for php-version that requires the trailing
// slash to be removed as it does not allow the usage // slash to be removed as it does not allow the usage
// of the subfolders within the given folder, fixes #797 // of the sub-folders within the given folder, fixes #797
if ((PHP_MINOR_VERSION == 2 && PHP_VERSION_ID >= 50216) || PHP_VERSION_ID >= 50304) { if ((PHP_MINOR_VERSION == 2 && PHP_VERSION_ID >= 50216) || PHP_VERSION_ID >= 50304) {
// check trailing slash // check trailing slash
if (substr($path, -1, 1) == '/') { if (substr($path, -1, 1) == '/') {

View File

@@ -30,8 +30,10 @@ use PDO;
class IpAddr class IpAddr
{ {
/**
public static function getIpAddresses() * @return array
*/
public static function getIpAddresses(): array
{ {
$result_stmt = Database::query(" $result_stmt = Database::query("
SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC
@@ -51,21 +53,30 @@ class IpAddr
return $system_ipaddress_array; return $system_ipaddress_array;
} }
public static function getSslIpPortCombinations() /**
* @return array
* @throws \Exception
*/
public static function getSslIpPortCombinations(): array
{ {
return [ return [
'' => lng('panel.none_value') '' => lng('panel.none_value')
] + self::getIpPortCombinations(true); ] + self::getIpPortCombinations(true);
} }
public static function getIpPortCombinations($ssl = false) /**
* @param bool $ssl
* @return array
* @throws \Exception
*/
public static function getIpPortCombinations(bool $ssl = false): array
{ {
global $userinfo; global $userinfo;
$additional_conditions_params = []; $additional_conditions_params = [];
$additional_conditions_array = []; $additional_conditions_array = [];
if ($userinfo['ip'] != '-1') { if (!empty($userinfo) && $userinfo['ip'] != '-1') {
$admin_ip_stmt = Database::prepare(" $admin_ip_stmt = Database::prepare("
SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = IN (:ipid) SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = IN (:ipid)
"); ");

View File

@@ -38,30 +38,24 @@ class FileDir
* which had to be created below with correct Owner/Group * which had to be created below with correct Owner/Group
* (Copied from cron_tasks.php:rev1189 as we'll need this more often in future) * (Copied from cron_tasks.php:rev1189 as we'll need this more often in future)
* *
* @param string $homeDir * @param string $homeDir The homedir of the user
* The homedir of the user * @param string $dirToCreate The dir which should be created
* @param string $dirToCreate * @param int $uid The uid of the user
* The dir which should be created * @param int $gid The gid of the user
* @param int $uid * @param bool $placeindex Place standard-index.html into the new folder
* The uid of the user * @param bool $allow_notwithinhomedir Allow creating a directory out of the customers docroot
* @param int $gid
* The gid of the user
* @param bool $placeindex
* Place standard-index.html into the new folder
* @param bool $allow_notwithinhomedir
* Allow creating a directory out of the customers docroot
* *
* @return bool true if everything went okay, false if something went wrong * @return bool true if everything went okay, false if something went wrong
* @throws Exception * @throws Exception
*/ */
public static function mkDirWithCorrectOwnership( public static function mkDirWithCorrectOwnership(
$homeDir, string $homeDir,
$dirToCreate, string $dirToCreate,
$uid, int $uid,
$gid, int $gid,
$placeindex = false, bool $placeindex = false,
$allow_notwithinhomedir = false bool $allow_notwithinhomedir = false
) { ): bool {
if ($homeDir != '' && $dirToCreate != '') { if ($homeDir != '' && $dirToCreate != '') {
$homeDir = self::makeCorrectDir($homeDir); $homeDir = self::makeCorrectDir($homeDir);
$dirToCreate = self::makeCorrectDir($dirToCreate); $dirToCreate = self::makeCorrectDir($dirToCreate);
@@ -116,15 +110,14 @@ class FileDir
* Function which returns a correct dirname, means to add slashes at the beginning and at the end if there weren't * Function which returns a correct dirname, means to add slashes at the beginning and at the end if there weren't
* some * some
* *
* @param string $path * @param string $dir the path to correct
* the path to correct
* *
* @return string the corrected path * @return string the corrected path
* @throws Exception * @throws Exception
*/ */
public static function makeCorrectDir($dir) public static function makeCorrectDir(string $dir): string
{ {
if (is_string($dir) && strlen($dir) > 0) { if (strlen($dir) > 0) {
$dir = trim($dir); $dir = trim($dir);
if (substr($dir, -1, 1) != '/') { if (substr($dir, -1, 1) != '/') {
$dir .= '/'; $dir .= '/';
@@ -140,12 +133,11 @@ class FileDir
/** /**
* Function which returns a secure path, means to remove all multiple dots and slashes * Function which returns a secure path, means to remove all multiple dots and slashes
* *
* @param string $path * @param string $path the path to secure
* the path to secure
* *
* @return string the corrected path * @return string the corrected path
*/ */
public static function makeSecurePath($path) public static function makeSecurePath(string $path): string
{ {
// check for bad characters, some are allowed with escaping, // check for bad characters, some are allowed with escaping,
// but we generally don't want them in our directory-names, // but we generally don't want them in our directory-names,
@@ -191,16 +183,13 @@ class FileDir
/** /**
* Wrapper around the exec command. * Wrapper around the exec command.
* *
* @param string $exec_string * @param string $exec_string command to be executed
* command to be executed * @param mixed $return_value referenced variable where the output is stored
* @param string $return_value * @param ?array $allowedChars optional array of allowed characters in path/command
* referenced variable where the output is stored
* @param array $allowedChars
* optional array of allowed characters in path/command
* *
* @return array result of exec() * @return array result of exec()
*/ */
public static function safe_exec($exec_string, &$return_value = false, $allowedChars = null) public static function safe_exec(string $exec_string, &$return_value = false, $allowedChars = null)
{ {
$disallowed = [ $disallowed = [
';', ';',
@@ -245,19 +234,20 @@ class FileDir
/** /**
* store the default index-file in a given destination folder * store the default index-file in a given destination folder
* *
* @param string $loginname * @param string $loginname customers loginname
* customers loginname * @param string $destination path where to create the file
* @param string $destination * @param object $logger FroxlorLogger object
* path where to create the file * @param bool $force force creation whatever the settings say (needed for task #2, create new user)
* @param object $logger
* FroxlorLogger object
* @param boolean $force
* force creation whatever the settings say (needed for task #2, create new user)
* *
* @return null * @return void
* @throws Exception
*/ */
public static function storeDefaultIndex($loginname = null, $destination = null, $logger = null, $force = false) public static function storeDefaultIndex(
{ string $loginname,
string $destination,
$logger = null,
bool $force = false
) {
if ($force || (int)Settings::Get('system.store_index_file_subs') == 1) { if ($force || (int)Settings::Get('system.store_index_file_subs') == 1) {
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT `t`.`value`, `c`.`email` AS `customer_email`, `a`.`email` AS `admin_email`, `c`.`loginname` AS `customer_login`, `a`.`loginname` AS `admin_login` SELECT `t`.`value`, `c`.`email` AS `customer_email`, `a`.`email` AS `admin_email`, `c`.`loginname` AS `customer_login`, `a`.`loginname` AS `admin_login`
@@ -306,18 +296,16 @@ class FileDir
self::safe_exec('cp -a ' . Froxlor::getInstallDir() . '/templates/misc/standardcustomer/* ' . escapeshellarg($destination)); self::safe_exec('cp -a ' . Froxlor::getInstallDir() . '/templates/misc/standardcustomer/* ' . escapeshellarg($destination));
} }
} }
return;
} }
/** /**
* Function which returns a correct filename, means to add a slash at the beginning if there wasn't one * Function which returns a correct filename, means to add a slash at the beginning if there wasn't one
* *
* @param string $filename * @param string $filename the filename
* the filename
* *
* @return string the corrected filename * @return string the corrected filename
*/ */
public static function makeCorrectFile(string $filename) public static function makeCorrectFile(string $filename): string
{ {
if (trim($filename) == '') { if (trim($filename) == '') {
$error = 'Given filename for function ' . __FUNCTION__ . ' is empty.' . "\n"; $error = 'Given filename for function ' . __FUNCTION__ . ' is empty.' . "\n";
@@ -333,21 +321,19 @@ class FileDir
$filename = '/' . $filename; $filename = '/' . $filename;
} }
$filename = self::makeSecurePath($filename); return self::makeSecurePath($filename);
return $filename;
} }
/** /**
* checks a directory against disallowed paths which could * checks a directory against disallowed paths which could
* lead to a damaged system if you use them * lead to a damaged system if you use them
* *
* @param string $fieldname * @param string|null $path
* @param array $fielddata
* @param mixed $newfieldvalue
* *
* @return boolean|array * @return bool
* @throws Exception
*/ */
public static function checkDisallowedPaths($path = null) public static function checkDisallowedPaths(string $path): bool
{ {
/* /*
* disallow base-directories and / * disallow base-directories and /
@@ -385,12 +371,11 @@ class FileDir
/** /**
* Function which returns a correct destination for Postfix Virtual Table * Function which returns a correct destination for Postfix Virtual Table
* *
* @param * @param string $destination The destinations
* string The destinations *
* @return string the corrected destinations * @return string the corrected destinations
* @author Florian Lippert <flo@syscp.org> (2003-2009)
*/ */
public static function makeCorrectDestination($destination) public static function makeCorrectDestination(string $destination): string
{ {
$search = '/ +/'; $search = '/ +/';
$replace = ' '; $replace = ' ';
@@ -410,27 +395,25 @@ class FileDir
/** /**
* Returns a valid html tag for the chosen $fieldType for paths * Returns a valid html tag for the chosen $fieldType for paths
* *
* @param * @param string $path The path to start searching in
* string path The path to start searching in * @param int $uid The uid which must match the found directories
* @param * @param int $gid The gid which must match the found directories
* integer uid The uid which must match the found directories * @param string $value the value for the input-field
* @param * @param bool $dom
* integer gid The gid which must match the found directories
* @param
* string value the value for the input-field
* *
* @return string The html tag for the chosen $fieldType * @return array
* *
* @author Martin Burchert <martin.burchert@syscp.de> * @throws Exception
* @author Manuel Bernhardt <manuel.bernhardt@syscp.de> * @author Manuel Bernhardt <manuel.bernhardt@syscp.de>
* @author Martin Burchert <martin.burchert@syscp.de>
*/ */
public static function makePathfield($path, $uid, $gid, $value = '', $dom = false) public static function makePathfield(string $path, int $uid, int $gid, string $value = '', bool $dom = false): array
{ {
$value = str_replace($path, '', $value); $value = str_replace($path, '', $value);
$field = []; $field = [];
// path is given without starting slash // path is given without starting slash
// but dirList holds the paths with starting slash // but dirList holds the paths with starting slash,
// so we just add one here to get the correct // so we just add one here to get the correct
// default path selected, #225 // default path selected, #225
if (substr($value, 0, 1) != '/' && !$dom) { if (substr($value, 0, 1) != '/' && !$dom) {
@@ -464,7 +447,8 @@ class FileDir
$field = [ $field = [
'type' => 'select', 'type' => 'select',
'select_var' => $_field, 'select_var' => $_field,
'selected' => $value 'selected' => $value,
'value' => $value
]; ];
} else { } else {
$field = [ $field = [
@@ -484,16 +468,14 @@ class FileDir
* This function checks every found directory if they match either $uid or $gid, if they do * This function checks every found directory if they match either $uid or $gid, if they do
* the found directory is valid. It uses recursive-iterators to find subdirectories. * the found directory is valid. It uses recursive-iterators to find subdirectories.
* *
* @param string $path * @param string $path the path to start searching in
* the path to start searching in * @param int $uid the uid which must match the found directories
* @param int $uid * @param int $gid the gid which must match the found directories
* the uid which must match the found directories
* @param int $gid
* the gid which must match the found directories
* *
* @return array Array of found valid paths * @return array Array of found valid paths
* @throws Exception
*/ */
private static function findDirs($path, $uid, $gid) private static function findDirs(string $path, int $uid, int $gid): array
{ {
$_fileList = []; $_fileList = [];
$path = self::makeCorrectDir($path); $path = self::makeCorrectDir($path);
@@ -503,7 +485,8 @@ class FileDir
// Will exclude everything under these directories // Will exclude everything under these directories
$exclude = [ $exclude = [
'awstats', 'awstats',
'webalizer' 'webalizer',
'goaccess'
]; ];
/** /**
@@ -569,7 +552,7 @@ class FileDir
* *
* @return string functionname + parameter (not the file) * @return string functionname + parameter (not the file)
*/ */
private static function getImmutableFunction(bool $remove = false) private static function getImmutableFunction(bool $remove = false): string
{ {
if (self::isFreeBSD()) { if (self::isFreeBSD()) {
// FreeBSD style // FreeBSD style
@@ -589,7 +572,7 @@ class FileDir
* *
* @return bool * @return bool
*/ */
public static function isFreeBSD(bool $exact = false) public static function isFreeBSD(bool $exact = false): bool
{ {
if (($exact && PHP_OS == 'FreeBSD') || (!$exact && stristr(PHP_OS, 'BSD'))) { if (($exact && PHP_OS == 'FreeBSD') || (!$exact && stristr(PHP_OS, 'BSD'))) {
return true; return true;

View File

@@ -31,10 +31,10 @@ final class Froxlor
{ {
// Main version variable // Main version variable
const VERSION = '2.0.10'; const VERSION = '2.0.23';
// Database version (YYYYMMDDC where C is a daily counter) // Database version (YYYYMMDDC where C is a daily counter)
const DBVERSION = '202301180'; const DBVERSION = '202304260';
// Distribution branding-tag (used for Debian etc.) // Distribution branding-tag (used for Debian etc.)
const BRANDING = ''; const BRANDING = '';
@@ -45,7 +45,7 @@ final class Froxlor
* *
* @return string * @return string
*/ */
public static function getInstallDir() public static function getInstallDir(): string
{ {
return dirname(__DIR__, 2) . '/'; return dirname(__DIR__, 2) . '/';
} }
@@ -55,7 +55,7 @@ final class Froxlor
* *
* @return string * @return string
*/ */
public static function getVersion() public static function getVersion(): string
{ {
return self::VERSION; return self::VERSION;
} }
@@ -65,7 +65,7 @@ final class Froxlor
* *
* @return string * @return string
*/ */
public static function getVersionString() public static function getVersionString(): string
{ {
return self::getFullVersion() . ' (' . self::DBVERSION . ')'; return self::getFullVersion() . ' (' . self::DBVERSION . ')';
} }
@@ -75,7 +75,7 @@ final class Froxlor
* *
* @return string * @return string
*/ */
public static function getFullVersion() public static function getFullVersion(): string
{ {
return self::VERSION . self::BRANDING; return self::VERSION . self::BRANDING;
} }
@@ -85,12 +85,11 @@ final class Froxlor
* *
* checks if a given version is not equal the current one * checks if a given version is not equal the current one
* *
* @param string $to_check * @param string $to_check version to check, if empty current version is used
* version to check, if empty current version is used
* *
* @return bool true if version to check does not match, else false * @return bool true if version to check does not match, else false
*/ */
public static function hasUpdates($to_check = null) public static function hasUpdates(string $to_check = ''): bool
{ {
if (empty($to_check)) { if (empty($to_check)) {
$to_check = self::VERSION; $to_check = self::VERSION;
@@ -102,16 +101,15 @@ final class Froxlor
} }
/** /**
* Function hasUpdates * Function hasDbUpdates
* *
* checks if a given database-version is not equal the current one * checks if a given database-version is not equal the current one
* *
* @param int $to_check * @param string $to_check version to check, if empty current dbversion is used
* version to check, if empty current dbversion is used
* *
* @return bool true if version to check does not match, else false * @return bool true if version to check does not match, else false
*/ */
public static function hasDbUpdates($to_check = null) public static function hasDbUpdates(string $to_check = ''): bool
{ {
if (empty($to_check)) { if (empty($to_check)) {
$to_check = self::DBVERSION; $to_check = self::DBVERSION;
@@ -127,12 +125,11 @@ final class Froxlor
* *
* checks if a given database-version is the current one * checks if a given database-version is the current one
* *
* @param int $to_check * @param string $to_check version to check
* version to check
* *
* @return bool true if version to check matches, else false * @return bool true if version to check matches, else false
*/ */
public static function isDatabaseVersion($to_check = null) public static function isDatabaseVersion(string $to_check): bool
{ {
if (Settings::Get('panel.frontend') == 'froxlor' && Settings::Get('panel.db_version') == $to_check) { if (Settings::Get('panel.frontend') == 'froxlor' && Settings::Get('panel.db_version') == $to_check) {
return true; return true;
@@ -146,14 +143,14 @@ final class Froxlor
* updates the panel.version field * updates the panel.version field
* to the given value (no checks here!) * to the given value (no checks here!)
* *
* @param string $new_version * @param string $new_version new-version
* new-version
* *
* @return bool true on success, else false * @return bool true on success, else false
* @throws \Exception
*/ */
public static function updateToDbVersion($new_version = null) public static function updateToDbVersion(string $new_version): bool
{ {
if ($new_version !== null && $new_version != '') { if ($new_version != '') {
$upd_stmt = Database::prepare(" $upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :newversion UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :newversion
WHERE `settinggroup` = 'panel' AND `varname` = 'db_version'"); WHERE `settinggroup` = 'panel' AND `varname` = 'db_version'");
@@ -172,14 +169,14 @@ final class Froxlor
* updates the panel.version field * updates the panel.version field
* to the given value (no checks here!) * to the given value (no checks here!)
* *
* @param string $new_version * @param string $new_version new-version
* new-version
* *
* @return bool true on success, else false * @return bool true on success, else false
* @throws \Exception
*/ */
public static function updateToVersion($new_version = null) public static function updateToVersion(string $new_version): bool
{ {
if ($new_version !== null && $new_version != '') { if ($new_version != '') {
$upd_stmt = Database::prepare(" $upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :newversion UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :newversion
WHERE `settinggroup` = 'panel' AND `varname` = 'version'"); WHERE `settinggroup` = 'panel' AND `varname` = 'version'");
@@ -199,7 +196,7 @@ final class Froxlor
* *
* @return bool true if panel is froxlor, else false * @return bool true if panel is froxlor, else false
*/ */
public static function isFroxlor() public static function isFroxlor(): bool
{ {
if (Settings::Get('panel.frontend') !== null && Settings::Get('panel.frontend') == 'froxlor') { if (Settings::Get('panel.frontend') !== null && Settings::Get('panel.frontend') == 'froxlor') {
return true; return true;
@@ -213,12 +210,11 @@ final class Froxlor
* checks if a given version is the * checks if a given version is the
* current one (and panel is froxlor) * current one (and panel is froxlor)
* *
* @param string $to_check * @param string $to_check version to check
* version to check
* *
* @return bool true if version to check matches, else false * @return bool true if version to check matches, else false
*/ */
public static function isFroxlorVersion($to_check = null) public static function isFroxlorVersion(string $to_check): bool
{ {
if (Settings::Get('panel.frontend') == 'froxlor' && Settings::Get('panel.version') == $to_check) { if (Settings::Get('panel.frontend') == 'froxlor' && Settings::Get('panel.version') == $to_check) {
return true; return true;
@@ -231,10 +227,11 @@ final class Froxlor
* *
* @param int $length * @param int $length
* @return string * @return string
* @throws \Exception
*/ */
public static function genSessionId(int $length = 16) public static function genSessionId(int $length = 16): string
{ {
if (intval($length) <= 8) { if ($length <= 8) {
$length = 16; $length = 16;
} }
if (function_exists('random_bytes')) { if (function_exists('random_bytes')) {
@@ -256,9 +253,9 @@ final class Froxlor
* @param string $a * @param string $a
* @param string $b * @param string $b
* *
* @return integer 0 if equal, 1 if a>b and -1 if b>a * @return int 0 if equal, 1 if a>b and -1 if b>a
*/ */
public static function versionCompare2($a, $b) public static function versionCompare2(string $a, string $b): int
{ {
// split version into pieces and remove trailing .0 // split version into pieces and remove trailing .0
$a = explode(".", $a); $a = explode(".", $a);
@@ -295,7 +292,11 @@ final class Froxlor
return (count($a) < count($b)) ? -1 : 0; return (count($a) < count($b)) ? -1 : 0;
} }
private static function parseVersionArray(&$arr = null) /**
* @param array|null $arr
* @return void
*/
private static function parseVersionArray(array &$arr = null)
{ {
// -dev or -beta or -rc ? // -dev or -beta or -rc ?
if (stripos($arr[count($arr) - 1], '-') !== false) { if (stripos($arr[count($arr) - 1], '-') !== false) {
@@ -306,16 +307,20 @@ final class Froxlor
$arr[] = '2'; // dev < beta < rc $arr[] = '2'; // dev < beta < rc
// number of rc // number of rc
$arr[] = substr($x[1], 2); $arr[] = substr($x[1], 2);
} else if (stripos($x[1], 'beta') !== false) { } else {
$arr[] = '-1'; if (stripos($x[1], 'beta') !== false) {
$arr[] = '1'; // dev < beta < rc $arr[] = '-1';
// number of beta $arr[] = '1'; // dev < beta < rc
$arr[] = substr($x[1], 3); // number of beta
} else if (stripos($x[1], 'dev') !== false) { $arr[] = substr($x[1], 3);
$arr[] = '-1'; } else {
$arr[] = '0'; // dev < beta < rc if (stripos($x[1], 'dev') !== false) {
// number of dev $arr[] = '-1';
$arr[] = substr($x[1], 3); $arr[] = '0'; // dev < beta < rc
// number of dev
$arr[] = substr($x[1], 3);
}
}
} }
} }
} }

View File

@@ -45,38 +45,42 @@ class FroxlorLogger
/** /**
* current \Monolog\Logger object * current \Monolog\Logger object
* *
* @var Logger * @var ?Logger
*/ */
private static $ml = null; private static ?Logger $ml = null;
/** /**
* LogTypes Array * LogTypes Array
* *
* @var array * @var ?array
*/ */
private static $logtypes = null; private static ?array $logtypes = null;
/** /**
* whether to output log-messages to STDOUT (cron) * whether to output log-messages to STDOUT (cron)
* *
* @var bool * @var bool
*/ */
private static $crondebug_flag = false; private static bool $crondebug_flag = false;
/** /**
* user info of logged in user * user info of logged-in user
* *
* @var array * @var array
*/ */
private static $userinfo = []; private static array $userinfo = [];
/** /**
* whether the logger object has already been initialized * whether the logger object has already been initialized
* *
* @var bool * @var bool
*/ */
private static $is_initialized = false; private static bool $is_initialized = false;
/** /**
* Class constructor. * Class constructor.
*
* @param array $userinfo
*
* @throws \Exception
*/ */
protected function __construct($userinfo = []) protected function __construct(array $userinfo = [])
{ {
$this->initMonolog(); $this->initMonolog();
self::$userinfo = $userinfo; self::$userinfo = $userinfo;
@@ -143,8 +147,9 @@ class FroxlorLogger
* @param array $userinfo * @param array $userinfo
* *
* @return FroxlorLogger * @return FroxlorLogger
* @throws \Exception
*/ */
public static function getInstanceOf($userinfo = []) public static function getInstanceOf(array $userinfo = [])
{ {
if (empty($userinfo)) { if (empty($userinfo)) {
$userinfo = [ $userinfo = [
@@ -159,9 +164,9 @@ class FroxlorLogger
* *
* @param int $action * @param int $action
* @param int $type * @param int $type
* @param string $text * @param ?string $text
*/ */
public function logAction($action = FroxlorLogger::USR_ACTION, $type = LOG_NOTICE, $text = null) public function logAction($action = FroxlorLogger::USR_ACTION, int $type = LOG_NOTICE, string $text = null)
{ {
// not logging normal stuff if not set to "paranoid" logging // not logging normal stuff if not set to "paranoid" logging
if (!self::$crondebug_flag && Settings::Get('logger.severity') == '1' && $type > LOG_NOTICE) { if (!self::$crondebug_flag && Settings::Get('logger.severity') == '1' && $type > LOG_NOTICE) {
@@ -208,7 +213,11 @@ class FroxlorLogger
} }
} }
public function getLogLevelDesc($type) /**
* @param int $type
* @return string
*/
public function getLogLevelDesc(int $type): string
{ {
switch ($type) { switch ($type) {
case LOG_INFO: case LOG_INFO:
@@ -236,7 +245,11 @@ class FroxlorLogger
return $_type; return $_type;
} }
private function getActionTypeDesc($action) /**
* @param $action
* @return string
*/
private function getActionTypeDesc($action): string
{ {
switch ($action) { switch ($action) {
case FroxlorLogger::USR_ACTION: case FroxlorLogger::USR_ACTION:
@@ -268,7 +281,7 @@ class FroxlorLogger
* *
* @return int * @return int
*/ */
public function setCronLog(int $cronlog = 0) public function setCronLog(int $cronlog = 0): int
{ {
if ($cronlog < 0 || $cronlog > 2) { if ($cronlog < 0 || $cronlog > 2) {
$cronlog = 0; $cronlog = 0;

View File

@@ -47,15 +47,17 @@ class Directory
* *
* @param string $dir * @param string $dir
*/ */
public function __construct($dir = null) public function __construct(string $dir = null)
{ {
$this->dir = $dir; $this->dir = $dir;
} }
/** /**
* check whether the directory has options set in panel_htaccess * check whether the directory has options set in panel_htaccess
*
* @return bool
*/ */
public function hasUserOptions() public function hasUserOptions(): bool
{ {
$uo_stmt = Database::prepare(" $uo_stmt = Database::prepare("
SELECT COUNT(`id`) as `usropts` FROM `" . TABLE_PANEL_HTACCESS . "` WHERE `path` = :dir SELECT COUNT(`id`) as `usropts` FROM `" . TABLE_PANEL_HTACCESS . "` WHERE `path` = :dir
@@ -63,7 +65,7 @@ class Directory
$uo_res = Database::pexecute_first($uo_stmt, [ $uo_res = Database::pexecute_first($uo_stmt, [
'dir' => FileDir::makeCorrectDir($this->dir) 'dir' => FileDir::makeCorrectDir($this->dir)
]); ]);
if ($uo_res != false && isset($uo_res['usropts'])) { if ($uo_res && isset($uo_res['usropts'])) {
return $uo_res['usropts'] > 0; return $uo_res['usropts'] > 0;
} }
return false; return false;
@@ -71,8 +73,10 @@ class Directory
/** /**
* check whether the directory is protected using panel_htpasswd * check whether the directory is protected using panel_htpasswd
*
* @return bool
*/ */
public function isUserProtected() public function isUserProtected(): bool
{ {
$up_stmt = Database::prepare(" $up_stmt = Database::prepare("
SELECT COUNT(`id`) as `usrprot` FROM `" . TABLE_PANEL_HTPASSWDS . "` WHERE `path` = :dir SELECT COUNT(`id`) as `usrprot` FROM `" . TABLE_PANEL_HTPASSWDS . "` WHERE `path` = :dir
@@ -80,7 +84,7 @@ class Directory
$up_res = Database::pexecute_first($up_stmt, [ $up_res = Database::pexecute_first($up_stmt, [
'dir' => FileDir::makeCorrectDir($this->dir) 'dir' => FileDir::makeCorrectDir($this->dir)
]); ]);
if ($up_res != false && isset($up_res['usrprot'])) { if ($up_res && isset($up_res['usrprot'])) {
return $up_res['usrprot'] > 0; return $up_res['usrprot'] > 0;
} }
return false; return false;
@@ -90,12 +94,11 @@ class Directory
* Checks if a given directory is valid for multiple configurations * Checks if a given directory is valid for multiple configurations
* or should rather be used as a single file * or should rather be used as a single file
* *
* @param bool $ifexists * @param bool $ifexists also check whether file/dir exists
* also check whether file/dir exists
* *
* @return bool true if usable as dir, false otherwise * @return bool true if usable as dir, false otherwise
*/ */
public function isConfigDir($ifexists = false) public function isConfigDir(bool $ifexists = false): bool
{ {
if (is_null($this->dir)) { if (is_null($this->dir)) {
trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' has been called with a null value', E_USER_WARNING); trigger_error(__CLASS__ . '::' . __FUNCTION__ . ' has been called with a null value', E_USER_WARNING);

View File

@@ -33,6 +33,10 @@ class HttpClient
/** /**
* Executes simple GET request * Executes simple GET request
* *
* @param string $url
* @param bool $follow_location
* @param int $timeout
*
* @return bool|string * @return bool|string
* @throws Exception * @throws Exception
*/ */
@@ -59,6 +63,10 @@ class HttpClient
/** /**
* Downloads and stores a file from an url * Downloads and stores a file from an url
* *
* @param string $url
* @param string $target
*
* @return bool|string
* @throws Exception * @throws Exception
*/ */
public static function fileGet(string $url, string $target) public static function fileGet(string $url, string $target)

View File

@@ -37,7 +37,7 @@ class PhpConfig
* *
* @return array * @return array
*/ */
public static function getPhpConfigs() public static function getPhpConfigs(): array
{ {
$configs_array = []; $configs_array = [];

View File

@@ -0,0 +1,55 @@
<?php
namespace Froxlor\Http;
use Froxlor\Froxlor;
use Froxlor\Settings;
use Froxlor\UI\Panel\UI;
class RateLimiter
{
private static int $limit_per_interval = 60;
private static int $reset_time = 0;
public static function run(bool $install_mode = false)
{
// default interval = 60 sec
self::$reset_time = time() + 60;
if (!$install_mode) {
self::$limit_per_interval = Settings::Get('system.req_limit_per_interval') ?? 60;
self::$reset_time = time() + Settings::Get('system.req_limit_interval') ?? 60;
}
// Get the remaining requests and reset time from the headers
$remaining = isset($_SESSION['HTTP_X_RATELIMIT_REMAINING']) ? (int)$_SESSION['HTTP_X_RATELIMIT_REMAINING'] : self::$limit_per_interval;
$reset = isset($_SESSION['HTTP_X_RATELIMIT_RESET']) ? (int)$_SESSION['HTTP_X_RATELIMIT_RESET'] : self::$reset_time;
// check if reset time is due
if (time() > $reset) {
$remaining = self::$limit_per_interval;
$reset = self::$reset_time;
}
// If we've hit the limit, return an error
if ($remaining <= 0) {
header('HTTP/1.1 429 Too Many Requests');
header("Retry-After: $reset");
UI::twig()->addGlobal('install_mode', '1');
echo UI::twig()->render('Froxlor/misc/ratelimithint.html.twig', [
'retry' => $reset,
'installdir' => Froxlor::getInstallDir()
]);
die();
}
// Decrement the remaining requests and update the headers
$remaining--;
$_SESSION['HTTP_X_RATELIMIT_REMAINING'] = $remaining;
$_SESSION['HTTP_X_RATELIMIT_RESET'] = $reset;
header("X-RateLimit-Limit: " . self::$limit_per_interval);
header("X-RateLimit-Remaining: " . $remaining);
header("X-RateLimit-Reset: " . $reset);
}
}

View File

@@ -36,16 +36,22 @@ class Statistics
* Create or modify the AWStats configuration file for the given domain. * Create or modify the AWStats configuration file for the given domain.
* Modified by Berend Dekens to allow custom configurations. * Modified by Berend Dekens to allow custom configurations.
* *
* @param * @param string $logFile
* logFile * @param string $siteDomain
* @param * @param string $hostAliases
* siteDomain * @param string $customerDocroot
* @param * @param array $domain_data
* hostAliases *
* @return null * @return null
* @throws \Exception
*/ */
public static function createAWStatsConf($logFile, $siteDomain, $hostAliases, $customerDocroot, $awstats_params = []) public static function createAWStatsConf(
{ string $logFile,
string $siteDomain,
string $hostAliases,
string $customerDocroot,
array $domain_data = []
) {
// Generation header // Generation header
$header = "## GENERATED BY FROXLOR\n"; $header = "## GENERATED BY FROXLOR\n";
$header2 = "## Do not remove the line above! This tells Froxlor to update this configuration\n## If you wish to manually change this configuration file, remove the first line to make sure Froxlor won't rebuild this file\n## Generated for domain {SITE_DOMAIN} on " . date('l dS \of F Y h:i:s A') . "\n"; $header2 = "## Do not remove the line above! This tells Froxlor to update this configuration\n## If you wish to manually change this configuration file, remove the first line to make sure Froxlor won't rebuild this file\n## Generated for domain {SITE_DOMAIN} on " . date('l dS \of F Y h:i:s A') . "\n";
@@ -55,7 +61,7 @@ class Statistics
FileDir::safe_exec('mkdir -p ' . escapeshellarg($awstats_dir)); FileDir::safe_exec('mkdir -p ' . escapeshellarg($awstats_dir));
} }
// chown created folder, #258 // chown created folder, #258
self::makeChownWithNewStats($awstats_params); self::makeChownWithNewStats($domain_data);
// weird but could happen... // weird but could happen...
if (!is_dir(Settings::Get('system.awstats_conf'))) { if (!is_dir(Settings::Get('system.awstats_conf'))) {
@@ -127,16 +133,15 @@ class Statistics
} }
/** /**
* chowns either awstats or webalizer folder, * chowns stats-tools folder, either with webserver-user or
* either with webserver-user or - if fcgid * if fcgid/php-fpm is used, the customers name, #258
* is used - the customers name, #258
* *
* @param array $row * @param array $row array of panel_customers
* array if panel_customers
* *
* @return void * @return void
* @throws \Exception
*/ */
public static function makeChownWithNewStats($row) public static function makeChownWithNewStats(array $row)
{ {
// get correct user // get correct user
if ((Settings::Get('system.mod_fcgid') == '1' || Settings::Get('phpfpm.enabled') == '1') && isset($row['deactivated']) && $row['deactivated'] == '0') { if ((Settings::Get('system.mod_fcgid') == '1' || Settings::Get('phpfpm.enabled') == '1') && isset($row['deactivated']) && $row['deactivated'] == '0') {

View File

@@ -54,16 +54,15 @@ class IdnaWrapper
} }
/** /**
* Encode a domain name, a email address or a list of one of both. * Encode a domain name, an email address or a list of one of both.
* *
* @param * @param string $to_encode May be either a single domain name, e single email address or a list of one
* string May be either a single domain name, e single email address or a list of one
* separated either by ',', ';' or ' '. * separated either by ',', ';' or ' '.
* *
* @return string Returns either a single domain name, a single email address or a list of one of * @return string Returns either a single domain name, a single email address or a list of one of
* both separated by the same string as the input. * both separated by the same string as the input.
*/ */
public function encode($to_encode) public function encode(string $to_encode): string
{ {
$to_encode = $this->isUtf8($to_encode) ? $to_encode : utf8_encode($to_encode); $to_encode = $this->isUtf8($to_encode) ? $to_encode : utf8_encode($to_encode);
try { try {
@@ -83,7 +82,7 @@ class IdnaWrapper
* *
* @return boolean * @return boolean
*/ */
private function isUtf8($string = null) private function isUtf8(string $string)
{ {
if (function_exists("mb_detect_encoding")) { if (function_exists("mb_detect_encoding")) {
if (mb_detect_encoding($string, 'UTF-8, ISO-8859-1') === 'UTF-8') { if (mb_detect_encoding($string, 'UTF-8, ISO-8859-1') === 'UTF-8') {
@@ -119,16 +118,15 @@ class IdnaWrapper
} }
/** /**
* Decode a domain name, a email address or a list of one of both. * Decode a domain name, an email address or a list of one of both.
* *
* @param * @param string $to_decode May be either a single domain name, e single email address or a list of one
* string May be either a single domain name, e single email address or a list of one
* separated either by ',', ';' or ' '. * separated either by ',', ';' or ' '.
* *
* @return string Returns either a single domain name, a single email address or a list of one of * @return string Returns either a single domain name, a single email address or a list of one of
* both separated by the same string as the input. * both separated by the same string as the input.
*/ */
public function decode($to_decode) public function decode(string $to_decode): string
{ {
return $this->idna_converter->decode($to_decode); return $this->idna_converter->decode($to_decode);
} }

View File

@@ -42,7 +42,7 @@ class Install
public $phpVersion; public $phpVersion;
public $formfield; public $formfield;
public string $requiredVersion = '7.4.0'; public string $requiredVersion = '7.4.0';
public array $requiredExtensions = ['session', 'ctype', 'xml', 'filter', 'posix', 'mbstring', 'curl', 'gmp', 'json']; public array $requiredExtensions = ['session', 'ctype', 'xml', 'filter', 'posix', 'mbstring', 'curl', 'gmp', 'json', 'gd'];
public array $suggestedExtensions = ['bcmath', 'zip']; public array $suggestedExtensions = ['bcmath', 'zip'];
public array $suggestions = []; public array $suggestions = [];
public array $criticals = []; public array $criticals = [];

View File

@@ -420,7 +420,7 @@ class Core
} }
$this->updateSetting($upd_stmt, $this->validatedData['activate_newsfeed'], 'admin', 'show_news_feed'); $this->updateSetting($upd_stmt, $this->validatedData['activate_newsfeed'], 'admin', 'show_news_feed');
$this->updateSetting($upd_stmt, dirname(__FILE__, 3), 'system', 'letsencryptchallengepath'); $this->updateSetting($upd_stmt, dirname(__FILE__, 5), 'system', 'letsencryptchallengepath');
// insert the lastcronrun to be the installation date // insert the lastcronrun to be the installation date
$this->updateSetting($upd_stmt, time(), 'system', 'lastcronrun'); $this->updateSetting($upd_stmt, time(), 'system', 'lastcronrun');
@@ -448,7 +448,11 @@ class Core
$reload = "service php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "-fpm restart"; $reload = "service php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "-fpm restart";
$config_dir = "/etc/php/" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "/fpm/pool.d/"; $config_dir = "/etc/php/" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "/fpm/pool.d/";
// fcgid // fcgid
$binary = "/usr/bin/php" . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "-cgi"; 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 . "';");

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