mailwolt/app/Livewire/Ui/Security/ClamavManager.php

159 lines
5.0 KiB
PHP

<?php
namespace App\Livewire\Ui\Security;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
#[Layout('layouts.dvx')]
#[Title('Virenschutz · Mailwolt')]
class ClamavManager extends Component
{
public bool $installed = false;
public bool $running = false;
public bool $enabled = false;
public string $dbDate = '—';
public string $dbVersion = '—';
public ?int $ramMb = null;
public string $lastError = '';
public bool $installing = false;
public function mount(): void
{
$this->refresh();
}
public function refresh(): void
{
$this->lastError = '';
$this->installed = $this->isInstalled();
$this->running = $this->installed && $this->serviceActive();
$this->enabled = $this->installed && $this->serviceEnabled();
$this->ramMb = $this->running ? $this->readRamMb() : null;
[$this->dbDate, $this->dbVersion] = $this->readDbInfo();
}
public function install(): void
{
$this->installing = true;
$out = [];
@exec('sudo -n /usr/local/sbin/mailwolt-clamav install 2>&1', $out, $rc);
$this->installing = false;
if ($rc !== 0) {
$this->lastError = implode(' ', $out) ?: 'Installation fehlgeschlagen.';
} else {
$this->dispatch('toast', type: 'success', badge: 'ClamAV',
title: 'Installiert', text: 'ClamAV wurde installiert. Sie können ihn jetzt aktivieren.');
}
$this->refresh();
}
public function enable(): void
{
if (!$this->installed) {
$this->lastError = 'ClamAV ist nicht installiert.';
return;
}
$out = [];
@exec('sudo -n /usr/local/sbin/mailwolt-clamav enable 2>&1', $out, $rc);
if ($rc !== 0) {
$this->lastError = implode(' ', $out) ?: 'Fehler beim Aktivieren.';
} else {
$this->dispatch('toast', type: 'success', badge: 'ClamAV',
title: 'Aktiviert', text: 'ClamAV läuft jetzt und startet automatisch beim Booten.');
}
$this->refresh();
}
public function disable(): void
{
$out = [];
@exec('sudo -n /usr/local/sbin/mailwolt-clamav disable 2>&1', $out, $rc);
if ($rc !== 0) {
$this->lastError = implode(' ', $out) ?: 'Fehler beim Deaktivieren.';
} else {
$this->dispatch('toast', type: 'success', badge: 'ClamAV',
title: 'Deaktiviert', text: 'ClamAV wurde gestoppt und aus dem Autostart entfernt.');
}
$this->refresh();
}
public function updateDb(): void
{
$out = [];
@exec('sudo -n /usr/local/sbin/mailwolt-clamav freshclam 2>&1', $out, $rc);
if ($rc !== 0) {
$this->lastError = implode(' ', $out) ?: 'Fehler beim Aktualisieren.';
} else {
$this->dispatch('toast', type: 'success', badge: 'ClamAV',
title: 'Datenbank aktualisiert', text: 'Virensignaturen wurden aktualisiert.');
}
$this->refresh();
}
private function isInstalled(): bool
{
return is_executable('/usr/bin/clamscan') || is_executable('/usr/sbin/clamd')
|| @file_exists('/usr/sbin/clamd') || @file_exists('/usr/bin/clamdscan');
}
private function serviceActive(): bool
{
$exit = null;
@exec('systemctl is-active --quiet clamav-daemon 2>/dev/null', $_, $exit);
return $exit === 0;
}
private function serviceEnabled(): bool
{
$exit = null;
@exec('systemctl is-enabled --quiet clamav-daemon 2>/dev/null', $_, $exit);
return $exit === 0;
}
private function readRamMb(): ?int
{
$out = [];
@exec("ps -C clamd -o rss= 2>/dev/null", $out);
$kb = array_sum(array_map('intval', array_filter($out)));
return $kb > 0 ? (int) round($kb / 1024) : null;
}
private function readDbInfo(): array
{
$paths = [
'/var/lib/clamav/main.cvd',
'/var/lib/clamav/main.cld',
'/var/lib/clamav/daily.cvd',
'/var/lib/clamav/daily.cld',
];
$latest = 0;
foreach ($paths as $p) {
if (@file_exists($p)) {
$mtime = @filemtime($p);
if ($mtime > $latest) $latest = $mtime;
}
}
if ($latest === 0) return ['—', '—'];
$date = date('d.m.Y H:i', $latest);
$ver = '—';
$out = [];
@exec('sigtool --info /var/lib/clamav/daily.cld 2>/dev/null | grep "^Version:" | head -1', $out);
if (empty($out)) {
@exec('sigtool --info /var/lib/clamav/daily.cvd 2>/dev/null | grep "^Version:" | head -1', $out);
}
if (!empty($out[0])) {
$ver = trim(str_replace('Version:', '', $out[0]));
}
return [$date, $ver];
}
public function render()
{
return view('livewire.ui.security.clamav-manager');
}
}