mailwolt/app/Console/Commands/UpdateMailboxStats.php

137 lines
4.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Console\Commands;
use App\Models\MailUser;
use App\Models\Setting;
use Illuminate\Console\Command;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
class UpdateMailboxStats extends Command
{
protected $signature = 'mail:update-stats {--user=}';
protected $description = 'Aktualisiert Mailquota und Nachrichtenzahl für alle Mailboxen (oder einen Benutzer)';
public function handle(): int
{
$q = MailUser::query()
->where('is_active', true)
->whereNotNull('email')
->where('email', 'like', '%@%');
if ($email = trim((string)$this->option('user'))) {
$q->where('email', $email);
}
$users = $q->get();
if ($users->isEmpty()) {
$this->warn('Keine passenden Mailboxen gefunden.');
return self::SUCCESS;
}
foreach ($users as $u) {
$email = trim($u->email);
if (!preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) {
// ungültig → überspringen
continue;
}
[$local, $domain] = explode('@', $email, 2);
$maildir = "/var/mail/vhosts/{$domain}/{$local}";
// 1) Größe (rekursiv, ohne `du`)
$usedBytes = 0;
if (is_dir($maildir)) {
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($maildir, \FilesystemIterator::SKIP_DOTS)
);
foreach ($it as $file) {
if ($file->isFile()) {
$usedBytes += $file->getSize();
}
}
}
// 2) Nachrichtenzahl bevorzugt via doveadm (sudo -u vmail), Fallback: Dateien zählen
$messageCount = $this->messageCountViaDoveadm($email);
if ($messageCount === null) {
$messageCount = $this->messageCountViaFilesystem($maildir);
}
Setting::set("mailbox.{$email}", [
'used_bytes' => $usedBytes,
'message_count' => $messageCount,
'updated_at' => now()->toDateTimeString(),
]);
$this->line(sprintf(
"%-35s %7.1f MiB %5d msgs",
$email,
$usedBytes / 1024 / 1024,
$messageCount
));
}
$this->info('Mailbox-Statistiken aktualisiert.');
return self::SUCCESS;
}
/**
* Holt die Anzahl der Nachrichten in INBOX über doveadm als vmail (sudo).
* Gibt int bei Erfolg, oder null bei Fehlern zurück.
*/
private function messageCountViaDoveadm(string $email): ?int
{
// bevorzugtes Format: tabellarisch → "INBOX\t123"
$cmd = sprintf(
"sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u %s messages INBOX 2>/dev/null",
escapeshellarg($email)
);
$out = trim((string) shell_exec($cmd));
if ($out === '') {
// Fallback auf normales Format: "messages=123"
$cmd2 = sprintf(
"sudo -n -u vmail /usr/bin/doveadm mailbox status -u %s messages INBOX 2>/dev/null",
escapeshellarg($email)
);
$out = trim((string) shell_exec($cmd2));
}
if ($out === '') {
return null;
}
// Match "INBOX<TAB>123" oder "messages=123"
if (preg_match('/\t(\d+)\s*$/', $out, $m) || preg_match('/messages=(\d+)/', $out, $m)) {
return (int) $m[1];
}
return null;
}
/**
* Fallback: Zählt Dateien in cur/ + new/ (Maildir).
*/
private function messageCountViaFilesystem(string $maildir): int
{
$count = 0;
foreach (['cur', 'new'] as $sub) {
$dir = "{$maildir}/{$sub}";
if (is_dir($dir) && is_readable($dir)) {
$dh = opendir($dir);
if ($dh) {
while (($fn = readdir($dh)) !== false) {
if ($fn !== '.' && $fn !== '..' && $fn[0] !== '.') {
$count++;
}
}
closedir($dh);
}
}
}
return $count;
}
}