Fix: Mailbox Stats über Dovecot mit config/mailpool.php

main v1.0.114
boban 2025-10-31 03:14:26 +01:00
parent 792f0e3528
commit 81860d1851
1 changed files with 71 additions and 15 deletions

View File

@ -21,31 +21,48 @@ class Fail2BanJailModal extends ModalComponent
{
$jail = $this->jail;
[, $s] = $this->f2b('status '.escapeshellarg($jail));
$ipList = $this->firstMatch('/Banned IP list:\s*(.+)$/mi', $s) ?: '';
$ips = $ipList !== '' ? array_values(array_filter(array_map('trim', preg_split('/\s+/', $ipList)))) : [];
[, $s] = $this->f2b('status '.escapeshellarg($jail));
$ipList = $this->firstMatch('/Banned IP list:\s*(.+)$/mi', $s) ?: '';
$ips = $ipList !== '' ? array_values(array_filter(array_map('trim', preg_split('/\s+/', $ipList)))) : [];
$bantime = $this->getBantime($jail);
$defaultBantime = $this->getBantime($jail);
$rows = [];
foreach ($ips as $ip) {
$banAt = $this->lastBanTimestamp($jail, $ip); // Unix-Timestamp oder null
$banAt = null; $until = null; $remaining = null;
$remaining = null; $until = null;
if ($bantime === -1) {
$remaining = -1; // permanent
} elseif ($banAt !== null) {
$remaining = max(0, $bantime - (time() - $banAt));
$until = $remaining > 0 ? $banAt + $bantime : null;
} else {
$remaining = -2; // ≈ Approximation (Bantime bekannt, Start unbekannt)
// 1) Primärquelle: DB
if ($info = $this->banInfoFromDb($jail, $ip)) {
$banAt = $info['banned_at'];
if ((int)$info['expire'] === -1) {
$remaining = -1; // permanent
} elseif ((int)$info['expire'] > 0) {
$until = (int)$info['expire'];
$remaining = max(0, $until - time());
}
}
// 2) Fallback: Log + Jail-Bantime
if ($remaining === null) {
$banAt = $banAt ?? $this->lastBanTimestamp($jail, $ip);
if ($banAt !== null) {
$remaining = max(0, $defaultBantime - (time() - $banAt));
$until = $remaining > 0 ? $banAt + $defaultBantime : null;
} else {
$remaining = -2; // ~approx
}
}
// 3) Wenn 0 Sekunden, aber Fail2Ban hält die IP noch → „verlängert/unbekannt“
if ($remaining === 0 && $this->isStillBanned($jail, $ip)) {
$remaining = -2; // markiere als „~ unbekannt/verlängert“
}
[$timeText, $metaText, $boxClass] = $this->present($remaining, $banAt, $until);
$rows[] = [
'ip' => $ip,
'bantime' => $bantime,
'bantime' => $defaultBantime,
'banned_at' => $banAt,
'remaining' => $remaining,
'until' => $until,
@ -57,7 +74,7 @@ class Fail2BanJailModal extends ModalComponent
$this->rows = $rows;
}
/** ROBUST: findet Binaries automatisch */
private function bin(string $name): string
{
@ -117,6 +134,18 @@ class Fail2BanJailModal extends ModalComponent
return null;
}
private function getDbFile(): string
{
[, $out] = $this->f2b('get dbfile');
$path = trim($out);
return $path !== '' ? $path : '/var/lib/fail2ban/fail2ban.sqlite3';
}
private function sql(string $s): string
{
return "'".str_replace("'", "''", $s)."'";
}
/** Darstellung: permanent / Restzeit / abgelaufen / ~approx / unbekannt */
private function present(?int $remaining, ?int $banAt, ?int $until): array
{
@ -155,6 +184,33 @@ class Fail2BanJailModal extends ModalComponent
return [$ok, $out];
}
private function banInfoFromDb(string $jail, string $ip): ?array
{
$sudo = $this->bin('sudo');
$sqlite = $this->bin('sqlite3');
$db = $this->getDbFile();
$q = sprintf(
"SELECT timeofban, expiretime FROM bans WHERE jail=%s AND ip=%s ORDER BY timeofban DESC LIMIT 1",
$this->sql($jail),
$this->sql($ip)
);
$cmd = "$sudo -n $sqlite -readonly ".escapeshellarg($db).' '.escapeshellarg($q);
$out = trim((string)@shell_exec($cmd));
if ($out === '') return null;
[$timeofban, $expire] = array_map('intval', explode('|', $out)) + [null, null];
return ['banned_at' => $timeofban ?: null, 'expire' => $expire ?? null];
}
private function isStillBanned(string $jail, string $ip): bool
{
[, $out] = $this->f2b('get '.escapeshellarg($jail).' banip '.escapeshellarg($ip));
// gibt in der Regel "1" (banned) oder nichts/0 zurück
return (bool)preg_match('/\b1\b/', $out);
}
private function getBantime(string $jail): int
{
[, $out] = $this->f2b('get '.escapeshellarg($jail).' bantime');