112 lines
3.8 KiB
PHP
112 lines
3.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Mail;
|
|
|
|
use App\Models\Instance;
|
|
use App\Models\Mailbox;
|
|
use App\Support\Settings;
|
|
|
|
/**
|
|
* Welche Mailwerte die Nextcloud eines Kunden bekommt — und wann keine.
|
|
*
|
|
* Server und Zugangsdaten liegen in diesem Projekt an ZWEI Orten, und das ist
|
|
* Absicht: der Server steht einmal in `app_settings` (ein Anbieterwechsel ist
|
|
* dann eine Karte, nicht fuenf Zeilen), die Zugangsdaten je Absender am
|
|
* Mailbox-Datensatz. Diese Klasse ist die einzige Stelle, die beides
|
|
* zusammenlegt.
|
|
*
|
|
* `available() === false` heisst: NICHTS schreiben. Eine halb eingetragene
|
|
* Mailkonfiguration ist schlimmer als keine — Nextcloud versucht dann bei
|
|
* jedem Versand zu senden und scheitert still, ohne dass jemand es merkt.
|
|
*
|
|
* DIE NAHTSTELLE fuer spaeter: `mailboxFor()` gibt heute das gemeinsame
|
|
* Versandkonto zurueck. Ein Konto je Kunde (ueber die mailcow-Schnittstelle
|
|
* angelegt) haengt genau hier — der Bereitstellungsschritt und alles darueber
|
|
* aendern sich dafuer nicht um eine Zeile.
|
|
*/
|
|
final class GuestMailConfig
|
|
{
|
|
/** Der Schluessel des gemeinsamen Versandkontos in `mailboxes`. */
|
|
public const RELAY_KEY = 'instance-relay';
|
|
|
|
private function __construct(
|
|
private readonly ?string $problem,
|
|
/** @var array<string, string> */
|
|
private readonly array $values,
|
|
private readonly string $password,
|
|
) {}
|
|
|
|
public static function for(Instance $instance): self
|
|
{
|
|
$host = trim((string) Settings::get('mail.host', ''));
|
|
$port = (int) Settings::get('mail.port', 0);
|
|
|
|
if ($host === '' || $port < 1) {
|
|
return new self('no_server', [], '');
|
|
}
|
|
|
|
$box = self::mailboxFor($instance);
|
|
|
|
if ($box === null || ! $box->isConfigured()) {
|
|
return new self('no_mailbox', [], '');
|
|
}
|
|
|
|
// Die Adresse zerfaellt in die beiden Werte, die Nextcloud getrennt
|
|
// fuehrt: den linken Teil als Absender, den rechten als Maildomain.
|
|
[$local, $domain] = array_pad(explode('@', $box->address, 2), 2, '');
|
|
|
|
// Ein Postfach OHNE Anmeldung ist erlaubt (Mailbox::isConfigured() —
|
|
// ein vertrauenswuerdiges lokales Relay verlangt kein Passwort). Ein
|
|
// fest verdrahtetes 'true' wuerde Nextcloud trotzdem zur Anmeldung
|
|
// zwingen, sie versucht es dann mit einem leeren Passwort, und JEDER
|
|
// Versand scheitert — still, denn der Fehler zeigt sich erst beim
|
|
// naechsten Mailversand des Kunden, nie bei dieser Konfiguration.
|
|
return new self(null, [
|
|
'mail_smtpmode' => 'smtp',
|
|
'mail_smtphost' => $host,
|
|
'mail_smtpport' => (string) $port,
|
|
'mail_smtpsecure' => (string) Settings::get('mail.encryption', 'tls'),
|
|
'mail_smtpauth' => $box->authenticates ? 'true' : 'false',
|
|
'mail_smtpname' => $box->smtpUsername(),
|
|
'mail_from_address' => $local,
|
|
'mail_domain' => $domain,
|
|
], (string) $box->password);
|
|
}
|
|
|
|
/**
|
|
* Heute fuer jede Instanz dasselbe Konto. Siehe Klassenkopf — hier haengt
|
|
* die spaetere Fassung mit einem Konto je Kunde.
|
|
*/
|
|
private static function mailboxFor(Instance $instance): ?Mailbox
|
|
{
|
|
return Mailbox::findByKey(self::RELAY_KEY);
|
|
}
|
|
|
|
public function available(): bool
|
|
{
|
|
return $this->problem === null;
|
|
}
|
|
|
|
/** `no_server`, `no_mailbox` oder null. */
|
|
public function problem(): ?string
|
|
{
|
|
return $this->problem;
|
|
}
|
|
|
|
/**
|
|
* Alles ausser dem Passwort. Das geht einen eigenen Weg, damit es beim
|
|
* Bauen der Befehle nicht versehentlich mitwandert.
|
|
*
|
|
* @return array<string, string>
|
|
*/
|
|
public function values(): array
|
|
{
|
|
return $this->values;
|
|
}
|
|
|
|
public function password(): string
|
|
{
|
|
return $this->password;
|
|
}
|
|
}
|