From 0cb7212d4b4154e83e515fb4e74316c84165fbbf Mon Sep 17 00:00:00 2001 From: boban Date: Wed, 29 Oct 2025 03:54:39 +0100 Subject: [PATCH] =?UTF-8?q?Fix:=20Mailbox=20Stats=20=C3=BCber=20Dovecot=20?= =?UTF-8?q?mit=20config/mailpool.php?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Ui/Domain/Modal/DomainDnsModal.php | 232 +++++++++--------- app/Livewire/Ui/System/UpdateCard.php | 1 + 2 files changed, 120 insertions(+), 113 deletions(-) diff --git a/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php b/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php index 377ddc6..672ba25 100644 --- a/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php +++ b/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php @@ -312,98 +312,22 @@ class DomainDnsModal extends ModalComponent return $line; } -// private function stateFor(string $type, string $expected, string $actual, bool $optional): string -// { -// if ($actual === '') { -// return $optional ? 'neutral' : 'missing'; -// } -// -// $type = strtoupper($type); -// $exp = $this->normExpected($type, $expected); -// $act = $this->normActual($type, $actual); -// -// // Syntax plausibilisieren -// $syntaxOk = $this->validateSyntax($type, $act); -// if (!$syntaxOk) return 'syntax'; -// -// // TXT-Policies: nur „Startet mit v=…“ prüfen → OK, -// // selbst wenn Inhalt nicht 1:1 dem Vorschlag entspricht. -// if ($type === 'TXT') { -// $upperExp = strtoupper($exp); -// $upperAct = strtoupper($act); -// if (str_starts_with($upperExp, 'V=SPF1')) return str_starts_with($upperAct, 'V=SPF1') ? 'ok' : 'syntax'; -// if (str_starts_with($upperExp, 'V=DMARC1')) return str_starts_with($upperAct, 'V=DMARC1') ? 'ok' : 'syntax'; -// if (str_starts_with($upperExp, 'V=DKIM1')) return str_starts_with($upperAct, 'V=DKIM1') ? 'ok' : 'syntax'; -// return ($act !== '') ? 'ok' : ($optional ? 'neutral' : 'missing'); -// } -// -// // MX: „prio host“ – wir prüfen Host grob -// if ($type === 'MX') { -// $parts = preg_split('/\s+/', $act); -// $host = strtolower($parts[1] ?? $act); -// $expHost = strtolower(preg_replace('/^\d+\s+/', '', $exp)); -// return ($host === $expHost) ? 'ok' : 'syntax'; -// } -// -// // SRV: „prio weight port host“ – Port + Host grob -// if ($type === 'SRV') { -// $ap = preg_split('/\s+/', $act); -// $ep = preg_split('/\s+/', $exp); -// if (count($ap) >= 4 && count($ep) >= 4) { -// $aport = (int)$ap[2]; -// $eport = (int)$ep[2]; -// $ahost = strtolower(rtrim(end($ap), '.')); -// $ehost = strtolower(rtrim(end($ep), '.')); -// return ($aport === $eport && $ahost === $ehost) ? 'ok' : 'syntax'; -// } -// return 'syntax'; -// } -// -// // CNAME/A/AAAA/PTR/TLSA: Gleichheit nach Normalisierung -// return ($act === $exp) ? 'ok' : 'syntax'; -// } - -// private function normExpected(string $type, string $v): string -// { -// $v = trim($v); -// $t = strtoupper($type); -// if (in_array($t, ['MX','CNAME','SRV'])) $v = rtrim($v, '.'); -// if ($t === 'PTR') $v = strtolower(rtrim($v, '.')); -// if ($t === 'TLSA') $v = preg_replace('/\s+/', ' ', $v); -// return $v; -// } - -// private function normActual(string $type, string $v): string -// { -// $v = trim($v); -// $t = strtoupper($type); -// if (in_array($t, ['MX','CNAME','SRV'])) $v = rtrim($v, '.'); -// if ($t === 'PTR') $v = strtolower(rtrim($v, '.')); -// if ($t === 'TLSA') { -// $v = preg_replace('/\s+/', '', $v); // Hash-Zeilen zusammenfügen -// $v = preg_replace('/^([0-3][\s]+[01][\s]+[123])/', '$1 ', $v); // spacing nach Header erzwingen -// } -// return $v; -// } - - private function stateFor(string $type, string $expected, string $actual, bool $optional): string - { + private function stateFor(string $type, string $expected, string $actual, bool $optional): string { if ($actual === '') return $optional ? 'neutral' : 'missing'; $type = strtoupper($type); $exp = $this->normExpected($type, $expected); $act = $this->normActual($type, $actual); - // Syntaxcheck nach Normalisierung + // Syntaxcheck auf dem **actual** if (!$this->validateSyntax($type, $act)) return 'syntax'; - // TXT: nur „v=…“-Präfix grob prüfen if ($type === 'TXT') { - $E = strtoupper($exp); - $A = strtoupper($act); - if (str_starts_with($E, 'V=SPF1')) return str_starts_with($A, 'V=SPF1') ? 'ok' : 'syntax'; - if (str_starts_with($E, 'V=DMARC1')) return str_starts_with($A, 'V=DMARC1') ? 'ok' : 'syntax'; - if (str_starts_with($E, 'V=DKIM1')) return str_starts_with($A, 'V=DKIM1') ? 'ok' : 'syntax'; + $ue = strtoupper($exp); + $ua = strtoupper($act); + if (str_starts_with($ue,'V=SPF1')) return str_starts_with($ua,'V=SPF1') ? 'ok' : 'syntax'; + if (str_starts_with($ue,'V=DMARC1')) return str_starts_with($ua,'V=DMARC1') ? 'ok' : 'syntax'; + if (str_starts_with($ue,'V=DKIM1')) return str_starts_with($ua,'V=DKIM1') ? 'ok' : 'syntax'; return 'ok'; } @@ -423,10 +347,51 @@ class DomainDnsModal extends ModalComponent return 'syntax'; } - // TLSA / A / AAAA / CNAME / PTR: exakter Vergleich nach Norm. + // A/AAAA/CNAME/PTR/TLSA return ($act === $exp) ? 'ok' : 'syntax'; } +// private function stateFor(string $type, string $expected, string $actual, bool $optional): string +// { +// if ($actual === '') return $optional ? 'neutral' : 'missing'; +// +// $type = strtoupper($type); +// $exp = $this->normExpected($type, $expected); +// $act = $this->normActual($type, $actual); +// +// // Syntaxcheck nach Normalisierung +// if (!$this->validateSyntax($type, $act)) return 'syntax'; +// +// // TXT: nur „v=…“-Präfix grob prüfen +// if ($type === 'TXT') { +// $E = strtoupper($exp); +// $A = strtoupper($act); +// if (str_starts_with($E, 'V=SPF1')) return str_starts_with($A, 'V=SPF1') ? 'ok' : 'syntax'; +// if (str_starts_with($E, 'V=DMARC1')) return str_starts_with($A, 'V=DMARC1') ? 'ok' : 'syntax'; +// if (str_starts_with($E, 'V=DKIM1')) return str_starts_with($A, 'V=DKIM1') ? 'ok' : 'syntax'; +// return 'ok'; +// } +// +// if ($type === 'MX') { +// $parts = preg_split('/\s+/', $act); +// $host = strtolower($parts[1] ?? $act); +// $expHost = strtolower(preg_replace('/^\d+\s+/', '', $exp)); +// return ($host === $expHost) ? 'ok' : 'syntax'; +// } +// +// if ($type === 'SRV') { +// $ap = preg_split('/\s+/', $act); +// $ep = preg_split('/\s+/', $exp); +// if (count($ap) >= 4 && count($ep) >= 4) { +// return ((int)$ap[2] === (int)$ep[2] && strtolower(end($ap)) === strtolower(end($ep))) ? 'ok' : 'syntax'; +// } +// return 'syntax'; +// } +// +// // TLSA / A / AAAA / CNAME / PTR: exakter Vergleich nach Norm. +// return ($act === $exp) ? 'ok' : 'syntax'; +// } + private function canonicalizeTlsa(?string $v): ?string { if (!$v) return null; @@ -444,26 +409,67 @@ class DomainDnsModal extends ModalComponent return sprintf('%s %s %s %s', $u, $s, $m, $hash); } - private function normExpected(string $type, string $v): string - { + private function normExpected(string $type, string $v): string { $v = trim($v); $t = strtoupper($type); if (in_array($t, ['MX','CNAME','SRV'])) $v = rtrim($v, '.'); - if ($t === 'PTR') $v = strtolower(rtrim($v, '.')); - if ($t === 'TLSA') $v = $this->canonicalizeTlsa($v) ?? $v; + if ($t === 'PTR') $v = strtolower(rtrim($v, '.')); // Hostname als Value + if ($t === 'TLSA') $v = strtolower(preg_replace('/\s+/', '', $v)); // hex compact, lc + if ($t === 'TXT') $v = trim($v, "\"'"); return $v; } - private function normActual(string $type, string $v): string - { + private function normActual(string $type, string $v): string { $v = trim($v); $t = strtoupper($type); + // nur 1. Antwortzeile + if ($v !== '') $v = trim(explode("\n", $v)[0]); if (in_array($t, ['MX','CNAME','SRV'])) $v = rtrim($v, '.'); - if ($t === 'PTR') $v = strtolower(rtrim($v, '.')); - if ($t === 'TLSA') $v = $this->canonicalizeTlsa($v) ?? $v; + if ($t === 'PTR') $v = strtolower(rtrim($v, '.')); // Hostname als Value + if ($t === 'TLSA') $v = strtolower(preg_replace('/\s+/', '', $v)); // hex compact, lc + if ($t === 'TXT') $v = trim($v, "\"'"); return $v; } + private function validateSyntax(string $type, string $val): bool { + $t = strtoupper($type); + if ($val === '') return false; + + return match ($t) { + 'A' => (bool)filter_var($val, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4), + 'AAAA' => (bool)filter_var($val, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6), + 'CNAME' => (bool)preg_match('/^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i', $val), + // PTR: Value ist ein Hostname (rdns zeigt auf FQDN) + 'PTR' => (bool)preg_match('/^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i', $val), + 'MX' => (bool)preg_match('/^\d+\s+[a-z0-9._-]+$/i', $val), + 'SRV' => (bool)preg_match('/^\d+\s+\d+\s+\d+\s+[a-z0-9._-]+$/i', $val), + // TLSA: „usage selector matching hex…“ (hex jetzt schon lc/kompakt) + 'TLSA' => (bool)preg_match('/^[0-3][01][123][0-9a-f]{32,}$/', str_replace(' ','',$val)), + 'TXT' => strlen($val) > 0, + default => true, + }; + } + +// private function normExpected(string $type, string $v): string +// { +// $v = trim($v); +// $t = strtoupper($type); +// if (in_array($t, ['MX','CNAME','SRV'])) $v = rtrim($v, '.'); +// if ($t === 'PTR') $v = strtolower(rtrim($v, '.')); +// if ($t === 'TLSA') $v = $this->canonicalizeTlsa($v) ?? $v; +// return $v; +// } +// +// private function normActual(string $type, string $v): string +// { +// $v = trim($v); +// $t = strtoupper($type); +// if (in_array($t, ['MX','CNAME','SRV'])) $v = rtrim($v, '.'); +// if ($t === 'PTR') $v = strtolower(rtrim($v, '.')); +// if ($t === 'TLSA') $v = $this->canonicalizeTlsa($v) ?? $v; +// return $v; +// } + // private function validateSyntax(string $type, string $val): bool // { // $t = strtoupper($type); @@ -482,27 +488,27 @@ class DomainDnsModal extends ModalComponent // }; // } - private function validateSyntax(string $type, string $val): bool - { - $t = strtoupper($type); - if ($val === '') return false; - - if ($t === 'TLSA') { - $canon = $this->canonicalizeTlsa($val); - return is_string($canon) && (bool)preg_match('/^[0-3]\s+[01]\s+[123]\s+[0-9a-f]{32,}$/', $canon); - } - - return match ($t) { - 'A' => (bool)filter_var($val, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4), - 'AAAA' => (bool)filter_var($val, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6), - 'CNAME' => (bool)preg_match('/^[a-z0-9._-]+$/i', $val), - 'PTR' => (bool)preg_match('/\.(in-addr|ip6)\.arpa$/i', $val), - 'MX' => (bool)preg_match('/^\d+\s+[a-z0-9._-]+$/i', $val), - 'SRV' => (bool)preg_match('/^\d+\s+\d+\s+\d+\s+[a-z0-9._-]+$/i', $val), - 'TXT' => strlen($val) > 0, - default => true, - }; - } +// private function validateSyntax(string $type, string $val): bool +// { +// $t = strtoupper($type); +// if ($val === '') return false; +// +// if ($t === 'TLSA') { +// $canon = $this->canonicalizeTlsa($val); +// return is_string($canon) && (bool)preg_match('/^[0-3]\s+[01]\s+[123]\s+[0-9a-f]{32,}$/', $canon); +// } +// +// return match ($t) { +// 'A' => (bool)filter_var($val, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4), +// 'AAAA' => (bool)filter_var($val, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6), +// 'CNAME' => (bool)preg_match('/^[a-z0-9._-]+$/i', $val), +// 'PTR' => (bool)preg_match('/\.(in-addr|ip6)\.arpa$/i', $val), +// 'MX' => (bool)preg_match('/^\d+\s+[a-z0-9._-]+$/i', $val), +// 'SRV' => (bool)preg_match('/^\d+\s+\d+\s+\d+\s+[a-z0-9._-]+$/i', $val), +// 'TXT' => strlen($val) > 0, +// default => true, +// }; +// } /** Vergleich je Typ robust normalisieren (nicht genutzt, bleibt aber da) */ private function compareDns(string $type, string $expected, string $actual): bool diff --git a/app/Livewire/Ui/System/UpdateCard.php b/app/Livewire/Ui/System/UpdateCard.php index def4b92..7b5177b 100644 --- a/app/Livewire/Ui/System/UpdateCard.php +++ b/app/Livewire/Ui/System/UpdateCard.php @@ -120,6 +120,7 @@ class UpdateCard extends Component $ver = $this->displayCurrent ?? 'aktuelle Version'; $this->progressLine = 'Update abgeschlossen: ' . $ver; + @shell_exec('nohup php /var/www/mailwolt/artisan optimize:clear >/dev/null 2>&1 &'); // Optional: NICHT sofort reloaden – Nutzer entscheidet // $this->dispatch('reload-page', delay: 6000); } elseif ($this->rc !== null && $this->rc !== 0 && !$this->postActionsDone) {