213 lines
6.6 KiB
PHP
213 lines
6.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace KTXM\ProviderJmapc\Providers\Mail;
|
|
|
|
use KTXF\Mail\Provider\ProviderBaseInterface;
|
|
use KTXF\Mail\Provider\ProviderServiceDiscoverInterface;
|
|
use KTXF\Mail\Provider\ProviderServiceMutateInterface;
|
|
use KTXF\Mail\Provider\ProviderServiceTestInterface;
|
|
use KTXF\Mail\Service\ServiceBaseInterface;
|
|
use KTXF\Resource\Provider\ResourceServiceLocationInterface;
|
|
use KTXF\Resource\Provider\ResourceServiceMutateInterface;
|
|
use KTXM\ProviderJmapc\Service\Discovery;
|
|
use KTXM\ProviderJmapc\Service\Remote\RemoteService;
|
|
use KTXM\ProviderJmapc\Stores\ServiceStore;
|
|
|
|
/**
|
|
* JMAP Mail Provider
|
|
*
|
|
* Provides Mail services via JMAP protocol.
|
|
* Filters services by urn:ietf:params:jmap:mail capability.
|
|
*/
|
|
class Provider implements ProviderServiceMutateInterface, ProviderServiceDiscoverInterface, ProviderServiceTestInterface
|
|
{
|
|
|
|
public const JSON_TYPE = ProviderBaseInterface::JSON_TYPE;
|
|
protected const PROVIDER_IDENTIFIER = 'jmap';
|
|
protected const PROVIDER_LABEL = 'JMAP Mail Provider';
|
|
protected const PROVIDER_DESCRIPTION = 'Provides mail services via JMAP protocol (RFC 8620)';
|
|
protected const PROVIDER_ICON = 'mdi-email-sync';
|
|
|
|
protected array $providerAbilities = [
|
|
self::CAPABILITY_SERVICE_LIST => true,
|
|
self::CAPABILITY_SERVICE_FETCH => true,
|
|
self::CAPABILITY_SERVICE_EXTANT => true,
|
|
self::CAPABILITY_SERVICE_CREATE => true,
|
|
self::CAPABILITY_SERVICE_MODIFY => true,
|
|
self::CAPABILITY_SERVICE_DESTROY => true,
|
|
self::CAPABILITY_SERVICE_TEST => true,
|
|
];
|
|
|
|
public function __construct(
|
|
private readonly ServiceStore $serviceStore,
|
|
) {}
|
|
|
|
public function jsonSerialize(): array
|
|
{
|
|
return [
|
|
self::JSON_PROPERTY_TYPE => self::JSON_TYPE,
|
|
self::JSON_PROPERTY_IDENTIFIER => self::PROVIDER_IDENTIFIER,
|
|
self::JSON_PROPERTY_LABEL => self::PROVIDER_LABEL,
|
|
self::JSON_PROPERTY_CAPABILITIES => $this->providerAbilities,
|
|
];
|
|
}
|
|
|
|
public function jsonDeserialize(array|string $data): static
|
|
{
|
|
return $this;
|
|
}
|
|
|
|
public function type(): string
|
|
{
|
|
return self::TYPE_MAIL;
|
|
}
|
|
|
|
public function identifier(): string
|
|
{
|
|
return self::PROVIDER_IDENTIFIER;
|
|
}
|
|
|
|
public function label(): string
|
|
{
|
|
return self::PROVIDER_LABEL;
|
|
}
|
|
|
|
public function description(): string
|
|
{
|
|
return self::PROVIDER_DESCRIPTION;
|
|
}
|
|
|
|
public function icon(): string
|
|
{
|
|
return self::PROVIDER_ICON;
|
|
}
|
|
|
|
public function capable(string $value): bool
|
|
{
|
|
return !empty($this->providerAbilities[$value]);
|
|
}
|
|
|
|
public function capabilities(): array
|
|
{
|
|
return $this->providerAbilities;
|
|
}
|
|
|
|
public function serviceList(string $tenantId, string $userId, array $filter = []): array
|
|
{
|
|
$list = $this->serviceStore->list($tenantId, $userId, $filter);
|
|
foreach ($list as $entry) {
|
|
$service = new Service();
|
|
$service->fromStore($entry);
|
|
$list[$service->identifier()] = $service;
|
|
}
|
|
return $list;
|
|
}
|
|
|
|
public function serviceExtant(string $tenantId, string $userId, string|int ...$identifiers): array
|
|
{
|
|
return $this->serviceStore->extant($tenantId, $userId, $identifiers);
|
|
}
|
|
|
|
public function serviceFetch(string $tenantId, string $userId, string|int $identifier): ?Service
|
|
{
|
|
return $this->serviceStore->fetch($tenantId, $userId, $identifier);
|
|
}
|
|
|
|
public function serviceFindByAddress(string $tenantId, string $userId, string $address): ?Service
|
|
{
|
|
/** @var Service[] $services */
|
|
$services = $this->serviceList($tenantId, $userId);
|
|
foreach ($services as $service) {
|
|
if ($service->hasAddress($address)) {
|
|
return $service;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function serviceFresh(): ResourceServiceMutateInterface
|
|
{
|
|
return new Service();
|
|
}
|
|
|
|
public function serviceCreate(string $tenantId, string $userId, ResourceServiceMutateInterface $service): string
|
|
{
|
|
if (!($service instanceof Service)) {
|
|
throw new \InvalidArgumentException('Service must be instance of JMAP Service');
|
|
}
|
|
|
|
$created = $this->serviceStore->create($tenantId, $userId, $service);
|
|
return (string) $created->identifier();
|
|
}
|
|
|
|
public function serviceModify(string $tenantId, string $userId, ResourceServiceMutateInterface $service): string
|
|
{
|
|
if (!($service instanceof Service)) {
|
|
throw new \InvalidArgumentException('Service must be instance of JMAP Service');
|
|
}
|
|
|
|
$updated = $this->serviceStore->modify($tenantId, $userId, $service);
|
|
return (string) $updated->identifier();
|
|
}
|
|
|
|
public function serviceDestroy(string $tenantId, string $userId, ResourceServiceMutateInterface $service): bool
|
|
{
|
|
if (!($service instanceof Service)) {
|
|
return false;
|
|
}
|
|
|
|
return $this->serviceStore->delete($tenantId, $userId, $service->identifier());
|
|
}
|
|
|
|
public function serviceDiscover(
|
|
string $tenantId,
|
|
string $userId,
|
|
string $identity,
|
|
?string $location = null,
|
|
?string $secret = null
|
|
): ResourceServiceLocationInterface|null {
|
|
$discovery = new Discovery();
|
|
|
|
// TODO: Make SSL verification configurable based on tenant/user settings
|
|
$verifySSL = true;
|
|
|
|
return $discovery->discover($identity, $location, $secret, $verifySSL);
|
|
}
|
|
|
|
public function serviceTest(ServiceBaseInterface $service, array $options = []): array {
|
|
$startTime = microtime(true);
|
|
|
|
try {
|
|
if (!($service instanceof Service)) {
|
|
throw new \InvalidArgumentException('Service must be instance of JMAP Service');
|
|
}
|
|
|
|
$client = RemoteService::freshClient($service);
|
|
$session = $client->connect();
|
|
|
|
$latency = round((microtime(true) - $startTime) * 1000); // ms4
|
|
|
|
return [
|
|
'success' => true,
|
|
'message' => 'JMAP connection successful'
|
|
. ' (Account ID: ' . ($session->username() ?? 'N/A') . ')'
|
|
. ' (Latency: ' . $latency . ' ms)',
|
|
];
|
|
} catch (\Exception $e) {
|
|
$latency = round((microtime(true) - $startTime) * 1000);
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Test failed: ' . $e->getMessage(),
|
|
];
|
|
}
|
|
}
|
|
|
|
}
|