feat: implement global autoresponder plugin

This commit is contained in:
Marek Miklewicz
2026-06-02 19:19:00 +02:00
commit 6f989af278
62 changed files with 2018 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
final class MailboxDirectory
{
public function __construct(private string $virtualRoot = '/etc/virtual')
{
}
/** @return array<int,array{domain:string,mailbox:string,email:string}> */
public function mailboxesForUser(string $username): array
{
$domains = $this->domainsForUser($username);
$out = [];
foreach ($domains as $domain) {
$passwd = $this->virtualRoot . '/' . $domain . '/passwd';
if (!is_file($passwd)) {
continue;
}
$lines = file($passwd, FILE_IGNORE_NEW_LINES) ?: [];
foreach ($lines as $line) {
$local = trim(strtok($line, ':') ?: '');
if ($local === '' || !preg_match('/^[A-Za-z0-9._+-]+$/', $local)) {
continue;
}
$out[] = ['domain' => $domain, 'mailbox' => $local, 'email' => $local . '@' . $domain];
}
}
usort($out, static fn (array $a, array $b): int => strcmp($a['email'], $b['email']));
return $out;
}
/** @return string[] */
private function domainsForUser(string $username): array
{
$path = $this->virtualRoot . '/domainowners';
if (!is_file($path)) {
return [];
}
$domains = [];
foreach (file($path, FILE_IGNORE_NEW_LINES) ?: [] as $line) {
if (!str_contains($line, ':')) {
continue;
}
[$domain, $owner] = array_map('trim', explode(':', $line, 2));
if ($owner !== $username || !preg_match('/^[A-Za-z0-9.-]+$/', $domain)) {
continue;
}
$domains[] = strtolower($domain);
}
sort($domains);
return $domains;
}
}