generated from Nodarx/template
refactor: use split socket location class
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -150,12 +150,11 @@ class ConnectCommand extends Command
|
||||
// ── Build service object ────────────────────────────────────────────
|
||||
|
||||
$location = new ServiceLocation(
|
||||
host: $host,
|
||||
port: $port > 0 ? $port : 993,
|
||||
encryption: $encryption,
|
||||
verifyPeer: !$noVerify,
|
||||
verifyPeerName: !$noVerify,
|
||||
allowSelfSigned: $noVerify,
|
||||
inboundHost: $host,
|
||||
inboundPort: $port > 0 ? $port : 993,
|
||||
inboundEncryption: $encryption,
|
||||
inboundVerifyPeer: !$noVerify,
|
||||
inboundVerifyHost: !$noVerify,
|
||||
);
|
||||
|
||||
$identity = (new ServiceIdentityBasic())->jsonDeserialize([
|
||||
|
||||
@@ -139,7 +139,7 @@ class DisconnectCommand extends Command
|
||||
}
|
||||
|
||||
$label = $service->getLabel() ?? $serviceId;
|
||||
$host = $service->getLocation()?->getHost() ?? 'unknown';
|
||||
$host = $service->getLocation()?->getInboundHost() ?? 'unknown';
|
||||
|
||||
$io->title('Disconnect IMAP Service');
|
||||
$io->definitionList(
|
||||
|
||||
@@ -151,7 +151,7 @@ class DiscoverCommand extends Command
|
||||
/** @var array<string, ServiceLocation> $choiceMap */
|
||||
$choiceMap = [];
|
||||
foreach ($candidates as $c) {
|
||||
$label = sprintf('%s : %d [%s]', $c->getHost(), $c->getPort(), $encLabel($c->getEncryption()));
|
||||
$label = sprintf('%s : %d [%s]', $c->getInboundHost(), $c->getInboundPort(), $encLabel($c->getInboundEncryption()));
|
||||
$choiceMap[$label] = $c;
|
||||
}
|
||||
|
||||
@@ -170,9 +170,9 @@ class DiscoverCommand extends Command
|
||||
|
||||
$io->success('Server selected:');
|
||||
$io->definitionList(
|
||||
['Host' => $location->getHost()],
|
||||
['Port' => (string) $location->getPort()],
|
||||
['Encryption' => $encLabel($location->getEncryption())],
|
||||
['Host' => $location->getInboundHost()],
|
||||
['Port' => (string) $location->getInboundPort()],
|
||||
['Encryption' => $encLabel($location->getInboundEncryption())],
|
||||
);
|
||||
|
||||
if (!$save) {
|
||||
|
||||
@@ -308,7 +308,7 @@ class TestCommand extends Command
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
return sprintf('%s://%s:%d', $location->getEncryption(), $location->getHost(), $location->getPort());
|
||||
return sprintf('%s://%s:%d', $location->getInboundEncryption(), $location->getInboundHost(), $location->getInboundPort());
|
||||
}
|
||||
|
||||
private function formatSender(Message $message): string
|
||||
|
||||
@@ -11,22 +11,29 @@ namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use KTXM\ProviderImap\Client\ConnectionConfig;
|
||||
use KTXM\ProviderImap\Client\ConnectionSecurity;
|
||||
use KTXF\Resource\Provider\ResourceServiceLocationInterface;
|
||||
use KTXM\ProviderImap\Smtp\ConnectionConfig as SmtpConnectionConfig;
|
||||
use KTXM\ProviderImap\Smtp\ConnectionSecurity as SmtpConnectionSecurity;
|
||||
use KTXF\Resource\Provider\ResourceServiceLocationSocketSplit;
|
||||
|
||||
/**
|
||||
* IMAP Service Location
|
||||
* IMAP/SMTP Service Location
|
||||
*
|
||||
* Connection details for an IMAP server (host / port / encryption).
|
||||
* Split socket location: the inbound side describes the IMAP server, the
|
||||
* outbound side describes the SMTP submission server.
|
||||
*/
|
||||
class ServiceLocation implements ResourceServiceLocationInterface
|
||||
class ServiceLocation implements ResourceServiceLocationSocketSplit
|
||||
{
|
||||
public function __construct(
|
||||
private string $host = '',
|
||||
private int $port = 993,
|
||||
private string $encryption = 'ssl', // ssl | tls | starttls | none
|
||||
private bool $verifyPeer = true,
|
||||
private bool $verifyPeerName = true,
|
||||
private bool $allowSelfSigned = false,
|
||||
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 ────────────────────────────────────────────────────────
|
||||
@@ -43,15 +50,19 @@ class ServiceLocation implements ResourceServiceLocationInterface
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return array_filter([
|
||||
'type' => self::TYPE_URI,
|
||||
'host' => $this->host,
|
||||
'port' => $this->port,
|
||||
'encryption' => $this->encryption,
|
||||
'verifyPeer' => $this->verifyPeer,
|
||||
'verifyPeerName' => $this->verifyPeerName,
|
||||
'allowSelfSigned' => $this->allowSelfSigned,
|
||||
], fn($v) => $v !== null && $v !== '');
|
||||
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
|
||||
@@ -60,70 +71,119 @@ class ServiceLocation implements ResourceServiceLocationInterface
|
||||
$data = json_decode($data, true);
|
||||
}
|
||||
|
||||
$this->host = $data['host'] ?? '';
|
||||
$this->port = (int)($data['port'] ?? 993);
|
||||
$this->encryption = $data['encryption'] ?? 'ssl';
|
||||
$this->verifyPeer = $data['verifyPeer'] ?? true;
|
||||
$this->verifyPeerName = $data['verifyPeerName'] ?? true;
|
||||
$this->allowSelfSigned = $data['allowSelfSigned'] ?? false;
|
||||
$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;
|
||||
}
|
||||
|
||||
// ── ResourceServiceLocationInterface ─────────────────────────────────────
|
||||
// ── ResourceServiceLocationSocketSplit ───────────────────────────────────
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return self::TYPE_URI;
|
||||
return self::TYPE_SOCKET_SPLIT;
|
||||
}
|
||||
|
||||
public function location(): string
|
||||
public function locationInbound(): string
|
||||
{
|
||||
return $this->encryption . '://' . $this->host . ':' . $this->port;
|
||||
return $this->inboundEncryption . '://' . $this->inboundHost . ':' . $this->inboundPort;
|
||||
}
|
||||
|
||||
// ── Accessors ────────────────────────────────────────────────────────────
|
||||
public function locationOutbound(): string
|
||||
{
|
||||
return $this->outboundEncryption . '://' . $this->outboundHost . ':' . $this->outboundPort;
|
||||
}
|
||||
|
||||
public function getHost(): string { return $this->host; }
|
||||
public function setHost(string $v): void { $this->host = $v; }
|
||||
public function getInboundHost(): string { return $this->inboundHost; }
|
||||
public function setInboundHost(string $value): void { $this->inboundHost = $value; }
|
||||
|
||||
public function getPort(): int { return $this->port; }
|
||||
public function setPort(int $v): void { $this->port = $v; }
|
||||
public function getOutboundHost(): string { return $this->outboundHost; }
|
||||
public function setOutboundHost(string $value): void { $this->outboundHost = $value; }
|
||||
|
||||
public function getEncryption(): string { return $this->encryption; }
|
||||
public function setEncryption(string $v): void { $this->encryption = $v; }
|
||||
public function getInboundPort(): int { return $this->inboundPort; }
|
||||
public function setInboundPort(int $value): void { $this->inboundPort = $value; }
|
||||
|
||||
public function getVerifyPeer(): bool { return $this->verifyPeer; }
|
||||
public function setVerifyPeer(bool $v): void { $this->verifyPeer = $v; }
|
||||
public function getOutboundPort(): int { return $this->outboundPort; }
|
||||
public function setOutboundPort(int $value): void { $this->outboundPort = $value; }
|
||||
|
||||
public function getVerifyPeerName(): bool { return $this->verifyPeerName; }
|
||||
public function setVerifyPeerName(bool $v): void { $this->verifyPeerName = $v; }
|
||||
public function getInboundEncryption(): string { return $this->inboundEncryption; }
|
||||
public function setInboundEncryption(string $value): void { $this->inboundEncryption = $value; }
|
||||
|
||||
public function getAllowSelfSigned(): bool { return $this->allowSelfSigned; }
|
||||
public function setAllowSelfSigned(bool $v): void { $this->allowSelfSigned = $v; }
|
||||
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 a standalone IMAP client ConnectionConfig from this location.
|
||||
* Build an IMAP ConnectionConfig from the inbound side.
|
||||
*/
|
||||
public function toConnectionConfig(?string $username = null, ?string $password = null): ConnectionConfig
|
||||
{
|
||||
$security = match ($this->encryption) {
|
||||
'ssl', 'tls' => ConnectionSecurity::Tls,
|
||||
'starttls' => ConnectionSecurity::StartTls,
|
||||
default => ConnectionSecurity::Plain,
|
||||
};
|
||||
|
||||
return new ConnectionConfig(
|
||||
host: $this->host,
|
||||
port: $this->port,
|
||||
security: $security,
|
||||
host: $this->inboundHost,
|
||||
port: $this->inboundPort,
|
||||
security: $this->imapSecurity($this->inboundEncryption),
|
||||
username: $username,
|
||||
password: $password,
|
||||
verifyPeer: $this->verifyPeer,
|
||||
verifyPeerName: $this->verifyPeerName,
|
||||
allowSelfSigned: $this->allowSelfSigned,
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ class Discovery
|
||||
];
|
||||
$seen = [];
|
||||
foreach ($results as $r) {
|
||||
$seen[$r->getHost()] = true;
|
||||
$seen[$r->getInboundHost()] = true;
|
||||
}
|
||||
foreach ($candidates as $host) {
|
||||
if (isset($seen[$host])) {
|
||||
@@ -203,10 +203,11 @@ class Discovery
|
||||
|
||||
$loc = new ServiceLocation();
|
||||
$loc->jsonDeserialize([
|
||||
'host' => $host,
|
||||
'port' => $port,
|
||||
'encryption' => $encryption,
|
||||
'verifyPeer' => $verifySSL,
|
||||
'inboundHost' => $host,
|
||||
'inboundPort' => $port,
|
||||
'inboundEncryption' => $encryption,
|
||||
'inboundVerifyPeer' => $verifySSL,
|
||||
'inboundVerifyHost' => $verifySSL,
|
||||
]);
|
||||
return $loc;
|
||||
} catch (\Throwable) {
|
||||
|
||||
@@ -1,26 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { LocationSocketSole } from '@KTXM/MailManager/models/location'
|
||||
import { LocationSocketSplit } from '@KTXM/MailManager/models/location'
|
||||
import { ServiceObject } from '@KTXM/MailManager/models/service'
|
||||
import type {
|
||||
ServiceLocation,
|
||||
ServiceLocationSocketSole,
|
||||
ServiceLocationSocketSplit,
|
||||
} from '@KTXM/MailManager/types/service'
|
||||
import type {
|
||||
ProviderProtocolPanelProps,
|
||||
ProviderProtocolPanelEmits,
|
||||
} from '@KTXM/MailManager/types/integration'
|
||||
|
||||
type ImapEncryption = 'none' | 'ssl' | 'tls' | 'starttls'
|
||||
type Encryption = 'none' | 'ssl' | 'tls' | 'starttls'
|
||||
|
||||
const props = defineProps<ProviderProtocolPanelProps>()
|
||||
const emit = defineEmits<ProviderProtocolPanelEmits>()
|
||||
|
||||
const host = ref('')
|
||||
const encryption = ref<ImapEncryption>('ssl')
|
||||
const port = ref('993')
|
||||
const verifyPeer = ref(true)
|
||||
const verifyHost = ref(true)
|
||||
// ── Inbound (IMAP) ──────────────────────────────────────────────────────────
|
||||
const inboundHost = ref('')
|
||||
const inboundEncryption = ref<Encryption>('ssl')
|
||||
const inboundPort = ref('993')
|
||||
const inboundVerifyPeer = ref(true)
|
||||
const inboundVerifyHost = ref(true)
|
||||
|
||||
// ── Outbound (SMTP) ─────────────────────────────────────────────────────────
|
||||
const outboundHost = ref('')
|
||||
const outboundEncryption = ref<Encryption>('starttls')
|
||||
const outboundPort = ref('587')
|
||||
const outboundVerifyPeer = ref(true)
|
||||
const outboundVerifyHost = ref(true)
|
||||
|
||||
const encryptionOptions = [
|
||||
{ title: 'Implicit TLS (SSL)', value: 'ssl' },
|
||||
@@ -36,23 +44,30 @@ const rules = {
|
||||
return Number.isInteger(numericValue) && numericValue >= 1 && numericValue <= 65535
|
||||
? true
|
||||
: 'Port must be between 1 and 65535'
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const isValid = computed(() => !!host.value && rules.port(port.value) === true)
|
||||
const inboundValid = computed(() => !!inboundHost.value && rules.port(inboundPort.value) === true)
|
||||
const outboundValid = computed(() => !!outboundHost.value && rules.port(outboundPort.value) === true)
|
||||
const isValid = computed(() => inboundValid.value && outboundValid.value)
|
||||
|
||||
const currentLocation = computed((): ServiceLocationSocketSole | null => {
|
||||
const currentLocation = computed((): ServiceLocationSocketSplit | null => {
|
||||
if (!isValid.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'SOCKET_SOLE',
|
||||
host: host.value,
|
||||
port: Number(port.value),
|
||||
encryption: encryption.value,
|
||||
verifyPeer: verifyPeer.value,
|
||||
verifyHost: verifyHost.value,
|
||||
type: 'SOCKET_SPLIT',
|
||||
inboundHost: inboundHost.value,
|
||||
inboundPort: Number(inboundPort.value),
|
||||
inboundEncryption: inboundEncryption.value,
|
||||
inboundVerifyPeer: inboundVerifyPeer.value,
|
||||
inboundVerifyHost: inboundVerifyHost.value,
|
||||
outboundHost: outboundHost.value,
|
||||
outboundPort: Number(outboundPort.value),
|
||||
outboundEncryption: outboundEncryption.value,
|
||||
outboundVerifyPeer: outboundVerifyPeer.value,
|
||||
outboundVerifyHost: outboundVerifyHost.value,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -80,14 +95,20 @@ watch(
|
||||
return
|
||||
}
|
||||
|
||||
if (nextService.location instanceof LocationSocketSole) {
|
||||
nextService.location.host = location.host
|
||||
nextService.location.port = location.port
|
||||
nextService.location.encryption = location.encryption
|
||||
nextService.location.verifyPeer = location.verifyPeer ?? true
|
||||
nextService.location.verifyHost = location.verifyHost ?? true
|
||||
if (nextService.location instanceof LocationSocketSplit) {
|
||||
const target = nextService.location
|
||||
target.inboundHost = location.inboundHost
|
||||
target.inboundPort = location.inboundPort
|
||||
target.inboundEncryption = location.inboundEncryption
|
||||
target.inboundVerifyPeer = location.inboundVerifyPeer ?? true
|
||||
target.inboundVerifyHost = location.inboundVerifyHost ?? true
|
||||
target.outboundHost = location.outboundHost
|
||||
target.outboundPort = location.outboundPort
|
||||
target.outboundEncryption = location.outboundEncryption
|
||||
target.outboundVerifyPeer = location.outboundVerifyPeer ?? true
|
||||
target.outboundVerifyHost = location.outboundVerifyHost ?? true
|
||||
} else {
|
||||
nextService.location = LocationSocketSole.fromJson(location)
|
||||
nextService.location = LocationSocketSplit.fromJson(location)
|
||||
}
|
||||
|
||||
emit('update:service', nextService)
|
||||
@@ -95,25 +116,34 @@ watch(
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
watch(encryption, (next, previous) => {
|
||||
const previousDefault = defaultPortFor(previous)
|
||||
if (!port.value || Number(port.value) === previousDefault) {
|
||||
port.value = String(defaultPortFor(next))
|
||||
watch(inboundEncryption, (next, previous) => {
|
||||
const previousDefault = defaultInboundPort(previous)
|
||||
if (!inboundPort.value || Number(inboundPort.value) === previousDefault) {
|
||||
inboundPort.value = String(defaultInboundPort(next))
|
||||
}
|
||||
})
|
||||
|
||||
watch(outboundEncryption, (next, previous) => {
|
||||
const previousDefault = defaultOutboundPort(previous)
|
||||
if (!outboundPort.value || Number(outboundPort.value) === previousDefault) {
|
||||
outboundPort.value = String(defaultOutboundPort(next))
|
||||
}
|
||||
})
|
||||
|
||||
function syncFromLocation(location: ServiceLocation | null) {
|
||||
const socketLocation = getSocketLocation(location)
|
||||
const split = location?.type === 'SOCKET_SPLIT' ? location : null
|
||||
|
||||
host.value = socketLocation?.host ?? ''
|
||||
encryption.value = socketLocation?.encryption ?? 'ssl'
|
||||
port.value = String(socketLocation?.port ?? defaultPortFor(encryption.value))
|
||||
verifyPeer.value = socketLocation?.verifyPeer ?? true
|
||||
verifyHost.value = socketLocation?.verifyHost ?? true
|
||||
}
|
||||
inboundHost.value = split?.inboundHost ?? ''
|
||||
inboundEncryption.value = split?.inboundEncryption ?? 'ssl'
|
||||
inboundPort.value = String(split?.inboundPort ?? defaultInboundPort(inboundEncryption.value))
|
||||
inboundVerifyPeer.value = split?.inboundVerifyPeer ?? true
|
||||
inboundVerifyHost.value = split?.inboundVerifyHost ?? true
|
||||
|
||||
function getSocketLocation(location?: ServiceLocation | null): ServiceLocationSocketSole | null {
|
||||
return location?.type === 'SOCKET_SOLE' ? location : null
|
||||
outboundHost.value = split?.outboundHost ?? ''
|
||||
outboundEncryption.value = split?.outboundEncryption ?? 'starttls'
|
||||
outboundPort.value = String(split?.outboundPort ?? defaultOutboundPort(outboundEncryption.value))
|
||||
outboundVerifyPeer.value = split?.outboundVerifyPeer ?? true
|
||||
outboundVerifyHost.value = split?.outboundVerifyHost ?? true
|
||||
}
|
||||
|
||||
function sameLocation(a: ServiceLocation | null, b: ServiceLocation | null): boolean {
|
||||
@@ -121,30 +151,51 @@ function sameLocation(a: ServiceLocation | null, b: ServiceLocation | null): boo
|
||||
return a === b
|
||||
}
|
||||
|
||||
if (a.type !== 'SOCKET_SOLE' || b.type !== 'SOCKET_SOLE') {
|
||||
if (a.type !== 'SOCKET_SPLIT' || b.type !== 'SOCKET_SPLIT') {
|
||||
return false
|
||||
}
|
||||
|
||||
return a.host === b.host
|
||||
&& a.port === b.port
|
||||
&& a.encryption === b.encryption
|
||||
&& (a.verifyPeer ?? true) === (b.verifyPeer ?? true)
|
||||
&& (a.verifyHost ?? true) === (b.verifyHost ?? true)
|
||||
return a.inboundHost === b.inboundHost
|
||||
&& a.inboundPort === b.inboundPort
|
||||
&& a.inboundEncryption === b.inboundEncryption
|
||||
&& (a.inboundVerifyPeer ?? true) === (b.inboundVerifyPeer ?? true)
|
||||
&& (a.inboundVerifyHost ?? true) === (b.inboundVerifyHost ?? true)
|
||||
&& a.outboundHost === b.outboundHost
|
||||
&& a.outboundPort === b.outboundPort
|
||||
&& a.outboundEncryption === b.outboundEncryption
|
||||
&& (a.outboundVerifyPeer ?? true) === (b.outboundVerifyPeer ?? true)
|
||||
&& (a.outboundVerifyHost ?? true) === (b.outboundVerifyHost ?? true)
|
||||
}
|
||||
|
||||
function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
return nextEncryption === 'ssl' || nextEncryption === 'tls' ? 993 : 143
|
||||
function defaultInboundPort(encryption: Encryption): number {
|
||||
return encryption === 'ssl' || encryption === 'tls' ? 993 : 143
|
||||
}
|
||||
|
||||
function defaultOutboundPort(encryption: Encryption): number {
|
||||
if (encryption === 'ssl') {
|
||||
return 465
|
||||
}
|
||||
if (encryption === 'none') {
|
||||
return 25
|
||||
}
|
||||
return 587
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="imap-protocol-panel">
|
||||
<h3 class="text-h6 mb-4">IMAP Connection Settings</h3>
|
||||
<p class="text-body-2 mb-6">Configure the server address, transport security, and certificate verification for your IMAP mailbox.</p>
|
||||
<h3 class="text-h6 mb-4">Connection Settings</h3>
|
||||
<p class="text-body-2 mb-6">Configure the incoming (IMAP) and outgoing (SMTP) servers, transport security, and certificate verification for this mailbox.</p>
|
||||
|
||||
<!-- ── Incoming (IMAP) ─────────────────────────────────────────────────── -->
|
||||
<div class="d-flex align-center mb-4">
|
||||
<v-icon start color="primary">mdi-email-arrow-left</v-icon>
|
||||
<span class="text-subtitle-1 font-weight-medium">Incoming mail (IMAP)</span>
|
||||
</div>
|
||||
|
||||
<v-text-field
|
||||
v-model="host"
|
||||
label="Server Host"
|
||||
v-model="inboundHost"
|
||||
label="IMAP Server Host"
|
||||
hint="For example: imap.example.com"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
@@ -157,7 +208,7 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
/>
|
||||
|
||||
<v-select
|
||||
v-model="encryption"
|
||||
v-model="inboundEncryption"
|
||||
:items="encryptionOptions"
|
||||
label="Security"
|
||||
variant="outlined"
|
||||
@@ -166,7 +217,7 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="port"
|
||||
v-model="inboundPort"
|
||||
label="Port"
|
||||
hint="Defaults to 993 for TLS/SSL and 143 for plain or STARTTLS"
|
||||
persistent-hint
|
||||
@@ -179,7 +230,7 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
:rules="[rules.required, rules.port]"
|
||||
/>
|
||||
|
||||
<v-expansion-panels class="mt-4">
|
||||
<v-expansion-panels class="mb-6">
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title>
|
||||
<v-icon start>mdi-cog</v-icon>
|
||||
@@ -187,7 +238,7 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-switch
|
||||
v-model="verifyPeer"
|
||||
v-model="inboundVerifyPeer"
|
||||
label="Verify TLS certificate"
|
||||
color="primary"
|
||||
hint="Disable only for trusted internal or test environments"
|
||||
@@ -196,7 +247,7 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
/>
|
||||
|
||||
<v-switch
|
||||
v-model="verifyHost"
|
||||
v-model="inboundVerifyHost"
|
||||
label="Verify certificate hostname"
|
||||
color="primary"
|
||||
hint="Checks that the certificate matches the IMAP host"
|
||||
@@ -207,9 +258,78 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
|
||||
<v-alert type="info" variant="tonal">
|
||||
STARTTLS is accepted for compatibility, but the current IMAP client transport does not perform STARTTLS negotiation. Prefer TLS on port 993 when available.
|
||||
</v-alert>
|
||||
<v-divider class="mb-6" />
|
||||
|
||||
<!-- ── Outgoing (SMTP) ─────────────────────────────────────────────────── -->
|
||||
<div class="d-flex align-center mb-4">
|
||||
<v-icon start color="primary">mdi-email-arrow-right</v-icon>
|
||||
<span class="text-subtitle-1 font-weight-medium">Outgoing mail (SMTP)</span>
|
||||
</div>
|
||||
|
||||
<v-text-field
|
||||
v-model="outboundHost"
|
||||
label="SMTP Server Host"
|
||||
hint="For example: smtp.example.com"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-server-network"
|
||||
class="mb-4"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:rules="[rules.required]"
|
||||
/>
|
||||
|
||||
<v-select
|
||||
v-model="outboundEncryption"
|
||||
:items="encryptionOptions"
|
||||
label="Security"
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-shield-lock"
|
||||
class="mb-4"
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="outboundPort"
|
||||
label="Port"
|
||||
hint="Defaults to 465 for implicit TLS, 587 for STARTTLS, and 25 for plain"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-numeric"
|
||||
class="mb-4"
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
:rules="[rules.required, rules.port]"
|
||||
/>
|
||||
|
||||
<v-expansion-panels>
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title>
|
||||
<v-icon start>mdi-cog</v-icon>
|
||||
Security Options
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-switch
|
||||
v-model="outboundVerifyPeer"
|
||||
label="Verify TLS certificate"
|
||||
color="primary"
|
||||
hint="Disable only for trusted internal or test environments"
|
||||
persistent-hint
|
||||
class="mb-4"
|
||||
/>
|
||||
|
||||
<v-switch
|
||||
v-model="outboundVerifyHost"
|
||||
label="Verify certificate hostname"
|
||||
color="primary"
|
||||
hint="Checks that the certificate matches the SMTP host"
|
||||
persistent-hint
|
||||
class="mb-4"
|
||||
/>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -232,4 +352,4 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
letter-spacing: 0.0178571429em;
|
||||
color: rgba(var(--v-theme-on-surface), 0.7);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user