94 lines
3.1 KiB
PHP
94 lines
3.1 KiB
PHP
<?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 array{owner:string,domain:string,mailbox:string,email:string}|null */
|
|
public function resolveRecipient(string $email): ?array
|
|
{
|
|
$email = strtolower(trim($email));
|
|
if (!preg_match('/^([A-Za-z0-9._+-]+)@([A-Za-z0-9.-]+)$/', $email, $m)) {
|
|
return null;
|
|
}
|
|
$local = $m[1];
|
|
$domain = $m[2];
|
|
$owners = $this->domainOwners();
|
|
$owner = $owners[$domain] ?? null;
|
|
if ($owner === null) {
|
|
return null;
|
|
}
|
|
$passwd = $this->virtualRoot . '/' . $domain . '/passwd';
|
|
if (!is_file($passwd)) {
|
|
return null;
|
|
}
|
|
foreach (file($passwd, FILE_IGNORE_NEW_LINES) ?: [] as $line) {
|
|
$mailbox = strtolower(trim(strtok($line, ':') ?: ''));
|
|
if ($mailbox === $local) {
|
|
return ['owner' => $owner, 'domain' => $domain, 'mailbox' => $local, 'email' => $local . '@' . $domain];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** @return string[] */
|
|
private function domainsForUser(string $username): array
|
|
{
|
|
$domains = [];
|
|
foreach ($this->domainOwners() as $domain => $owner) {
|
|
if ($owner === $username) {
|
|
$domains[] = $domain;
|
|
}
|
|
}
|
|
sort($domains);
|
|
return $domains;
|
|
}
|
|
|
|
/** @return array<string,string> */
|
|
private function domainOwners(): array
|
|
{
|
|
$path = $this->virtualRoot . '/domainowners';
|
|
if (!is_file($path)) {
|
|
return [];
|
|
}
|
|
$owners = [];
|
|
foreach (file($path, FILE_IGNORE_NEW_LINES) ?: [] as $line) {
|
|
if (!str_contains($line, ':')) {
|
|
continue;
|
|
}
|
|
[$domain, $owner] = array_map('trim', explode(':', $line, 2));
|
|
if (!preg_match('/^[A-Za-z0-9.-]+$/', $domain) || !preg_match('/^[A-Za-z0-9._-]+$/', $owner)) {
|
|
continue;
|
|
}
|
|
$owners[strtolower($domain)] = $owner;
|
|
}
|
|
return $owners;
|
|
}
|
|
}
|