* SPDX-License-Identifier: AGPL-3.0-or-later */ namespace KTXM\ProviderImap\Providers; use KTXM\ProviderImap\Client\ConnectionConfig; use KTXM\ProviderImap\Client\ConnectionSecurity; use KTXM\ProviderImap\Smtp\ConnectionConfig as SmtpConnectionConfig; use KTXM\ProviderImap\Smtp\ConnectionSecurity as SmtpConnectionSecurity; use KTXF\Resource\Provider\ResourceServiceLocationSocketSplit; /** * IMAP/SMTP Service Location * * Split socket location: the inbound side describes the IMAP server, the * outbound side describes the SMTP submission server. */ class ServiceLocation implements ResourceServiceLocationSocketSplit { public function __construct( private string $inboundHost = '', private int $inboundPort = 993, private string $inboundEncryption = 'ssl', // none | ssl | tls | starttls private bool $inboundVerifyPeer = true, private bool $inboundVerifyHost = true, private string $outboundHost = '', private int $outboundPort = 587, private string $outboundEncryption = 'starttls', // none | ssl | tls | starttls private bool $outboundVerifyPeer = true, private bool $outboundVerifyHost = true, ) {} // ── Serialisation ──────────────────────────────────────────────────────── public function toStore(): array { return $this->jsonSerialize(); } public function fromStore(array $data): static { return $this->jsonDeserialize($data); } public function jsonSerialize(): array { return [ 'type' => self::TYPE_SOCKET_SPLIT, 'inboundHost' => $this->inboundHost, 'inboundPort' => $this->inboundPort, 'inboundEncryption' => $this->inboundEncryption, 'inboundVerifyPeer' => $this->inboundVerifyPeer, 'inboundVerifyHost' => $this->inboundVerifyHost, 'outboundHost' => $this->outboundHost, 'outboundPort' => $this->outboundPort, 'outboundEncryption' => $this->outboundEncryption, 'outboundVerifyPeer' => $this->outboundVerifyPeer, 'outboundVerifyHost' => $this->outboundVerifyHost, ]; } public function jsonDeserialize(array|string $data): static { if (is_string($data)) { $data = json_decode($data, true); } $this->inboundHost = $data['inboundHost'] ?? ''; $this->inboundPort = (int) ($data['inboundPort'] ?? 993); $this->inboundEncryption = $data['inboundEncryption'] ?? 'ssl'; $this->inboundVerifyPeer = $data['inboundVerifyPeer'] ?? true; $this->inboundVerifyHost = $data['inboundVerifyHost'] ?? true; $this->outboundHost = $data['outboundHost'] ?? ''; $this->outboundPort = (int) ($data['outboundPort'] ?? 587); $this->outboundEncryption = $data['outboundEncryption'] ?? 'starttls'; $this->outboundVerifyPeer = $data['outboundVerifyPeer'] ?? true; $this->outboundVerifyHost = $data['outboundVerifyHost'] ?? true; return $this; } // ── ResourceServiceLocationSocketSplit ─────────────────────────────────── public function type(): string { return self::TYPE_SOCKET_SPLIT; } public function locationInbound(): string { return $this->inboundEncryption . '://' . $this->inboundHost . ':' . $this->inboundPort; } public function locationOutbound(): string { return $this->outboundEncryption . '://' . $this->outboundHost . ':' . $this->outboundPort; } public function getInboundHost(): string { return $this->inboundHost; } public function setInboundHost(string $value): void { $this->inboundHost = $value; } public function getOutboundHost(): string { return $this->outboundHost; } public function setOutboundHost(string $value): void { $this->outboundHost = $value; } public function getInboundPort(): int { return $this->inboundPort; } public function setInboundPort(int $value): void { $this->inboundPort = $value; } public function getOutboundPort(): int { return $this->outboundPort; } public function setOutboundPort(int $value): void { $this->outboundPort = $value; } public function getInboundEncryption(): string { return $this->inboundEncryption; } public function setInboundEncryption(string $value): void { $this->inboundEncryption = $value; } public function getOutboundEncryption(): string { return $this->outboundEncryption; } public function setOutboundEncryption(string $value): void { $this->outboundEncryption = $value; } public function getInboundVerifyPeer(): bool { return $this->inboundVerifyPeer; } public function setInboundVerifyPeer(bool $value): void { $this->inboundVerifyPeer = $value; } public function getInboundVerifyHost(): bool { return $this->inboundVerifyHost; } public function setInboundVerifyHost(bool $value): void { $this->inboundVerifyHost = $value; } public function getOutboundVerifyPeer(): bool { return $this->outboundVerifyPeer; } public function setOutboundVerifyPeer(bool $value): void { $this->outboundVerifyPeer = $value; } public function getOutboundVerifyHost(): bool { return $this->outboundVerifyHost; } public function setOutboundVerifyHost(bool $value): void { $this->outboundVerifyHost = $value; } // ── Client helpers ─────────────────────────────────────────────────────── /** * Build an IMAP ConnectionConfig from the inbound side. */ public function toConnectionConfig(?string $username = null, ?string $password = null): ConnectionConfig { return new ConnectionConfig( host: $this->inboundHost, port: $this->inboundPort, security: $this->imapSecurity($this->inboundEncryption), username: $username, password: $password, verifyPeer: $this->inboundVerifyPeer, verifyPeerName: $this->inboundVerifyHost, allowSelfSigned: !$this->inboundVerifyPeer, ); } /** * Build an SMTP submission ConnectionConfig from the outbound side. */ public function toSmtpConnectionConfig(?string $username = null, ?string $password = null): SmtpConnectionConfig { return new SmtpConnectionConfig( host: $this->outboundHost, port: $this->outboundPort, security: $this->smtpSecurity($this->outboundEncryption), username: $username, password: $password, verifyPeer: $this->outboundVerifyPeer, verifyPeerName: $this->outboundVerifyHost, allowSelfSigned: !$this->outboundVerifyPeer, ); } private function imapSecurity(string $encryption): ConnectionSecurity { return match ($encryption) { 'ssl', 'tls' => ConnectionSecurity::Tls, 'starttls' => ConnectionSecurity::StartTls, default => ConnectionSecurity::Plain, }; } private function smtpSecurity(string $encryption): SmtpConnectionSecurity { return match ($encryption) { 'ssl', 'tls' => SmtpConnectionSecurity::Tls, 'starttls' => SmtpConnectionSecurity::StartTls, default => SmtpConnectionSecurity::Plain, }; } }