option('user')) ?: null; $t0 = microtime(true); // Basis-Query: nur aktive, keine System-Mailboxen und keine System-Domains $base = MailUser::query() ->select(['id', 'domain_id', 'localpart', 'email', 'is_active', 'is_system']) ->with(['domain:id,domain,is_system']) ->where('is_active', true) ->where('is_system', false) ->whereHas('domain', fn($d) => $d->where('is_system', false)); if ($onlyUser) { $base->where('email', $onlyUser); } $checked = 0; $changed = 0; $log->info('mail:update-stats START', ['only' => $onlyUser]); $base->orderBy('id')->chunkById(200, function ($users) use (&$checked, &$changed, $log) { foreach ($users as $u) { $checked++; // Email robust bestimmen (raw -> accessor -> zusammengesetzt) $raw = (string)($u->getRawOriginal('email') ?? ''); $email = $raw !== '' ? $raw : ($u->email ?? $u->address ?? null); if (!is_string($email) || !preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) { // still – kein Log-Spam continue; } [$local, $domain] = explode('@', $email, 2); $maildir = "/var/mail/vhosts/{$domain}/{$local}"; // Größe in Bytes (rekursiv) $usedBytes = 0; if (is_dir($maildir)) { $it = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($maildir, \FilesystemIterator::SKIP_DOTS) ); foreach ($it as $f) { if ($f->isFile()) $usedBytes += $f->getSize(); } } // Message-Count $messageCount = $this->countViaDoveadm($email); if ($messageCount === null) { $messageCount = $this->countViaFilesystem($maildir); } $key = "mailbox.{$email}"; $prev = (array)(Setting::get($key, []) ?: []); $new = [ 'used_bytes' => (int)$usedBytes, 'message_count' => (int)$messageCount, 'updated_at' => now()->toDateTimeString(), ]; if (($prev['used_bytes'] ?? null) !== $new['used_bytes'] || ($prev['message_count'] ?? null) !== $new['message_count']) { Setting::set($key, $new); $changed++; // kurze Ausgabe & Info-Log NUR bei Änderung $this->line(sprintf("%-35s %7.1f MiB %5d msgs", $email, $usedBytes / 1048576, $messageCount)); $log->info('updated', ['email' => $email, 'used_bytes' => $new['used_bytes'], 'message_count' => $new['message_count']]); } } }); $ms = (int)((microtime(true) - $t0) * 1000); $log->info('mail:update-stats DONE', compact('checked', 'changed', 'ms')); $this->info('Mailbox-Statistiken aktualisiert.'); return self::SUCCESS; } private function countViaDoveadm(string $email): ?int { $cmd = "sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u " . escapeshellarg($email) . " messages INBOX 2>&1"; $out = []; $rc = 0; exec($cmd, $out, $rc); if ($rc !== 0) return null; foreach ($out as $line) { if (preg_match('/^\s*INBOX\s+(\d+)\s*$/i', trim($line), $m)) { return (int)$m[1]; } } return null; } private function countViaFilesystem(string $maildir): int { $n = 0; foreach (['cur', 'new'] as $sub) { $dir = "{$maildir}/{$sub}"; if (!is_dir($dir)) continue; $h = opendir($dir); if (!$h) continue; while (($fn = readdir($h)) !== false) { if ($fn === '.' || $fn === '..' || $fn[0] === '.') continue; $n++; } closedir($h); } return $n; } } //namespace App\Console\Commands; // //use App\Models\MailUser; //use App\Models\Setting; //use Illuminate\Console\Command; //use Illuminate\Support\Facades\Log; //use RecursiveDirectoryIterator; //use RecursiveIteratorIterator; // //class UpdateMailboxStats extends Command //{ // protected $signature = 'mail:update-stats {--user=}'; // protected $description = 'Aktualisiert Quota & Nachrichtenzahl (Settings/Redis; ohne MailUser-DB-Felder).'; // // public function handle(): int // { // $log = Log::channel('mailstats'); // // $onlyUser = trim((string)$this->option('user')) ?: null; // $started = microtime(true); // // $q = MailUser::query() // ->with('domain:id,domain,is_system') // ->where('is_active', true) // ->where('is_system', false) // keine System-Mailboxen // ->whereHas('domain', fn($d) => $d->where('is_system', false)); // keine Systemdomains // // if ($onlyUser) { // $q->where('email', $onlyUser); // } // // $users = $q->get(); // // $log->info('mail:update-stats START', [ // 'only' => $onlyUser, // 'users' => $users->count(), // ]); // // if ($users->isEmpty()) { // $this->warn('Keine passenden Mailboxen gefunden.'); // $log->info('mail:update-stats DONE (no users)', ['ms' => (int)((microtime(true) - $started) * 1000)]); // return self::SUCCESS; // } // // $changed = 0; // $checked = 0; // // foreach ($users as $u) { // $checked++; // // // Email robust ermitteln // $raw = (string)($u->getRawOriginal('email') ?? ''); // $email = $raw !== '' ? $raw : ($u->email ?? $u->address ?? null); // // if (!is_string($email) || !preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) { // // nur debug – vermeidet Spam // $log->debug('skip invalid email', ['user_id' => $u->id, 'raw' => $raw, 'computed' => $email]); // continue; // } // // [$local, $domain] = explode('@', $email, 2); // $maildir = "/var/mail/vhosts/{$domain}/{$local}"; // // // Größe (Dateisystem) // $usedBytes = 0; // if (is_dir($maildir)) { // $it = new RecursiveIteratorIterator( // new RecursiveDirectoryIterator($maildir, \FilesystemIterator::SKIP_DOTS) // ); // foreach ($it as $f) { // if ($f->isFile()) { // $usedBytes += $f->getSize(); // } // } // } // // // Nachrichten zählen // $messageCount = $this->countViaDoveadm($email); // if ($messageCount === null) { // $messageCount = $this->countViaFilesystem($maildir); // } // // // Nur bei Änderungen persistieren + loggen // $key = "mailbox.{$email}"; // $prev = Setting::get($key, null) ?: []; // // $new = [ // 'used_bytes' => (int)$usedBytes, // 'message_count' => (int)$messageCount, // 'updated_at' => now()->toDateTimeString(), // ]; // // if ( // !is_array($prev) || // ($prev['used_bytes'] ?? null) !== $new['used_bytes'] || // ($prev['message_count'] ?? null) !== $new['message_count'] // ) { // Setting::set($key, $new); // $changed++; // // // kurze, nützliche Info – nur bei Änderung // $this->line(sprintf("%-35s %7.1f MiB %5d msgs", // $email, $usedBytes / 1024 / 1024, $messageCount)); // // $log->info('updated', [ // 'email' => $email, // 'used_bytes' => $new['used_bytes'], // 'message_count' => $new['message_count'], // ]); // } else { // // keine Änderung → kein Info-Log // $log->debug('unchanged', ['email' => $email]); // } // } // // $ms = (int)((microtime(true) - $started) * 1000); // $log->info('mail:update-stats DONE', [ // 'checked' => $checked, // 'changed' => $changed, // 'ms' => $ms, // ]); // // $this->info('Mailbox-Statistiken aktualisiert.'); // return self::SUCCESS; // } // // protected function countViaDoveadm(string $email): ?int // { // $cmd = "sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u " // . escapeshellarg($email) . " messages INBOX 2>&1"; // $out = []; // $rc = 0; // exec($cmd, $out, $rc); // // if ($rc !== 0) return null; // // foreach ($out as $line) { // if (preg_match('/^\s*INBOX\s+(\d+)\s*$/i', trim($line), $m)) { // return (int)$m[1]; // } // } // return null; // } // // protected function countViaFilesystem(string $maildir): int // { // $n = 0; // foreach (['cur', 'new'] as $sub) { // $dir = "{$maildir}/{$sub}"; // if (!is_dir($dir)) continue; // $h = opendir($dir); // if (!$h) continue; // while (($fn = readdir($h)) !== false) { // if ($fn === '.' || $fn === '..' || $fn[0] === '.') continue; // $n++; // } // closedir($h); // } // return $n; // } //} //namespace App\Console\Commands; // //use App\Models\MailUser; //use App\Models\Setting; //use Illuminate\Console\Command; //use Illuminate\Support\Facades\Log; //use RecursiveDirectoryIterator; //use RecursiveIteratorIterator; // //class UpdateMailboxStats extends Command //{ // protected $signature = 'mail:update-stats {--user=}'; // protected $description = 'Aktualisiert Quota & Nachrichtenzahl (Settings/Redis; DB-frei)'; // // public function handle(): int // { // Log::info('=== mail:update-stats START ===', ['userOpt' => $this->option('user')]); // // $q = MailUser::query() // ->with('domain:id,domain') // ->active(); // // if ($only = trim((string)$this->option('user'))) { // $q->byEmail($only); // } // // $users = $q->get(); // if ($users->isEmpty()) { // Log::warning('Keine passenden Mailboxen gefunden.'); // $this->warn('Keine passenden Mailboxen gefunden.'); // return self::SUCCESS; // } // // foreach ($users as $u) { // // 1) sichere E-Mail ermitteln: roher DB-Wert → Accessor → zusammengesetzt // $raw = (string)($u->getRawOriginal('email') ?? ''); // $email = $raw !== '' ? $raw : ($u->email ?? $u->address ?? ''); // // if (!preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', (string)$email)) { // Log::warning('Ungültige effektive Adresse – skip', [ // 'user_id' => $u->id, 'raw' => $raw, 'computed' => $email // ]); // continue; // } // // [$local, $domain] = explode('@', $email, 2); // $maildir = "/var/mail/vhosts/{$domain}/{$local}"; // // // 2) Größe (Dateisystem) // $usedBytes = 0; // if (is_dir($maildir)) { // $it = new RecursiveIteratorIterator( // new RecursiveDirectoryIterator($maildir, \FilesystemIterator::SKIP_DOTS) // ); // foreach ($it as $f) { // if ($f->isFile()) $usedBytes += $f->getSize(); // } // } // // // 3) Message-Count – prefer doveadm als vmail; Fallback: Files // $messageCount = $this->countViaDoveadm($email); // if ($messageCount === null) { // $messageCount = $this->countViaFilesystem($maildir); // } // // // 4) In Settings (→ Redis + DB-Backup) persistieren // Setting::set("mailbox.{$email}", [ // 'used_bytes' => (int)$usedBytes, // 'message_count' => (int)$messageCount, // 'updated_at' => now()->toDateTimeString(), // ]); // // $this->line(sprintf("%-35s %7.1f MiB %5d msgs", // $email, $usedBytes / 1024 / 1024, $messageCount)); // // Log::info('Mailbox-Stat OK', [ // 'email' => $email, // 'maildir' => $maildir, // 'used_bytes' => $usedBytes, // 'message_count' => $messageCount, // ]); // } // // Log::info('=== mail:update-stats DONE ==='); // $this->info('Mailbox-Statistiken aktualisiert.'); // return self::SUCCESS; // } // // protected function countViaDoveadm(string $email): ?int // { // $cmd = "sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u " // . escapeshellarg($email) . " messages INBOX 2>&1"; // $out = []; // $rc = 0; // exec($cmd, $out, $rc); // if ($rc !== 0) return null; // // foreach ($out as $line) { // if (preg_match('/^\s*INBOX\s+(\d+)\s*$/i', trim($line), $m)) { // return (int)$m[1]; // } // } // return null; // } // // protected function countViaFilesystem(string $maildir): int // { // $n = 0; // foreach (['cur', 'new'] as $sub) { // $dir = "{$maildir}/{$sub}"; // if (!is_dir($dir)) continue; // $h = opendir($dir); // if (!$h) continue; // while (($fn = readdir($h)) !== false) { // if ($fn === '.' || $fn === '..' || $fn[0] === '.') continue; // $n++; // } // closedir($h); // } // return $n; // } //} //namespace App\Console\Commands; // //use Illuminate\Console\Command; //use Illuminate\Support\Facades\Log; //use App\Models\MailUser; //use App\Models\Setting; //use RecursiveDirectoryIterator; //use RecursiveIteratorIterator; //class UpdateMailboxStats extends Command //{ // protected $signature = 'mail:update-stats {--user=}'; // protected $description = 'Aktualisiert Mailquota und Nachrichtenzahl'; // // public function handle(): int // { // Log::info('=== mail:update-stats START ===', ['userOpt' => $this->option('user')]); // // $q = MailUser::query() // ->with('domain:id,domain') // <— Domain fürs Fallback laden // ->where('is_active', true); // // if ($only = trim((string)$this->option('user'))) { // $q->where('email', $only); // } else { // $q->whereNotNull('email')->where('email', 'like', '%@%'); // } // // $users = $q->get(); // if ($users->isEmpty()) { // Log::warning('Keine passenden Mailboxen gefunden.'); // $this->warn('Keine passenden Mailboxen gefunden.'); // return self::SUCCESS; // } // // foreach ($users as $u) { // // 1) rohen DB-Wert nehmen (ohne Accessor/Casts) // $rawEmail = (string) $u->getRawOriginal('email'); // // // 2) Fallback aus localpart + domain, falls roher Wert leer // $fallback = ($u->localpart ?? '') . '@' . ($u->domain->domain ?? ''); // $email = trim($rawEmail !== '' ? $rawEmail : $fallback); // // if (!preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) { // Log::warning('Ungültige effektive Adresse – skip', [ // 'raw' => $rawEmail, // 'fallback' => $fallback, // 'got' => $email, // 'user_id' => $u->id, // ]); // continue; // } // // [$local, $domain] = explode('@', $email, 2); // $maildir = "/var/mail/vhosts/{$domain}/{$local}"; // // // Größe (Dateisystem) // $usedBytes = 0; // if (is_dir($maildir)) { // $it = new RecursiveIteratorIterator( // new RecursiveDirectoryIterator($maildir, \FilesystemIterator::SKIP_DOTS) // ); // foreach ($it as $f) { // if ($f->isFile()) $usedBytes += $f->getSize(); // } // } // // // Zähler via doveadm (als vmail), Fallback: Files zählen // $messageCount = $this->countViaDoveadm($email); // if ($messageCount === null) { // $messageCount = $this->countViaFilesystem($maildir); // } // // // Optional zusätzlich cachen (UI liest das bereits) // Setting::set("mailbox.{$email}", [ // 'used_bytes' => (int)$usedBytes, // 'message_count' => (int)$messageCount, // 'updated_at' => now()->toDateTimeString(), // ]); // // $this->line(sprintf("%-35s %7.1f MiB %5d msgs", $email, $usedBytes/1024/1024, $messageCount)); // Log::info('Mailbox-Stat OK', compact('email','maildir','usedBytes','messageCount')); // } // // Log::info('=== mail:update-stats DONE ==='); // $this->info('Mailbox-Statistiken aktualisiert.'); // return self::SUCCESS; // } // // protected function countViaDoveadm(string $email): ?int // { // $cmd = "sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u " . escapeshellarg($email) . " messages INBOX 2>&1"; // $out = []; $rc = 0; // exec($cmd, $out, $rc); // if ($rc !== 0) return null; // foreach ($out as $line) { // if (preg_match('/^\s*INBOX\s+(\d+)\s*$/i', trim($line), $m)) return (int)$m[1]; // } // return null; // } // // protected function countViaFilesystem(string $maildir): int // { // $n = 0; // foreach (['cur','new'] as $sub) { // $dir = "{$maildir}/{$sub}"; // if (!is_dir($dir)) continue; // $h = opendir($dir); // if (!$h) continue; // while (($fn = readdir($h)) !== false) { // if ($fn === '.' || $fn === '..' || $fn[0] === '.') continue; // $n++; // } // closedir($h); // } // return $n; // } //} //namespace App\Console\Commands; // //use App\Models\MailUser; //use App\Models\Setting; //use Illuminate\Console\Command; //use Illuminate\Support\Facades\Log; //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 // { // Log::channel('mailstats')->info('=== mail:update-stats START ===', [ // 'userOpt' => $this->option('user') // ]); // // $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()) { // Log::channel('mailstats')->warning('Keine passenden Mailboxen gefunden.'); // $this->warn('Keine passenden Mailboxen gefunden.'); // return self::SUCCESS; // } // // foreach ($users as $u) { // $email = trim((string)$u->email); // // if (!preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) { // Log::channel('mailstats')->warning('Überspringe ungültige Adresse', ['email' => $email]); // continue; // } // // [$local, $domain] = explode('@', $email, 2); // $maildir = "/var/mail/vhosts/{$domain}/{$local}"; // // // 1) Größe berechnen (Dateisystem) // $usedBytes = 0; // if (is_dir($maildir)) { // $it = new RecursiveIteratorIterator( // new RecursiveDirectoryIterator($maildir, \FilesystemIterator::SKIP_DOTS) // ); // foreach ($it as $f) { // if ($f->isFile()) $usedBytes += $f->getSize(); // } // } // // // 2) Nachrichten zählen – bevorzugt via doveadm als vmail // $messageCount = $this->countViaDoveadm($email); // if ($messageCount === null) { // $messageCount = $this->countViaFilesystem($maildir); // } // // // optional: in Settings/Redis cachen (für UI) // Setting::set("mailbox.{$email}", [ // 'used_bytes' => (int)$usedBytes, // 'message_count' => (int)$messageCount, // 'updated_at' => now()->toDateTimeString(), // ]); // // $line = sprintf("%-35s %7.1f MiB %5d msgs", $email, $usedBytes / 1024 / 1024, $messageCount); // $this->line($line); // Log::channel('mailstats')->info('OK', [ // 'email' => $email, // 'maildir' => $maildir, // 'used_bytes' => $usedBytes, // 'message_count' => $messageCount, // ]); // } // // Log::channel('mailstats')->info('=== mail:update-stats DONE ==='); // $this->info('Mailbox-Statistiken aktualisiert.'); // return self::SUCCESS; // } // // protected function countViaDoveadm(string $email): ?int // { // $cmd = "sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u " . escapeshellarg($email) . " messages INBOX 2>&1"; // $out = []; // $rc = 0; // exec($cmd, $out, $rc); // // Log::channel('mailstats')->debug('doveadm exec', ['cmd' => $cmd, 'rc' => $rc, 'out' => $out]); // // if ($rc !== 0) return null; // // // Erwartet: "mailbox\tmessages\nINBOX\t" // foreach ($out as $line) { // if (preg_match('/^\s*INBOX\s+(\d+)\s*$/i', trim($line), $m)) { // return (int)$m[1]; // } // } // return null; // } // // protected function countViaFilesystem(string $maildir): int // { // $n = 0; // foreach (['cur', 'new'] as $sub) { // $dir = "{$maildir}/{$sub}"; // if (!is_dir($dir)) continue; // $dh = opendir($dir); // if (!$dh) continue; // while (($fn = readdir($dh)) !== false) { // if ($fn === '.' || $fn === '..' || $fn[0] === '.') continue; // $n++; // } // closedir($dh); // } // Log::channel('mailstats')->debug('fs count', ['maildir' => $maildir, 'count' => $n]); // return $n; // } //} //namespace App\Console\Commands; // //use App\Models\MailUser; //use App\Models\Setting; //use Illuminate\Console\Command; //use Illuminate\Support\Facades\Log; //use RecursiveDirectoryIterator; //use RecursiveIteratorIterator; //use Symfony\Component\Process\Process; // //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 // { // $log = Log::channel('mailstats'); // $log->info('=== mail:update-stats gestartet', [ // 'user_opt' => $this->option('user'), // 'php_user' => get_current_user(), // 'uid' => getmyuid(), // ]); // // $q = MailUser::query() // ->where('is_active', true) // ->with(['domain:id,domain']); // // if ($one = trim((string)$this->option('user'))) { // if (str_contains($one, '@')) { // [$local, $dom] = explode('@', $one, 2); // $q->where('localpart', $local) // ->whereHas('domain', fn($d) => $d->where('domain', $dom)); // } else { // $q->where('localpart', $one); // } // } // // $users = $q->get(); // $log->info('Gefundene Mailboxen', ['count' => $users->count(), 'ids' => $users->pluck('id')]); // // if ($users->isEmpty()) { // $this->warn('Keine passenden Mailboxen gefunden.'); // $log->warning('Abbruch: keine Mailboxen gefunden'); // return self::SUCCESS; // } // // foreach ($users as $u) { // $local = trim((string)$u->localpart); // $domain = optional($u->domain)->domain; // $email = trim((string)($u->email ?? '')); // // if ($email === '' && $local !== '' && $domain) { // $email = "{$local}@{$domain}"; // } // // if ($email === '' || !preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) { // $log->warning('Überspringe: ungültige Adresse', ['id' => $u->id, 'local' => $local, 'domain' => $domain, 'email' => $u->email]); // continue; // } // // $maildir = "/var/mail/vhosts/{$domain}/{$local}"; // $log->info('Verarbeite Mailbox', ['id' => $u->id, 'email' => $email, 'maildir' => $maildir]); // // // 1) Größe berechnen // $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(); // } // } // } else { // $log->warning('Maildir nicht gefunden', ['path' => $maildir]); // } // // // 2) Nachrichtenzahl // $messageCount = $this->countViaDoveadm($email, $log); // if ($messageCount === null) { // $messageCount = $this->countViaFilesystem($maildir, $log); // } // // // Cache (Settings/Redis) // Setting::set("mailbox.{$email}", [ // 'used_bytes' => (int)$usedBytes, // 'message_count' => (int)$messageCount, // 'ts' => now()->toISOString(), // ]); // // $this->line(sprintf("%-40s %7.1f MiB %5d msgs", $email, $usedBytes / 1024 / 1024, $messageCount)); // $log->info('Aktualisiert', [ // 'email' => $email, // 'used_bytes' => $usedBytes, // 'message_count' => $messageCount, // ]); // } // // $log->info('=== mail:update-stats fertig ==='); // $this->info('Mailbox-Statistiken aktualisiert.'); // return self::SUCCESS; // } // // protected function countViaDoveadm(string $email, $log): ?int // { // $cmd = ['sudo', '-n', '-u', 'vmail', '/usr/bin/doveadm', '-f', 'tab', 'mailbox', 'status', '-u', $email, 'messages', 'INBOX']; // $p = new Process($cmd, null, ['LANG' => 'C']); // $p->setTimeout(10); // // try { // $log->debug('Starte doveadm', ['cmd' => implode(' ', $cmd)]); // $p->run(); // $rc = $p->getExitCode(); // $out = trim($p->getOutput()); // $err = trim($p->getErrorOutput()); // $log->debug('doveadm Ergebnis', ['rc' => $rc, 'out' => $out, 'err' => $err]); // // if ($rc !== 0) return null; // // $lines = preg_split('/\R+/', $out); // $last = end($lines); // if (preg_match('/\t(\d+)$/', (string)$last, $m)) { // return (int)$m[1]; // } // return null; // } catch (\Throwable $e) { // $log->error('doveadm Exception', ['msg' => $e->getMessage()]); // return null; // } // } // // protected function countViaFilesystem(string $maildir, $log): int // { // $count = 0; // foreach (['cur', 'new'] as $sub) { // $dir = "{$maildir}/{$sub}"; // if (is_dir($dir) && ($dh = @opendir($dir))) { // while (false !== ($fn = readdir($dh))) { // if ($fn !== '.' && $fn !== '..' && $fn[0] !== '.') { // $count++; // } // } // closedir($dh); // } else { // $log->debug('Ordner fehlt/leer', ['path' => $dir]); // } // } // return $count; // } //} // //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 Messages", // $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 "INBOX123" 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; // } //}