Compare commits

..

122 Commits

Author SHA1 Message Date
Michael Kaufmann
809e8ef45b set version to 2.1.7 for maintenance release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-17 08:30:38 +01:00
Michael Kaufmann
0a091a99e8 wrap SetHandler to php-fpm in file-exists check, as we do for customer-domains already
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-17 08:24:56 +01:00
dependabot[bot]
e299fbe665 Bump follow-redirects from 1.15.4 to 1.15.6 (#1244)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-17 08:15:09 +01:00
Michael Kaufmann
67e8b622d8 correctly save pass_authorizationheader flag for php-configs if FCGID is used; correctly add 'FcgidPassHeader' for froxlor-vhost itself if set
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-17 08:13:24 +01:00
Michael Kaufmann
ce509273d4 correctly validate if a symlink is within the customers home-directory if it's not an absolute path; fixes #1242
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-08 09:23:58 +01:00
Michael Kaufmann
bcf588a2e4 correctly disabled ssl-related settings when domain update sets ssl-enbled flag to false; fixes #1241
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-04 16:49:20 +01:00
Michael Kaufmann
f08d540e66 dont escape panel_password_special_char field
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-03 10:37:42 +01:00
Michael Kaufmann
e06db3d8c5 re-trigger vhost regeneration on tmp. ssl-redirect
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-03-03 10:36:41 +01:00
Michael Kaufmann
c5c04ebe9c fix adding/editing domains as customer when php is not enabled for the domain; don't add custom-vhost-content to deactivated domain-vhosts
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-05 22:10:33 +01:00
Michael Kaufmann
c9faa38f6c fix regression bug in 'incorrect top-5 customers' sorting in traffic-overview which leads to incorrect customer-links due to wrong indexing in the array; fixes #1236
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-04 20:34:48 +01:00
Michael Kaufmann
c188f047dc backport UI/Callback fixes from 2.2-dev (main); fixes #1235
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-04 18:53:12 +01:00
Michael Kaufmann
775d50306c set version to 2.1.6 for bugfix/regression release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-03 14:22:33 +01:00
Michael Kaufmann
3821144c3b also fix unittests accordingly
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-03 14:08:23 +01:00
Michael Kaufmann
a1da70c221 fix password crypt hash being always evaluated to argon2i as the case always returns true if PASSWORD_ARGON2I is defined but the froxlor setting might be set to another hash leading to a useless password
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-03 13:49:43 +01:00
Michael Kaufmann
bb2db0fed0 set version to 2.1.5 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-02-02 11:18:48 +01:00
Michael Kaufmann
9680f24640 fix check for allowed_phpconfigs if using mod_php when adding/editing a customer
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-30 19:32:10 +01:00
Michael Kaufmann
c732fbd81b set correct channel for update-check if switching from apt-installed stable/testing to nightly
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-26 13:57:51 +01:00
Michael Kaufmann
7980b8d14d create empty dns-server config if no (dns-enabled) domain is determined; fixes #1230
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-24 08:39:16 +01:00
Michael Kaufmann
13e88f5b47 fix incorrect top-5 customers in traffic overview for admins; show manual update command if webupdate is disabled
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-19 09:22:12 +01:00
sro0
031596301b Check for argon2 support before using constant PASSWORD_ARGON2X (#1228) 2024-01-16 21:40:03 +01:00
Michael Kaufmann
b34ab45746 disable pam auth in dovecot for debian bookworm (like the other distros do it)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-07 09:06:15 +01:00
Michael Kaufmann
dbf83c6f24 build nightly only from main branch #2
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-06 15:01:52 +01:00
Michael Kaufmann
4cb974839c build nightly only from main branch
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-06 14:54:23 +01:00
Michael Kaufmann
1fa714ef2c add v2.1 branch to security md as currently supported as well as update main-branch version; add field.disabled attribute to formfield-input-template
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-06 14:48:41 +01:00
Michael Kaufmann
63bbcd4e00 add missing language string
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-05 14:48:28 +01:00
Michael Kaufmann
49d67d7c27 set version to 2.1.4 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-05 14:33:45 +01:00
Michael Kaufmann
7cc4c9fedb possibility to specify sender address for froxlor as the admin-email address, custom or empty for system-default; fixes #1217
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-04 08:29:19 +01:00
Michael Kaufmann
afd110a6ed use correct regex for dnscheck-resolver; fixes #1220
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-03 07:35:28 +01:00
Michael Kaufmann
7cdf6c8d64 don't output ipv6 in brackets for system.ipaddress setting as the brackets will be added to the value resulting in an invalid mysql-access-host; fixes #1215
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-01 17:04:02 +01:00
Michael Kaufmann
60621da243 dont use deprecated 'mysql_native_password' for mysql8; fixes #1214
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2024-01-01 12:24:45 +01:00
Michael Kaufmann
96ccdda304 use different language string for password-placeholder when adding a new customer; fixes #1216
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-31 18:01:39 +01:00
Michael Kaufmann
4073984fd7 traffic-cron: check for standardsubdomain to be in the domainlist array to avoid undefined index if e.g. an alias was set to the standardsubdomain
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-31 16:16:01 +01:00
Michael Kaufmann
ea31c8a64d fix font-color in apcu info; clear updatecheck-cache for nightly users
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-30 23:30:52 +01:00
Michael Kaufmann
832ee07e0e Don't show stats-icon for domains with redirect; hide goaccess output in traffic cron and keepalive database connection for long-running log-analysis; use same certificate-file if child-domain inherits the parentdomains certificate data (avoid possible http 421 Misdirected Request)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-29 10:15:45 +01:00
Michael Kaufmann
b542b140c6 set version to 2.1.3 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-22 14:33:11 +01:00
Michael Kaufmann
ac89fc7120 adjust order of css files
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-22 14:30:23 +01:00
Michael Kaufmann
150858485d include custom.css from config.json if preset correctly
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-21 10:59:15 +01:00
Michael Kaufmann
e7810e2066 correctly merge fielddetails with prefetched-formfielddata in form-processing
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-20 09:39:01 +01:00
Michael Kaufmann
4879446567 domains in php-configs are not sortable
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-19 17:17:51 +01:00
Michael Kaufmann
43eff78088 use panel.password_min_length setting for Froxlor.generatePassword() default length parameter; allow '::1' as valid mysql localhost value; wrapper to clean output for cli installation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-16 20:20:58 +01:00
Gamerboy59
55a2ae3801 Add manual_config install var to cli (#1208)
Make the manual_config var, which is available to the web installer, usuable for the cli installer too. If manual_config is set to true skip else (not set or false) proceed with auto config.
2023-12-16 20:13:58 +01:00
Michael Kaufmann
a3b0332d13 set version to 2.1.2 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-15 09:41:16 +01:00
Michael Kaufmann
4b1846883d Merge pull request from GHSA-625g-fm5w-w7w4
* fix possibility to have empty name/surname and empty company

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

* let js validation for customer add/edit form also trim() entered data to avoid empty values pass the client-side validation

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

---------

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-15 09:36:06 +01:00
Michael Kaufmann
778fd3ba65 fix wrong size-unit for mailquota-dashboard-info; fixes #1207
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-15 09:32:02 +01:00
Michael Kaufmann
00456a35e5 fix 2fa login when using email validation, thx to wysiwtf; adjusting row-format of larger tables
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-13 16:20:28 +01:00
Michael Kaufmann
5958f0516b do not css-check/clean passwords of the installation process; fixes #1203
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-12 09:33:28 +01:00
Michael Kaufmann
166ffedf04 correctly merge themeoptions array to use correct image on login when using darkmode
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-12 08:59:56 +01:00
Michael Kaufmann
36dfee1263 fix non-empty value for file-input fields when using uploaded logos
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-12 08:58:33 +01:00
Michael Kaufmann
ec0026ecfd fix wrong type when dns zone for system-hostname is active
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-11 14:20:08 +01:00
Michael Kaufmann
a721bb3f21 remove old 0.10.x and 2.0.x distribution-config-xml's for updaters
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-10 20:47:31 +01:00
Michael Kaufmann
83de3dd719 handle unknown distribution if there's a now unsupported distribution selected for the config-templates
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-10 19:23:58 +01:00
Michael Kaufmann
5615decd96 set version to 2.1.1 for bugfix release (dns and install)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-10 08:20:12 +01:00
Michael Kaufmann
0348b1ec7e fix wrong result in Domain::getMainSubdomainIds(); fixes #1202
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-09 14:25:58 +01:00
Michael Kaufmann
1467dab58f set version to 2.1.0 for upcoming stable release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-08 11:48:32 +01:00
Michael Kaufmann
3a8f48de35 check subclass for cli commands to be \Symfony\Component\Console\Command\Command as the installcommand does not use \Froxlor\Cli\CliCommand
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-07 11:16:53 +01:00
Michael Kaufmann
46391c06ec Merge branch 'main' of github.com:Froxlor/Froxlor 2023-12-06 08:11:17 +01:00
dependabot[bot]
7103f7dd51 Bump vite from 4.4.11 to 4.4.12 (#1201)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.4.11 to 4.4.12.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v4.4.12/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.4.12/packages/vite)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-06 08:11:01 +01:00
Michael Kaufmann
9fc1dfee41 better check for invalid cli classes
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-05 12:50:57 +01:00
Michael Kaufmann
82dc76fdc6 fix wrong escaping of backslash in class-names when updating cronjobs_run table; add missing validateFormField-method for type 'image' (needs to be present but image-validation is handled elsewhere
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-05 11:16:41 +01:00
Michael Kaufmann
02ae52e3df remove old files in updater; avoid including old cli files in froxlor-cli; fix css for card list-groups
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-05 10:22:12 +01:00
Michael Kaufmann
5c06683e27 set version to 2.1.0-rc3
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-01 21:41:21 +01:00
Michael Kaufmann
2684372156 little work on installation; replace hardcoded strings with variables/constants; update dependencies
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-30 11:41:20 +01:00
Michael Kaufmann
d80c6d5714 dynamically read in CLI commands for froxlor-bin
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-28 09:07:30 +01:00
Michael Kaufmann
1ae5311b81 disable default php-fpm config for apache as for some users, it is enabled and used prior to froxlor generated virtual-host configs resulting in no php-rendering
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-27 16:43:13 +01:00
Michael Kaufmann
e1e7555cce minor textual adjustments; add non-session-based csrf-token for js/axios as it is configured to append it to the http-request
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-27 16:42:15 +01:00
Michael Kaufmann
4f79d7cf4b check php-extension requirements not only on installation (e.g. when php version was changed)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-26 22:22:39 +01:00
Michael Kaufmann
b13b1e8ac7 correctly handle empty logger.logfile setting if 'file' is in the activated log-types and no file name was given, thx to Oops
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-26 17:56:30 +01:00
Michael Kaufmann
6a1e7cc539 actually create notice file for 'unconfigured/unmanaged domain' and redirect it for potential dynamic contents (e.g. file extension php) to work properly
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-26 15:19:49 +01:00
Michael Kaufmann
2e87633ef7 table-adjustments for panel_templates #2
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-26 10:58:19 +01:00
Michael Kaufmann
8a23d0b72c table-adjustments for panel_templates
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-26 10:55:24 +01:00
Michael Kaufmann
735ef85088 make unconfigured/unknown domain page a file-template
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-26 10:53:41 +01:00
Michael Kaufmann
75cf44a6d2 respect custom-theme variants in UI::getTheme(); add margin to customer-services dashboard-badges
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-26 09:24:44 +01:00
Michael Kaufmann
7e0073f4a3 on building nightly, of course also install composer dependencies
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-15 22:53:23 +01:00
Michael Kaufmann
c9291df345 rename validateFormFieldHiddenString to validateFormFieldPassword
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-15 22:37:25 +01:00
Michael Kaufmann
fd5e97d48c introduce nightly builds and nightly-update-channel
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-15 22:16:29 +01:00
Michael Kaufmann
64a9fb163a remove duplicated code-line
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-15 10:34:31 +01:00
Michael Kaufmann
b0256ffb7d add REBUILD_VHOST task if only openbasedir-path value changes; fixes #1200
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-15 08:08:48 +01:00
Michael Kaufmann
e606bdc97f Merge branch 'main' of github.com:Froxlor/Froxlor 2023-11-12 13:09:07 +01:00
Michael Kaufmann
b53b3a924a fix wrong database-update procedure in update-command, fix distribution guessing on installation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-12 13:08:40 +01:00
Michael Kaufmann
539ea7c8fc corrected passing of ref-variable to workflow 2023-11-11 22:02:11 +01:00
Michael Kaufmann
5e8763e160 Update build-docs.yml 2023-11-11 21:55:22 +01:00
Michael Kaufmann
d52f33a50c adjust spf-entry-regex; check for valid spf-entry in updater; set version to 2.1.0-rc2
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-11 21:38:24 +01:00
Maurice Preuß (envoyr)
287ad84b18 various html and js fixes
Signed-off-by: Maurice Preuß (envoyr) <envoyr@froxlor.org>
2023-11-11 21:29:02 +01:00
Maurice Preuß
3f1b792f60 Merge pull request #1199 from Froxlor/dependabot/npm_and_yarn/axios-1.6.0
Bump axios from 1.5.1 to 1.6.0
2023-11-11 18:35:02 +01:00
dependabot[bot]
d94317421d Bump axios from 1.5.1 to 1.6.0
Bumps [axios](https://github.com/axios/axios) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.5.1...v1.6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-11 16:17:05 +00:00
Michael Kaufmann
7717a82d5c adjust searchbar-size for better ux, fixes #1197
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-11 14:53:18 +01:00
Michael Kaufmann
ace1651ceb add extra validation for new domains
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-11 14:31:45 +01:00
Michael Kaufmann
1f74bf059c adjust security.md
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-10 22:12:15 +01:00
Michael Kaufmann
c98e912fc5 add description for 'disable_otp_security_check' flag in config.example.inc.php
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-10 14:35:44 +01:00
Michael Kaufmann
d04a8e7bbf create rebuild-vhost task when only changing ssl-enabled-flag when editing domain
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-08 21:22:39 +01:00
Michael Kaufmann
d4a940b723 fix 2fa code verification if method==email altogether
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-08 11:50:33 +01:00
Michael Kaufmann
0dd20bc29a fix 2fa code verification if method==email for changing system-critical settings
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-08 10:47:12 +01:00
Michael Kaufmann
f71ee9f1f2 Merge branch 'main' of github.com:Froxlor/Froxlor 2023-11-08 10:27:13 +01:00
Fabian Welzer
dd61302445 replace deprecated function utf8_encode (#1198)
utf8_encode is deprecated since PHP 8.2.0
2023-11-08 10:27:04 +01:00
Michael Kaufmann
0bee1f03de add missing language string
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-07 16:37:45 +01:00
Ruben Barkow-Kuder
a59aaa3dc9 add minimum node version to packages.json (#1196) 2023-11-06 11:32:29 +01:00
Michael Kaufmann
1debe9d939 set version to 2.1.0-rc1
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-03 13:44:49 +01:00
Michael Kaufmann
3d2e81b457 mark lighttpd as deprecated
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-03 13:39:15 +01:00
Michael Kaufmann
ac759cd9a4 make ssl-cert and ssl-key optional only if a system fallback is specified, else they are required in IpsAndPorts.add() and IpsAndPorts.update()
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-03 10:07:53 +01:00
Michael Kaufmann
05c77929e4 add unconfigured domain template; enhance contrast of tables in light-theme
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-03 09:53:18 +01:00
Michael Kaufmann
cefd9226bd fix possible missing _ecc suffix of let's encrypt folder when cleaning up after deleting a domain
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-11-02 11:33:08 +01:00
Michael Kaufmann
762f295d3d Show nice note if requested domain is 'unknown' to froxlor and thus is being lead to its vhost
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-31 16:43:21 +01:00
Michael Kaufmann
d3e6063027 more password-suggestion fields modernized as the others; little beautifications here and there
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-31 14:55:02 +01:00
Michael Kaufmann
f18c14e119 update readme (cosmetics)
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-30 09:27:08 +01:00
Michael Kaufmann
77bcd10729 removed deprecated/old x-xss-protection http-header
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-25 15:03:57 +02:00
Michael Kaufmann
6ee990af0a switch from huntr.dev to github security advisories as huntr drops support for non-AI/ML projects
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-24 23:08:48 +02:00
Michael Kaufmann
a3fe37b69b use absolute path in settings-export to avoid errors when invoking the cli scripts from out of froxlor's homedir
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-24 19:00:09 +02:00
Michael Kaufmann
56388ede54 fix unescaped quotes for input-fields in settings
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-18 21:04:16 +02:00
Michael Kaufmann
b98035bf3a fix froxlor:update cli command; fix html-syntax issue in updater-result-template which leads to a white page after update
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-18 17:08:30 +02:00
Michael Kaufmann
95abe465ef set version to 2.1.0-beta2
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-18 16:00:06 +02:00
Michael Kaufmann
780f607332 remove unnecessary vite-required; fix fonts-path on subdirectory-installation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-18 15:01:49 +02:00
Michael Kaufmann
a11d26522a fix js integrations
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-18 14:25:02 +02:00
Michael Kaufmann
462a798cb6 more beautification b/c of bootstrap 5.3 #2
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-17 15:00:52 +02:00
Michael Kaufmann
7556685881 more beautification b/c of bootstrap 5.3
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-17 14:25:02 +02:00
Michael Kaufmann
965e2dfd95 darkmode optimizations
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-17 10:17:20 +02:00
Michael Kaufmann
1f2cce6195 more work on bootstrap darkmode implementation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-16 18:19:36 +02:00
envoyr
f4f84aa397 update npm packages
Signed-off-by: envoyr <hello@envoyr.com>
2023-10-16 12:50:29 +02:00
envoyr
0f37dfb1eb remove mix; add vite
Signed-off-by: envoyr <hello@envoyr.com>
2023-10-16 12:48:35 +02:00
Michael Kaufmann
7438786a24 adjustments to support bootstrap 5.3 color-scheme; set gentoo config-templates to deprecated as there is no active maintainer for it; remove debian 10 and ubuntu 18.04 as they were deprecated in 2.0.x
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-15 15:49:12 +02:00
Michael Kaufmann
041c2d176c more bootstrap-5.3 adjustments in css-classes etc.
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-15 11:26:08 +02:00
Michael Kaufmann
597e765677 replace deprecated text-muted css class with bootstrap-5.3's text-body-secondary
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-15 10:29:43 +02:00
Michael Kaufmann
f757233d61 dont check for standardsubdomain in SubDomains.listingCount() as it was also removed from SubDomains.listing()
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-10-13 16:29:53 +02:00
190 changed files with 3066 additions and 20233 deletions

View File

@@ -2,7 +2,8 @@ name: build-documentation
on:
release:
types: [published]
# only run for stable releases
types: [released]
jobs:
build_docs:
@@ -11,4 +12,4 @@ jobs:
- env:
GITHUB_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN }}
run: |
gh workflow run --repo Froxlor/Documentation build-and-deploy.yml -f type=tags ref=${{github.ref_name}}
gh workflow run --repo Froxlor/Documentation build-and-deploy.yml -f type=tags -f ref=${{github.ref_name}}

View File

@@ -1,5 +1,5 @@
name: Froxlor-CI-MariaDB
on: ['push', 'pull_request', 'create']
on: [ 'push', 'pull_request', 'create' ]
jobs:
froxlor:
@@ -8,8 +8,8 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.1']
mariadb-version: [10.5, 10.4]
php-versions: [ '7.4', '8.2' ]
mariadb-version: [ 10.11, 10.5 ]
steps:
- name: Checkout
uses: actions/checkout@v3
@@ -49,33 +49,81 @@ jobs:
- name: Run testing
run: ant quick-build
# - name: irc push
# uses: rectalogic/notify-irc@v1
# if: github.event_name == 'push'
# with:
# channel: "#froxlor"
# server: "irc.libera.chat"
# nickname: froxlor-ci
# message: |
# ${{ github.actor }} pushed ${{ github.event.ref }} ${{ github.event.compare }}
# ${{ join(github.event.commits.*.message) }}
nightly:
name: Create nightly/testing tarball
runs-on: ubuntu-latest
needs: froxlor
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
# - name: irc pull request
# uses: rectalogic/notify-irc@v1
# if: github.event_name == 'pull_request'
# with:
# channel: "#froxlor"
# server: "irc.libera.chat"
# nickname: froxlor-ci
# message: |
# ${{ github.actor }} opened PR ${{ github.event.pull_request.html_url }}
steps:
- name: Checkout
uses: actions/checkout@v3
# - name: irc tag created
# uses: rectalogic/notify-irc@v1
# if: github.event_name == 'create' && github.event.ref_type == 'tag'
# with:
# channel: "#froxlor"
# server: "irc.libera.chat"
# nickname: froxlor-ci
# message: |
# ${{ github.actor }} tagged ${{ github.repository }} ${{ github.event.ref }}
- name: Setup PHP with PECL extension
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
tools: composer:v2
extensions: mbstring, xml, ctype, pdo_mysql, mysql, curl, json, zip, session, filter, posix, openssl, fileinfo, bcmath, gmp, gnupg
- name: Install composer dependencies
run: composer install --no-dev
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: '20.x'
- name: Install npm dependencies
run: npm install
- name: Build assets
run: npm run build
working-directory: .
- name: Setting file/directory permissions
run: |
find -exec chmod ugo+r,u+w,go-w {} \;
find -type f -exec chmod ugo-x {} \;
find -type d -exec chmod ugo+x {} \;
chmod 0755 bin/froxlor-cli
- name: Remove vcs and unneeded files
run: |
rm .gitignore
rm .editorconfig
rm -rf node_modules
rm composer.json
rm composer.lock
rm package.json
rm package-lock.json
rm *.xml
rm vite.config.js
- name: Create empty index.html in built assets directory
run: |
touch templates/Froxlor/build/index.html
touch templates/Froxlor/build/assets/index.html
- name: Set outputs
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Set nightly branding
run: |
sed -i "s/const BRANDING = '';/const BRANDING = '+nightly.${{steps.vars.outputs.sha_short}}';/" lib/Froxlor/Froxlor.php
zip -r froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip . -x "*.git*"
sha256sum froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip > froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip.sha256
mkdir dist
mv froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip dist/
mv froxlor-nightly.${{steps.vars.outputs.sha_short}}.zip.sha256 dist/
- name: Deploy nightly to server
uses: easingthemes/ssh-deploy@v3.4.3
env:
ARGS: "-rltDzvO --chown=${{ secrets.WEB_USER }}:${{ secrets.WEB_USER }}"
SOURCE: "dist/"
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
TARGET: "${{ secrets.REMOTE_TARGET }}"

View File

@@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.1']
php-versions: ['7.4', '8.2']
mysql-version: [8.0, 5.7]
steps:
- name: Checkout

5
.gitignore vendored
View File

@@ -22,8 +22,5 @@ fonts/
templates/*
!templates/index.html
!templates/Froxlor/
templates/Froxlor/assets/mix-manifest.json
templates/Froxlor/assets/css/
templates/Froxlor/assets/js/
templates/Froxlor/assets/webfonts/
templates/Froxlor/build/
!templates/misc/

View File

@@ -34,19 +34,13 @@ You may find help in the following places:
The froxlor community discord server can be found here: https://discord.froxlor.org
### IRC
froxlor may be found on libera.chat, channel #froxlor:
irc://irc.libera.chat/froxlor
### Forum
The community is located on https://forum.froxlor.org/
### Wiki
### Documentation
More documentation may be found in the froxlor - documentation:
https://docs.froxlor.org/
The documentation may be found at https://docs.froxlor.org/
## License

View File

@@ -10,9 +10,11 @@ With that, good luck hacking us ;)
## Supported versions
- ️✅ **2.x** (`main` git-branch)
- ❌ 0.10.x (`0.10.x` git-branch)
-0.9.x (`0.9.x`git-branch)
- ️✅ **2.2.x** (`main` git-branch)
- ️✅ **2.1.x** (`v2.1` git-branch)
-2.0.x (`2.0.x`-tags)
- ❌ 0.10.x (`0.10.x`-tags)
- ❌ other git-branches
## Qualifying Vulnerabilities
@@ -26,7 +28,7 @@ With that, good luck hacking us ;)
### Vulnerabilities we accept
Only reproducable issues on a default/clean setup from the latest stable release of a supported version will be accepted.
Only reproducible issues on a default/clean setup from the latest stable release of a supported version will be accepted.
## Non-Qualifying Vulnerabilities
@@ -34,6 +36,8 @@ Only reproducable issues on a default/clean setup from the latest stable release
- Theoretical attacks without proof of exploitability
- Attacks that are the result of a third party library should be reported to the library maintainers
- Social engineering
- Attacks that require disabling security features or reducing the security level of the environment
- Exploits by an admin user itself (privileged user and implicitly trusted)
- Reflected file download
- Physical attacks
- Weak SSL/TLS/SSH algorithms or protocols
@@ -44,4 +48,4 @@ Only reproducable issues on a default/clean setup from the latest stable release
## Reporting a Vulnerability
If you think you have found a vulnerability in froxlor, please head over to [https://huntr.dev/repos/froxlor/froxlor](https://huntr.dev/repos/froxlor/froxlor) and use the reporting possibilities there as we are funding the prize-pot for froxlor on this platform. Also, please give us appropriate time to fix the issue and build update-packages before publishing anything into the wild. Alternatively you can send us an email to [team@froxlor.org](team@froxlor.org).
If you think you have found a vulnerability in froxlor, please head over to [https://github.com/Froxlor/Froxlor/security/advisories](https://github.com/Froxlor/Froxlor/security/advisories/new) and use the reporting possibilities there. Also, please give us appropriate time to fix the issue and build update-packages before publishing anything into the wild. Alternatively you can email us to [team@froxlor.org](team@froxlor.org).

View File

@@ -130,7 +130,8 @@ return [
'default' => 'stable',
'select_var' => [
'stable' => lng('serversettings.uc_stable'),
'testing' => lng('serversettings.uc_testing')
'testing' => lng('serversettings.uc_testing'),
'nightly' => lng('serversettings.uc_nightly')
],
'save_method' => 'storeSettingField',
'advanced_mode' => true
@@ -171,16 +172,6 @@ return [
'default' => false,
'save_method' => 'storeSettingField'
],
'system_index_file_extension' => [
'label' => lng('serversettings.index_file_extension'),
'settinggroup' => 'system',
'varname' => 'index_file_extension',
'type' => 'text',
'string_regexp' => '/^[a-zA-Z0-9]{1,6}$/',
'default' => 'html',
'save_method' => 'storeSettingField',
'advanced_mode' => true
],
'system_store_index_file_subs' => [
'label' => lng('serversettings.system_store_index_file_subs'),
'settinggroup' => 'system',

View File

@@ -248,7 +248,7 @@ return [
'settinggroup' => 'system',
'varname' => 'le_domain_dnscheck_resolver',
'type' => 'text',
'string_regexp' => '/^(([0-9]+ [a-z0-9\-\._]+, ?)*[0-9]+ [a-z0-9\-\._]+)?$/i',
'string_type' => 'validate_ip',
'string_emptyallowed' => true,
'default' => '',
'save_method' => 'storeSettingField'

View File

@@ -43,7 +43,8 @@ return [
'settinggroup' => 'spf',
'varname' => 'spf_entry',
'type' => 'text',
'default' => '"v=spf1 a mx -all"',
'string_regexp' => '/^v=spf[a-z0-9:~?\s.-]+$/i',
'default' => 'v=spf1 a mx -all',
'save_method' => 'storeSettingField'
]
]

View File

@@ -60,7 +60,9 @@ if ($userinfo['change_serversettings'] == '1') {
if (!empty($distribution)) {
if (!file_exists($config_dir . '/' . $distribution . ".xml")) {
Response::dynamicError("Unknown distribution");
// unknown distribution -> redirect to select a valid distribution for config-templates
Settings::Set('system.distribution', '');
Response::redirectTo('admin_configfiles.php', ['reselect' => 1]);
}
// update setting if different

View File

@@ -60,7 +60,8 @@ if (Settings::Get('panel.sendalternativemail') == 1) {
}
$file_templates = [
'index_html'
'index_html',
'unconfigured_html'
];
$languages = Language::getLanguages();

View File

@@ -24,20 +24,8 @@
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/
declare(strict_types=1);
use Froxlor\Cli\ConfigDiff;
use Symfony\Component\Console\Application;
use Froxlor\Cli\RunApiCommand;
use Froxlor\Cli\ConfigServices;
use Froxlor\Cli\PhpSessionclean;
use Froxlor\Cli\SwitchServerIp;
use Froxlor\Cli\UpdateCommand;
use Froxlor\Cli\InstallCommand;
use Froxlor\Cli\MasterCron;
use Froxlor\Cli\UserCommand;
use Froxlor\Cli\ValidateAcmeWebroot;
use Froxlor\Froxlor;
use Symfony\Component\Console\Application;
// validate correct php version
if (version_compare("7.4.0", PHP_VERSION, ">=")) {
@@ -53,14 +41,31 @@ require dirname(__DIR__) . '/vendor/autoload.php';
require dirname(__DIR__) . '/lib/tables.inc.php';
$application = new Application('froxlor-cli', Froxlor::getFullVersion());
$application->add(new RunApiCommand());
$application->add(new ConfigServices());
$application->add(new PhpSessionclean());
$application->add(new SwitchServerIp());
$application->add(new UpdateCommand());
$application->add(new InstallCommand());
$application->add(new MasterCron());
$application->add(new UserCommand());
$application->add(new ValidateAcmeWebroot());
$application->add(new ConfigDiff());
// files that are no commands
$fileIgnoreList = [
// Current non-command files
'CliCommand.php',
'index.html',
'install.functions.php',
];
// directory of commands to include
$cmd_files = glob(Froxlor::getInstallDir() . '/lib/Froxlor/Cli/*.php');
// include and add commands
foreach ($cmd_files as $cmdFile) {
// check ignore-list
if (!in_array(basename($cmdFile), $fileIgnoreList)) {
// include class-file
require $cmdFile;
// create class-name including namespace
$cmdClass = "\\Froxlor\\Cli\\" . substr(basename($cmdFile), 0, -4);
// check whether it exists
if (class_exists($cmdClass) && is_subclass_of($cmdClass, '\Symfony\Component\Console\Command\Command')) {
// add to cli application
$application->add(new $cmdClass());
}
}
}
$application->run();

View File

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

144
composer.lock generated
View File

@@ -8,16 +8,16 @@
"packages": [
{
"name": "amnuts/opcache-gui",
"version": "3.5.2",
"version": "3.5.4",
"source": {
"type": "git",
"url": "https://github.com/amnuts/opcache-gui.git",
"reference": "db9df06889067f99d95bb9f226ec99e663aed196"
"reference": "f3a8fe44c0a4c69dd69b9999d68f9097ee362946"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amnuts/opcache-gui/zipball/db9df06889067f99d95bb9f226ec99e663aed196",
"reference": "db9df06889067f99d95bb9f226ec99e663aed196",
"url": "https://api.github.com/repos/amnuts/opcache-gui/zipball/f3a8fe44c0a4c69dd69b9999d68f9097ee362946",
"reference": "f3a8fe44c0a4c69dd69b9999d68f9097ee362946",
"shasum": ""
},
"require": {
@@ -58,7 +58,7 @@
"support": {
"email": "andy@amnuts.com",
"issues": "https://github.com/amnuts/opcache-gui/issues",
"source": "https://github.com/amnuts/opcache-gui/tree/3.5.2"
"source": "https://github.com/amnuts/opcache-gui/tree/3.5.4"
},
"funding": [
{
@@ -66,7 +66,7 @@
"type": "github"
}
],
"time": "2023-10-02T10:23:26+00:00"
"time": "2023-10-25T19:09:56+00:00"
},
{
"name": "dflydev/dot-access-data",
@@ -674,16 +674,16 @@
},
{
"name": "phpmailer/phpmailer",
"version": "v6.8.1",
"version": "v6.9.1",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "e88da8d679acc3824ff231fdc553565b802ac016"
"reference": "039de174cd9c17a8389754d3b877a2ed22743e18"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e88da8d679acc3824ff231fdc553565b802ac016",
"reference": "e88da8d679acc3824ff231fdc553565b802ac016",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/039de174cd9c17a8389754d3b877a2ed22743e18",
"reference": "039de174cd9c17a8389754d3b877a2ed22743e18",
"shasum": ""
},
"require": {
@@ -703,6 +703,7 @@
"yoast/phpunit-polyfills": "^1.0.4"
},
"suggest": {
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
@@ -742,7 +743,7 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.1"
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.1"
},
"funding": [
{
@@ -750,7 +751,7 @@
"type": "github"
}
],
"time": "2023-08-29T08:26:30+00:00"
"time": "2023-11-25T22:23:28+00:00"
},
{
"name": "psr/container",
@@ -972,16 +973,16 @@
},
{
"name": "symfony/console",
"version": "v5.4.28",
"version": "v5.4.32",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "f4f71842f24c2023b91237c72a365306f3c58827"
"reference": "c70df1ffaf23a8d340bded3cfab1b86752ad6ed7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/f4f71842f24c2023b91237c72a365306f3c58827",
"reference": "f4f71842f24c2023b91237c72a365306f3c58827",
"url": "https://api.github.com/repos/symfony/console/zipball/c70df1ffaf23a8d340bded3cfab1b86752ad6ed7",
"reference": "c70df1ffaf23a8d340bded3cfab1b86752ad6ed7",
"shasum": ""
},
"require": {
@@ -1051,7 +1052,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v5.4.28"
"source": "https://github.com/symfony/console/tree/v5.4.32"
},
"funding": [
{
@@ -1067,7 +1068,7 @@
"type": "tidelift"
}
],
"time": "2023-08-07T06:12:30+00:00"
"time": "2023-11-18T18:23:04+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -1872,16 +1873,16 @@
},
{
"name": "symfony/string",
"version": "v5.4.29",
"version": "v5.4.32",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "e41bdc93def20eaf3bfc1537c4e0a2b0680a152d"
"reference": "91bf4453d65d8231688a04376c3a40efe0770f04"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/e41bdc93def20eaf3bfc1537c4e0a2b0680a152d",
"reference": "e41bdc93def20eaf3bfc1537c4e0a2b0680a152d",
"url": "https://api.github.com/repos/symfony/string/zipball/91bf4453d65d8231688a04376c3a40efe0770f04",
"reference": "91bf4453d65d8231688a04376c3a40efe0770f04",
"shasum": ""
},
"require": {
@@ -1938,7 +1939,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v5.4.29"
"source": "https://github.com/symfony/string/tree/v5.4.32"
},
"funding": [
{
@@ -1954,30 +1955,31 @@
"type": "tidelift"
}
],
"time": "2023-09-13T11:47:41+00:00"
"time": "2023-11-26T13:43:46+00:00"
},
{
"name": "twig/twig",
"version": "v3.7.1",
"version": "v3.8.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554"
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554",
"reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3"
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php80": "^1.22"
},
"require-dev": {
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.3"
"symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0"
},
"type": "library",
"autoload": {
@@ -2013,7 +2015,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.7.1"
"source": "https://github.com/twigphp/Twig/tree/v3.8.0"
},
"funding": [
{
@@ -2025,7 +2027,7 @@
"type": "tidelift"
}
],
"time": "2023-08-28T11:09:02+00:00"
"time": "2023-11-21T18:54:41+00:00"
},
{
"name": "voku/anti-xss",
@@ -2291,16 +2293,16 @@
"packages-dev": [
{
"name": "composer/pcre",
"version": "3.1.0",
"version": "3.1.1",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2"
"reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2",
"reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2",
"url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9",
"reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9",
"shasum": ""
},
"require": {
@@ -2342,7 +2344,7 @@
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.1.0"
"source": "https://github.com/composer/pcre/tree/3.1.1"
},
"funding": [
{
@@ -2358,7 +2360,7 @@
"type": "tidelift"
}
],
"time": "2022-11-17T09:50:14+00:00"
"time": "2023-10-11T07:11:09+00:00"
},
{
"name": "composer/xdebug-handler",
@@ -2613,23 +2615,24 @@
},
{
"name": "pdepend/pdepend",
"version": "2.15.1",
"version": "2.16.0",
"source": {
"type": "git",
"url": "https://github.com/pdepend/pdepend.git",
"reference": "d12f25bcdfb7754bea458a4a5cb159d55e9950d0"
"reference": "8dfc0c46529e2073fa97986552f80646eedac562"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pdepend/pdepend/zipball/d12f25bcdfb7754bea458a4a5cb159d55e9950d0",
"reference": "d12f25bcdfb7754bea458a4a5cb159d55e9950d0",
"url": "https://api.github.com/repos/pdepend/pdepend/zipball/8dfc0c46529e2073fa97986552f80646eedac562",
"reference": "8dfc0c46529e2073fa97986552f80646eedac562",
"shasum": ""
},
"require": {
"php": ">=5.3.7",
"symfony/config": "^2.3.0|^3|^4|^5|^6.0",
"symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0",
"symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0"
"symfony/config": "^2.3.0|^3|^4|^5|^6.0|^7.0",
"symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0|^7.0",
"symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0|^7.0",
"symfony/polyfill-mbstring": "^1.19"
},
"require-dev": {
"easy-doc/easy-doc": "0.0.0|^1.2.3",
@@ -2664,7 +2667,7 @@
],
"support": {
"issues": "https://github.com/pdepend/pdepend/issues",
"source": "https://github.com/pdepend/pdepend/tree/2.15.1"
"source": "https://github.com/pdepend/pdepend/tree/2.16.0"
},
"funding": [
{
@@ -2672,7 +2675,7 @@
"type": "tidelift"
}
],
"time": "2023-09-28T12:00:56+00:00"
"time": "2023-11-29T08:52:35+00:00"
},
{
"name": "phar-io/manifest",
@@ -2906,7 +2909,6 @@
"type": "github"
}
],
"abandoned": true,
"time": "2020-12-07T05:51:20+00:00"
},
{
@@ -2995,16 +2997,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.10.38",
"version": "1.10.46",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691"
"reference": "90d3d25c5b98b8068916bbf08ce42d5cb6c54e70"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/5302bb402c57f00fb3c2c015bac86e0827e4b691",
"reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/90d3d25c5b98b8068916bbf08ce42d5cb6c54e70",
"reference": "90d3d25c5b98b8068916bbf08ce42d5cb6c54e70",
"shasum": ""
},
"require": {
@@ -3053,7 +3055,7 @@
"type": "tidelift"
}
],
"time": "2023-10-06T14:19:14+00:00"
"time": "2023-11-28T14:57:26+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -4562,16 +4564,16 @@
},
{
"name": "symfony/config",
"version": "v5.4.26",
"version": "v5.4.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
"reference": "8109892f27beed9252bd1f1c1880aeb4ad842650"
"reference": "dd5ea39de228813aba0c23c3a4153da2a4cf3cd9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/8109892f27beed9252bd1f1c1880aeb4ad842650",
"reference": "8109892f27beed9252bd1f1c1880aeb4ad842650",
"url": "https://api.github.com/repos/symfony/config/zipball/dd5ea39de228813aba0c23c3a4153da2a4cf3cd9",
"reference": "dd5ea39de228813aba0c23c3a4153da2a4cf3cd9",
"shasum": ""
},
"require": {
@@ -4621,7 +4623,7 @@
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/config/tree/v5.4.26"
"source": "https://github.com/symfony/config/tree/v5.4.31"
},
"funding": [
{
@@ -4637,20 +4639,20 @@
"type": "tidelift"
}
],
"time": "2023-07-19T20:21:11+00:00"
"time": "2023-11-09T08:22:43+00:00"
},
{
"name": "symfony/dependency-injection",
"version": "v5.4.29",
"version": "v5.4.32",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
"reference": "338638ed8c9d5c7fcb136a73f5c7043465ae2f05"
"reference": "d5d48f215ed73f7973d01256b9a2fac729bef759"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/338638ed8c9d5c7fcb136a73f5c7043465ae2f05",
"reference": "338638ed8c9d5c7fcb136a73f5c7043465ae2f05",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/d5d48f215ed73f7973d01256b9a2fac729bef759",
"reference": "d5d48f215ed73f7973d01256b9a2fac729bef759",
"shasum": ""
},
"require": {
@@ -4710,7 +4712,7 @@
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/dependency-injection/tree/v5.4.29"
"source": "https://github.com/symfony/dependency-injection/tree/v5.4.32"
},
"funding": [
{
@@ -4726,7 +4728,7 @@
"type": "tidelift"
}
],
"time": "2023-09-20T06:23:43+00:00"
"time": "2023-11-29T06:58:28+00:00"
},
{
"name": "symfony/filesystem",
@@ -4873,16 +4875,16 @@
},
{
"name": "theseer/tokenizer",
"version": "1.2.1",
"version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"shasum": ""
},
"require": {
@@ -4911,7 +4913,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
"source": "https://github.com/theseer/tokenizer/tree/1.2.1"
"source": "https://github.com/theseer/tokenizer/tree/1.2.2"
},
"funding": [
{
@@ -4919,7 +4921,7 @@
"type": "github"
}
],
"time": "2021-07-28T10:34:58+00:00"
"time": "2023-11-20T00:12:19+00:00"
}
],
"aliases": [],

View File

@@ -72,7 +72,7 @@ if ($page == 'overview' || $page == 'domains') {
}
$actions_links[] = [
'href' => 'https://docs.froxlor.org/v2/user-guide/domains/',
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/domains/',
'target' => '_blank',
'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary'

View File

@@ -76,7 +76,7 @@ if ($page == 'overview' || $page == 'emails') {
}
$actions_links[] = [
'href' => 'https://docs.froxlor.org/v2/user-guide/emails/',
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/emails/',
'target' => '_blank',
'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary'
@@ -138,7 +138,7 @@ if ($page == 'email_domain') {
];
}
$actions_links[] = [
'href' => 'https://docs.froxlor.org/v2/user-guide/emails/',
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/emails/',
'target' => '_blank',
'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary'

View File

@@ -75,7 +75,7 @@ if ($page == 'overview' || $page == 'htpasswds') {
];
$actions_links[] = [
'href' => 'https://docs.froxlor.org/v2/user-guide/extras/',
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/extras/',
'target' => '_blank',
'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary'
@@ -200,7 +200,7 @@ if ($page == 'overview' || $page == 'htpasswds') {
];
$actions_links[] = [
'href' => 'https://docs.froxlor.org/v2/user-guide/extras/',
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/extras/',
'target' => '_blank',
'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary'
@@ -349,7 +349,7 @@ if ($page == 'overview' || $page == 'htpasswds') {
$actions_links = [
[
'href' => 'https://docs.froxlor.org/v2/user-guide/extras/',
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/extras/',
'target' => '_blank',
'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary'

View File

@@ -65,7 +65,7 @@ if ($page == 'overview' || $page == 'accounts') {
];
}
$actions_links[] = [
'href' => 'https://docs.froxlor.org/v2/user-guide/ftp-accounts/',
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/ftp-accounts/',
'target' => '_blank',
'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary'

View File

@@ -115,8 +115,8 @@ if ($page == 'overview') {
$userinfo['traffic_bytes_used'] = $userinfo['traffic_used'] * 1024;
if (Settings::Get('system.mail_quota_enabled')) {
$userinfo['email_quota_bytes'] = ($userinfo['email_quota'] > -1) ? $userinfo['email_quota'] * 1024 : -1;
$userinfo['email_quota_bytes_used'] = $userinfo['email_quota_used'] * 1024;
$userinfo['email_quota_bytes'] = ($userinfo['email_quota'] > -1) ? $userinfo['email_quota'] * 1024 * 1024 : -1;
$userinfo['email_quota_bytes_used'] = $userinfo['email_quota_used'] * 1024 * 1024;
}
if ($usages) {

View File

@@ -75,7 +75,7 @@ if ($page == 'overview' || $page == 'mysqls') {
}
$actions_links[] = [
'href' => 'https://docs.froxlor.org/v2/user-guide/databases/',
'href' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/databases/',
'target' => '_blank',
'icon' => 'fa-solid fa-circle-info',
'class' => 'btn-outline-secondary'

View File

@@ -74,27 +74,26 @@ if ($action == '2fa_entercode') {
$code = isset($_POST['2fa_code']) ? $_POST['2fa_code'] : null;
// verify entered code
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
$result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3));
// get user-data
$table = $_SESSION['uidtable_2fa'];
$field = $_SESSION['uidfield_2fa'];
$uid = $_SESSION['uid_2fa'];
$isadmin = $_SESSION['unfo_2fa'];
if ($_SESSION['secret_2fa'] == 'email') {
// verify code set to user's data_2fa field
$sel_stmt = Database::prepare("SELECT `data_2fa` FROM " . $table . " WHERE `" . $field . "` = :uid");
$userinfo_code = Database::pexecute_first($sel_stmt, ['uid' => $uid]);
$result = $tfa->verifyCode($userinfo_code['data_2fa'], $code);
} else {
$result = $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3);
}
// either the code is valid when using authenticator-app, or we will select userdata by id and entered code
// which is temporarily stored for the customer when using email-2fa
if ($result) {
$sel_param = [
'uid' => $uid
];
if ($_SESSION['secret_2fa'] == 'email') {
// verify code by selecting user by id and the temp. stored code,
// so only if it's the correct code, we get the user-data
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
$sel_param['code'] = $code;
} else {
// Authenticator-verification has already happened at this point, so just get the user-data
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid");
}
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid");
$userinfo = Database::pexecute_first($sel_stmt, $sel_param);
// whoops, no (valid) user? Start again
if (empty($userinfo)) {
@@ -327,11 +326,12 @@ if ($action == '2fa_entercode') {
if ($userinfo['type_2fa'] == 1) {
// generate code
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
$code = $tfa->getCode($tfa->createSecret());
$secret = $tfa->createSecret();
$code = $tfa->getCode($secret);
// set code for user
$stmt = Database::prepare("UPDATE $table SET `data_2fa` = :d2fa WHERE `$uid` = :uid");
Database::pexecute($stmt, [
"d2fa" => $code,
"d2fa" => $secret,
"uid" => $userinfo[$uid]
]);
// build up & send email

View File

@@ -157,7 +157,7 @@ CREATE TABLE `panel_admins` (
`api_allowed` tinyint(1) NOT NULL default '1',
PRIMARY KEY (`adminid`),
UNIQUE KEY `loginname` (`loginname`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC;
DROP TABLE IF EXISTS `panel_customers`;
@@ -299,7 +299,7 @@ CREATE TABLE `panel_domains` (
KEY `customerid` (`customerid`),
KEY `parentdomain` (`parentdomainid`),
KEY `domain` (`domain`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC;
DROP TABLE IF EXISTS `panel_ipsandports`;
@@ -356,23 +356,6 @@ CREATE TABLE `panel_htpasswds` (
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
DROP TABLE IF EXISTS `panel_sessions`;
CREATE TABLE `panel_sessions` (
`hash` varchar(32) NOT NULL default '',
`userid` int(11) unsigned NOT NULL default '0',
`ipaddress` varchar(255) NOT NULL default '',
`useragent` varchar(255) NOT NULL default '',
`lastactivity` int(11) unsigned NOT NULL default '0',
`lastpaging` varchar(255) NOT NULL default '',
`formtoken` char(32) NOT NULL default '',
`language` varchar(64) NOT NULL default '',
`adminsession` tinyint(1) unsigned NOT NULL default '0',
`theme` varchar(255) NOT NULL default '',
PRIMARY KEY (`hash`),
KEY `userid` (`userid`)
) ENGINE=HEAP;
DROP TABLE IF EXISTS `panel_settings`;
CREATE TABLE `panel_settings` (
`settingid` int(11) unsigned NOT NULL auto_increment,
@@ -408,7 +391,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES
('admin', 'show_version_footer', '0'),
('caa', 'caa_entry', ''),
('spf', 'use_spf', '0'),
('spf', 'spf_entry', '"v=spf1 a mx -all"'),
('spf', 'spf_entry', 'v=spf1 a mx -all'),
('dkim', 'dkim_algorithm', 'all'),
('dkim', 'dkim_keylength', '1024'),
('dkim', 'dkim_servicetype', '0'),
@@ -579,7 +562,6 @@ opcache.validate_timestamps'),
('system', 'mod_fcgid_wrapper', '1'),
('system', 'mod_fcgid_starter', '0'),
('system', 'mod_fcgid_peardir', '/usr/share/php/:/usr/share/php5/'),
('system', 'index_file_extension', 'html'),
('system', 'mod_fcgid_maxrequests', '250'),
('system', 'ssl_key_file','/etc/ssl/froxlor_selfsigned.key'),
('system', 'ssl_ca_file', ''),
@@ -696,7 +678,7 @@ opcache.validate_timestamps'),
('system', 'distribution', ''),
('system', 'update_channel', 'stable'),
('system', 'updatecheck_data', ''),
('system', 'update_notify_last', '2.1.0-beta1'),
('system', 'update_notify_last', ''),
('system', 'traffictool', 'goaccess'),
('system', 'req_limit_per_interval', 60),
('system', 'req_limit_interval', 60),
@@ -704,7 +686,7 @@ opcache.validate_timestamps'),
('api', 'customer_default', '1'),
('2fa', 'enabled', '1'),
('panel', 'decimal_places', '4'),
('panel', 'adminmail', 'admin@SERVERNAME'),
('panel', 'adminmail', 'ADMIN_MAIL'),
('panel', 'phpmyadmin_url', ''),
('panel', 'webmail_url', ''),
('panel', 'webftp_url', ''),
@@ -744,8 +726,8 @@ opcache.validate_timestamps'),
('panel', 'logo_overridecustom', '0'),
('panel', 'settings_mode', '0'),
('panel', 'menu_collapsed', '1'),
('panel', 'version', '2.1.0-beta1'),
('panel', 'db_version', '202305240');
('panel', 'version', '2.1.7'),
('panel', 'db_version', '202312120');
DROP TABLE IF EXISTS `panel_tasks`;
@@ -768,6 +750,7 @@ CREATE TABLE `panel_templates` (
`templategroup` varchar(255) NOT NULL default '',
`varname` varchar(255) NOT NULL default '',
`value` longtext NOT NULL,
`file_extension` varchar(50) NOT NULL default 'html',
PRIMARY KEY (id),
KEY adminid (adminid)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;

View File

@@ -38,6 +38,7 @@ if (!defined('_CRON_UPDATE')) {
if (Froxlor::isFroxlorVersion('2.0.24')) {
Update::showUpdateStep("Cleaning domains table");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ROW_FORMAT=DYNAMIC;");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` DROP COLUMN `ismainbutsubto`;");
Update::lastStepStatus(0);
@@ -67,15 +68,18 @@ if (Froxlor::isFroxlorVersion('2.0.24')) {
}
Update::showUpdateStep("Adjusting cronjobs");
Database::query("
$cfupd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET
`module`= 'froxlor/export',
`cronfile` = 'export',
`cronclass` = '\\Froxlor\\Cron\\System\\ExportCron',
`cronclass` = :cc,
`interval` = '1 HOUR',
`desc_lng_key` = 'cron_export'
WHERE `module` = 'froxlor/backup'
");
Database::pexecute($cfupd_stmt, [
'cc' => '\\Froxlor\\Cron\\System\\ExportCron'
]);
Update::lastStepStatus(0);
Update::showUpdateStep("Adjusting system for data-export function");
@@ -93,3 +97,200 @@ if (Froxlor::isFroxlorVersion('2.1.0-dev1')) {
Update::showUpdateStep("Updating from 2.1.0-dev1 to 2.1.0-beta1", false);
Froxlor::updateToVersion('2.1.0-beta1');
}
if (Froxlor::isFroxlorVersion('2.1.0-beta1')) {
Update::showUpdateStep("Updating from 2.1.0-beta1 to 2.1.0-beta2", false);
Update::showUpdateStep("Removing unused table");
Database::query("DROP TABLE IF EXISTS `panel_sessions`;");
Update::lastStepStatus(0);
Froxlor::updateToVersion('2.1.0-beta2');
}
if (Froxlor::isFroxlorVersion('2.1.0-beta2')) {
Update::showUpdateStep("Updating from 2.1.0-beta2 to 2.1.0-rc1", false);
Froxlor::updateToVersion('2.1.0-rc1');
}
if (Froxlor::isFroxlorVersion('2.1.0-rc1')) {
Update::showUpdateStep("Updating from 2.1.0-rc1 to 2.1.0-rc2", false);
Update::showUpdateStep("Adjusting setting spf_entry");
$spf_entry = Settings::Get('spf.spf_entry');
if (!preg_match('/^v=spf[a-z0-9:~?\s.-]+$/i', $spf_entry)) {
Settings::Set('spf.spf_entry', 'v=spf1 a mx -all');
Update::lastStepStatus(1, 'corrected');
} else {
Update::lastStepStatus(0);
}
Froxlor::updateToVersion('2.1.0-rc2');
}
if (Froxlor::isDatabaseVersion('202305240')) {
Update::showUpdateStep("Adjusting file-template file extension setttings");
$current_fileextension = Settings::Get('system.index_file_extension');
Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup`= 'system' AND `varname`= 'index_file_extension'");
Database::query("ALTER TABLE `" . TABLE_PANEL_TEMPLATES . "` ADD `file_extension` varchar(50) NOT NULL default 'html';");
if (!empty(trim($current_fileextension)) && strtolower(trim($current_fileextension)) != 'html') {
$stmt = Database::prepare("UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET `file_extension` = :ext WHERE `templategroup` = 'files'");
Database::pexecute($stmt, ['ext' => strtolower(trim($current_fileextension))]);
}
Update::lastStepStatus(0);
Froxlor::updateToDbVersion('202311260');
}
if (Froxlor::isFroxlorVersion('2.1.0-rc2')) {
Update::showUpdateStep("Updating from 2.1.0-rc2 to 2.1.0-rc3", false);
Froxlor::updateToVersion('2.1.0-rc3');
}
if (Froxlor::isDatabaseVersion('202311260')) {
Update::showUpdateStep("Cleaning up old files");
$to_clean = array(
"install/updates/froxlor/update_2.x.inc.php",
"install/updates/preconfig/preconfig_2.x.inc.php",
"lib/Froxlor/Api/Commands/CustomerBackups.php",
"lib/Froxlor/Cli/Action",
"lib/Froxlor/Cli/Action.php",
"lib/Froxlor/Cli/CmdLineHandler.php",
"lib/Froxlor/Cli/ConfigServicesCmd.php",
"lib/Froxlor/Cli/PhpSessioncleanCmd.php",
"lib/Froxlor/Cli/SwitchServerIpCmd.php",
"lib/Froxlor/Cli/UpdateCliCmd.php",
"lib/Froxlor/Cron/System/BackupCron.php",
"lib/formfields/customer/extras/formfield.backup.php",
"lib/tablelisting/customer/tablelisting.backups.php",
"templates/Froxlor/assets/mix-manifest.json",
"templates/Froxlor/assets/css",
"templates/Froxlor/assets/webfonts",
"templates/Froxlor/assets/js/main.js",
"templates/Froxlor/assets/js/main.js.LICENSE.txt",
"templates/Froxlor/src",
"templates/Froxlor/user/change_language.html.twig",
"templates/Froxlor/user/change_password.html.twig",
"templates/Froxlor/user/change_theme.html.twig",
"tests/Backup/CustomerBackupsTest.php"
);
$disabled = explode(',', ini_get('disable_functions'));
$exec_allowed = !in_array('exec', $disabled);
$del_list = "";
foreach ($to_clean as $filedir) {
$complete_filedir = Froxlor::getInstallDir() . $filedir;
if (file_exists($complete_filedir)) {
if ($exec_allowed) {
FileDir::safe_exec("rm -rf " . escapeshellarg($complete_filedir));
} else {
$del_list .= "rm -rf " . escapeshellarg($complete_filedir) . PHP_EOL;
}
}
}
if ($exec_allowed) {
Update::lastStepStatus(0);
} else {
if (empty($del_list)) {
// none of the files existed
Update::lastStepStatus(0);
} else {
Update::lastStepStatus(
1,
'manual commands needed',
'Please run the following commands manually:<br><pre>' . $del_list . '</pre>'
);
}
}
Froxlor::updateToDbVersion('202312050');
}
if (Froxlor::isFroxlorVersion('2.1.0-rc3')) {
Update::showUpdateStep("Updating from 2.1.0-rc3 to 2.1.0 stable", false);
Froxlor::updateToVersion('2.1.0');
}
if (Froxlor::isFroxlorVersion('2.1.0')) {
Update::showUpdateStep("Updating from 2.1.0 to 2.1.1", false);
Froxlor::updateToVersion('2.1.1');
}
if (Froxlor::isDatabaseVersion('202312050')) {
Update::showUpdateStep("Cleaning up old files");
$to_clean = array(
"lib/configfiles/centos7.xml",
"lib/configfiles/centos8.xml",
"lib/configfiles/stretch.xml",
"lib/configfiles/xenial.xml",
"lib/configfiles/buster.xml",
"lib/configfiles/bionic.xml",
);
$disabled = explode(',', ini_get('disable_functions'));
$exec_allowed = !in_array('exec', $disabled);
$del_list = "";
foreach ($to_clean as $filedir) {
$complete_filedir = Froxlor::getInstallDir() . $filedir;
if (file_exists($complete_filedir)) {
if ($exec_allowed) {
FileDir::safe_exec("rm -rf " . escapeshellarg($complete_filedir));
} else {
$del_list .= "rm -rf " . escapeshellarg($complete_filedir) . PHP_EOL;
}
}
}
if ($exec_allowed) {
Update::lastStepStatus(0);
} else {
if (empty($del_list)) {
// none of the files existed
Update::lastStepStatus(0);
} else {
Update::lastStepStatus(
1,
'manual commands needed',
'Please run the following commands manually:<br><pre>' . $del_list . '</pre>'
);
}
}
Froxlor::updateToDbVersion('202312100');
}
if (Froxlor::isDatabaseVersion('202312100')) {
Update::showUpdateStep("Adjusting table row format of larger tables");
Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` ROW_FORMAT=DYNAMIC;");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ROW_FORMAT=DYNAMIC;");
Update::lastStepStatus(0);
Froxlor::updateToDbVersion('202312120');
}
if (Froxlor::isFroxlorVersion('2.1.1')) {
Update::showUpdateStep("Updating from 2.1.1 to 2.1.2", false);
Froxlor::updateToVersion('2.1.2');
}
if (Froxlor::isFroxlorVersion('2.1.2')) {
Update::showUpdateStep("Updating from 2.1.2 to 2.1.3", false);
Froxlor::updateToVersion('2.1.3');
}
if (Froxlor::isFroxlorVersion('2.1.3')) {
Update::showUpdateStep("Updating from 2.1.3 to 2.1.4", false);
Froxlor::updateToVersion('2.1.4');
}
if (Froxlor::isFroxlorVersion('2.1.4')) {
Update::showUpdateStep("Updating from 2.1.4 to 2.1.5", false);
Froxlor::updateToVersion('2.1.5');
}
if (Froxlor::isFroxlorVersion('2.1.5')) {
Update::showUpdateStep("Updating from 2.1.5 to 2.1.6", false);
Froxlor::updateToVersion('2.1.6');
}
if (Froxlor::isFroxlorVersion('2.1.6')) {
Update::showUpdateStep("Updating from 2.1.6 to 2.1.7", false);
Froxlor::updateToVersion('2.1.7');
}

View File

@@ -400,7 +400,10 @@ class Customers extends ApiCommand implements ResourceEntity
$allowed_phpconfigs = array_map('intval', $allowed_phpconfigs);
if (empty($allowed_phpconfigs) && $phpenabled == 1) {
Response::standardError('customerphpenabledbutnoconfig', '', true);
// only required if not using mod_php
if ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {
Response::standardError('customerphpenabledbutnoconfig', '', true);
}
}
$allowed_mysqlserver = array();
@@ -1053,7 +1056,7 @@ class Customers extends ApiCommand implements ResourceEntity
$email = $this->getParam('email', true, $idna_convert->decode($result['email']));
$name = $this->getParam('name', true, $result['name']);
$firstname = $this->getParam('firstname', true, $result['firstname']);
$company_required = empty($result['company']) && ((!empty($name) && empty($firstname)) || (empty($name) && !empty($firstname)) || (empty($name) && empty($firstname)));
$company_required = (!empty($name) && empty($firstname)) || (empty($name) && !empty($firstname)) || (empty($name) && empty($firstname));
$company = $this->getParam('company', !$company_required, $result['company']);
$street = $this->getParam('street', true, $result['street']);
$zipcode = $this->getParam('zipcode', true, $result['zipcode']);
@@ -1114,7 +1117,10 @@ class Customers extends ApiCommand implements ResourceEntity
$allowed_phpconfigs = array_map('intval', $allowed_phpconfigs);
}
if (empty($allowed_phpconfigs) && $phpenabled == 1) {
Response::standardError('customerphpenabledbutnoconfig', '', true);
// only required if not using mod_php
if ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {
Response::standardError('customerphpenabledbutnoconfig', '', true);
}
}
// add permission for allowed mysql usage if customer was not allowed to use mysql prior

View File

@@ -349,6 +349,8 @@ class Domains extends ApiCommand implements ResourceEntity
if (substr($p_domain, 0, 4) == 'xn--') {
Response::standardError('domain_nopunycode', '', true);
} elseif (Validate::validate_ip2($p_domain, true, '', true, true)) {
Response::standardError('domain_noipaddress', '', true);
}
$idna_convert = new IdnaWrapper();
@@ -517,7 +519,8 @@ class Domains extends ApiCommand implements ResourceEntity
$mod_fcgid_maxrequests = '-1';
}
} else {
$phpenabled = '1';
// set default to whether the customer has php enabled or not
$phpenabled = $customer['phpenabled'];
$openbasedir = '1';
if ((int)Settings::Get('phpfpm.enabled') == 1) {
@@ -1525,13 +1528,12 @@ class Domains extends ApiCommand implements ResourceEntity
// enabled ssl for the domain but no ssl ip/port is selected
Response::standardError('nosslippportgiven', '', true);
}
if (Settings::Get('system.use_ssl') == "0" || empty($ssl_ipandports)) {
if (Settings::Get('system.use_ssl') == "0" || empty($ssl_ipandports) || !$sslenabled) {
$ssl_redirect = 0;
$letsencrypt = 0;
$http2 = 0;
// we need this for the json_encode
// if ssl is disabled or no ssl-ip/port exists
$ssl_ipandports[] = -1;
// act like $remove_ssl_ipandport
$ssl_ipandports = [];
// HSTS
$hsts_maxage = 0;
@@ -1650,6 +1652,7 @@ class Domains extends ApiCommand implements ResourceEntity
|| $iswildcarddomain != $result['iswildcarddomain']
|| $phpenabled != $result['phpenabled']
|| $openbasedir != $result['openbasedir']
|| $openbasedir_path != $result['openbasedir_path']
|| $phpsettingid != $result['phpsettingid']
|| $mod_fcgid_starter != $result['mod_fcgid_starter']
|| $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests']
@@ -1667,6 +1670,7 @@ class Domains extends ApiCommand implements ResourceEntity
|| $hsts_sub != $result['hsts_sub']
|| $hsts_preload != $result['hsts_preload']
|| $ocsp_stapling != $result['ocsp_stapling']
|| $sslenabled != $result['ssl_enabled']
) {
Cronjob::inserttask(TaskId::REBUILD_VHOST);
}
@@ -1815,7 +1819,7 @@ class Domains extends ApiCommand implements ResourceEntity
$update_data['wwwserveralias'] = $wwwserveralias;
$update_data['iswildcarddomain'] = $iswildcarddomain;
$update_data['phpenabled'] = $phpenabled;
$update_data['openbasedir'] = $openbasedir;;
$update_data['openbasedir'] = $openbasedir;
$update_data['openbasedir_path'] = $openbasedir_path;
$update_data['speciallogfile'] = $speciallogfile;
$update_data['phpsettingid'] = $phpsettingid;

View File

@@ -157,10 +157,10 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
// prefix hash-algo
switch (Settings::Get('system.passwordcryptfunc')) {
case PASSWORD_ARGON2I:
case 'argon2i':
$cpPrefix = '{ARGON2I}';
break;
case PASSWORD_ARGON2ID:
case 'argon2id':
$cpPrefix = '{ARGON2ID}';
break;
default:
@@ -404,10 +404,10 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
$password = Crypt::validatePassword($password, true);
// prefix hash-algo
switch (Settings::Get('system.passwordcryptfunc')) {
case PASSWORD_ARGON2I:
case 'argon2i':
$cpPrefix = '{ARGON2I}';
break;
case PASSWORD_ARGON2ID:
case 'argon2id':
$cpPrefix = '{ARGON2ID}';
break;
default:

View File

@@ -82,7 +82,7 @@ class Froxlor extends ApiCommand
if ($aucheck == 1) {
// anzeige über version-status mit ggfls. formular
// zum update schritt #1 -> download
$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') == 'testing' ? 'testing ' : ''), AutoUpdate::getFromResult('version'), $this->version]);
$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') != 'stable' ? Settings::Get('system.update_channel').' ' : ''), AutoUpdate::getFromResult('version'), $this->version]);
$response = [
'isnewerversion' => (int) !AutoUpdate::getFromResult('has_latest'),
'version' => $this->version,
@@ -91,7 +91,7 @@ class Froxlor extends ApiCommand
'additional_info' => AutoUpdate::getFromResult('info'),
'aucheck' => $aucheck
];
} else if ($aucheck < 0 || $aucheck > 1) {
} elseif ($aucheck < 0 || $aucheck > 1) {
// errors
if ($aucheck < 0) {
$errmsg = AutoUpdate::getLastError();
@@ -259,14 +259,15 @@ class Froxlor extends ApiCommand
* returns a random password based on froxlor settings for min-length, included characters, etc.
*
* @param int $length
* optional length of password, defaults to 10
* optional length of password, defaults to 0 (panel.password_min_length)
*
* @access admin, customer
* @return string
* @throws Exception
*/
public function generatePassword()
public function generatePassword(): string
{
$length = $this->getParam('length', true, 10);
$length = $this->getParam('length', true, 0);
return $this->response(Crypt::generatePassword($length));
}

View File

@@ -176,8 +176,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
if ((int)Settings::Get('system.use_ssl') == 1) {
$ssl = (bool)$this->getBoolParam('ssl', true, 0);
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', !$ssl, ''), 'ssl_cert_file', '', '', [], true);
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', !$ssl, ''), 'ssl_key_file', '', '', [], true);
$cert_optional = !($ssl && empty(Settings::Get('system.ssl_cert_file')));
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $cert_optional, ''), 'ssl_cert_file', '', '', [], true);
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $cert_optional, ''), 'ssl_key_file', '', '', [], true);
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, ''), 'ssl_ca_file', '', '', [], true);
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, ''), 'ssl_cert_chainfile', '', '', [], true);
$sslss = $this->getParam('ssl_specialsettings', true, '');
@@ -415,8 +416,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
if ((int)Settings::Get('system.use_ssl') == 1) {
$ssl = (bool)$this->getBoolParam('ssl', true, $result['ssl']);
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', !$ssl, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true);
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', !$ssl, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);
$cert_optional = !($ssl && empty(Settings::Get('system.ssl_cert_file')));
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $cert_optional, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true);
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $cert_optional, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, $result['ssl_ca_file']), 'ssl_ca_file', '', '', [], true);
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', [], true);
$sslss = $this->getParam('ssl_specialsettings', true, $result['ssl_specialsettings']);

View File

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

View File

@@ -296,21 +296,24 @@ class SubDomains extends ApiCommand implements ResourceEntity
// assign default config
$phpsid_result['phpsettingid'] = 1;
}
// check whether the customer has chosen its own php-config
if ($phpsettingid > 0 && $phpsettingid != $phpsid_result['phpsettingid']) {
$phpsid_result['phpsettingid'] = intval($phpsettingid);
}
$allowed_phpconfigs = $customer['allowed_phpconfigs'];
if (!empty($allowed_phpconfigs)) {
$allowed_phpconfigs = json_decode($allowed_phpconfigs, true);
} else {
$allowed_phpconfigs = [];
}
// only with fcgid/fpm enabled will it be possible to select a php-setting
if ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {
if (!in_array($phpsid_result['phpsettingid'], $allowed_phpconfigs)) {
Response::standardError('notallowedphpconfigused', '', true);
if ($domain_check['phpenabled'] == 1) {
// check whether the customer has chosen its own php-config
if ($phpsettingid > 0 && $phpsettingid != $phpsid_result['phpsettingid']) {
$phpsid_result['phpsettingid'] = intval($phpsettingid);
}
$allowed_phpconfigs = $customer['allowed_phpconfigs'];
if (!empty($allowed_phpconfigs)) {
$allowed_phpconfigs = json_decode($allowed_phpconfigs, true);
} else {
$allowed_phpconfigs = [];
}
// only with fcgid/fpm enabled will it be possible to select a php-setting
if ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {
if (!in_array($phpsid_result['phpsettingid'], $allowed_phpconfigs)) {
Response::standardError('notallowedphpconfigused', '', true);
}
}
}
@@ -797,7 +800,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
$allowed_phpconfigs = [];
}
// only with fcgid/fpm enabled will it be possible to select a php-setting
if ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) {
if ((int)$result['phpenabled'] == 1 && ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1)) {
if (!in_array($phpsettingid, $allowed_phpconfigs)) {
Response::standardError('notallowedphpconfigused', '', true);
}
@@ -1078,10 +1081,8 @@ class SubDomains extends ApiCommand implements ResourceEntity
$custom_list_result = $_custom_list_result['list'];
}
$customer_ids = [];
$customer_stdsubs = [];
foreach ($custom_list_result as $customer) {
$customer_ids[] = $customer['customerid'];
$customer_stdsubs[$customer['customerid']] = $customer['standardsubdomain'];
}
} else {
if (Settings::IsInList('panel.customer_hide_options', 'domains')) {
@@ -1090,9 +1091,6 @@ class SubDomains extends ApiCommand implements ResourceEntity
$customer_ids = [
$this->getUserDetail('customerid')
];
$customer_stdsubs = [
$this->getUserDetail('customerid') => $this->getUserDetail('standardsubdomain')
];
}
if (!empty($customer_ids)) {
// prepare select statement
@@ -1101,7 +1099,6 @@ class SubDomains extends ApiCommand implements ResourceEntity
FROM `" . TABLE_PANEL_DOMAINS . "` `d`
WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ")
AND `d`.`email_only` = '0'
AND `d`.`id` NOT IN (" . implode(', ', $customer_stdsubs) . ")
");
$result = Database::pexecute_first($domains_stmt, null, true, true);
if ($result) {

View File

@@ -402,7 +402,7 @@ final class ConfigServices extends CliCommand
case "file":
if (array_key_exists('content', $action)) {
$output->writeln('<comment>Creating file "' . $action['name'] . '"</>');
file_put_contents($action['name'], trim(strtr($action['content'], $replace_arr)));
file_put_contents($action['name'], trim(strtr($action['content'], $replace_arr)) . PHP_EOL);
} elseif (array_key_exists('subcommands', $action)) {
foreach ($action['subcommands'] as $fileaction) {
if (array_key_exists('execute', $fileaction) && $fileaction['execute'] == "pre") {
@@ -411,7 +411,7 @@ final class ConfigServices extends CliCommand
exec(strtr($fileaction['content'], $replace_arr));
} elseif ($fileaction['type'] == 'file') {
$output->writeln('<comment>Creating file "' . $fileaction['name'] . '"</>');
file_put_contents($fileaction['name'], trim(strtr($fileaction['content'], $replace_arr)));
file_put_contents($fileaction['name'], trim(strtr($fileaction['content'], $replace_arr)) . PHP_EOL);
}
}
}
@@ -514,6 +514,7 @@ final class ConfigServices extends CliCommand
'<WEBSERVER_GROUP>' => Settings::Get('system.httpgroup'),
'<SSL_CERT_FILE>' => Settings::Get('system.ssl_cert_file'),
'<SSL_KEY_FILE>' => Settings::Get('system.ssl_key_file'),
'<ADMIN_MAIL>' => Settings::Get('panel.adminmail'),
];
}
}

View File

@@ -211,7 +211,7 @@ final class InstallCommand extends Command
$ask_field = false;
}
$fielddata['value'] = $this->formfielddata[$fieldname] ?? ($fielddata['value'] ?? null);
$fielddata['label'] = strip_tags(str_replace("<br>", " ", $fielddata['label']));
$fielddata['label'] = $this->cliTextFormat($fielddata['label'], " ");
if ($ask_field) {
if ($fielddata['type'] == 'password') {
$this->formfielddata[$fieldname] = $this->io->askHidden($fielddata['label'], function ($value) use ($fielddata) {
@@ -267,14 +267,16 @@ final class InstallCommand extends Command
case 4:
$section = $inst->formfield['install']['sections']['step' . $step] ?? [];
$this->io->section($section['title']);
$this->io->note($section['description']);
$this->io->note($this->cliTextFormat($section['description']));
$cmdfield = $section['fields']['system'];
$this->io->success([
$cmdfield['label'],
$cmdfield['value']
]);
if (!empty($decoded_input) || $this->io->confirm('Execute command now?', false)) {
passthru($cmdfield['value']);
if (!isset($decoded_input['manual_config']) || (bool)$decoded_input['manual_config'] === false) {
if (!empty($decoded_input) || $this->io->confirm('Execute command now?', false)) {
passthru($cmdfield['value']);
}
}
break;
}
@@ -305,7 +307,7 @@ final class InstallCommand extends Command
$json_output = [];
foreach ($fields['install']['sections'] as $section => $section_fields) {
foreach ($section_fields['fields'] as $name => $field) {
if ($name == 'system' || $name == 'manual_config' || $name == 'target_servername') {
if ($name == 'system' || $name == 'target_servername') {
continue;
}
if ($field['type'] == 'text' || $field['type'] == 'email') {
@@ -318,7 +320,7 @@ final class InstallCommand extends Command
$fieldval = '******';
} elseif ($field['type'] == 'select') {
$fieldval = implode("|", array_keys($field['select_var']));
} else if ($field['type'] == 'checkbox') {
} elseif ($field['type'] == 'checkbox') {
$fieldval = "1|0";
} else {
$fieldval = "?";
@@ -346,4 +348,10 @@ final class InstallCommand extends Command
curl_close($ch);
fclose($fp);
}
private function cliTextFormat(string $text, string $nl_char = "\n"): string
{
$text = str_replace(['<br>', '<br/>', '<br />'], [$nl_char, $nl_char, $nl_char], $text);
return strip_tags($text);
}
}

View File

@@ -263,6 +263,8 @@ final class MasterCron extends CliCommand
if ($jobcount > 0) {
if (Settings::Get('system.nssextrausers') == 1) {
Extrausers::generateFiles($this->cronLog);
// reload crond as shell users might use crontab and the user is only known to crond if reloaded
FileDir::safe_exec(escapeshellcmd(Settings::Get('system.crondreload')));
return;
}
@@ -275,6 +277,8 @@ final class MasterCron extends CliCommand
FileDir::safe_exec('nscd -i group 1> /dev/null', $false_val, [
'>'
]);
// reload crond as shell users might use crontab and the user is only known to crond if reloaded
FileDir::safe_exec(escapeshellcmd(Settings::Get('system.crondreload')));
}
}
}

View File

@@ -56,7 +56,7 @@ final class UpdateCommand extends CliCommand
// database update only
if ($input->getOption('database')) {
$result = $this->validateRequirements($input, $output, true);
$result = $this->validateRequirements($output, true);
if ($result == self::SUCCESS) {
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
$output->writeln('<info>' . lng('updates.dbupdate_required') . '</>');
@@ -77,7 +77,7 @@ final class UpdateCommand extends CliCommand
return $result;
}
$result = $this->validateRequirements($input, $output);
$result = $this->validateRequirements($output);
if ($result != self::SUCCESS) {
// requirements failed, exit
@@ -100,7 +100,7 @@ final class UpdateCommand extends CliCommand
}
// there is a new version
if ($input->getOption('check-only')) {
$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') == 'testing' ? 'testing ' : ''), AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') != 'stable' ? Settings::Get('system.update_channel').' ' : ''), AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
} else {
$text = lng('admin.newerversionavailable') . ' ' . lng('admin.newerversiondetails', [AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
}
@@ -110,7 +110,7 @@ final class UpdateCommand extends CliCommand
$newversionavail = true;
$output->writeln('<comment>' . $text . '</>');
$result = self::SUCCESS;
} else if ($aucheck < 0 || $aucheck > 1) {
} elseif ($aucheck < 0 || $aucheck > 1) {
if ($input->getOption('integer-return')) {
$output->write(-1);
return self::INVALID;
@@ -175,7 +175,7 @@ final class UpdateCommand extends CliCommand
$result = self::SUCCESS;
$question = new ConfirmationQuestion('Update database? [no] ', false, '/^(y|j)/i');
if ($yestoall || $helper->ask($input, $output, $question)) {
$result = $this->updateDatabase();
$result = $this->runUpdate($output, true);
}
} else {
$errmsg = 'error.autoupdate_' . $auex;
@@ -199,7 +199,7 @@ final class UpdateCommand extends CliCommand
if ($input->getOption('mail-notify')) {
$last_check_version = Settings::Get('system.update_notify_last');
if (Update::versionInUpdate($last_check_version, AutoUpdate::getFromResult('version'))) {
$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') == 'testing' ? 'testing ' : ''), AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
$text = lng('update.uc_newinfo', [(Settings::Get('system.update_channel') != 'stable' ? Settings::Get('system.update_channel').' ' : ''), AutoUpdate::getFromResult('version'), Froxlor::VERSION]);
$mail = new Mailer(true);
$mail->Body = $text;
$mail->Subject = "[froxlor] " . lng('update.notify_subject');

View File

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

View File

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

View File

@@ -244,7 +244,7 @@ abstract class DnsBase
'zonefile' => '',
'froxlorhost' => '1'
];
$domains['none'] = $hostname_arr;
$domains[0] = $hostname_arr;
}
if (empty($domains)) {

View File

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

View File

@@ -25,19 +25,21 @@
namespace Froxlor\Cron\Http;
use Froxlor\Froxlor;
use Froxlor\Cron\Http\Php\PhpInterface;
use Froxlor\Cron\TaskId;
use Froxlor\Customer\Customer;
use Froxlor\Database\Database;
use Froxlor\Domain\Domain;
use Froxlor\FileDir;
use Froxlor\Froxlor;
use Froxlor\FroxlorLogger;
use Froxlor\Http\Directory;
use Froxlor\Http\Statistics;
use Froxlor\PhpHelper;
use Froxlor\Settings;
use Froxlor\Validate\Validate;
use Froxlor\System\Cronjob;
use Froxlor\System\Crypt;
use Froxlor\Validate\Validate;
use PDO;
class Apache extends HttpConfigBase
@@ -133,6 +135,7 @@ class Apache extends HttpConfigBase
if (Settings::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) {
$this->virtualhosts_data[$vhosts_filename] .= '# temp. disabled ssl-redirect due to Let\'s Encrypt certificate generation.' . PHP_EOL;
$is_redirect = false;
Cronjob::inserttask(TaskId::REBUILD_VHOST);
} else {
$_sslport = $this->checkAlternativeSslPort();
@@ -159,7 +162,7 @@ class Apache extends HttpConfigBase
if (Settings::Get('system.froxlordirectlyviahostname')) {
$relpath = "/";
} else {
$relpath = "/".basename(Froxlor::getInstallDir());
$relpath = "/" . basename(Froxlor::getInstallDir());
}
// protect lib/userdata.inc.php
$this->virtualhosts_data[$vhosts_filename] .= ' <Directory "' . rtrim($relpath, "/") . '/lib/">' . "\n";
@@ -205,7 +208,9 @@ class Apache extends HttpConfigBase
];
$php = new PhpInterface($domain);
$phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost'));
if ($phpconfig['pass_authorizationheader'] == '1') {
$this->virtualhosts_data[$vhosts_filename] .= ' FcgidPassHeader Authorization' . "\n";
}
$starter_filename = FileDir::makeCorrectFile($configdir . '/php-fcgi-starter');
$this->virtualhosts_data[$vhosts_filename] .= ' SuexecUserGroup "' . Settings::Get('system.mod_fcgid_httpuser') . '" "' . Settings::Get('system.mod_fcgid_httpgroup') . '"' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' <Directory "' . $mypath . '">' . "\n";
@@ -276,7 +281,9 @@ class Apache extends HttpConfigBase
// start block, cut off last pipe and close block
$filesmatch = '(' . str_replace(".", "\.", substr($filesmatch, 0, -1)) . ')';
$this->virtualhosts_data[$vhosts_filename] .= ' <FilesMatch \.' . $filesmatch . '$>' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' <If "-f %{SCRIPT_FILENAME}">' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' </If>' . "\n";
$this->virtualhosts_data[$vhosts_filename] .= ' </FilesMatch>' . "\n";
if ($phpconfig['pass_authorizationheader'] == '1') {
$this->virtualhosts_data[$vhosts_filename] .= ' <Directory "' . $mypath . '">' . "\n";
@@ -515,13 +522,7 @@ class Apache extends HttpConfigBase
*/
private function createStandardDirectoryEntry()
{
$vhosts_folder = '';
if (is_dir(Settings::Get('system.apacheconf_vhost'))) {
$vhosts_folder = FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'));
} else {
$vhosts_folder = FileDir::makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost')));
}
$vhosts_filename = FileDir::makeCorrectFile($vhosts_folder . '/05_froxlor_dirfix_nofcgid.conf');
$vhosts_filename = $this->getCustomVhostFilename('05_froxlor_dirfix_nofcgid.conf');
if (!isset($this->virtualhosts_data[$vhosts_filename])) {
$this->virtualhosts_data[$vhosts_filename] = '';
@@ -545,7 +546,7 @@ class Apache extends HttpConfigBase
}
$this->virtualhosts_data[$vhosts_filename] .= ' </Directory>' . "\n";
$ocsp_cache_filename = FileDir::makeCorrectFile($vhosts_folder . '/03_froxlor_ocsp_cache.conf');
$ocsp_cache_filename = $this->getCustomVhostFilename('03_froxlor_ocsp_cache.conf');
if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.apache24') == 1) {
$this->virtualhosts_data[$ocsp_cache_filename] = 'SSLStaplingCache ' . Settings::Get('system.apache24_ocsp_cache_path') . "\n";
} else {
@@ -562,14 +563,7 @@ class Apache extends HttpConfigBase
private function createStandardErrorHandler()
{
if (Settings::Get('defaultwebsrverrhandler.enabled') == '1' && (Settings::Get('defaultwebsrverrhandler.err401') != '' || Settings::Get('defaultwebsrverrhandler.err403') != '' || Settings::Get('defaultwebsrverrhandler.err404') != '' || Settings::Get('defaultwebsrverrhandler.err500') != '')) {
$vhosts_folder = '';
if (is_dir(Settings::Get('system.apacheconf_vhost'))) {
$vhosts_folder = FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'));
} else {
$vhosts_folder = FileDir::makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost')));
}
$vhosts_filename = FileDir::makeCorrectFile($vhosts_folder . '/05_froxlor_default_errorhandler.conf');
$vhosts_filename = $this->getCustomVhostFilename('05_froxlor_default_errorhandler.conf');
if (!isset($this->virtualhosts_data[$vhosts_filename])) {
$this->virtualhosts_data[$vhosts_filename] = '';
@@ -855,24 +849,26 @@ class Apache extends HttpConfigBase
}
$vhost_content .= $this->getLogfiles($domain);
if ($domain['specialsettings'] != '' && ($ssl_vhost == false || ($ssl_vhost == true && $domain['include_specialsettings'] == 1))) {
$vhost_content .= $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
}
if ($this->deactivated == false) {
if ($domain['specialsettings'] != '' && ($ssl_vhost == false || ($ssl_vhost == true && $domain['include_specialsettings'] == 1))) {
$vhost_content .= $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
}
if ($domain['ssl_specialsettings'] != '' && $ssl_vhost == true) {
$vhost_content .= $this->processSpecialConfigTemplate($domain['ssl_specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
}
if ($domain['ssl_specialsettings'] != '' && $ssl_vhost == true) {
$vhost_content .= $this->processSpecialConfigTemplate($domain['ssl_specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
}
if ($_vhost_content != '') {
$vhost_content .= $_vhost_content;
}
if ($_vhost_content != '') {
$vhost_content .= $_vhost_content;
}
if (Settings::Get('system.default_vhostconf') != '' && ($ssl_vhost == false || ($ssl_vhost == true && Settings::Get('system.include_default_vhostconf') == 1))) {
$vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
}
if (Settings::Get('system.default_vhostconf') != '' && ($ssl_vhost == false || ($ssl_vhost == true && Settings::Get('system.include_default_vhostconf') == 1))) {
$vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
}
if (Settings::Get('system.default_sslvhostconf') != '' && $ssl_vhost == true) {
$vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_sslvhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
if (Settings::Get('system.default_sslvhostconf') != '' && $ssl_vhost == true) {
$vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_sslvhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n";
}
}
}
@@ -977,8 +973,8 @@ class Apache extends HttpConfigBase
if ($domain['openbasedir'] == '1') {
if ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], ":") !== false) {
$_phpappendopenbasedir = Domain::appendOpenBasedirPath($domain['customerroot'], true);
} else if ($domain['openbasedir_path'] == '2' && strpos(dirname($domain['documentroot']).'/', $domain['customerroot']) !== false) {
$_phpappendopenbasedir = Domain::appendOpenBasedirPath(dirname($domain['documentroot']).'/', true);
} else if ($domain['openbasedir_path'] == '2' && strpos(dirname($domain['documentroot']) . '/', $domain['customerroot']) !== false) {
$_phpappendopenbasedir = Domain::appendOpenBasedirPath(dirname($domain['documentroot']) . '/', true);
} else {
$_phpappendopenbasedir = Domain::appendOpenBasedirPath($domain['documentroot'], true);
}
@@ -1026,10 +1022,10 @@ class Apache extends HttpConfigBase
}
$statDocroot = FileDir::makeCorrectFile($domain['customerroot'] . '/' . $statTool . $statDomain);
$stats_text .= ' Alias /'.$statTool.' "' . $statDocroot . '"' . "\n";
$stats_text .= ' Alias /' . $statTool . ' "' . $statDocroot . '"' . "\n";
// awstats special requirement for icons
if ($statTool == 'awstats') {
$stats_text .= ' Alias /awstats-icon "' . FileDir::makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n";
$stats_text .= ' Alias /awstats-icon "' . FileDir::makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n";
}
return $stats_text;

View File

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

View File

@@ -202,4 +202,13 @@ class HttpConfigBase
}
return FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $filename);
}
protected function getCustomVhostFilename(string $name)
{
$vhosts_folder = FileDir::makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost')));
if (is_dir(Settings::Get('system.apacheconf_vhost'))) {
$vhosts_folder = FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'));
}
return FileDir::makeCorrectFile($vhosts_folder . '/' . $name);
}
}

View File

@@ -26,6 +26,7 @@
namespace Froxlor\Cron\Http\LetsEncrypt;
use Froxlor\Cron\FroxlorCron;
use Froxlor\Cron\TaskId;
use Froxlor\Database\Database;
use Froxlor\Domain\Domain;
use Froxlor\FileDir;
@@ -83,7 +84,7 @@ class AcmeSh extends FroxlorCron
$renew_domains = self::renewDomains(true);
if ($issue_froxlor || !empty($issue_domains) || !empty($renew_froxlor) || $renew_domains) {
// insert task to generate certificates and vhost-configs
Cronjob::inserttask(1);
Cronjob::inserttask(TaskId::REBUILD_VHOST);
}
return 0;
}
@@ -203,7 +204,7 @@ class AcmeSh extends FroxlorCron
// This is easiest done by just creating a new task ;)
if ($changedetected) {
if (self::$no_inserttask == false) {
Cronjob::inserttask(1);
Cronjob::inserttask(TaskId::REBUILD_VHOST);
}
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Let's Encrypt certificates have been updated");
} else {
@@ -520,6 +521,8 @@ EOC;
self::runAcmeSh($certrow, $domains, $cronlog, $do_force);
} else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect");
// we need another reconfigure in order to get the certificate
Cronjob::inserttask(TaskId::REBUILD_VHOST);
}
}
}

View File

@@ -1161,14 +1161,7 @@ class Nginx extends HttpConfigBase
private function createStandardErrorHandler()
{
if (Settings::Get('defaultwebsrverrhandler.enabled') == '1' && (Settings::Get('defaultwebsrverrhandler.err401') != '' || Settings::Get('defaultwebsrverrhandler.err403') != '' || Settings::Get('defaultwebsrverrhandler.err404') != '' || Settings::Get('defaultwebsrverrhandler.err500') != '')) {
$vhosts_folder = '';
if (is_dir(Settings::Get('system.apacheconf_vhost'))) {
$vhosts_folder = FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'));
} else {
$vhosts_folder = FileDir::makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost')));
}
$vhosts_filename = FileDir::makeCorrectFile($vhosts_folder . '/05_froxlor_default_errorhandler.conf');
$vhosts_filename = $this->getCustomVhostFilename('05_froxlor_default_errorhandler.conf');
if (!isset($this->nginx_data[$vhosts_filename])) {
$this->nginx_data[$vhosts_filename] = '';

View File

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

View File

@@ -187,7 +187,8 @@ class CurrentUser
if (self::getField('type_2fa') == 1) {
// generate code
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
$code = $tfa->getCode($tfa->createSecret());
$secret = $tfa->createSecret();
$code = $tfa->getCode($secret);
// set code for user
$table = TABLE_PANEL_CUSTOMERS;
$uid = 'customerid';
@@ -197,7 +198,7 @@ class CurrentUser
}
$stmt = Database::prepare("UPDATE $table SET `data_2fa` = :d2fa WHERE `$uid` = :uid");
Database::pexecute($stmt, [
"d2fa" => $code,
"d2fa" => $secret,
"uid" => self::getField($uid)
]);
// build up & send email

View File

@@ -256,7 +256,7 @@ class Domain
]);
$result = [];
while ($entry = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result = $entry['id'];
$result[] = $entry['id'];
}
return $result;
}
@@ -320,12 +320,15 @@ class Domain
* @throws \Exception
*/
public static function triggerLetsEncryptCSRForAliasDestinationDomain(
int $aliasDestinationDomainID,
int $aliasDestinationDomainID,
FroxlorLogger $log
) {
if ($aliasDestinationDomainID > 0) {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO,
"LetsEncrypt CSR triggered for domain ID " . $aliasDestinationDomainID);
$log->logAction(
FroxlorLogger::ADM_ACTION,
LOG_INFO,
"LetsEncrypt CSR triggered for domain ID " . $aliasDestinationDomainID
);
$upd_stmt = Database::prepare("UPDATE
`" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
SET
@@ -349,15 +352,20 @@ class Domain
$acmesh = AcmeSh::getAcmeSh();
if (file_exists($acmesh)) {
$certificate_folder = AcmeSh::getWorkingDirFromEnv($domainname);
if (file_exists($certificate_folder)) {
$certificate_ecc_folder = AcmeSh::getWorkingDirFromEnv($domainname, true);
if (file_exists($certificate_folder) || file_exists($certificate_ecc_folder)) {
$params = " --remove -d " . $domainname;
if (Settings::Get('system.leecc') > 0) {
if (file_exists($certificate_ecc_folder)) {
$params .= " --ecc";
}
// run remove command
FileDir::safe_exec($acmesh . $params);
// remove certificates directory
FileDir::safe_exec('rm -rf ' . $certificate_folder);
if (file_exists($certificate_folder)) {
FileDir::safe_exec('rm -rf ' . $certificate_folder);
} elseif (file_exists($certificate_ecc_folder)) {
FileDir::safe_exec('rm -rf ' . $certificate_ecc_folder);
}
}
}
return true;

View File

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

View File

@@ -140,6 +140,12 @@ class FileDir
if (is_link($check_dir)) {
$original_target = $check_dir;
$check_dir = readlink($check_dir);
$link_dir = dirname($original_target);
// check whether the link is relative or absolute
if (substr($check_dir, 0, 1) != '/') {
// relative directory, prepend link_dir
$check_dir = $link_dir . '/' . $check_dir;
}
if (substr($check_dir, 0, strlen($fixed_homedir)) != $fixed_homedir) {
throw new Exception("Found symlink pointing outside of customer home directory: " . substr($original_target, strlen($fixed_homedir)));
}
@@ -257,6 +263,41 @@ class FileDir
return $return;
}
/**
* Read unconfigured-domain template from database if exists or fallback to default
*
* @param string $servername
*
* @return string
* @throws Exception
*/
public static function getUnknownDomainTemplate(string $servername = "")
{
$result_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_TEMPLATES . "` WHERE `templategroup` = 'files' AND `varname` = 'unconfigured_html'
");
Database::pexecute($result_stmt);
if (Database::num_rows() > 0) {
$template = $result_stmt->fetch(PDO::FETCH_ASSOC);
$replace_arr = [
'SERVERNAME' => $servername,
];
$tpl_content = PhpHelper::replaceVariables($template['value'], $replace_arr);
$tpl_ext = $template['file_extension'];
} else {
$tpl_ext = 'html';
$unconfiguredPath = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/templates/misc/unconfigured/index.html');
if (file_exists($unconfiguredPath)) {
$tpl_content = file_get_contents($unconfiguredPath);
} else {
$tpl_content = lng('admin.templates.unconfigured_content_fallback');
}
}
$redirect_file = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/notice.' . $tpl_ext);
file_put_contents($redirect_file, $tpl_content);
return basename($redirect_file);
}
/**
* store the default index-file in a given destination folder
*
@@ -277,7 +318,7 @@ class FileDir
{
if ($force || (int)Settings::Get('system.store_index_file_subs') == 1) {
$result_stmt = Database::prepare("
SELECT `t`.`value`, `c`.`email` AS `customer_email`, `a`.`email` AS `admin_email`, `c`.`loginname` AS `customer_login`, `a`.`loginname` AS `admin_login`
SELECT `t`.`value`, `t`.`file_extension`, `c`.`email` AS `customer_email`, `a`.`email` AS `admin_email`, `c`.`loginname` AS `customer_login`, `a`.`loginname` AS `admin_login`
FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c` INNER JOIN `" . TABLE_PANEL_ADMINS . "` AS `a`
ON `c`.`adminid` = `a`.`adminid`
INNER JOIN `" . TABLE_PANEL_TEMPLATES . "` AS `t`
@@ -300,7 +341,7 @@ class FileDir
// replaceVariables
$htmlcontent = PhpHelper::replaceVariables($template['value'], $replace_arr);
$indexhtmlpath = self::makeCorrectFile($destination . '/index.' . Settings::Get('system.index_file_extension'));
$indexhtmlpath = self::makeCorrectFile($destination . '/index.' . $template['file_extension']);
$index_html_handler = fopen($indexhtmlpath, 'w');
fwrite($index_html_handler, $htmlcontent);
fclose($index_html_handler);
@@ -308,7 +349,7 @@ class FileDir
$logger->logAction(
FroxlorLogger::CRON_ACTION,
LOG_NOTICE,
'Creating \'index.' . Settings::Get('system.index_file_extension') . '\' for Customer \'' . $template['customer_login'] . '\' based on template in directory ' . escapeshellarg($indexhtmlpath)
'Creating \'index.' . $template['file_extension'] . '\' for Customer \'' . $template['customer_login'] . '\' based on template in directory ' . escapeshellarg($indexhtmlpath)
);
}
} else {

View File

@@ -31,14 +31,16 @@ final class Froxlor
{
// Main version variable
const VERSION = '2.1.0-beta1';
const VERSION = '2.1.7';
// Database version (YYYYMMDDC where C is a daily counter)
const DBVERSION = '202305240';
const DBVERSION = '202312120';
// Distribution branding-tag (used for Debian etc.)
const BRANDING = '';
const DOCS_URL = 'https://docs.froxlor.org/v2.1/';
/**
* return path to where froxlor is installed, e.g.
* /var/www/froxlor/

View File

@@ -104,17 +104,15 @@ class FroxlorLogger
self::$ml->pushHandler(new SyslogHandler('froxlor', LOG_USER, Logger::DEBUG));
break;
case 'file':
$setings_logfile = Settings::Get('logger.logfile');
if (empty($setings_logfile)) {
Settings::Set('logger.logfile', 'froxlor.log');
}
$logger_logfile = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/logs/' . Settings::Get('logger.logfile'));
// is_writable needs an existing file to check if it's actually writable
@touch($logger_logfile);
if (empty($logger_logfile) || !is_writable($logger_logfile)) {
Settings::Set('logger.logfile', 'froxlor.log');
$logger_logfile = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/logs/froxlor.log');
@touch($logger_logfile);
if (empty($logger_logfile) || !is_writable($logger_logfile)) {
// not writable in our own directory? Skip
break;
}
if (!@touch($logger_logfile) || !is_writable($logger_logfile)) {
// not writable in our own directory? Skip
break;
}
self::$ml->pushHandler(new StreamHandler($logger_logfile, Logger::DEBUG));
break;

View File

@@ -64,7 +64,7 @@ class IdnaWrapper
*/
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 : mb_convert_encoding($to_encode, 'UTF-8');
try {
return $this->idna_converter->encode($to_encode);
} catch (InvalidArgumentException $iae) {

View File

@@ -51,13 +51,13 @@ class AutoUpdate
/**
* returns status about whether there is a newer version
*
*
* 0 = no new version available
* 1 = new version available
* -1 = remote error message
* >1 = local error message
*
* @return int
* @return int
*/
public static function checkVersion(): int
{
@@ -68,6 +68,12 @@ class AutoUpdate
$channel = '';
if (Settings::Get('system.update_channel') == 'testing') {
$channel = '/testing';
} elseif (Settings::Get('system.update_channel') == 'nightly') {
if (empty(Froxlor::BRANDING) || substr(Froxlor::BRANDING, 0, 1) == '-') {
$channel = '/nightly.0000000';
} else {
$channel = '/' . substr(Froxlor::BRANDING, 1);
}
}
$latestversion = HttpClient::urlGet(self::UPDATE_URI . Froxlor::VERSION . $channel, true, 3);
} catch (Exception $e) {
@@ -81,7 +87,7 @@ class AutoUpdate
if (!empty(self::$latestversion['error']) && self::$latestversion['error']) {
$result = -1;
self::$lasterror = self::$latestversion['message'];
} else if (isset(self::$latestversion['has_latest']) && self::$latestversion['has_latest'] == false) {
} elseif (isset(self::$latestversion['has_latest']) && self::$latestversion['has_latest'] == false) {
$result = 1;
}
}
@@ -145,6 +151,8 @@ class AutoUpdate
$zip->close();
// success - remove unused archive
@unlink($localArchive);
// reset cached version check
Settings::Set('system.updatecheck_data', '');
// wait a bit before we redirect to be sure
sleep(3);
return 0;

View File

@@ -26,13 +26,14 @@
namespace Froxlor\Install;
use Exception;
use PDO;
use Froxlor\Config\ConfigParser;
use Froxlor\Froxlor;
use Froxlor\Install\Install\Core;
use Froxlor\System\IPTools;
use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\Config\ConfigParser;
use Froxlor\Validate\Validate;
use Froxlor\System\IPTools;
use PDO;
class Install
{
@@ -41,34 +42,37 @@ class Install
public $maxSteps;
public $phpVersion;
public $formfield;
public string $requiredVersion = '7.4.0';
public array $requiredExtensions = ['session', 'ctype', 'xml', 'filter', 'posix', 'mbstring', 'curl', 'gmp', 'json', 'gd'];
public array $suggestedExtensions = ['bcmath', 'zip', 'gnupg'];
public array $suggestions = [];
public array $criticals = [];
public array $loadedExtensions;
public array $supportedOS = [];
public array $webserverBackend = [
'php-fpm' => 'PHP-FPM',
'fcgid' => 'FCGID',
'fcgid' => 'FCGID (apache2 only)',
'mod_php' => 'mod_php (not recommended)',
];
public function __construct(array $cliData = [])
{
// set actual php version and extensions
$this->phpVersion = phpversion();
$this->loadedExtensions = get_loaded_extensions();
// get all supported OS
// show list of available distro's
$distros = glob(dirname(__DIR__, 3) . '/lib/configfiles/*.xml');
$distributions_select[''] = '-';
// read in all the distros
foreach ($distros as $distribution) {
// get configparser object
$dist = new ConfigParser($distribution);
// store in tmp array
$this->supportedOS[str_replace(".xml", "", strtolower(basename($distribution)))] = $dist->getCompleteDistroName();
if (in_array('xml', $this->loadedExtensions)) {
// read in all the distros
foreach ($distros as $distribution) {
// get configparser object
$dist = new ConfigParser($distribution);
// store in tmp array
$this->supportedOS[str_replace(".xml", "", strtolower(basename($distribution)))] = $dist->getCompleteDistroName();
}
// sort by distribution name
asort($this->supportedOS);
}
// sort by distribution name
asort($this->supportedOS);
// guess distribution and webserver to preselect in formfield
$webserverBackend = $this->webserverBackend;
@@ -84,10 +88,6 @@ class Install
$this->extendedView = $cliData['extended'] ?? Request::any('extended', 0);
$this->maxSteps = count($this->formfield['install']['sections']);
// set actual php version and extensions
$this->phpVersion = phpversion();
$this->loadedExtensions = get_loaded_extensions();
if (empty($cliData)) {
// set global variables
UI::twig()->addGlobal('install_mode', true);
@@ -99,7 +99,7 @@ class Install
}
// check for url manipulation or wrong step
if ((isset($_SESSION['installation']['stepCompleted']) && ($this->currentStep + 1) > ($_SESSION['installation']['stepCompleted'] ?? 0))
if ((isset($_SESSION['installation']['stepCompleted']) && $this->currentStep > $_SESSION['installation']['stepCompleted'])
|| (!isset($_SESSION['installation']['stepCompleted']) && $this->currentStep > 0)
) {
$this->currentStep = isset($_SESSION['installation']['stepCompleted']) ? $_SESSION['installation']['stepCompleted'] + 1 : 1;
@@ -136,6 +136,7 @@ class Install
'section' => $this->formfield['install']['sections']['step' . $this->currentStep] ?? [],
'error' => $error ?? null,
'extended' => $this->extendedView,
'csrf_token' => Froxlor::genSessionId(20),
]);
// output view
@@ -151,16 +152,14 @@ class Install
if ($this->currentStep <= $this->maxSteps) {
// Validate user data
$validatedData = $this->validateRequest($formfield['sections']['step' . $this->currentStep]['fields']);
// Check database connection (
if ($this->currentStep == 1) {
// Check database connection
$this->checkDatabase($validatedData);
}
// Check validity of admin user data
elseif ($this->currentStep == 2) {
} elseif ($this->currentStep == 2) {
// Check validity of admin user data
$this->checkAdminUser($validatedData);
}
// Check validity of system data
elseif ($this->currentStep == 3) {
} elseif ($this->currentStep == 3) {
// Check validity of system data
$this->checkSystem($validatedData);
}
$validatedData['stepCompleted'] = ($this->currentStep < $this->maxSteps) ? $this->currentStep : ($this->maxSteps - 1);
@@ -192,7 +191,7 @@ class Install
private function checkInstallStateFinished(): bool
{
$core = new Core($_SESSION['installation']);
if (isset($_SESSION['installation']['manual_config']) && (int) $_SESSION['installation']['manual_config'] == 1) {
if (isset($_SESSION['installation']['manual_config']) && (int)$_SESSION['installation']['manual_config'] == 1) {
$core->createUserdataConf();
return true;
}
@@ -200,7 +199,7 @@ class Install
$stmt = $pdo->prepare("SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'panel' AND `varname` = 'is_configured'");
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result && (int) $result['value'] == 1) {
if ($result && (int)$result['value'] == 1) {
$core->createUserdataConf();
return true;
}
@@ -223,7 +222,7 @@ class Install
}
// check for required extensions
foreach ($this->requiredExtensions as $requiredExtension) {
foreach (Requirements::REQUIRED_EXTENSIONS as $requiredExtension) {
if (in_array($requiredExtension, $this->loadedExtensions)) {
continue;
}
@@ -231,7 +230,7 @@ class Install
}
// check for suggested extensions
foreach ($this->suggestedExtensions as $suggestedExtension) {
foreach (Requirements::SUGGESTED_EXTENSIONS as $suggestedExtension) {
if (in_array($suggestedExtension, $this->loadedExtensions)) {
continue;
}
@@ -250,11 +249,11 @@ class Install
*/
private function getInformationText(): string
{
if (version_compare($this->requiredVersion, PHP_VERSION, "<")) {
if (version_compare(Requirements::REQUIRED_VERSION, PHP_VERSION, "<")) {
$text = lng('install.phpinfosuccess', [$this->phpVersion]);
} else {
$text = lng('install.phpinfowarn', [$this->requiredVersion]);
$this->criticals[] = lng('install.phpinfoupdate', [$this->phpVersion, $this->requiredVersion]);
$text = lng('install.phpinfowarn', [Requirements::REQUIRED_VERSION]);
$this->criticals[] = lng('install.phpinfoupdate', [$this->phpVersion, Requirements::REQUIRED_VERSION]);
}
return $text;
}
@@ -302,9 +301,9 @@ class Install
throw new Exception(lng('install.errors.nov4andnov6ip'));
} elseif (!empty($serveripv4) && (!Validate::validate_ip2($serveripv4, true, '', false, true) || IPTools::is_ipv6($serveripv4))) {
throw new Exception(lng('error.invalidip', [$serveripv4]));
} elseif (!empty($serveripv6) && (!Validate::validate_ip2($serveripv6, true, '', false, true) || IPTools::is_ipv6($serveripv6) == false)) {
} elseif (!empty($serveripv6) && (!Validate::validate_ip2($serveripv6, true, '', false, true) || !IPTools::is_ipv6($serveripv6))) {
throw new Exception(lng('error.invalidip', [$serveripv6]));
} elseif (!Validate::validateDomain($servername) && !Validate::validateLocalHostname($servername)) {
} elseif (!Validate::validateDomain($servername)) {
throw new Exception(lng('install.errors.servernameneedstobevalid'));
} elseif (posix_getpwnam($httpuser) === false) {
throw new Exception(lng('install.errors.websrvuserdoesnotexist'));
@@ -323,6 +322,8 @@ class Install
$email = $validatedData['admin_email'] ?? '';
$password = $validatedData['admin_pass'] ?? '';
$password_confirm = $validatedData['admin_pass_confirm'] ?? '';
$useadminmailassender = $validatedData['use_admin_email_as_sender'] ?? '1';
$senderemail = $validatedData['sender_email'] ?? '';
if (!preg_match('/^[^\r\n\t\f\0]*$/D', $name)) {
throw new Exception(lng('error.stringformaterror', ['admin_name']));
@@ -330,6 +331,8 @@ class Install
throw new Exception(lng('error.loginnameiswrong', [$loginname]));
} elseif (empty(trim($email)) || !Validate::validateEmail($email)) {
throw new Exception(lng('error.emailiswrong', [$email]));
} elseif ((int)$useadminmailassender == 0 && !empty(trim($senderemail)) && !Validate::validateEmail($senderemail)) {
throw new Exception(lng('error.emailiswrong', [$senderemail]));
} elseif (empty($password) || $password != $password_confirm) {
throw new Exception(lng('error.newpasswordconfirmerror'));
} elseif ($password == $loginname) {
@@ -410,7 +413,7 @@ class Install
} else {
$osrf = explode("\n", file_get_contents('/etc/os-release'));
foreach ($osrf as $line) {
$osrfline = explode("\n", $line);
$osrfline = explode("=", $line);
if ($osrfline[0] == 'VERSION_CODENAME') {
$os_dist['VERSION_CODENAME'] = $osrfline[1];
} else if ($osrfline[0] == 'ID') {

View File

@@ -301,8 +301,8 @@ class Core
/* continue */
}
}
if (version_compare($db_root->getAttribute(PDO::ATTR_SERVER_VERSION), '10.0.0', '>=')) {
// mariadb compatibility
if (version_compare($db_root->getAttribute(PDO::ATTR_SERVER_VERSION), '8.0.11', '>=')) {
// mariadb & mysql8
// create user
$stmt = $db_root->prepare("CREATE USER '" . $username . "'@'" . $access_host . "' IDENTIFIED BY :password");
$stmt->execute([
@@ -314,19 +314,6 @@ class Core
"username" => $username,
"host" => $access_host
]);
} elseif (version_compare($db_root->getAttribute(PDO::ATTR_SERVER_VERSION), '8.0.11', '>=')) {
// mysql8 compatibility
// create user
$stmt = $db_root->prepare("CREATE USER '" . $username . "'@'" . $access_host . "' IDENTIFIED WITH mysql_native_password BY :password");
$stmt->execute([
"password" => $password
]);
// grant privileges
$stmt = $db_root->prepare("GRANT ALL ON `" . $database . "`.* TO :username@:host");
$stmt->execute([
"username" => $username,
"host" => $access_host
]);
} else {
// grant privileges
$stmt = $db_root->prepare("GRANT ALL PRIVILEGES ON `" . $database . "`.* TO :username@:host IDENTIFIED BY :password");
@@ -378,7 +365,14 @@ class Core
$mainip = !empty($this->validatedData['serveripv6']) ? $this->validatedData['serveripv6'] : $this->validatedData['serveripv4'];
$this->updateSetting($upd_stmt, 'admin@' . $this->validatedData['servername'], 'panel', 'adminmail');
if ($this->validatedData['use_admin_email_as_sender'] == '1') {
$adminmail_value = $this->validatedData['admin_email'];
} elseif ($this->validatedData['use_admin_email_as_sender'] == '0' && !empty($this->validatedData['sender_email'])) {
$adminmail_value = $this->validatedData['sender_email'];
} else {
$adminmail_value = 'admin@' . $this->validatedData['servername'];
}
$this->updateSetting($upd_stmt, $adminmail_value, 'panel', 'adminmail');
$this->updateSetting($upd_stmt, $mainip, 'system', 'ipaddress');
if ($this->validatedData['use_ssl']) {
$this->updateSetting($upd_stmt, 1, 'system', 'use_ssl');
@@ -576,7 +570,7 @@ class Core
'password' => password_hash($this->validatedData['admin_pass'], PASSWORD_DEFAULT),
'adminname' => $this->validatedData['admin_name'],
'email' => $this->validatedData['admin_email'],
'deflang' => 'en' // TODO: set lanuage
'deflang' => 'en' // TODO: set language
];
$ins_stmt = $db_user->prepare("
INSERT INTO `" . TABLE_PANEL_ADMINS . "` SET

View File

@@ -0,0 +1,10 @@
<?php
namespace Froxlor\Install;
class Requirements
{
const REQUIRED_VERSION = '7.4.0';
const REQUIRED_EXTENSIONS = ['session', 'ctype', 'xml', 'filter', 'posix', 'mbstring', 'pdo_mysql', 'curl', 'gmp', 'json', 'gd'];
const SUGGESTED_EXTENSIONS = ['bcmath', 'zip', 'gnupg'];
}

View File

@@ -85,7 +85,7 @@ class Update
self::$update_tasks[self::$task_counter]['result'] = 1;
break;
default:
self::$update_tasks[self::$task_counter]['result'] = -1;
self::$update_tasks[self::$task_counter]['result'] = -1;
break;
}

View File

@@ -220,8 +220,11 @@ class PhpHelper
if (is_dir($data_dirname)) {
$data_dirhandle = opendir($data_dirname);
while (false !== ($data_filename = readdir($data_dirhandle))) {
if ($data_filename != '.' && $data_filename != '..' && $data_filename != '' && substr($data_filename,
-4) == '.php') {
if ($data_filename != '.'
&& $data_filename != '..'
&& $data_filename != ''
&& substr($data_filename, -4) == '.php'
) {
$data_files[] = $data_dirname . $data_filename;
}
}
@@ -415,8 +418,8 @@ class PhpHelper
*/
public static function recursive_array_search(
string $needle,
array $haystack,
array &$keys = [],
array $haystack,
array &$keys = [],
string $currentKey = ''
): bool {
foreach ($haystack as $key => $value) {
@@ -458,6 +461,11 @@ class PhpHelper
'directory_password',
'ftp_password',
'mysql_password',
'mysql_root_pass',
'mysql_unprivileged_pass',
'admin_pass',
'admin_pass_confirm',
'panel_password_special_char',
];
if (!empty($global)) {
$tmp = $global;
@@ -557,4 +565,17 @@ class PhpHelper
}
return $tab . $str;
}
public static function array_merge_recursive_distinct(array &$array1, array &$array2)
{
$merged = $array1;
foreach ($array2 as $key => &$value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = self::array_merge_recursive_distinct($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
}

View File

@@ -65,7 +65,7 @@ class SImExporter
public static function export()
{
$settings_definitions = [];
foreach (PhpHelper::loadConfigArrayDir('./actions/admin/settings/')['groups'] as $group) {
foreach (PhpHelper::loadConfigArrayDir(Froxlor::getInstallDir() . '/actions/admin/settings/')['groups'] as $group) {
foreach ($group['fields'] as $field) {
$settings_definitions[$field['settinggroup']][$field['varname']] = $field;
}

View File

@@ -102,6 +102,14 @@ class Traffic
$years_avail = $sel_stmt->fetchAll(\PDO::FETCH_ASSOC);
}
// sort users by total traffic
uasort($users, function ($user_a, $user_b) {
if ($user_a['total'] == $user_b['total']) {
return 0;
}
return ($user_a['total'] < $user_b['total']) ? 1 : -1;
});
return [
'metrics' => $metrics,
'users' => $users,

View File

@@ -25,6 +25,7 @@
namespace Froxlor\UI\Callbacks;
use Froxlor\CurrentUser;
use Froxlor\Database\Database;
use Froxlor\Domain\Domain as DDomain;
use Froxlor\FileDir;
@@ -33,6 +34,11 @@ use Froxlor\UI\Panel\UI;
class Domain
{
public static function domainLink(array $attributes)
{
return '<a href="https://' . $attributes['data'] . '" target="_blank">' . $attributes['data'] . '</a>';
}
public static function domainWithCustomerLink(array $attributes)
{
$linker = UI::getLinker();
@@ -76,7 +82,7 @@ class Domain
return lng('domains.aliasdomain') . ' ' . $attributes['fields']['aliasdomain'];
}
public static function domainExternalLinkInfo(array $attributes)
public static function domainExternalLinkInfo(array $attributes): string
{
$result = '';
if ($attributes['fields']['parentdomainid'] != 0) {
@@ -84,7 +90,11 @@ class Domain
}
$result .= '<a href="http://' . $attributes['data'] . '" target="_blank">' . $attributes['data'] . '</a>';
// check for statistics if parentdomainid==0 to show stats-link for customers
if ((int)UI::getCurrentUser()['adminsession'] == 0 && $attributes['fields']['parentdomainid'] == 0 && $attributes['fields']['deactivated'] == 0) {
if ((int)UI::getCurrentUser()['adminsession'] == 0
&& $attributes['fields']['parentdomainid'] == 0
&& $attributes['fields']['deactivated'] == 0
&& preg_match('/^https?:\/\/(.*)/i', $attributes['fields']['documentroot']) == false
) {
$statsapp = Settings::Get('system.traffictool');
$result .= ' <a href="http://' . $attributes['data'] . '/' . $statsapp . '" rel="external" target="_blank" title="' . lng('domains.statstics') . '"><i class="fa-solid fa-chart-line text-secondary"></i></a>';
}
@@ -104,7 +114,7 @@ class Domain
public static function canViewLogs(array $attributes): bool
{
if ((int)$attributes['fields']['email_only'] == 0 && !$attributes['fields']['deactivated']) {
if ((!CurrentUser::isAdmin() || (CurrentUser::isAdmin() && (int)$attributes['fields']['email_only'] == 0)) && !$attributes['fields']['deactivated']) {
if ((int)UI::getCurrentUser()['adminsession'] == 0 && (bool)UI::getCurrentUser()['logviewenabled']) {
return true;
} elseif ((int)UI::getCurrentUser()['adminsession'] == 1) {
@@ -146,7 +156,7 @@ class Domain
public static function hasLetsEncryptActivated(array $attributes): bool
{
return ((bool)$attributes['fields']['letsencrypt'] && (int)$attributes['fields']['email_only'] == 0);
return ((bool)$attributes['fields']['letsencrypt'] && (!CurrentUser::isAdmin() || (CurrentUser::isAdmin() && (int)$attributes['fields']['email_only'] == 0)));
}
public static function canEditSSL(array $attributes): bool
@@ -156,7 +166,7 @@ class Domain
&& DDomain::domainHasSslIpPort($attributes['fields']['id'])
&& (int)$attributes['fields']['caneditdomain'] == 1
&& (int)$attributes['fields']['letsencrypt'] == 0
&& (int)$attributes['fields']['email_only'] == 0
&& (!CurrentUser::isAdmin() || (CurrentUser::isAdmin() && (int)$attributes['fields']['email_only'] == 0))
&& !$attributes['fields']['deactivated']
) {
return true;

View File

@@ -25,23 +25,24 @@
namespace Froxlor\UI\Callbacks;
use Froxlor\CurrentUser;
use Froxlor\Settings;
class Style
{
public static function deactivated(array $attributes): string
{
return $attributes['fields']['deactivated'] ? 'bg-danger' : '';
return $attributes['fields']['deactivated'] ? 'table-danger' : '';
}
public static function loginDisabled(array $attributes): string
{
return $attributes['fields']['login_enabled'] == 'N' ? 'bg-danger' : '';
return $attributes['fields']['login_enabled'] == 'N' ? 'table-danger' : '';
}
public static function resultIntegrityBad(array $attributes): string
{
return $attributes['fields']['result'] ? '' : 'bg-warning';
return $attributes['fields']['result'] ? '' : 'table-warning';
}
public static function invalidApiKey(array $attributes): string
@@ -53,7 +54,7 @@ class Style
$isValid = false;
}
}
return $isValid ? '' : 'bg-danger';
return $isValid ? '' : 'table-danger';
}
public static function resultDomainTerminatedOrDeactivated(array $attributes): string
@@ -63,25 +64,24 @@ class Style
if (!empty($termination_date)) {
$cdate = strtotime($termination_date . " 23:59:59");
$today = time();
$termination_css = 'bg-warning';
$termination_css = 'table-warning';
if ($cdate < $today) {
$termination_css = 'bg-danger text-light';
$termination_css = 'table-danger';
}
}
$deactivated = $attributes['fields']['deactivated'] || $attributes['fields']['customer_deactivated'];
return $deactivated ? 'bg-info text-light' : $termination_css;
$deactivated = $attributes['fields']['deactivated'] || (CurrentUser::isAdmin() && $attributes['fields']['customer_deactivated']);
return $deactivated ? 'table-info' : $termination_css;
}
public static function resultCustomerLockedOrDeactivated(array $attributes): string
{
$row_css = '';
if ((int)$attributes['fields']['deactivated'] == 1) {
$row_css = 'bg-info text-light';
} elseif (
$attributes['fields']['loginfail_count'] >= Settings::Get('login.maxloginattempts')
$row_css = 'table-info';
} elseif ($attributes['fields']['loginfail_count'] >= Settings::Get('login.maxloginattempts')
&& $attributes['fields']['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))
) {
$row_css = 'bg-warning';
$row_css = 'table-warning';
}
return $row_css;
@@ -97,9 +97,9 @@ class Style
$style = '';
if ((int)$attributes[$field] >= 0) {
if (($attributes[$field] / 100) * $report_max < $attributes[$field . '_used']) {
$style = 'bg-danger';
$style = 'table-danger';
} elseif (($attributes[$field] / 100) * ($report_max - 15) < $attributes[$field . '_used']) {
$style = 'bg-warning';
$style = 'table-warning';
}
}
return $style;

View File

@@ -90,9 +90,10 @@ class Text
public static function customerNoteDetailModal(array $attributes): array
{
$note = $attributes['fields']['custom_notes'] ?? '';
$key = $attributes['fields']['customerid'] ?? $attributes['fields']['adminid'];
return [
'entry' => $attributes['fields']['id'],
'id' => 'cnModal' . $attributes['fields']['id'],
'entry' => $key,
'id' => 'cnModal' . $key,
'title' => lng('usersettings.custom_notes.title') . ': ' . ($attributes['fields']['loginname'] ?? $attributes['fields']['adminname']),
'body' => nl2br(Markdown::cleanCustomNotes($note))
];

View File

@@ -193,10 +193,14 @@ class Form
if (!$do_show) {
$fielddata['note'] = lng('serversettings.option_requires_otp');
if (!$otp_enabled_system) {
$fielddata['disabled'] = true;
$fielddata['note'] .= '<br>' . lng('2fa.2fa_not_activated');
} elseif (!$otp_enabled_user) {
$fielddata['disabled'] = true;
$fielddata['note'] .= '<br>' . lng('2fa.2fa_not_activated_for_user');
}
// show field in any case
$do_show = true;
}
}
@@ -213,7 +217,8 @@ class Form
{
$returnvalue = [];
if (is_array($fielddata) && isset($fielddata['type']) && $fielddata['type'] == 'select') {
if ((!isset($fielddata['select_var']) || !is_array($fielddata['select_var']) || empty($fielddata['select_var'])) && (isset($fielddata['option_options_method']))) {
if ((empty($fielddata['select_var']) || !is_array($fielddata['select_var'])) && (isset($fielddata['option_options_method']))
) {
$returnvalue['select_var'] = call_user_func($fielddata['option_options_method']);
}
}
@@ -232,8 +237,8 @@ class Form
if (\Froxlor\Validate\Form::validateFieldDefinition($groupdetails)) {
// Prefetch form fields
foreach ($groupdetails['fields'] as $fieldname => $fielddetails) {
if (!$only_enabledisable || ($only_enabledisable && isset($fielddetails['overview_option']))) {
$groupdetails['fields'][$fieldname] = self::arrayMergePrefix($fielddetails, $fielddetails['type'], self::prefetchFormFieldData($fieldname, $fielddetails));
if (!$only_enabledisable || isset($fielddetails['overview_option'])) {
$groupdetails['fields'][$fieldname] = array_merge($fielddetails, self::prefetchFormFieldData($fieldname, $fielddetails));
$form['groups'][$groupname]['fields'][$fieldname] = $groupdetails['fields'][$fieldname];
}
}
@@ -343,7 +348,7 @@ class Form
if (\Froxlor\Validate\Form::validateFieldDefinition($groupdetails)) {
// Save fields
foreach ($groupdetails['fields'] as $fieldname => $fielddetails) {
if (!$only_enabledisable || ($only_enabledisable && isset($fielddetails['overview_option']))) {
if (!$only_enabledisable || (isset($fielddetails['overview_option']))) {
if (isset($changed_fields[$fieldname])) {
if (($saved_field = self::saveFormField($fieldname, $fielddetails, self::manipulateFormFieldData($fieldname, $fielddetails, $changed_fields[$fieldname]))) !== false) {
$saved_fields = array_merge($saved_fields, $saved_field);
@@ -360,24 +365,7 @@ class Form
// Save form
return self::saveForm($form, $saved_fields);
}
}
private static function arrayMergePrefix($array1, $key_prefix, $array2)
{
if (is_array($array1) && is_array($array2)) {
if ($key_prefix != '') {
foreach ($array2 as $key => $value) {
$array1[$key_prefix . '_' . $key] = $value;
unset($array2[$key]);
}
unset($array2);
return $array1;
} else {
return array_merge($array1, $array2);
}
} else {
return $array1;
}
return false;
}
public static function getFormFieldData($fieldname, $fielddata, &$input)

View File

@@ -92,6 +92,10 @@ class FroxlorTwig extends AbstractExtension
new TwigFunction('mix', [
$this,
'getMix'
]),
new TwigFunction('vite', [
$this,
'getVite'
])
];
}
@@ -167,4 +171,9 @@ class FroxlorTwig extends AbstractExtension
{
return mix($mix);
}
public function getVite($basehref = '', $vite = [], $defaults = [])
{
return vite($basehref, $vite ?? $defaults);
}
}

View File

@@ -142,8 +142,6 @@ class UI
header("X-Content-Security-Policy: " . $csp_content);
header("X-WebKit-CSP: " . $csp_content);
header("X-XSS-Protection: 1; mode=block");
// Don't allow to load Froxlor in an iframe to prevent i.e. clickjacking
header("X-Frame-Options: DENY");
@@ -323,6 +321,10 @@ class UI
}
}
}
// check for template-variant
if (preg_match("/([a-z0-9.\-]+)_([a-z0-9.\-]+)/i", $theme, $matches)) {
$theme = $matches[1];
}
if (!file_exists(Froxlor::getInstallDir() . '/templates/' . $theme)) {
PhpHelper::phpErrHandler(E_USER_WARNING, "Theme '" . $theme . "' could not be found.", __FILE__, __LINE__);
$theme = self::$default_theme;

View File

@@ -35,6 +35,11 @@ class Data
return self::validateFormFieldString($fieldname, $fielddata, $newfieldvalue);
}
public static function validateFormFieldPassword($fieldname, $fielddata, $newfieldvalue)
{
return self::validateFormFieldString($fieldname, $fielddata, $newfieldvalue);
}
public static function validateFormFieldString($fieldname, $fielddata, $newfieldvalue)
{
if (isset($fielddata['string_delimiter']) && $fielddata['string_delimiter'] != '') {
@@ -210,75 +215,6 @@ class Data
}
}
public static function validateFormFieldHiddenString($fieldname, $fielddata, $newfieldvalue)
{
if (isset($fielddata['string_delimiter']) && $fielddata['string_delimiter'] != '') {
$newfieldvalues = explode($fielddata['string_delimiter'], $newfieldvalue);
unset($fielddata['string_delimiter']);
$returnvalue = true;
foreach ($newfieldvalues as $single_newfieldvalue) {
/**
* don't use tabs in value-fields, #81
*/
$single_newfieldvalue = str_replace("\t", " ", $single_newfieldvalue);
$single_returnvalue = Data::validateFormFieldString($fieldname, $fielddata, $single_newfieldvalue);
if ($single_returnvalue !== true) {
$returnvalue = $single_returnvalue;
break;
}
}
} else {
$returnvalue = false;
/**
* don't use tabs in value-fields, #81
*/
$newfieldvalue = str_replace("\t", " ", $newfieldvalue);
if (isset($fielddata['string_type']) && $fielddata['string_type'] == 'mail') {
$returnvalue = Validate::validateEmail($newfieldvalue);
} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'url') {
$returnvalue = Validate::validateUrl($newfieldvalue);
} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'dir') {
// add trailing slash to validate path if needed
// refs #331
if (substr($newfieldvalue, -1) != '/') {
$newfieldvalue .= '/';
}
$returnvalue = ($newfieldvalue == FileDir::makeCorrectDir($newfieldvalue));
} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'file') {
$returnvalue = ($newfieldvalue == FileDir::makeCorrectFile($newfieldvalue));
} elseif (isset($fielddata['string_type']) && $fielddata['string_type'] == 'filedir') {
$returnvalue = (($newfieldvalue == FileDir::makeCorrectDir($newfieldvalue)) || ($newfieldvalue == FileDir::makeCorrectFile($newfieldvalue)));
} elseif (preg_match('/^[^\r\n\t\f\0]*$/D', $newfieldvalue)) {
$returnvalue = true;
}
if (isset($fielddata['string_regexp']) && $fielddata['string_regexp'] != '') {
if (preg_match($fielddata['string_regexp'], $newfieldvalue)) {
$returnvalue = true;
} else {
$returnvalue = false;
}
}
if (isset($fielddata['string_emptyallowed']) && $fielddata['string_emptyallowed'] === true && $newfieldvalue === '') {
$returnvalue = true;
} elseif (isset($fielddata['string_emptyallowed']) && $fielddata['string_emptyallowed'] === false && $newfieldvalue === '') {
$returnvalue = 'stringmustntbeempty';
}
}
if ($returnvalue === true) {
return true;
} elseif ($returnvalue === false) {
return 'stringformaterror';
} else {
return $returnvalue;
}
}
public static function validateFormFieldNumber($fieldname, $fielddata, $newfieldvalue)
{
if (isset($fielddata['min']) && (int)$newfieldvalue < (int)$fielddata['min']) {
@@ -323,10 +259,16 @@ class Data
if (preg_match($fielddata['string_regexp'], $newfieldvalue)) {
$returnvalue = true;
}
} else if (preg_match('/^[^\0]*$/', $newfieldvalue)) {
} elseif (preg_match('/^[^\0]*$/', $newfieldvalue)) {
$returnvalue = true;
}
return $returnvalue;
}
public static function validateFormFieldImage($fieldname, $fielddata, $newfieldvalue)
{
// validation is handled in \Froxlor\Settings\Store::storeSettingImage()
return true;
}
}

View File

@@ -176,7 +176,7 @@ class Validate
}
// special case where localhost ip is allowed (mysql-access-hosts for example)
if ($allow_localhost && $ip == '127.0.0.1') {
if ($allow_localhost && ($ip == '127.0.0.1' || $ip == '::1')) {
return $ip . $cidr;
}
@@ -224,7 +224,7 @@ class Validate
* Check if the submitted string is a valid domainname
*
* @param string $domainname The domainname which should be checked.
* @param bool $allow_underscore optional if true, allowes the underscore character in a domain label (DKIM etc.)
* @param bool $allow_underscore optional if true, allows the underscore character in a domain label (DKIM etc.)
*
* @return string|boolean the domain-name if the domain is valid, false otherwise
*/

View File

@@ -9,11 +9,18 @@ return [
* recommended value for debian/ubuntu package users is false to rely on apt and not have version mixup.
* This is also useful for providers that manage the servers but give admin access to froxlor to handle
* updates the way the providers does it (e.g. automation, etc.)
*
* Default: false
*/
'enable_webupdate' => false,
/**
* @todo description
* settings that have a major impact on the system or which values are used to be executed with high
* privileges on the system require the admin-user to have set up and enabled OTP for the corresponding
* account to change these values.
* To disable this extra security validation, set the value of this to true
*
* Default: false
*/
'disable_otp_security_check' => false,
];

File diff suppressed because it is too large Load Diff

View File

@@ -92,7 +92,7 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}
<command><![CDATA[{{settings.system.apachereload_command}}]]></command>
</daemon>
<!-- HTTP Lighttpd -->
<daemon name="lighttpd" title="LigHTTPd">
<daemon name="lighttpd" title="LigHTTPd (deprecated)">
<install><![CDATA[apt-get install lighttpd]]></install>
<file name="/etc/lighttpd/lighttpd.conf">
<content><![CDATA[
@@ -1556,7 +1556,7 @@ noc: root
security: root
# change this to a valid e-mail address you can access
root: root@<SERVERNAME>
root: <ADMIN_MAIL>
]]>
</content>
</file>
@@ -2547,6 +2547,7 @@ plugin {
</file>
</files>
<commands index="1">
<command><![CDATA[sed -i.bak 's/^!include auth-system.conf.ext/#!include auth-system.conf.ext/' /etc/dovecot/conf.d/10-auth.conf]]></command>
<command><![CDATA[service dovecot restart]]></command>
</commands>
</general>
@@ -3383,6 +3384,11 @@ aliases: files
</visibility>
<command><![CDATA[a2dismod php8.2]]></command>
</commands>
<commands index="5">
<visibility mode="equals" value="apache2">{{settings.system.webserver}}
</visibility>
<command><![CDATA[a2disconf php8.2-fpm]]></command>
</commands>
<!-- instead of just restarting apache, we let the cronjob do all the
dirty work -->
<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>

View File

@@ -92,7 +92,7 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}
<command><![CDATA[{{settings.system.apachereload_command}}]]></command>
</daemon>
<!-- HTTP Lighttpd -->
<daemon name="lighttpd" title="LigHTTPd">
<daemon name="lighttpd" title="LigHTTPd (deprecated)">
<install><![CDATA[apt-get install lighttpd]]></install>
<file name="/etc/lighttpd/lighttpd.conf">
<content><![CDATA[
@@ -1556,7 +1556,7 @@ noc: root
security: root
# change this to a valid e-mail address you can access
root: root@<SERVERNAME>
root: <ADMIN_MAIL>
]]>
</content>
</file>

File diff suppressed because it is too large Load Diff

View File

@@ -91,7 +91,7 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}
<command><![CDATA[{{settings.system.apachereload_command}}]]></command>
</daemon>
<!-- HTTP Lighttpd -->
<daemon name="lighttpd" title="LigHTTPd">
<daemon name="lighttpd" title="LigHTTPd (deprecated)">
<install><![CDATA[apt-get install lighttpd]]></install>
<file name="/etc/lighttpd/lighttpd.conf">
<content><![CDATA[
@@ -1585,7 +1585,7 @@ noc: root
security: root
# change this to a valid e-mail address you can access
root: root@<SERVERNAME>
root: <ADMIN_MAIL>
]]>
</content>
</file>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<froxlor>
<distribution name="Gentoo" version="3.0"
defaulteditor="/usr/bin/nano">
defaulteditor="/usr/bin/nano" deprecated="true">
<!-- OS defaults to be loaded on installation -->
<defaults>
<default settinggroup="system" varname="nssextrausers" value="1"></default>
@@ -100,7 +100,7 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}
<command><![CDATA[{{settings.system.apachereload_command}}]]></command>
</daemon>
<!-- HTTP Lighttpd -->
<daemon name="lighttpd" title="LigHTTPd">
<daemon name="lighttpd" title="LigHTTPd (deprecated)">
<install><![CDATA[emerge www-servers/lighttpd]]></install>
<file name="/etc/lighttpd/lighttpd.conf">
<content><![CDATA[
@@ -1541,7 +1541,7 @@ noc: root
security: root
# change this to a valid e-mail address you can access
root: root@<SERVERNAME>
root: <ADMIN_MAIL>
]]>
</content>
</file>

View File

@@ -91,7 +91,7 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}
<command><![CDATA[{{settings.system.apachereload_command}}]]></command>
</daemon>
<!-- HTTP Lighttpd -->
<daemon name="lighttpd" title="LigHTTPd">
<daemon name="lighttpd" title="LigHTTPd (deprecated)">
<install><![CDATA[apt-get install lighttpd]]></install>
<file name="/etc/lighttpd/lighttpd.conf">
<content><![CDATA[
@@ -1585,7 +1585,7 @@ noc: root
security: root
# change this to a valid e-mail address you can access
root: root@<SERVERNAME>
root: <ADMIN_MAIL>
]]>
</content>
</file>
@@ -4172,6 +4172,11 @@ aliases: files
</visibility>
<command><![CDATA[a2dismod php8.1]]></command>
</commands>
<commands index="5">
<visibility mode="equals" value="apache2">{{settings.system.webserver}}
</visibility>
<command><![CDATA[a2disconf php8.1-fpm]]></command>
</commands>
<!-- instead of just restarting apache, we let the cronjob do all the
dirty work -->
<command><![CDATA[php {{const.install_dir}}bin/froxlor-cli froxlor:cron --force]]></command>

View File

@@ -59,7 +59,7 @@ return [
'label' => lng('login.password'),
'type' => 'password',
'autocomplete' => 'off',
'placeholder' => lng('admin.username_default_msg'),
'placeholder' => lng('admin.password_default_msg'),
'next_to' => [
'new_customer_password_suggestion' => [
'next_to_prefix' => lng('customer.generated_pwd') . ':',

View File

@@ -103,7 +103,7 @@ return [
'maxlength' => 10,
'value' => '5s'
],
'phpfpm_pass_authorizationheader' => [
'pass_authorizationheader' => [
'visible' => Settings::Get('system.webserver') == "apache2",
'label' => lng('admin.phpsettings.pass_authorizationheader'),
'type' => 'checkbox',

View File

@@ -106,7 +106,7 @@ return [
'maxlength' => 10,
'value' => $result['fpm_reqslow']
],
'phpfpm_pass_authorizationheader' => [
'pass_authorizationheader' => [
'visible' => Settings::Get('system.webserver') == "apache2",
'label' => lng('admin.phpsettings.pass_authorizationheader'),
'type' => 'checkbox',

View File

@@ -38,11 +38,20 @@ return [
'type' => 'select',
'select_var' => $free_templates
],
'file_extension' => [
'label' => lng('admin.templates.file_extension'),
'type' => 'text',
'string_regexp' => '/^[a-zA-Z0-9]{1,6}$/',
'default' => 'html',
'value' => 'html',
'mandatory' => true
],
'filecontent' => [
'label' => lng('admin.templates.filecontent'),
'type' => 'textarea',
'cols' => 60,
'rows' => 12
'rows' => 12,
'mandatory' => true
],
'filesend' => [
'type' => 'hidden',

View File

@@ -39,12 +39,21 @@ return [
'value' => lng('admin.templates.' . $row['varname']),
'display' => lng('admin.templates.' . $row['varname'])
],
'file_extension' => [
'label' => lng('admin.templates.file_extension'),
'type' => 'text',
'string_regexp' => '/^[a-zA-Z0-9]{1,6}$/',
'value' => $row['file_extension'],
'default' => 'html',
'mandatory' => true
],
'filecontent' => [
'label' => lng('admin.templates.filecontent'),
'type' => 'textarea',
'cols' => 60,
'rows' => 12,
'value' => $row['value']
'value' => $row['value'],
'mandatory' => true
],
'filesend' => [
'type' => 'hidden',

View File

@@ -55,13 +55,16 @@ return [
'label' => lng('login.password'),
'type' => 'password',
'autocomplete' => 'off',
'mandatory' => true
],
'directory_password_suggestion' => [
'label' => lng('customer.generated_pwd'),
'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword()
'mandatory' => true,
'next_to' => [
'directory_password_suggestion' => [
'next_to_prefix' => lng('customer.generated_pwd') . ':',
'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword(),
'readonly' => true
]
]
],
'directory_authname' => [
'label' => lng('extras.htpasswdauthname'),

View File

@@ -49,13 +49,16 @@ return [
'directory_password' => [
'label' => lng('login.password'),
'type' => 'password',
'autocomplete' => 'off'
],
'directory_password_suggestion' => [
'label' => lng('customer.generated_pwd'),
'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword()
'autocomplete' => 'off',
'next_to' => [
'directory_password_suggestion' => [
'next_to_prefix' => lng('customer.generated_pwd') . ':',
'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword(),
'readonly' => true
]
]
],
'directory_authname' => [
'label' => lng('extras.htpasswdauthname'),

View File

@@ -46,14 +46,16 @@ return [
'label' => lng('login.password'),
'type' => 'password',
'autocomplete' => 'off',
'mandatory' => true
],
'mysql_password_suggestion' => [
'label' => lng('customer.generated_pwd'),
'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword(),
'readonly' => true
'mandatory' => true,
'next_to' => [
'mysql_password_suggestion' => [
'next_to_prefix' => lng('customer.generated_pwd') . ':',
'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword(),
'readonly' => true
]
]
],
'sendinfomail' => [
'label' => lng('customer.sendinfomail'),

View File

@@ -52,14 +52,16 @@ return [
'mysql_password' => [
'label' => lng('changepassword.new_password_ifnotempty'),
'type' => 'password',
'autocomplete' => 'off'
],
'mysql_password_suggestion' => [
'label' => lng('customer.generated_pwd'),
'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword(),
'readonly' => true
'autocomplete' => 'off',
'next_to' => [
'mysql_password_suggestion' => [
'next_to_prefix' => lng('customer.generated_pwd') . ':',
'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword(),
'readonly' => true
]
]
]
]
]

View File

@@ -44,7 +44,8 @@ return [
'cols' => 100,
'rows' => 15,
'value' => $result['ssl_cert_file'],
'placeholder' => lng('domain.ssl_certificate_placeholder')
'placeholder' => lng('domain.ssl_certificate_placeholder'),
'mandatory' => true
],
'ssl_key_file' => [
'label' => lng('admin.ipsandports.ssl_key_file_content'),
@@ -53,7 +54,8 @@ return [
'cols' => 100,
'rows' => 15,
'value' => $result['ssl_key_file'],
'placeholder' => lng('domain.ssl_key_placeholder')
'placeholder' => lng('domain.ssl_key_placeholder'),
'mandatory' => true
],
'ssl_cert_chainfile' => [
'label' => lng('admin.ipsandports.ssl_cert_chainfile_content'),

View File

@@ -143,6 +143,18 @@ return [
'mandatory' => true,
'value' => old('admin_email', null, 'installation'),
],
'use_admin_email_as_sender' => [
'label' => lng('install.admin.use_admin_email_as_sender'),
'type' => 'checkbox',
'value' => '1',
'checked' => old('use_admin_email_as_sender', '1', 'installation'),
],
'sender_email' => [
'label' => lng('serversettings.adminmail.title'),
'placeholder' => lng('install.admin.use_autogenerated_email_as_sender'),
'type' => 'email',
'value' => old('sender_email', null, 'installation'),
],
]
],
'step3' => [

View File

@@ -74,6 +74,7 @@ function old(string $identifier, string $default = null, string $session = null)
* This file contains the hashed filenames of the assets.
* It must be always placed in the theme assets folder.
*
* @deprecated since 2.1.x no longer in use
* @param $filename
* @return mixed|string
*/
@@ -91,3 +92,50 @@ function mix($filename)
}
return $filename;
}
/**
* Loading the vite manifest file from given theme.
* This file contains the hashed filenames of the assets.
* It must be always placed in the theme assets folder.
*
* @param string|null $basehref
* @param array $filenames
* @return string
* @throws Exception
*/
function vite($basehref, array $filenames): string
{
// Get the hashed filenames from the manifest file
$links = [];
foreach ($filenames as $filename) {
if (preg_match("/templates\/([^\/]+)(.*)/", $filename, $matches)) {
$assetDirectory = '/templates/' . $matches[1] . '/build/';
$viteManifest = dirname(__DIR__) . $assetDirectory . '/manifest.json';
$manifest = json_decode(file_get_contents($viteManifest), true);
if (!empty($manifest[$filename]['file'])) {
$links[] = $basehref . ltrim($assetDirectory, '/') . $manifest[$filename]['file'];
} else {
// additional asset from config.json that was not prebuilt on release (e.g. custom.css)
$links[] = $filename;
}
} else {
$links[] = $filename;
}
}
// Update the links to the correct html tags
foreach ($links as $key => $link) {
switch (pathinfo($link, PATHINFO_EXTENSION)) {
case 'css':
$links[$key] = '<link rel="stylesheet" href="'. $link . '">';
break;
case 'js':
$links[$key] = '<script src="' . $link . '" type="module"></script>';
break;
default:
throw new Exception('Unknown file extension for file '. $link .' from manifest.json');
}
}
return implode("\n", $links);
}

View File

@@ -50,10 +50,12 @@ if (!file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
require dirname(__DIR__) . '/vendor/autoload.php';
use Froxlor\CurrentUser;
use Froxlor\FileDir;
use Froxlor\Froxlor;
use Froxlor\FroxlorLogger;
use Froxlor\Http\RateLimiter;
use Froxlor\Idna\IdnaWrapper;
use Froxlor\Install\Update;
use Froxlor\Language;
use Froxlor\PhpHelper;
use Froxlor\Settings;
@@ -63,7 +65,7 @@ use Froxlor\UI\Linker;
use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\UI\Response;
use Froxlor\Install\Update;
use Froxlor\Install\Requirements;
// include MySQL-tabledefinitions
require Froxlor::getInstallDir() . '/lib/tables.inc.php';
@@ -110,6 +112,40 @@ if (!isset($sql) || !is_array($sql)) {
die();
}
/**
* Show nice note if requested domain is "unknown" to froxlor and thus is being lead to its vhost
*/
$req_host = UI::getCookieHost();
if ($req_host != Settings::Get('system.hostname') &&
Settings::Get('panel.is_configured') == 1 &&
!filter_var($req_host, FILTER_VALIDATE_IP) && (
empty(Settings::Get('system.froxloraliases')) ||
(!empty(Settings::Get('system.froxloraliases')) && !in_array($req_host, array_map('trim', explode(',', Settings::Get('system.froxloraliases')))))
)) {
// not the froxlor system-hostname, show info page for domains not configured in froxlor
$redirect_file = FileDir::getUnknownDomainTemplate($req_host ?? "non-detectable http-host");
header('Location: '.$redirect_file);
die();
}
// validate php-extensions requirements
$loadedExtensions = get_loaded_extensions();
$missingExtensions = [];
foreach (Requirements::REQUIRED_EXTENSIONS as $requiredExtension) {
if (in_array($requiredExtension, $loadedExtensions)) {
continue;
}
$missingExtensions[] = $requiredExtension;
}
if (!empty($missingExtensions)) {
UI::twig()->addGlobal('install_mode', '1');
echo UI::twig()->render($_deftheme . '/misc/missingextensionhint.html.twig', [
'phpversion' => phpversion(),
'missing_extensions' => implode(", ", $missingExtensions),
]);
die();
}
// set error-handler
@set_error_handler([
'\\Froxlor\\PhpHelper',
@@ -168,7 +204,7 @@ if (Update::versionInUpdate(Settings::Get('panel.version'), '2.0.0-beta1')) {
// Check if a different variant of the theme is used
$themevariant = "default";
if (preg_match("/([a-z0-9\.\-]+)_([a-z0-9\.\-]+)/i", $theme, $matches)) {
if (preg_match("/([a-z0-9.\-]+)_([a-z0-9.\-]+)/i", $theme, $matches)) {
$theme = $matches[1];
$themevariant = $matches[2];
}
@@ -182,12 +218,16 @@ if (@file_exists('templates/' . $theme . '/config.json')) {
// check for existence of variant in theme
if (is_array($_themeoptions) && (!array_key_exists('variants', $_themeoptions) || !array_key_exists(
$themevariant,
$_themeoptions['variants']
))) {
$themevariant,
$_themeoptions['variants']
))) {
$themevariant = "default";
}
if (array_key_exists('global', $_themeoptions)) {
$_themeoptions['variants'][$themevariant] = PhpHelper::array_merge_recursive_distinct($_themeoptions['global'], $_themeoptions['variants'][$themevariant]);
}
// check for custom header-graphic
$hl_path = 'templates/' . $theme . '/assets/img';
@@ -209,8 +249,11 @@ if (Settings::Get('panel.logo_overridecustom') == 0 && file_exists($hl_path . '/
}
}
$color_scheme = $_themeoptions['variants'][$themevariant]['color-scheme'] ?? 'auto';
UI::twig()->addGlobal('header_logo_login', $header_logo_login);
UI::twig()->addGlobal('header_logo', $header_logo);
UI::twig()->addGlobal('color_scheme', $color_scheme);
/**
* Redirects to index.php (login page) if no session exists
@@ -235,7 +278,7 @@ if (CurrentUser::hasSession()) {
$log = FroxlorLogger::getInstanceOf($userinfo);
if ((CurrentUser::isAdmin() && AREA != 'admin') || (!CurrentUser::isAdmin() && AREA != 'customer')) {
// user tries to access an area not meant for him -> redirect to corresponding index
Response::redirectTo((CurrentUser::isAdmin() ? 'admin' : 'customer') . '_index.php', $params);
Response::redirectTo((CurrentUser::isAdmin() ? 'admin' : 'customer') . '_index.php');
exit();
}
}
@@ -274,29 +317,21 @@ if (AREA == 'admin' || AREA == 'customer') {
}
UI::twig()->addGlobal('nav_entries', $navigation);
$js = "";
$css = "";
if (is_array($_themeoptions) && array_key_exists('js', $_themeoptions['variants'][$themevariant])) {
if (is_array($_themeoptions['variants'][$themevariant]['js'])) {
foreach ($_themeoptions['variants'][$themevariant]['js'] as $jsfile) {
if (file_exists('templates/' . $theme . '/assets/js/' . $jsfile)) {
$js .= '<script type="text/javascript" src="' . mix('templates/' . $theme . '/assets/js/' . $jsfile) . '"></script>' . "\n";
}
}
}
if (is_array($_themeoptions['variants'][$themevariant]['css'])) {
foreach ($_themeoptions['variants'][$themevariant]['css'] as $cssfile) {
if (file_exists('templates/' . $theme . '/assets/css/' . $cssfile)) {
$css .= '<link href="' . mix('templates/' . $theme . '/assets/css/' . $cssfile) . '" rel="stylesheet" type="text/css" />' . "\n";
$theme_assets = [];
foreach (['css', 'js'] as $asset) {
if (is_array($_themeoptions) && array_key_exists($asset, $_themeoptions['variants'][$themevariant])) {
if (is_array($_themeoptions['variants'][$themevariant][$asset])) {
foreach ($_themeoptions['variants'][$themevariant][$asset] as $assetfile) {
if (file_exists('templates/' . $theme . '/' . $assetfile)) {
$theme_assets[] .= 'templates/' . $theme . '/' . $assetfile;
}
}
}
}
}
UI::twig()->addGlobal('theme_js', $js);
UI::twig()->addGlobal('theme_css', $css);
unset($js);
unset($css);
UI::twig()->addGlobal('theme_assets', $theme_assets);
unset($theme_assets);
$action = Request::any('action');
$page = Request::any('page', 'overview');
@@ -328,6 +363,7 @@ if (CurrentUser::hasSession()) {
if (in_array($_SERVER['REQUEST_METHOD'], ['POST', 'PUT', 'PATCH', 'DELETE'])) {
$current_token = $_POST['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? null;
if ($current_token != CurrentUser::getField('csrf_token')) {
http_response_code(403);
Response::dynamicError('CSRF validation failed');
}
}
@@ -341,4 +377,6 @@ if (CurrentUser::hasSession()) {
'samesite' => 'Strict'
];
setcookie(session_name(), $_COOKIE[session_name()], $cookie_params);
} else {
UI::twig()->addGlobal('csrf_token', Froxlor::genSessionId(20));
}

View File

@@ -161,13 +161,13 @@ return [
'show_element' => (!Settings::IsInList('panel.customer_hide_options', 'misc.documentation')),
'elements' => [
[
'url' => 'https://docs.froxlor.org/v2/user-guide/',
'url' => \Froxlor\Froxlor::DOCS_URL . 'user-guide/',
'label' => lng('admin.userguide'),
'new_window' => true,
'is_external' => true,
],
[
'url' => 'https://docs.froxlor.org/v2/api-guide/',
'url' => \Froxlor\Froxlor::DOCS_URL . 'api-guide/',
'label' => lng('admin.apiguide'),
'new_window' => true,
'show_element' => Settings::Get('api.enabled') == 1 && CurrentUser::getField('api_allowed') == 1,
@@ -348,13 +348,13 @@ return [
'icon' => 'fa-solid fa-circle-info',
'elements' => [
[
'url' => 'https://docs.froxlor.org/v2/admin-guide/',
'url' => \Froxlor\Froxlor::DOCS_URL . 'admin-guide/',
'label' => lng('admin.adminguide'),
'new_window' => true,
'is_external' => true,
],
[
'url' => 'https://docs.froxlor.org/v2/api-guide/',
'url' => \Froxlor\Froxlor::DOCS_URL . 'api-guide/',
'label' => lng('admin.apiguide'),
'new_window' => true,
'show_element' => Settings::Get('api.enabled') == 1,

View File

@@ -50,6 +50,7 @@ return [
'label' => lng('domains.domainname'),
'field' => 'domain_ace',
'isdefaultsearchfield' => true,
'callback' => [Domain::class, 'domainLink'],
],
'ipsandports' => [
'label' => lng('admin.ipsandports.ipsandports'),

View File

@@ -49,6 +49,7 @@ return [
'field' => 'domains',
'callback' => [PHPConf::class, 'domainList'],
'searchable' => false,
'sortable' => false,
],
'fpmdesc' => [
'label' => lng('admin.phpsettings.fpmdesc'),

View File

@@ -2324,7 +2324,7 @@ Atentament, el vostre administrador'
]
],
'install' => [
'slogal' => 'Panell de gestió del servidor froxlor',
'slogan' => 'Panell de gestió del servidor froxlor',
'preflight' => 'Comprovació del sistema',
'critical_error' => 'Error crític',
'suggestions' => 'No requerit però recomanat',

View File

@@ -67,6 +67,7 @@ return [
'customer_add' => 'Kunden anlegen',
'customer_edit' => 'Kunden bearbeiten',
'username_default_msg' => 'Leer lassen für automatische Benutzername-Vergabe',
'password_default_msg' => 'Leer lassen für Passwortgenerierung',
'domains' => 'Domains',
'domain_add' => 'Domain anlegen',
'domain_edit' => 'Domain bearbeiten',
@@ -140,12 +141,18 @@ return [
'TRAFFICUSED' => 'Wird mit Traffic, der vom Kunden bereits verbraucht wurde, ersetzt.',
'pop_success_alternative' => 'Willkommensmail für neue E-Mail-Konten für die alternative E-Mail-Adresse',
'EMAIL_PASSWORD' => 'Wird mit dem Passwort des neuen POP3/IMAP Kontos ersetzt.',
'index_html' => 'index.html Datei für neu erzeugte Kundenverzeichnisse',
'index_html' => 'index Datei für neu erzeugte Kundenverzeichnisse',
'unconfigured_html' => 'index Datei für unkonfigurierte/unbekannte Domains',
'unconfigured_content_fallback' => 'Diese Domain muss über das Froxlor-Serververwaltungspanel konfiguriert werden, da sie derzeit keinem Kunden zugewiesen ist.',
'file_extension' => [
'description' => 'Die Dateiendung für die index Datei muss zwischen 1 und 6 Zeichen lang sein und darf nur aus den Zeichen a-z, A-Z und 0-9 bestehen<br><br>Standard: html',
'title' => 'Dateiendung für Datei Vorlage',
],
'SERVERNAME' => 'Wird mit dem Servernamen ersetzt.',
'CUSTOMER' => 'Wird mit dem Loginnamen des Kunden ersetzt.',
'ADMIN' => 'Wird mit dem Loginnamen des Admins ersetzt.',
'CUSTOMER_EMAIL' => 'Wird mit der E-Mail-Adresse des Kunden ersetzt.',
'ADMIN_EMAIL' => 'Wird mit der E-Mail-Adresse des Admin ersetzt.',
'CUSTOMER' => 'Wird mit dem Loginnamen des Kunden ersetzt. Nur für "index Datei für neu erzeugte Kundenverzeichnisse".',
'ADMIN' => 'Wird mit dem Loginnamen des Admins ersetzt. Nur für "index Datei für neu erzeugte Kundenverzeichnisse".',
'CUSTOMER_EMAIL' => 'Wird mit der E-Mail-Adresse des Kunden ersetzt. Nur für "index Datei für neu erzeugte Kundenverzeichnisse".',
'ADMIN_EMAIL' => 'Wird mit der E-Mail-Adresse des Admin ersetzt. Nur für "index Datei für neu erzeugte Kundenverzeichnisse".',
'filetemplates' => 'Dateivorlagen',
'filecontent' => 'Dateiinhalt',
'new_database_by_customer' => 'Kunden-Benachrichtigungs nach Erstellung einer neuen Datenbank',
@@ -831,7 +838,6 @@ return [
'descriptioninvalid' => 'Der Beschreibungstext ist zu kurz, zu lang oder enthält ungültige Zeichen',
'info' => 'Info',
'filecontentnotset' => 'Diese Datei darf nicht leer sein!',
'index_file_extension' => 'Die Dateiendung für die index Datei muss zwischen 1 und 6 Zeichen lang sein und darf nur aus den Zeichen a-z, A-Z und 0-9 bestehen',
'customerdoesntexist' => 'Der ausgewählte Kunde existiert nicht.',
'admindoesntexist' => 'Der ausgewählte Admin existiert nicht.',
'ipportdoesntexist' => 'Die gewählte IP/Port-Kombination existiert nicht.',
@@ -920,6 +926,7 @@ return [
'dns_duplicate_entry' => 'Eintrag existiert bereits',
'dns_notfoundorallowed' => 'Domain nicht gefunden oder keine Berechtigung',
'domain_nopunycode' => 'Die Eingabe von Punycode (IDNA) ist nicht notwendig. Die Domain wird automatisch konvertiert.',
'domain_noipaddress' => 'Eine IP-Adresse kann nicht als Domain angelegt werden',
'dns_record_toolong' => 'Records/Labels können maximal 63 Zeichen lang sein',
'noipportgiven' => 'Keine IP/Port angegeben',
'nosslippportgiven' => 'Wenn SSL aktiviert ist, muss eine SSL IP/Port angegeben werden',
@@ -1308,6 +1315,8 @@ Vielen Dank, Ihr Administrator',
'dnsentry_reallydelete' => 'Wollen Sie den DNS-Eintrag wirklich löschen?',
'certificate_reallydelete' => 'Wollen Sie diese Zertifikat wirklich löschen?',
'cache_reallydelete' => 'Wollen Sie den Cache wirklich leeren?',
'please_enter_otp' => 'Bitte 2FA Code eingeben',
'admin_mysqlserver_reallydelete' => 'Wollen Sie wirklich diesen MySQL-Server löschen?',
],
'serversettings' => [
'session_timeout' => [
@@ -1608,10 +1617,6 @@ Vielen Dank, Ihr Administrator',
'removelink' => 'Hier klicken, um alle E-Mail-Kontingente zu entfernen',
'enforcelink' => 'Hier klicken, um allen Benutzern das Standard-Kontingent zu zuweisen.',
],
'index_file_extension' => [
'description' => 'Welche Dateiendung soll die index Datei in neu erstellten Kundenverzeichnissen haben? Diese Dateiendung wird dann verwendet, wenn Sie bzw. einer Ihrer Admins eigene index Dateivorlagen erstellt haben.',
'title' => 'Dateiendung für index Datei in neu erstellen Kundenverzeichnissen',
],
'session_allow_multiple_login' => [
'title' => 'Erlaube gleichzeitigen Login',
'description' => 'Wenn diese Option aktiviert ist, können sich Nutzer mehrmals gleichzeitig anmelden.',
@@ -2240,7 +2245,7 @@ Vielen Dank, Ihr Administrator',
'database' => [
'top' => 'Datenbank',
'title' => 'Datenbank und Benutzer erstellen',
'description' => 'Froxlor benötigt eine Datenbank und zusätzlich einen Benutzer mit privilegierten Rechten, welcher Benutzer und Datenbanken erstellen darf (GRANT Option). Die angegebene Datenbank und der unprivilegierte Benutzer werden automatisch in diesem Prozess erstellt. Der privilegierte Benutzer muss existieren.',
'description' => 'Froxlor benötigt eine Datenbank und zusätzlich <a href="https://docs.froxlor.org/latest/general/installation/tarball.html#_3-create-privileged-database-user" target="_blank">einen Benutzer mit privilegierten Rechten</a>, welcher Benutzer und Datenbanken erstellen darf (GRANT Option). Die angegebene Datenbank und der unprivilegierte Benutzer werden automatisch in diesem Prozess erstellt. Der privilegierte Benutzer muss existieren.',
'user' => 'Unprivilegierter Datenbank Benutzer',
'dbname' => 'Datenbank Name',
'force_create' => 'Sichern und überschreiben, sofern Datenbank existiert?',
@@ -2249,6 +2254,8 @@ Vielen Dank, Ihr Administrator',
'top' => 'Admin Konto',
'title' => 'Erstellen des Haupt-Administrators.',
'description' => 'Dieser Benutzer erhält alle Berechtigungen zur Anpassungen von Einstellungen und Erstellen/Bearbeiten/Löschen von Resourcen wie Kunden, Domains, etc.',
'use_admin_email_as_sender' => 'Verwende die oben angegebene E-Mail-Adresse als Absenderadresse. Wenn die Option deaktiviert ist, geben Sie unten bitte eine Absenderadresse an.',
'use_autogenerated_email_as_sender' => 'Leer lassen für Standard: admin@servername',
],
'system' => [
'top' => 'System Setup',
@@ -2263,7 +2270,7 @@ Vielen Dank, Ihr Administrator',
'install' => [
'top' => 'Abschluss',
'title' => 'Ein letzter Schritt...',
'description' => 'Der untenstehende Befehl lädt, installiert und konfiguriert die benötigten Dienste auf dem System aufgrund der Angaben die während des Installationsprozessen gesammelt wurden.<br><br><span class="text-danger">Führe die gezeigten Befehle als <b>root</b> in der Shell/Konsole des Servers aus.</span>',
'description' => 'Der untenstehende Befehl lädt, installiert und konfiguriert die benötigten Dienste auf dem System aufgrund der Angaben die während des Installationsprozessen gesammelt wurden.<br><br><span class="text-danger">Führe die gezeigten Befehle als <b>root</b> in der Shell/Konsole des Servers aus. <b>Beachte bitte</b> das dieser Befehl vorhandene Konfigurationen <b>überschreibt</b> (Sicherungsdateien werden erstellt)!<br>Sollte dies nicht gewünscht sein, wähle <i>Ich werden die Dienste manuell konfigurieren</i> am Ende dieser Seite.</span>',
'runcmd' => 'Folgende Befehle ausführen, um die Installation abzuschließen:',
'manual_config' => 'Ich werden die Dienste manuell konfigurieren, direkt zum Login umleiten',
'waitforconfig' => 'Warte auf Abschluss der Dienstkonfiguration...',

View File

@@ -68,6 +68,7 @@ return [
'customer_add' => 'Create customer',
'customer_edit' => 'Edit customer',
'username_default_msg' => 'Leave empty for autogenerated value',
'password_default_msg' => 'Autogenerated if empty',
'domains' => 'Domains',
'domain_add' => 'Create domain',
'domain_edit' => 'Edit domain',
@@ -144,11 +145,17 @@ return [
'pop_success_alternative' => 'Welcome mail for new email accounts sent to alternative address',
'EMAIL_PASSWORD' => 'Replaced with the POP3/IMAP account password.',
'index_html' => 'index file for newly created customer directories',
'unconfigured_html' => 'index file for unconfigured/unknown domains',
'unconfigured_content_fallback' => 'This domain requires configuration via the froxlor server management panel, as it is currently not assigned to any customer.',
'file_extension' => [
'description' => 'The file extension for the index file must be between 1 and 6 characters long. The extension can only contain characters like a-z, A-Z and 0-9<br><br>Default: html',
'title' => 'File extension for the file template',
],
'SERVERNAME' => 'Replaced with the servername.',
'CUSTOMER' => 'Replaced with the loginname of the customer.',
'ADMIN' => 'Replaced with the loginname of the admin.',
'CUSTOMER_EMAIL' => 'Replaced with the e-mail address of the customer.',
'ADMIN_EMAIL' => 'Replaced with the e-mail address of the admin.',
'CUSTOMER' => 'Replaced with the loginname of the customer. Only for "index file for newly created customer directories"',
'ADMIN' => 'Replaced with the loginname of the admin. Only for "index file for newly created customer directories"',
'CUSTOMER_EMAIL' => 'Replaced with the e-mail address of the customer. Only for "index file for newly created customer directories"',
'ADMIN_EMAIL' => 'Replaced with the e-mail address of the admin. Only for "index file for newly created customer directories"',
'filetemplates' => 'File templates',
'filecontent' => 'File content',
'new_database_by_customer' => 'Customer-notification when a database has been created',
@@ -903,7 +910,6 @@ return [
'descriptioninvalid' => 'The description is too short, too long or contains illegal characters.',
'info' => 'Info',
'filecontentnotset' => 'The file cannot be empty!',
'index_file_extension' => 'The file extension for the index file must be between 1 and 6 characters long. The extension can only contain characters like a-z, A-Z and 0-9',
'customerdoesntexist' => 'The customer you have chosen doesn\'t exist.',
'admindoesntexist' => 'The admin you have chosen doesn\'t exist.',
'ipportdoesntexist' => 'The ip/port combination you have chosen doesn\'t exist.',
@@ -992,6 +998,7 @@ return [
'dns_duplicate_entry' => 'Record already exists',
'dns_notfoundorallowed' => 'Domain not found or no permission',
'domain_nopunycode' => 'You must not specify punycode (IDNA). The domain will automatically be converted',
'domain_noipaddress' => 'Cannot add an IP address as domain',
'dns_record_toolong' => 'Records/labels can only be up to 63 characters',
'noipportgiven' => 'No IP/port given',
'nosslippportgiven' => 'When enabling SSL you need to select a SSL IP/port',
@@ -1423,6 +1430,8 @@ Yours sincerely, your administrator',
'dnsentry_reallydelete' => 'Do you really want to delete this zone entry?',
'certificate_reallydelete' => 'Do you really want to delete this certificate?',
'cache_reallydelete' => 'Do you really want to clear the cache?',
'please_enter_otp' => 'Please enter 2FA code',
'admin_mysqlserver_reallydelete' => 'Do you really want to delete this MySQL-server?',
],
'redirect_desc' => [
'rc_default' => 'default',
@@ -1730,10 +1739,6 @@ Yours sincerely, your administrator',
'removelink' => 'Click here to wipe all quotas for mail accounts.',
'enforcelink' => 'Click here to enforce default quota to all User mail accounts.',
],
'index_file_extension' => [
'description' => 'Which file extension should be used for the index file in newly created customer directories? This file extension will be used, if you or one of your admins has created its own index file template.',
'title' => 'File extension for index file in newly created customer directories',
],
'session_allow_multiple_login' => [
'title' => 'Allow multiple login',
'description' => 'If activated a user could login multiple times.',
@@ -2208,6 +2213,7 @@ Yours sincerely, your administrator',
],
'uc_stable' => 'stable',
'uc_testing' => 'testing',
'uc_nightly' => 'nightly',
'traffictool' => [
'toolselect' => 'Traffic analyzer',
'webalizer' => 'Webalizer',
@@ -2357,7 +2363,7 @@ Yours sincerely, your administrator',
],
],
'install' => [
'slogal' => 'froxlor Server Management Panel',
'slogan' => 'froxlor Server Management Panel',
'preflight' => 'System check',
'critical_error' => 'Critical error',
'suggestions' => 'Not required but recommended',
@@ -2375,7 +2381,7 @@ Yours sincerely, your administrator',
'database' => [
'top' => 'Database',
'title' => 'Create database and user',
'description' => 'Froxlor requires a database and additionally a privileged user to be able to create users and databases (GRANT option). The given database and unprivileged database-user will be created in this process. The privileged user must exist.',
'description' => 'Froxlor requires a database and additionally <a href="https://docs.froxlor.org/latest/general/installation/tarball.html#_3-create-privileged-database-user" target="_blank">a privileged user</a> to be able to create users and databases (GRANT option). The given database and unprivileged database-user will be created in this process. The privileged user must exist.',
'user' => 'Unprivileged database user',
'dbname' => 'Database name',
'force_create' => 'Backup and overwrite database if exists?',
@@ -2384,6 +2390,8 @@ Yours sincerely, your administrator',
'top' => 'Admin user',
'title' => 'Let\'s create the main administrator user.',
'description' => 'This user will be granted all privileges to adjust settings and add/update/delete resources like customers, domains, etc.',
'use_admin_email_as_sender' => 'Use the email address above as sender address. If unchecked, please specify a sender address below.',
'use_autogenerated_email_as_sender' => 'Leave empty for default: admin@servername',
],
'system' => [
'top' => 'System setup',
@@ -2398,7 +2406,7 @@ Yours sincerely, your administrator',
'install' => [
'top' => 'Finish setup',
'title' => 'One last step...',
'description' => 'The command below will download, install and configure required services on your system according to the data you have given in this installation process.<br><br><span class="text-danger">Be sure to run the following command as <b>root</b> on the server\'s shell/terminal.</span>',
'description' => 'The command below will download, install and configure required services on your system according to the data you have given in this installation process.<br><br><span class="text-danger">Be sure to run the following command as <b>root</b> on the server\'s shell/terminal and <b>be aware</b> that this command will <b>overwrite</b> any existing configuration for the used services (backups will be created)!.<br>If you do not want to overwrite any configurations, select <i>I will manually configure the services</i> at the bottom of this page!</span>',
'runcmd' => 'Run the following command to finish the installation:',
'manual_config' => 'I will manually configure the services, just take me to the login',
'waitforconfig' => 'Waiting for services to be configured...',

View File

@@ -2304,7 +2304,7 @@ Atentamente, su administrador'
]
],
'install' => [
'slogal' => 'Panel de gestión del servidor froxlor',
'slogan' => 'Panel de gestión del servidor froxlor',
'preflight' => 'Comprobación del sistema',
'critical_error' => 'Error crítico',
'suggestions' => 'No requerido pero recomendado',

9385
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,19 +2,27 @@
"name": "froxlor",
"private": true,
"scripts": {
"dev": "mix watch",
"build": "mix --production"
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^6.2.0",
"@popperjs/core": "^2.11.6",
"bootstrap": "^5.2.1",
"chart.js": "^3.9.1",
"@fortawesome/fontawesome-free": "^6.4.2",
"@popperjs/core": "^2.11.8",
"@vitejs/plugin-vue": "^4.0.0",
"axios": "^1.6.0",
"bootstrap": "^5.3.2",
"chart.js": "^4.4.0",
"jquery": "^3.6.1",
"jquery-validation": "^1.19.5",
"laravel-mix": "^6.0.42",
"jquery-validation": "^1.20.0",
"laravel-vite-plugin": "^0.8.0",
"lodash": "^4.17.19",
"postcss": "^8.1.14",
"resolve-url-loader": "^5.0.0",
"sass": "^1.49.7",
"sass-loader": "^12.5.0"
"sass": "^1.69.3",
"vite": "^4.4.12",
"vue": "^3.2.37"
},
"engines": {
"node": ">=18"
}
}

View File

@@ -0,0 +1,14 @@
import '@fortawesome/fontawesome-free';
import './bootstrap';
// Vue
import {createApp} from 'vue';
const app = createApp({});
// Load jquery components
Object.entries(import.meta.glob('./jquery/*.js', {eager: true})).forEach(([path, definition]) => {
definition.default();
});
app.mount('#app');

View File

@@ -0,0 +1,21 @@
import _ from 'lodash';
window._ = _;
// jQuery
import jQuery from 'jquery';
window.$ = jQuery;
import 'jquery-validation';
// Bootstrap
import * as bootstrap from 'bootstrap';
window.bootstrap = bootstrap;
// ChartJS
import Chart from 'chart.js/auto';
window.Chart = Chart;
// Axios
import axios from 'axios';
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

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