From 9d47d670a11580871555ecdc76530a7b584dda94 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Wed, 17 Jul 2024 21:21:47 +0200 Subject: [PATCH 1/7] fix correctly handling catchall-flag when updating email-address, fixes #1260 Signed-off-by: Michael Kaufmann --- lib/Froxlor/Api/Commands/Emails.php | 54 +++++++++++++++-------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/lib/Froxlor/Api/Commands/Emails.php b/lib/Froxlor/Api/Commands/Emails.php index 60e81f59..c52d6dc5 100644 --- a/lib/Froxlor/Api/Commands/Emails.php +++ b/lib/Froxlor/Api/Commands/Emails.php @@ -270,15 +270,6 @@ class Emails extends ApiCommand implements ResourceEntity throw new Exception("You cannot access this resource", 405); } - // if enabling catchall is not allowed by settings, we do not need - // to run update() - if (Settings::Get('catchall.catchall_enabled') != '1') { - Response::standardError([ - 'operationnotpermitted', - 'featureisdisabled' - ], 'catchall', true); - } - $id = $this->getParam('id', true, 0); $ea_optional = $id > 0; $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); @@ -297,30 +288,41 @@ class Emails extends ApiCommand implements ResourceEntity $iscatchall = $this->getBoolParam('iscatchall', true, $result['iscatchall']); $description = $this->getParam('description', true, $result['description']); + // if enabling catchall is not allowed by settings, we do not need + // to run update() + if ($iscatchall && $result['iscatchall'] == 0 && Settings::Get('catchall.catchall_enabled') != '1') { + Response::standardError([ + 'operationnotpermitted', + 'featureisdisabled' + ], 'catchall', true); + } + // get needed customer info to reduce the email-address-counter by one $customer = $this->getCustomerData(); // check for catchall-flag + $email = $result['email_full']; if ($iscatchall) { $iscatchall = '1'; - $email_parts = explode('@', $result['email_full']); - $email = '@' . $email_parts[1]; - // catchall check - $stmt = Database::prepare(" - SELECT `email_full` FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE `email` = :email AND `customerid` = :cid AND `iscatchall` = '1' - "); - $params = [ - "email" => $email, - "cid" => $customer['customerid'] - ]; - $email_check = Database::pexecute_first($stmt, $params, true, true); - if ($email_check) { - Response::standardError('youhavealreadyacatchallforthisdomain', '', true); + $email = $result['email']; + // update only required if it was not a catchall before + if ($result['iscatchall'] == 0) { + $email_parts = explode('@', $result['email_full']); + $email = '@' . $email_parts[1]; + // catchall check + $stmt = Database::prepare(" + SELECT `email_full` FROM `" . TABLE_MAIL_VIRTUAL . "` + WHERE `email` = :email AND `customerid` = :cid AND `iscatchall` = '1' + "); + $params = [ + "email" => $email, + "cid" => $customer['customerid'] + ]; + $email_check = Database::pexecute_first($stmt, $params, true, true); + if ($email_check) { + Response::standardError('youhavealreadyacatchallforthisdomain', '', true); + } } - } else { - $iscatchall = '0'; - $email = $result['email_full']; } $spam_tag_level = Validate::validate($spam_tag_level, 'spam_tag_level', '/^\d{1,}(\.\d{1,2})?$/', '', [7.0], true); From bda24d7d6364d9c8609ea6fb55fc8bb7ccb0990e Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Fri, 19 Jul 2024 22:16:41 +0200 Subject: [PATCH 2/7] show email-only domains in customers list for potential dns entries information (if necessary) Signed-off-by: Michael Kaufmann --- lib/Froxlor/Api/Commands/SubDomains.php | 7 ++++--- lib/Froxlor/Dns/Dns.php | 2 +- lib/Froxlor/UI/Callbacks/Domain.php | 10 ++++++--- .../domains/formfield.domains_edit.php | 21 +++++++++++-------- lng/de.lng.php | 1 + lng/en.lng.php | 1 + logfiles_viewer.php | 4 ++++ ssl_editor.php | 4 ++++ 8 files changed, 34 insertions(+), 16 deletions(-) diff --git a/lib/Froxlor/Api/Commands/SubDomains.php b/lib/Froxlor/Api/Commands/SubDomains.php index 6c796234..1c4870c2 100644 --- a/lib/Froxlor/Api/Commands/SubDomains.php +++ b/lib/Froxlor/Api/Commands/SubDomains.php @@ -983,9 +983,11 @@ class SubDomains extends ApiCommand implements ResourceEntity '`d`.`letsencrypt`', '`d`.`registration_date`', '`d`.`termination_date`', - '`d`.`deactivated`' + '`d`.`deactivated`', + '`d`.`email_only`', ]; } + $query_fields = []; // prepare select statement @@ -996,7 +998,6 @@ class SubDomains extends ApiCommand implements ResourceEntity LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `da` ON `da`.`aliasdomain`=`d`.`id` LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `pd` ON `pd`.`id`=`d`.`parentdomainid` WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ") - AND `d`.`email_only` = '0' " . $this->getSearchWhere($query_fields, true) . " GROUP BY `d`.`id` ORDER BY `parentdomainname` ASC, `d`.`parentdomainid` ASC " . $this->getOrderBy(true) . $this->getLimit()); $result = []; @@ -1092,13 +1093,13 @@ class SubDomains extends ApiCommand implements ResourceEntity $this->getUserDetail('customerid') ]; } + if (!empty($customer_ids)) { // prepare select statement $domains_stmt = Database::prepare(" SELECT COUNT(*) as num_subdom FROM `" . TABLE_PANEL_DOMAINS . "` `d` WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ") - AND `d`.`email_only` = '0' "); $result = Database::pexecute_first($domains_stmt, null, true, true); if ($result) { diff --git a/lib/Froxlor/Dns/Dns.php b/lib/Froxlor/Dns/Dns.php index a8ca8771..b49a7e22 100644 --- a/lib/Froxlor/Dns/Dns.php +++ b/lib/Froxlor/Dns/Dns.php @@ -54,7 +54,7 @@ class Dns $dom_data['uid'] = $userinfo['userid']; } } else { - $where_clause = '`customerid` = :uid AND '; + $where_clause = '`customerid` = :uid AND `email_only` = "0" AND '; $dom_data['uid'] = $userinfo['userid']; } diff --git a/lib/Froxlor/UI/Callbacks/Domain.php b/lib/Froxlor/UI/Callbacks/Domain.php index b8fb3f68..f635ae25 100644 --- a/lib/Froxlor/UI/Callbacks/Domain.php +++ b/lib/Froxlor/UI/Callbacks/Domain.php @@ -74,6 +74,9 @@ class Domain if ($attributes['fields']['deactivated']) { return lng('admin.deactivated'); } + if ($attributes['fields']['email_only']) { + return lng('domains.email_only'); + } // path or redirect if (preg_match('/^https?\:\/\//', $attributes['fields']['documentroot'])) { return [ @@ -127,7 +130,7 @@ class Domain public static function canViewLogs(array $attributes): bool { - if ((!CurrentUser::isAdmin() || (CurrentUser::isAdmin() && (int)$attributes['fields']['email_only'] == 0)) && !$attributes['fields']['deactivated']) { + if ((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) { @@ -157,6 +160,7 @@ class Domain && $attributes['fields']['caneditdomain'] == '1' && Settings::Get('system.bind_enable') == '1' && Settings::Get('system.dnsenabled') == '1' + && !$attributes['fields']['email_only'] && !$attributes['fields']['deactivated']; } @@ -169,7 +173,7 @@ class Domain public static function hasLetsEncryptActivated(array $attributes): bool { - return ((bool)$attributes['fields']['letsencrypt'] && (!CurrentUser::isAdmin() || (CurrentUser::isAdmin() && (int)$attributes['fields']['email_only'] == 0))); + return ((bool)$attributes['fields']['letsencrypt'] && (int)$attributes['fields']['email_only'] == 0); } /** @@ -181,7 +185,7 @@ class Domain && DDomain::domainHasSslIpPort($attributes['fields']['id']) && (CurrentUser::isAdmin() || (!CurrentUser::isAdmin() && (int)$attributes['fields']['caneditdomain'] == 1)) && (int)$attributes['fields']['letsencrypt'] == 0 - && (!CurrentUser::isAdmin() || (CurrentUser::isAdmin() && (int)$attributes['fields']['email_only'] == 0)) + && !(int)$attributes['fields']['email_only'] && !$attributes['fields']['deactivated'] ) { return true; diff --git a/lib/formfields/customer/domains/formfield.domains_edit.php b/lib/formfields/customer/domains/formfield.domains_edit.php index 47dde7bb..6e0ab1cc 100644 --- a/lib/formfields/customer/domains/formfield.domains_edit.php +++ b/lib/formfields/customer/domains/formfield.domains_edit.php @@ -47,13 +47,14 @@ return [ 'values' => $domainips ], 'alias' => [ - 'visible' => $alias_check == '0', + 'visible' => $alias_check == '0' && (int)$result['email_only'] == 0, 'label' => lng('domains.aliasdomain'), 'type' => 'select', 'select_var' => $domains, 'selected' => $result['aliasdomain'] ], 'path' => [ + 'visible' => (int)$result['email_only'] == 0, 'label' => lng('panel.path'), 'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? lng('panel.pathDescriptionSubdomain').(Settings::Get('system.documentroot_use_default_value') == 1 ? lng('panel.pathDescriptionEx') : '') : null), 'type' => $pathSelect['type'], @@ -63,13 +64,13 @@ return [ 'note' => $pathSelect['note'] ?? '', ], 'url' => [ - 'visible' => Settings::Get('panel.pathedit') == 'Dropdown', + 'visible' => Settings::Get('panel.pathedit') == 'Dropdown' && (int)$result['email_only'] == 0, 'label' => lng('panel.urloverridespath'), 'type' => 'text', 'value' => $urlvalue ], 'redirectcode' => [ - 'visible' => Settings::Get('customredirect.enabled') == '1', + 'visible' => Settings::Get('customredirect.enabled') == '1' && (int)$result['email_only'] == 0, 'label' => lng('domains.redirectifpathisurl'), 'desc' => lng('domains.redirectifpathisurlinfo'), 'type' => 'select', @@ -77,7 +78,7 @@ return [ 'selected' => $def_code ], 'selectserveralias' => [ - 'visible' => ($result['parentdomainid'] == '0' && $userinfo['subdomains'] != '0') || $result['parentdomainid'] != '0', + 'visible' => (($result['parentdomainid'] == '0' && $userinfo['subdomains'] != '0') || $result['parentdomainid'] != '0') && (int)$result['email_only'] == 0, 'label' => lng('admin.selectserveralias'), 'desc' => lng('admin.selectserveralias_desc'), 'type' => 'select', @@ -85,27 +86,28 @@ return [ 'selected' => $serveraliasoptions_selected ], 'isemaildomain' => [ - 'visible' => ($result['subcanemaildomain'] == '1' || $result['subcanemaildomain'] == '2') && $result['parentdomainid'] != '0', + 'visible' => (($result['subcanemaildomain'] == '1' || $result['subcanemaildomain'] == '2') && $result['parentdomainid'] != '0') && (int)$result['email_only'] == 0, 'label' => 'Emaildomain', 'type' => 'checkbox', 'value' => '1', 'checked' => $result['isemaildomain'] ], 'openbasedir_path' => [ - 'visible' => $result['openbasedir'] == '1', + 'visible' => $result['openbasedir'] == '1' && (int)$result['email_only'] == 0, 'label' => lng('domain.openbasedirpath'), 'type' => 'select', 'select_var' => $openbasedir, 'selected' => $result['openbasedir_path'] ], 'phpsettingid' => [ - 'visible' => ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) && count($phpconfigs) > 0 && $userinfo['phpenabled'] == '1' && $result['phpenabled'] == '1', + 'visible' => ((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) && count($phpconfigs) > 0 && $userinfo['phpenabled'] == '1' && $result['phpenabled'] == '1' && (int)$result['email_only'] == 0, 'label' => lng('admin.phpsettings.title'), 'type' => 'select', 'select_var' => $phpconfigs, 'selected' => $result['phpsettingid'] ], 'speciallogfile' => [ + 'visible' => (int)$result['email_only'] == 0, 'label' => lng('admin.speciallogfile.title'), 'desc' => lng('admin.speciallogfile.description'), 'type' => 'checkbox', @@ -139,7 +141,7 @@ return [ 'section_bssl' => [ 'title' => lng('admin.webserversettings_ssl'), 'image' => 'icons/domain_edit.png', - 'visible' => Settings::Get('system.use_ssl') == '1' && $ssl_ipsandports && Domain::domainHasSslIpPort($result['id']), + 'visible' => Settings::Get('system.use_ssl') == '1' && $ssl_ipsandports && Domain::domainHasSslIpPort($result['id']) && (int)$result['email_only'] == 0, 'fields' => [ 'sslenabled' => [ 'label' => lng('admin.domain_sslenabled'), @@ -194,6 +196,7 @@ return [ ] ] ] - ] + ], + 'buttons' => ((int)$result['email_only'] == 1) ? [] : null ] ]; diff --git a/lng/de.lng.php b/lng/de.lng.php index 8a3eb506..119cb0d0 100644 --- a/lng/de.lng.php +++ b/lng/de.lng.php @@ -713,6 +713,7 @@ return [ 'hsts' => 'HSTS aktiviert', 'aliasdomainid' => 'ID der Alias-Domain', 'nodomainsassignedbyadmin' => 'Diesem Account wurde noch keine (aktive) Domain zugewiesen. Bitte kontaktiere deinen Administrator, wenn du der Meinung bist, das ist nicht korrekt.', + 'email_only' => 'Nur E-Mail', ], 'emails' => [ 'description' => 'Hier können Sie Ihre E-Mail-Adressen einrichten.
Ein Konto ist wie Ihr Briefkasten vor der Haustür. Wenn jemand eine E-Mail an Sie schreibt, wird diese in dieses Konto gelegt.

Die Zugangsdaten lauten wie folgt: (Die Angaben in kursiver Schrift sind durch die jeweiligen Einträge zu ersetzen)

Hostname: Domainname
Benutzername: Kontoname / E-Mail-Adresse
Passwort: das gewählte Passwort', diff --git a/lng/en.lng.php b/lng/en.lng.php index 112686c7..d1cbb0c4 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -784,6 +784,7 @@ return [ 'hsts' => 'HSTS enabled', 'aliasdomainid' => 'ID of alias domain', 'nodomainsassignedbyadmin' => 'Your account has currently no (active) domains assigned to it. Please contact your administrator if you think this is wrong.', + 'email_only' => 'Email only', ], 'emails' => [ 'description' => 'Here you can create and change your email addresses.
An account is like your letterbox in front of your house. If someone sends you an email, it will be dropped into the account.

To download your emails use the following settings in your mailprogram: (The data in italics has to be changed to the equivalents you typed in!)
Hostname: domainname
Username: account name / e-mail address
password: the password you\'ve chosen', diff --git a/logfiles_viewer.php b/logfiles_viewer.php index 20d00d7c..e3a65872 100644 --- a/logfiles_viewer.php +++ b/logfiles_viewer.php @@ -61,6 +61,10 @@ if (function_exists('exec')) { } $domain = json_decode($json_result, true)['data']; + if ($domain['email_only']) { + Response::dynamicError("There are no webserver logfiles for email only domains."); + } + $speciallogfile = ''; if ($domain['speciallogfile'] == '1') { if ($domain['parentdomainid'] == '0') { diff --git a/ssl_editor.php b/ssl_editor.php index b0d1c770..8fea36d9 100644 --- a/ssl_editor.php +++ b/ssl_editor.php @@ -50,6 +50,10 @@ if ($action == '' || $action == 'view') { } $result_domain = json_decode($json_result, true)['data']; + if ($result_domain['email_only']) { + Response::dynamicError("There are no ssl-certificates for email only domains."); + } + if (Request::post('send') == 'send') { $do_insert = Request::post('do_insert', 0) == 1; try { From 2dae780e0bc73c81dd577d572788f361b0350fd3 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sat, 20 Jul 2024 10:16:48 +0200 Subject: [PATCH 3/7] implement 2fa remember browser, fixes #1259 Signed-off-by: Michael Kaufmann --- index.php | 62 +++++++++++++++++++--- install/froxlor.sql.php | 13 ++++- install/updates/froxlor/update_2.2.inc.php | 18 +++++++ lib/Froxlor/Cli/MasterCron.php | 3 +- lib/Froxlor/Froxlor.php | 2 +- lib/tables.inc.php | 1 + lng/de.lng.php | 1 + lng/en.lng.php | 1 + templates/Froxlor/login/enter2fa.html.twig | 8 +++ 9 files changed, 98 insertions(+), 11 deletions(-) diff --git a/index.php b/index.php index 205b0517..dd3f2673 100644 --- a/index.php +++ b/index.php @@ -72,6 +72,7 @@ if ($action == '2fa_entercode') { exit(); } $code = Request::post('2fa_code'); + $remember = Request::post('2fa_remember'); // verify entered code $tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname')); // get user-data @@ -105,13 +106,6 @@ if ($action == '2fa_entercode') { $userinfo['adminsession'] = $isadmin; $userinfo['userid'] = $uid; - // if not successful somehow - start again - if (!finishLogin($userinfo)) { - Response::redirectTo('index.php', [ - 'showmessage' => '2' - ]); - } - // when using email-2fa, remove the one-time-code if ($userinfo['type_2fa'] == '1') { $del_stmt = Database::prepare("UPDATE " . $table . " SET `data_2fa` = '' WHERE `" . $field . "` = :uid"); @@ -119,6 +113,42 @@ if ($action == '2fa_entercode') { 'uid' => $uid ]); } + + // when remember is activated, set the cookie + if ($remember) { + $selector = base64_encode(Froxlor::genSessionId(9)); + $authenticator = Froxlor::genSessionId(33); + $valid_until = time()+60*60*24*30; + $ins_stmt = Database::prepare(" + INSERT INTO `".TABLE_PANEL_2FA_TOKENS."` SET + `selector` = :selector, + `token` = :authenticator, + `userid` = :userid, + `valid_until` = :valid_until + "); + Database::pexecute($ins_stmt, [ + 'selector' => $selector, + 'authenticator' => hash('sha256', $authenticator), + 'userid' => $uid, + 'valid_until' => $valid_until + ]); + $cookie_params = [ + 'expires' => $valid_until, // 30 days + 'path' => '/', + 'domain' => UI::getCookieHost(), + 'secure' => UI::requestIsHttps(), + 'httponly' => true, + 'samesite' => 'Strict' + ]; + setcookie('frx_2fa_remember', $selector.':'.base64_encode($authenticator), $cookie_params); + } + + // if not successful somehow - start again + if (!finishLogin($userinfo)) { + Response::redirectTo('index.php', [ + 'showmessage' => '2' + ]); + } exit(); } // wrong 2fa code - treat like "wrong password" @@ -349,6 +379,22 @@ if ($action == '2fa_entercode') { // 2FA activated if (Settings::Get('2fa.enabled') == '1' && $userinfo['type_2fa'] > 0) { + + // check for remember cookie + if (!empty($_COOKIE['frx_2fa_remember'])) { + list($selector, $authenticator) = explode(':', $_COOKIE['frx_2fa_remember']); + $sel_stmt = Database::prepare("SELECT `token` FROM `".TABLE_PANEL_2FA_TOKENS."` WHERE `selector` = :selector AND `userid` = :uid AND `valid_until` >= UNIX_TIMESTAMP()"); + $token_check = Database::pexecute_first($sel_stmt, ['selector' => $selector, 'uid' => $userinfo[$uid]]); + if ($token_check && hash_equals($token_check['token'], hash('sha256', base64_decode($authenticator)))) { + if (!finishLogin($userinfo)) { + Response::redirectTo('index.php', [ + 'showmessage' => '2' + ]); + } + exit(); + } + } + // redirect to code-enter-page $_SESSION['secret_2fa'] = ($userinfo['type_2fa'] == 2 ? $userinfo['data_2fa'] : 'email'); $_SESSION['uid_2fa'] = $userinfo[$uid]; @@ -829,8 +875,8 @@ function finishLogin($userinfo) $theme = $userinfo['theme']; } else { $theme = Settings::Get('panel.default_theme'); - CurrentUser::setField('theme', $theme); } + CurrentUser::setField('theme', $theme); $qryparams = []; if (!empty($_SESSION['lastqrystr'])) { diff --git a/install/froxlor.sql.php b/install/froxlor.sql.php index 299560de..4c980b21 100644 --- a/install/froxlor.sql.php +++ b/install/froxlor.sql.php @@ -731,7 +731,7 @@ opcache.validate_timestamps'), ('panel', 'settings_mode', '0'), ('panel', 'menu_collapsed', '1'), ('panel', 'version', '2.2.0-rc1'), - ('panel', 'db_version', '202401090'); + ('panel', 'db_version', '202407200'); DROP TABLE IF EXISTS `panel_tasks`; @@ -1049,4 +1049,15 @@ CREATE TABLE `panel_loginlinks` ( `allowed_from` text NOT NULL, UNIQUE KEY `loginname` (`loginname`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; + + +DROP TABLE IF EXISTS `panel_2fa_tokens`; +CREATE TABLE `panel_2fa_tokens` ( + `id` int(11) NOT NULL auto_increment, + `selector` varchar(20) NOT NULL, + `token` varchar(200) NOT NULL, + `userid` int(11) NOT NULL default '0', + `valid_until` int(15) NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; FROXLORSQL; diff --git a/install/updates/froxlor/update_2.2.inc.php b/install/updates/froxlor/update_2.2.inc.php index 9e96a98a..61883f6a 100644 --- a/install/updates/froxlor/update_2.2.inc.php +++ b/install/updates/froxlor/update_2.2.inc.php @@ -122,3 +122,21 @@ if (Froxlor::isFroxlorVersion('2.2.0-dev1')) { Update::showUpdateStep("Updating from 2.2.0-dev1 to 2.2.0-rc1", false); Froxlor::updateToVersion('2.2.0-rc1'); } + +if (Froxlor::isDatabaseVersion('202401090')) { + + Update::showUpdateStep("Adding new table for 2fa tokens"); + Database::query("DROP TABLE IF EXISTS `panel_2fa_tokens`;"); + $sql = "CREATE TABLE `panel_2fa_tokens` ( + `id` int(11) NOT NULL auto_increment, + `selector` varchar(20) NOT NULL, + `token` varchar(200) NOT NULL, + `userid` int(11) NOT NULL default '0', + `valid_until` int(15) NOT NULL, + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;"; + Database::query($sql); + Update::lastStepStatus(0); + + Froxlor::updateToDbVersion('202407200'); +} diff --git a/lib/Froxlor/Cli/MasterCron.php b/lib/Froxlor/Cli/MasterCron.php index 61926ecc..7045de8e 100644 --- a/lib/Froxlor/Cli/MasterCron.php +++ b/lib/Froxlor/Cli/MasterCron.php @@ -171,8 +171,9 @@ final class MasterCron extends CliCommand FroxlorLogger::getInstanceOf()->setCronLog(0); } - // clean up possible old login-links + // clean up possible old login-links and 2fa tokens Database::query("DELETE FROM `" . TABLE_PANEL_LOGINLINKS . "` WHERE `valid_until` < UNIX_TIMESTAMP()"); + Database::query("DELETE FROM `" . TABLE_PANEL_2FA_TOKENS . "` WHERE `valid_until` < UNIX_TIMESTAMP()"); return $result; } diff --git a/lib/Froxlor/Froxlor.php b/lib/Froxlor/Froxlor.php index 83bc5501..ce905371 100644 --- a/lib/Froxlor/Froxlor.php +++ b/lib/Froxlor/Froxlor.php @@ -34,7 +34,7 @@ final class Froxlor const VERSION = '2.2.0-rc1'; // Database version (YYYYMMDDC where C is a daily counter) - const DBVERSION = '202401090'; + const DBVERSION = '202407200'; // Distribution branding-tag (used for Debian etc.) const BRANDING = ''; diff --git a/lib/tables.inc.php b/lib/tables.inc.php index 26214333..0805f290 100644 --- a/lib/tables.inc.php +++ b/lib/tables.inc.php @@ -57,3 +57,4 @@ const TABLE_PANEL_PLANS = 'panel_plans'; const TABLE_API_KEYS = 'api_keys'; const TABLE_PANEL_USERCOLUMNS = 'panel_usercolumns'; const TABLE_PANEL_LOGINLINKS = 'panel_loginlinks'; +const TABLE_PANEL_2FA_TOKENS = 'panel_2fa_tokens'; diff --git a/lng/de.lng.php b/lng/de.lng.php index 119cb0d0..841d9fe5 100644 --- a/lng/de.lng.php +++ b/lng/de.lng.php @@ -1032,6 +1032,7 @@ return [ 'combination_not_found' => 'Kombination aus Benutzername und E-Mail Adresse stimmen nicht überein.', '2fa' => 'Zwei-Faktor Authentifizierung (2FA)', '2facode' => 'Bitte 2FA Code angeben', + '2faremember' => 'Browser vertrauen', ], 'mails' => [ 'pop_success' => [ diff --git a/lng/en.lng.php b/lng/en.lng.php index d1cbb0c4..31035ed6 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -1104,6 +1104,7 @@ return [ 'combination_not_found' => 'Combination of user and email address not found.', '2fa' => 'Two-factor authentication (2FA)', '2facode' => 'Please enter 2FA code', + '2faremember' => 'Trust browser', ], 'mails' => [ 'pop_success' => [ diff --git a/templates/Froxlor/login/enter2fa.html.twig b/templates/Froxlor/login/enter2fa.html.twig index b435bb0b..37cc3d66 100644 --- a/templates/Froxlor/login/enter2fa.html.twig +++ b/templates/Froxlor/login/enter2fa.html.twig @@ -22,6 +22,14 @@ +
+
+ + + +
+
+
From 4d3cf5da9a14d90c758989aa69d1349610bbb4da Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sat, 20 Jul 2024 23:26:53 +0200 Subject: [PATCH 4/7] add column '2fa status' for customers and admins Signed-off-by: Michael Kaufmann --- lib/Froxlor/UI/Callbacks/Text.php | 8 ++++++++ lib/tablelisting/admin/tablelisting.admins.php | 6 ++++++ lib/tablelisting/admin/tablelisting.customers.php | 6 ++++++ lng/de.lng.php | 1 + lng/en.lng.php | 1 + templates/Froxlor/table/macros.html.twig | 10 ++++++++++ templates/Froxlor/table/table.html.twig | 2 ++ 7 files changed, 34 insertions(+) diff --git a/lib/Froxlor/UI/Callbacks/Text.php b/lib/Froxlor/UI/Callbacks/Text.php index 91df6fdf..c1a8cbef 100644 --- a/lib/Froxlor/UI/Callbacks/Text.php +++ b/lib/Froxlor/UI/Callbacks/Text.php @@ -52,6 +52,14 @@ class Text ]; } + public static function type2fa(array $attributes): array + { + return [ + 'macro' => 'type2fa', + 'data' => (int)$attributes['data'] + ]; + } + public static function customerfullname(array $attributes): string { return User::getCorrectFullUserDetails($attributes['fields'], true); diff --git a/lib/tablelisting/admin/tablelisting.admins.php b/lib/tablelisting/admin/tablelisting.admins.php index 637f3d4c..7af1eec3 100644 --- a/lib/tablelisting/admin/tablelisting.admins.php +++ b/lib/tablelisting/admin/tablelisting.admins.php @@ -110,6 +110,12 @@ return [ 'class' => 'text-center', 'callback' => [Text::class, 'boolean'], ], + 'type_2fa' => [ + 'label' => lng('2fa.type_2fa'), + 'field' => 'type_2fa', + 'class' => 'text-center', + 'callback' => [Text::class, 'type2fa'], + ], ], 'visible_columns' => Listing::getVisibleColumnsForListing('admin_list', [ 'loginname', diff --git a/lib/tablelisting/admin/tablelisting.customers.php b/lib/tablelisting/admin/tablelisting.customers.php index de3e8fb5..6c1c7979 100644 --- a/lib/tablelisting/admin/tablelisting.customers.php +++ b/lib/tablelisting/admin/tablelisting.customers.php @@ -149,6 +149,12 @@ return [ 'class' => 'text-center', 'callback' => [Text::class, 'boolean'], ], + 'c.type_2fa' => [ + 'label' => lng('2fa.type_2fa'), + 'field' => 'type_2fa', + 'class' => 'text-center', + 'callback' => [Text::class, 'type2fa'], + ], ], 'visible_columns' => Listing::getVisibleColumnsForListing('customer_list', [ 'c.name', diff --git a/lng/de.lng.php b/lng/de.lng.php index 841d9fe5..167cf8ee 100644 --- a/lng/de.lng.php +++ b/lng/de.lng.php @@ -48,6 +48,7 @@ return [ '2fa_ga_desc' => 'Das Konto ist eingerichtet, um zeitbasierte Einmalpasswörter via Authenticator-App zu erhalten. Um die gewünschte Authenticator-App einzurichten, scanne bitte den untenstehenden QR-Code. Zum Deaktivieren, klicke auf "2FA deaktivieren"', '2fa_not_activated' => 'Zwei-Faktor Authentifizierung ist nicht aktiviert', '2fa_not_activated_for_user' => 'Zwei-Faktor Authentifizierung ist für den aktuellen Benutzer nicht aktiviert', + 'type_2fa' => '2FA Status', ], 'admin' => [ 'overview' => 'Übersicht', diff --git a/lng/en.lng.php b/lng/en.lng.php index 31035ed6..d51a4506 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -49,6 +49,7 @@ return [ '2fa_ga_desc' => 'Your account is set up to use time-based one-time passwords via authenticator-app. Please scan the QR code below with your desired authenticator app to generate the codes. To deactivate, click on "Deactivate 2FA"', '2fa_not_activated' => 'Two-factor authentication is not enabled', '2fa_not_activated_for_user' => 'Two-factor authentication is not enabled for the current user', + 'type_2fa' => '2FA status', ], 'admin' => [ 'overview' => 'Overview', diff --git a/templates/Froxlor/table/macros.html.twig b/templates/Froxlor/table/macros.html.twig index 3a8e4910..c5cdab92 100644 --- a/templates/Froxlor/table/macros.html.twig +++ b/templates/Froxlor/table/macros.html.twig @@ -29,6 +29,16 @@ {% endif %} {% endmacro %} +{% macro type2fa(data) %} + {% if (data == 1) %} + + {% elseif (data == 2) %} + + {% else %} + + {% endif %} +{% endmacro %} + {% macro link(data) %} {% apply spaceless %} diff --git a/templates/Froxlor/table/table.html.twig b/templates/Froxlor/table/table.html.twig index 3c4384cb..578dad3f 100644 --- a/templates/Froxlor/table/table.html.twig +++ b/templates/Froxlor/table/table.html.twig @@ -47,6 +47,8 @@ {{ macros.domainWithSan(td.data.data) }} {% elseif td.data.macro == 'actions' %} {{ macros.actions(td.data.data) }} + {% elseif td.data.macro == 'type2fa' %} + {{ macros.type2fa(td.data.data) }} {% else %} Table macro '{{ td.data.macro|json_encode }}' is not implemented! Unable to handle this data: {{ td.data|json_encode }} From 585b16d199cb26a317e4c7743b310838038ee87a Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sun, 21 Jul 2024 10:15:53 +0200 Subject: [PATCH 5/7] set version to 2.2.0-rc2 for upcoming release-candidate Signed-off-by: Michael Kaufmann --- install/froxlor.sql.php | 2 +- install/updates/froxlor/update_2.2.inc.php | 5 +++++ lib/Froxlor/Froxlor.php | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql.php b/install/froxlor.sql.php index 4c980b21..5ffdb4a6 100644 --- a/install/froxlor.sql.php +++ b/install/froxlor.sql.php @@ -730,7 +730,7 @@ opcache.validate_timestamps'), ('panel', 'logo_overridecustom', '0'), ('panel', 'settings_mode', '0'), ('panel', 'menu_collapsed', '1'), - ('panel', 'version', '2.2.0-rc1'), + ('panel', 'version', '2.2.0-rc2'), ('panel', 'db_version', '202407200'); diff --git a/install/updates/froxlor/update_2.2.inc.php b/install/updates/froxlor/update_2.2.inc.php index 61883f6a..46645b5e 100644 --- a/install/updates/froxlor/update_2.2.inc.php +++ b/install/updates/froxlor/update_2.2.inc.php @@ -140,3 +140,8 @@ if (Froxlor::isDatabaseVersion('202401090')) { Froxlor::updateToDbVersion('202407200'); } + +if (Froxlor::isFroxlorVersion('2.2.0-rc1')) { + Update::showUpdateStep("Updating from 2.2.0-rc1 to 2.2.0-rc2", false); + Froxlor::updateToVersion('2.2.0-rc2'); +} diff --git a/lib/Froxlor/Froxlor.php b/lib/Froxlor/Froxlor.php index ce905371..0a5011e3 100644 --- a/lib/Froxlor/Froxlor.php +++ b/lib/Froxlor/Froxlor.php @@ -31,7 +31,7 @@ final class Froxlor { // Main version variable - const VERSION = '2.2.0-rc1'; + const VERSION = '2.2.0-rc2'; // Database version (YYYYMMDDC where C is a daily counter) const DBVERSION = '202407200'; From 75bc0142a0021efcfdab4da7b49e8c5301f7a5b4 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sat, 27 Jul 2024 19:51:55 +0200 Subject: [PATCH 6/7] add missing use-statement for opcacheinfo page; ease ClientConnectRate ban-filter for proftpd; allow null-mx entry in dns-editor, fixes #1263 Signed-off-by: Michael Kaufmann --- admin_opcacheinfo.php | 1 + lib/Froxlor/Api/Commands/DomainZones.php | 6 +++++- lib/configfiles/bookworm.xml | 2 +- lib/configfiles/bullseye.xml | 2 +- lib/configfiles/focal.xml | 2 +- lib/configfiles/jammy.xml | 2 +- lib/configfiles/noble.xml | 2 +- lng/de.lng.php | 2 +- lng/en.lng.php | 2 +- 9 files changed, 13 insertions(+), 8 deletions(-) diff --git a/admin_opcacheinfo.php b/admin_opcacheinfo.php index 443a11d1..c46c2534 100644 --- a/admin_opcacheinfo.php +++ b/admin_opcacheinfo.php @@ -35,6 +35,7 @@ require __DIR__ . '/lib/init.php'; use Froxlor\FroxlorLogger; use Froxlor\UI\HTML; use Froxlor\UI\Panel\UI; +use Froxlor\UI\Request; use Froxlor\UI\Response; if ($action == 'reset' && function_exists('opcache_reset') && $userinfo['change_serversettings'] == '1') { diff --git a/lib/Froxlor/Api/Commands/DomainZones.php b/lib/Froxlor/Api/Commands/DomainZones.php index 4a3020a1..331051cf 100644 --- a/lib/Froxlor/Api/Commands/DomainZones.php +++ b/lib/Froxlor/Api/Commands/DomainZones.php @@ -227,7 +227,7 @@ class DomainZones extends ApiCommand implements ResourceEntity // remove it for checks $content = substr($content, 0, -1); } - if (!Validate::validateDomain($content)) { + if (!empty($content) && !Validate::validateDomain($content)) { $errors[] = lng('error.dns_mx_needdom'); } else { // check whether there is a CNAME-record for the same resource @@ -244,6 +244,10 @@ class DomainZones extends ApiCommand implements ResourceEntity } // append trailing dot (again) $content .= '.'; + // if content is only ".", the prio needs to be 0 which results in a "null mx" entry + if ($content == '.' && $prio != 0) { + $prio = 0; + } } elseif ($type == 'NS') { // check for trailing dot if (substr($content, -1) == '.') { diff --git a/lib/configfiles/bookworm.xml b/lib/configfiles/bookworm.xml index 2e0c78a8..d0d2697b 100644 --- a/lib/configfiles/bookworm.xml +++ b/lib/configfiles/bookworm.xml @@ -3093,7 +3093,7 @@ MaxLoginAttempts 3 BanLog /var/log/proftpd/ban.log BanTable /etc/proftpd/ban.tab BanMessage "User %u was banned." -BanOnEvent ClientConnectRate 5/00:00:02 12:00:00 "Stop connecting frequently" +BanOnEvent ClientConnectRate 10/00:00:02 02:00:00 "Stop connecting frequently" BanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00 BanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99 BanControlsACLs all allow user root diff --git a/lib/configfiles/bullseye.xml b/lib/configfiles/bullseye.xml index 8a0e131f..d9913f62 100644 --- a/lib/configfiles/bullseye.xml +++ b/lib/configfiles/bullseye.xml @@ -4661,7 +4661,7 @@ MaxLoginAttempts 3 BanLog /var/log/proftpd/ban.log BanTable /etc/proftpd/ban.tab BanMessage "User %u was banned." -BanOnEvent ClientConnectRate 5/00:00:02 12:00:00 "Stop connecting frequently" +BanOnEvent ClientConnectRate 10/00:00:02 02:00:00 "Stop connecting frequently" BanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00 BanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99 BanControlsACLs all allow user root diff --git a/lib/configfiles/focal.xml b/lib/configfiles/focal.xml index 90c47e6e..7dc03c82 100644 --- a/lib/configfiles/focal.xml +++ b/lib/configfiles/focal.xml @@ -3880,7 +3880,7 @@ MaxLoginAttempts 3 BanLog /var/log/proftpd/ban.log BanTable /etc/proftpd/ban.tab BanMessage "User %u was banned." -BanOnEvent ClientConnectRate 5/00:00:02 12:00:00 "Stop connecting frequently" +BanOnEvent ClientConnectRate 10/00:00:02 02:00:00 "Stop connecting frequently" BanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00 BanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99 BanControlsACLs all allow user root diff --git a/lib/configfiles/jammy.xml b/lib/configfiles/jammy.xml index d7e4cd49..3ba120b7 100644 --- a/lib/configfiles/jammy.xml +++ b/lib/configfiles/jammy.xml @@ -3872,7 +3872,7 @@ MaxLoginAttempts 3 BanLog /var/log/proftpd/ban.log BanTable /etc/proftpd/ban.tab BanMessage "User %u was banned." -BanOnEvent ClientConnectRate 5/00:00:02 12:00:00 "Stop connecting frequently" +BanOnEvent ClientConnectRate 10/00:00:02 02:00:00 "Stop connecting frequently" BanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00 BanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99 BanControlsACLs all allow user root diff --git a/lib/configfiles/noble.xml b/lib/configfiles/noble.xml index f5a90212..08100a43 100644 --- a/lib/configfiles/noble.xml +++ b/lib/configfiles/noble.xml @@ -2547,7 +2547,7 @@ MaxLoginAttempts 3 BanLog /var/log/proftpd/ban.log BanTable /etc/proftpd/ban.tab BanMessage "User %u was banned." -BanOnEvent ClientConnectRate 5/00:00:02 12:00:00 "Stop connecting frequently" +BanOnEvent ClientConnectRate 10/00:00:02 02:00:00 "Stop connecting frequently" BanOnEvent MaxLoginAttempts 3/00:30:00 12:00:00 BanOnEvent AnonRejectPasswords 1/01:00:00 99:99:99 BanControlsACLs all allow user root diff --git a/lng/de.lng.php b/lng/de.lng.php index 167cf8ee..2f2c74dd 100644 --- a/lng/de.lng.php +++ b/lng/de.lng.php @@ -1824,7 +1824,7 @@ Vielen Dank, Ihr Administrator', ], 'documentroot_use_default_value' => [ 'title' => 'Verwende Domainnamen im Documentroot', - 'description' => 'Wenn aktiviert wird dem standard Documentroot zusätzlich der Domain-Name angehängt.

Beispiel:
/var/customers/customer_name/example.tld/
/var/customers/customer_name/subdomain.example.tld/', + 'description' => 'Wenn aktiviert wird dem standard Documentroot zusätzlich der Domain-Name angehängt.

Beispiel:
/var/customers/webs/customer_name/example.tld/
/var/customers/webs/customer_name/subdomain.example.tld/', ], 'panel_phpconfigs_hidesubdomains' => [ 'title' => 'Verstecke Subdomains in PHP-Konfigurations-Übersicht', diff --git a/lng/en.lng.php b/lng/en.lng.php index d51a4506..e784528e 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -1946,7 +1946,7 @@ Yours sincerely, your administrator', ], 'documentroot_use_default_value' => [ 'title' => 'Use domain name as default value for DocumentRoot path', - 'description' => 'If enabled and DocumentRoot path is empty, default value will be the (sub)domain name.

Examples:
/var/customers/customer_name/example.com/
/var/customers/customer_name/subdomain.example.com/', + 'description' => 'If enabled and DocumentRoot path is empty, default value will be the (sub)domain name.

Examples:
/var/customers/webs/customer_name/example.com/
/var/customers/webs/customer_name/subdomain.example.com/', ], 'panel_phpconfigs_hidesubdomains' => [ 'title' => 'Hide subdomains in PHP-configuration overview', From fc4041e88c7541fa11c5e91df5f966f6f9bc25b4 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 8 Aug 2024 09:18:49 +0200 Subject: [PATCH 7/7] fixing reports being sent daily under specific conditions; update dependencies Signed-off-by: Michael Kaufmann --- composer.lock | 192 ++++++++++++----------- lib/Froxlor/Cron/Traffic/ReportsCron.php | 44 +++--- 2 files changed, 124 insertions(+), 112 deletions(-) diff --git a/composer.lock b/composer.lock index f38eec75..d6752c98 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "amnuts/opcache-gui", - "version": "3.5.4", + "version": "3.5.5", "source": { "type": "git", "url": "https://github.com/amnuts/opcache-gui.git", - "reference": "f3a8fe44c0a4c69dd69b9999d68f9097ee362946" + "reference": "4ad9866c4bb5a96fd8a40353afb15f6934f4919b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amnuts/opcache-gui/zipball/f3a8fe44c0a4c69dd69b9999d68f9097ee362946", - "reference": "f3a8fe44c0a4c69dd69b9999d68f9097ee362946", + "url": "https://api.github.com/repos/amnuts/opcache-gui/zipball/4ad9866c4bb5a96fd8a40353afb15f6934f4919b", + "reference": "4ad9866c4bb5a96fd8a40353afb15f6934f4919b", "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.4" + "source": "https://github.com/amnuts/opcache-gui/tree/3.5.5" }, "funding": [ { @@ -66,20 +66,20 @@ "type": "github" } ], - "time": "2023-10-25T19:09:56+00:00" + "time": "2024-06-23T13:01:33+00:00" }, { "name": "dflydev/dot-access-data", - "version": "v3.0.2", + "version": "v3.0.3", "source": { "type": "git", "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "f41715465d65213d644d3141a6a93081be5d3549" + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", - "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", "shasum": "" }, "require": { @@ -139,9 +139,9 @@ ], "support": { "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" }, - "time": "2022-10-27T11:44:00+00:00" + "time": "2024-07-08T12:26:09+00:00" }, { "name": "froxlor/idna-convert-legacy", @@ -201,16 +201,16 @@ }, { "name": "league/commonmark", - "version": "2.4.2", + "version": "2.5.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf" + "reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/91c24291965bd6d7c46c46a12ba7492f83b1cadf", - "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/ac815920de0eff6de947eac0a6a94e5ed0fb147c", + "reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c", "shasum": "" }, "require": { @@ -223,8 +223,8 @@ }, "require-dev": { "cebe/markdown": "^1.0", - "commonmark/cmark": "0.30.3", - "commonmark/commonmark.js": "0.30.0", + "commonmark/cmark": "0.31.0", + "commonmark/commonmark.js": "0.31.0", "composer/package-versions-deprecated": "^1.8", "embed/embed": "^4.4", "erusev/parsedown": "^1.0", @@ -246,7 +246,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "2.6-dev" } }, "autoload": { @@ -303,7 +303,7 @@ "type": "tidelift" } ], - "time": "2024-02-02T11:59:32+00:00" + "time": "2024-07-24T12:52:09+00:00" }, { "name": "league/config", @@ -973,16 +973,16 @@ }, { "name": "symfony/console", - "version": "v5.4.40", + "version": "v5.4.42", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "aa73115c0c24220b523625bfcfa655d7d73662dd" + "reference": "cef62396a0477e94fc52e87a17c6e5c32e226b7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/aa73115c0c24220b523625bfcfa655d7d73662dd", - "reference": "aa73115c0c24220b523625bfcfa655d7d73662dd", + "url": "https://api.github.com/repos/symfony/console/zipball/cef62396a0477e94fc52e87a17c6e5c32e226b7f", + "reference": "cef62396a0477e94fc52e87a17c6e5c32e226b7f", "shasum": "" }, "require": { @@ -1052,7 +1052,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.40" + "source": "https://github.com/symfony/console/tree/v5.4.42" }, "funding": [ { @@ -1068,7 +1068,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-07-26T12:21:55+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1849,16 +1849,16 @@ }, { "name": "symfony/string", - "version": "v5.4.40", + "version": "v5.4.42", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "142877285aa974a6f7685e292ab5ba9aae86b143" + "reference": "909cec913edea162a3b2836788228ad45fcab337" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/142877285aa974a6f7685e292ab5ba9aae86b143", - "reference": "142877285aa974a6f7685e292ab5ba9aae86b143", + "url": "https://api.github.com/repos/symfony/string/zipball/909cec913edea162a3b2836788228ad45fcab337", + "reference": "909cec913edea162a3b2836788228ad45fcab337", "shasum": "" }, "require": { @@ -1915,7 +1915,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.40" + "source": "https://github.com/symfony/string/tree/v5.4.42" }, "funding": [ { @@ -1931,7 +1931,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-07-20T18:38:32+00:00" }, { "name": "twig/twig", @@ -2276,30 +2276,38 @@ "packages-dev": [ { "name": "composer/pcre", - "version": "3.1.4", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "04229f163664973f68f38f6f73d917799168ef24" + "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/04229f163664973f68f38f6f73d917799168ef24", - "reference": "04229f163664973f68f38f6f73d917799168ef24", + "url": "https://api.github.com/repos/composer/pcre/zipball/ea4ab6f9580a4fd221e0418f2c357cdd39102a90", + "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, + "conflict": { + "phpstan/phpstan": "<1.11.8" + }, "require-dev": { - "phpstan/phpstan": "^1.3", + "phpstan/phpstan": "^1.11.8", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" + "phpunit/phpunit": "^8 || ^9" }, "type": "library", "extra": { "branch-alias": { "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] } }, "autoload": { @@ -2327,7 +2335,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.4" + "source": "https://github.com/composer/pcre/tree/3.2.0" }, "funding": [ { @@ -2343,7 +2351,7 @@ "type": "tidelift" } ], - "time": "2024-05-27T13:40:54+00:00" + "time": "2024-07-25T09:36:02+00:00" }, { "name": "composer/xdebug-handler", @@ -2543,16 +2551,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { @@ -2563,7 +2571,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -2595,9 +2603,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { "name": "pdepend/pdepend", @@ -2988,16 +2996,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.5", + "version": "1.11.9", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "490f0ae1c92b082f154681d7849aee776a7c1443" + "reference": "e370bcddadaede0c1716338b262346f40d296f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/490f0ae1c92b082f154681d7849aee776a7c1443", - "reference": "490f0ae1c92b082f154681d7849aee776a7c1443", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e370bcddadaede0c1716338b262346f40d296f82", + "reference": "e370bcddadaede0c1716338b262346f40d296f82", "shasum": "" }, "require": { @@ -3042,7 +3050,7 @@ "type": "github" } ], - "time": "2024-06-17T15:10:54+00:00" + "time": "2024-08-01T16:25:18+00:00" }, { "name": "phpunit/php-code-coverage", @@ -3365,45 +3373,45 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.19", + "version": "9.6.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" + "reference": "49d7820565836236411f5dc002d16dd689cde42f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", + "reference": "49d7820565836236411f5dc002d16dd689cde42f", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", "sebastian/version": "^3.0.2" }, "suggest": { @@ -3448,7 +3456,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" }, "funding": [ { @@ -3464,7 +3472,7 @@ "type": "tidelift" } ], - "time": "2024-04-05T04:35:58+00:00" + "time": "2024-07-10T11:45:39+00:00" }, { "name": "sebastian/cli-parser", @@ -4493,16 +4501,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.10.1", + "version": "3.10.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877" + "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/8f90f7a53ce271935282967f53d0894f8f1ff877", - "reference": "8f90f7a53ce271935282967f53d0894f8f1ff877", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/86e5f5dd9a840c46810ebe5ff1885581c42a3017", + "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017", "shasum": "" }, "require": { @@ -4569,7 +4577,7 @@ "type": "open_collective" } ], - "time": "2024-05-22T21:24:41+00:00" + "time": "2024-07-21T23:26:44+00:00" }, { "name": "symfony/config", @@ -4652,16 +4660,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v5.4.40", + "version": "v5.4.42", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "408b33326496030c201b8051b003e9e8cdb2efc9" + "reference": "c8409889fdbf48b99271930ea0ebcf3ee5e1ceae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/408b33326496030c201b8051b003e9e8cdb2efc9", - "reference": "408b33326496030c201b8051b003e9e8cdb2efc9", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/c8409889fdbf48b99271930ea0ebcf3ee5e1ceae", + "reference": "c8409889fdbf48b99271930ea0ebcf3ee5e1ceae", "shasum": "" }, "require": { @@ -4721,7 +4729,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.40" + "source": "https://github.com/symfony/dependency-injection/tree/v5.4.42" }, "funding": [ { @@ -4737,20 +4745,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-07-25T13:57:40+00:00" }, { "name": "symfony/filesystem", - "version": "v5.4.40", + "version": "v5.4.41", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "26dd9912df6940810ea00f8f53ad48d6a3424995" + "reference": "6d29dd9340b372fa603f04e6df4dd76bb808591e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/26dd9912df6940810ea00f8f53ad48d6a3424995", - "reference": "26dd9912df6940810ea00f8f53ad48d6a3424995", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/6d29dd9340b372fa603f04e6df4dd76bb808591e", + "reference": "6d29dd9340b372fa603f04e6df4dd76bb808591e", "shasum": "" }, "require": { @@ -4788,7 +4796,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.40" + "source": "https://github.com/symfony/filesystem/tree/v5.4.41" }, "funding": [ { @@ -4804,7 +4812,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-06-28T09:36:24+00:00" }, { "name": "symfony/polyfill-php81", diff --git a/lib/Froxlor/Cron/Traffic/ReportsCron.php b/lib/Froxlor/Cron/Traffic/ReportsCron.php index cd8babaf..4a124b89 100644 --- a/lib/Froxlor/Cron/Traffic/ReportsCron.php +++ b/lib/Froxlor/Cron/Traffic/ReportsCron.php @@ -70,7 +70,7 @@ class ReportsCron extends FroxlorCron ) as `traffic_used` FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c` LEFT JOIN `" . TABLE_PANEL_ADMINS . "` AS `a` - ON `a`.`adminid` = `c`.`adminid` WHERE `c`.`reportsent` <> '1' + ON `a`.`adminid` = `c`.`adminid` WHERE `c`.`reportsent` & 1 = 0 "); $result_data = [ @@ -79,6 +79,11 @@ class ReportsCron extends FroxlorCron ]; Database::pexecute($result_stmt, $result_data); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = `reportsent` + 1 + WHERE `customerid` = :customerid + "); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { $row['traffic'] *= 1024; $row['traffic_used'] *= 1024; @@ -148,10 +153,6 @@ class ReportsCron extends FroxlorCron } $mail->ClearAddresses(); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = '1' - WHERE `customerid` = :customerid - "); Database::pexecute($upd_stmt, [ 'customerid' => $row['customerid'] ]); @@ -165,7 +166,7 @@ class ReportsCron extends FroxlorCron FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` `t` WHERE `t`.`adminid` = `a`.`adminid` AND `t`.`year` = :year AND `t`.`month` = :month ) as `traffic_used_total` - FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` = '0' + FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` & 1 = 0 "); $result_data = [ @@ -174,6 +175,11 @@ class ReportsCron extends FroxlorCron ]; Database::pexecute($result_stmt, $result_data); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = `reportsent` + 1 + WHERE `adminid` = :adminid + "); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { $row['traffic'] *= 1024; $row['traffic_used_total'] *= 1024; @@ -231,10 +237,6 @@ class ReportsCron extends FroxlorCron } $mail->ClearAddresses(); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = '1' - WHERE `adminid` = :adminid - "); Database::pexecute($upd_stmt, [ 'adminid' => $row['adminid'] ]); @@ -344,11 +346,16 @@ class ReportsCron extends FroxlorCron FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c` LEFT JOIN `" . TABLE_PANEL_ADMINS . "` AS `a` ON `a`.`adminid` = `c`.`adminid` - WHERE `c`.`diskspace` > '0' AND `c`.`reportsent` <> '2' + WHERE `c`.`diskspace` > '0' AND `c`.`reportsent` & 2 = 0 "); $mail = new Mailer(true); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = `reportsent` + 2 + WHERE `customerid` = :customerid + "); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { $row['diskspace'] *= 1024; $row['diskspace_used'] *= 1024; @@ -418,10 +425,6 @@ class ReportsCron extends FroxlorCron } $mail->ClearAddresses(); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = '2' - WHERE `customerid` = :customerid - "); Database::pexecute($upd_stmt, [ 'customerid' => $row['customerid'] ]); @@ -432,7 +435,12 @@ class ReportsCron extends FroxlorCron * report about diskusage for admins/reseller */ $result_stmt = Database::query(" - SELECT `a`.* FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` <> '2' + SELECT `a`.* FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` & 2 = 0 + "); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = `reportsent` + 2 + WHERE `adminid` = :adminid "); while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { @@ -492,10 +500,6 @@ class ReportsCron extends FroxlorCron } $mail->ClearAddresses(); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = '2' - WHERE `adminid` = :adminid - "); Database::pexecute($upd_stmt, [ 'adminid' => $row['adminid'] ]);