Skip to content

Commit

Permalink
feat: mail provider backend
Browse files Browse the repository at this point in the history
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
  • Loading branch information
SebastianKrupinski committed Jul 13, 2024
1 parent 662c2a5 commit 20dc43a
Show file tree
Hide file tree
Showing 10 changed files with 1,049 additions and 0 deletions.
9 changes: 9 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
use OCA\Mail\Listener\SpamReportListener;
use OCA\Mail\Listener\UserDeletedListener;
use OCA\Mail\Notification\Notifier;
use OCA\Mail\Provider\MailProvider;
use OCA\Mail\Search\FilteringProvider;
use OCA\Mail\Search\Provider;
use OCA\Mail\Service\Attachment\AttachmentService;
Expand Down Expand Up @@ -164,6 +165,14 @@ public function register(IRegistrationContext $context): void {
$context->registerSearchProvider(Provider::class);
}


// TODO: drop condition if nextcloud < 30 is not supported anymore
// evaluate, if mail provider registration is possible
if (method_exists($context, 'registerMailProvider')) {
// register mail provider
$context->registerMailProvider(MailProvider::class);

Check failure on line 173 in lib/AppInfo/Application.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

MissingDependency

lib/AppInfo/Application.php:173:35: MissingDependency: OCA\Mail\Provider\MailProvider depends on class or interface ocp\mail\provider\iprovider that does not exist (see https://psalm.dev/157)
}

$context->registerNotifierService(Notifier::class);

// bypass Horde Translation system
Expand Down
23 changes: 23 additions & 0 deletions lib/Db/MailAccountMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,29 @@ public function findByUserId(string $userId): array {
return $this->findEntities($query);
}

/**
* Finds a mail account(s) by user id and mail address
*
* @since 2024.05.25
*
* @param string $userId system user id
* @param string $address mail address (e.g. test@example.com)
*
* @return MailAccount[]
*
* @throws DoesNotExistException
*/
public function findByUserIdAndAddress(string $userId, string $address): array {
$qb = $this->db->getQueryBuilder();
$query = $qb
->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
->andWhere($qb->expr()->eq('email', $qb->createNamedParameter($address)));

return $this->findEntities($query);
}

/**
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
Expand Down
87 changes: 87 additions & 0 deletions lib/Provider/Command/MessageSend.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Mail\Provider\Command;

use OCA\Mail\Db\LocalMessage;
use OCA\Mail\Service\AccountService;
use OCA\Mail\Service\Attachment\AttachmentService;
use OCA\Mail\Service\OutboxService;
use OCA\Mail\Service\SmimeService;
use OCP\IConfig;
use OCP\Mail\Provider\IMessage;

class MessageSend {

public function __construct(
protected IConfig $config,
protected AccountService $accountService,
protected OutboxService $outboxService,
protected AttachmentService $attachmentService,
protected SmimeService $smimeService
) {
}

public function perform(string $userId, string $serviceId, IMessage $message, array $option = []): void {

Check failure on line 30 in lib/Provider/Command/MessageSend.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

UndefinedClass

lib/Provider/Command/MessageSend.php:30:61: UndefinedClass: Class, interface or enum named OCP\Mail\Provider\IMessage does not exist (see https://psalm.dev/019)
// find user mail account details
$account = $this->accountService->find($userId, (int) $serviceId);
// convert mail provider message to local message
$lm = new LocalMessage();
$lm->setType($lm::TYPE_OUTGOING);
$lm->setAccountId($account->getId());
$lm->setSubject($message->getSubject());
$lm->setBody($message->getBody());
$lm->setHtml(true);
$lm->setSendAt(time());

// convert all mail provider attachments to local attachments
$attachments = [];
if (count($message->getAttachments()) > 0) {
// iterate attachments and save them
foreach ($message->getAttachments() as $entry) {
$attachments[] = $this->attachmentService->addFileFromString(
$userId,
$entry->getName(),
$entry->getType(),
$entry->getContents()
)->jsonSerialize();
}
}
// convert recipiant addresses
$to = $this->convertAddressArray($message->getTo());
$cc = $this->convertAddressArray($message->getCc());
$bcc = $this->convertAddressArray($message->getBcc());
// save message for sending
$lm = $this->outboxService->saveMessage(
$account,
$lm,
$to,
$cc,
$bcc,
$attachments
);

// evaluate if job scheduler is NOT cron, send message right away otherwise let cron job handle it
if ($this->config->getAppValue('core', 'backgroundjobs_mode', 'ajax') !== 'cron') {
$lm = $this->outboxService->sendMessage($lm, $account);
}

}

protected function convertAddressArray(array|null $in) {
// construct place holder
$out = [];
// convert format
foreach ($in as $entry) {
$out[] = (!empty($entry->getLabel())) ? ['email' => $entry->getAddress(), 'label' => $entry->getLabel()] : ['email' => $entry->getAddress()];
}
// return converted addressess
return $out;
}

}
238 changes: 238 additions & 0 deletions lib/Provider/MailProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Mail\Provider;

use OCA\Mail\Account;
use OCA\Mail\Service\AccountService;

use OCP\Mail\Provider\Address as MailAddress;
use OCP\Mail\Provider\IProvider;
use OCP\Mail\Provider\IService;

use Psr\Container\ContainerInterface;

class MailProvider implements IProvider {

Check failure on line 20 in lib/Provider/MailProvider.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

UndefinedClass

lib/Provider/MailProvider.php:20:31: UndefinedClass: Class, interface or enum named OCP\Mail\Provider\IProvider does not exist (see https://psalm.dev/019)

private ?array $ServiceCollection = [];

public function __construct(
protected ContainerInterface $container,
protected AccountService $AccountService
) {
}

/**
* arbitrary unique text string identifying this provider
*
* @since 2024.05.25
*
* @return string id of this provider (e.g. UUID or 'IMAP/SMTP' or anything else)
*/
public function id(): string {

return 'mail-application';

}

/**
* localized human frendly name of this provider
*
* @since 2024.05.25
*
* @return string label/name of this provider (e.g. Plain Old IMAP/SMTP)
*/
public function label(): string {

return 'Mail Application';

}

/**
* determain if any services are configured for a specific user
*
* @since 2024.05.25
*
* @param string $userId user id
*
* @return bool true if any services are configure for the user
*/
public function hasServices(string $userId): bool {

return (count($this->listServices($userId)) > 0);

}

/**
* retrieve collection of services for a specific user
*
* @since 2024.05.25
*
* @param string $userId user id
*
* @return array<string,IService> collection of service id and object ['1' => IServiceObject]
*/
public function listServices(string $userId): array {

try {
// retrieve service(s) details from data store
$accounts = $this->AccountService->findByUserId($userId);
} catch (\Throwable $th) {
return [];
}
// construct temporary collection
$services = [];
// add services to collection
foreach ($accounts as $entry) {
// extract values
$serviceId = (string) $entry->getId();
$label = $entry->getName();
$address = new MailAddress($entry->getEmail(), $entry->getName());
$identity = new MailServiceIdentity();
$location = new MailServiceLocation();
// add service to collection
$services[$serviceId] = new MailService($this->container, $userId, $serviceId, $label, $address, $identity, $location);
}
// return list of services for user
return $services;

}

/**
* retrieve a service with a specific id
*
* @since 2024.05.25
*
* @param string $userId user id
* @param string $serviceId service id
*
* @return IService|null returns service object or null if none found
*/
public function findServiceById(string $userId, string $serviceId): IService | null {

// evaluate if id is a number
if (is_numeric($serviceId)) {
try {
// retrieve service details from data store
$account = $this->AccountService->find($userId, (int) $serviceId);
} catch(\Throwable $th) {
return null;
}
}
// evaliate if service details where found
if ($account instanceof Account) {
// extract values
$serviceId = (string) $account->getId();
$label = $account->getName();
$address = new MailAddress($account->getEmail(), $account->getName());
$identity = new MailServiceIdentity();
$location = new MailServiceLocation();
// return mail service object
return (new MailService($this->container, $userId, $serviceId, $label, $address, $identity, $location));
}

return null;

}

/**
* retrieve a service for a specific mail address
*
* @since 2024.05.25
*
* @param string $userId user id
* @param string $address mail address (e.g. test@example.com)
*
* @return IService returns service object or null if none found
*/
public function findServiceByAddress(string $userId, string $address): IService | null {

try {
// retrieve service details from data store
$accounts = $this->AccountService->findByUserIdAndAddress($userId, $address);
} catch(\Throwable $th) {
return null;
}
// evaliate if service details where found
if (is_array($accounts) && count($accounts) > 0 && $accounts[0] instanceof Account) {
// extract values
$serviceId = (string) $accounts[0]->getId();
$label = $accounts[0]->getName();
$address = new MailAddress($accounts[0]->getEmail(), $accounts[0]->getName());
$identity = new MailServiceIdentity();
$location = new MailServiceLocation();
// return mail service object
return (new MailService($this->container, $userId, $serviceId, $label, $address, $identity, $location));
}

return null;

}

/**
* construct a new empty service object
*
* @since 30.0.0
*
* @return IService blank service object
*/
public function initiateService(): IService {

return (new MailService($this->container));

}

/**
* create a service configuration for a specific user
*
* @since 2024.05.25
*
* @param string $userId user id
* @param IService $service service object
*
* @return string id of created service
*/
public function createService(string $userId, IService $service): string {

return '';

}

/**
* modify a service configuration for a specific user
*
* @since 2024.05.25
*
* @param string $userId user id
* @param IService $service service object
*
* @return string id of modifided service
*/
public function modifyService(string $userId, IService $service): string {

return '';

}

/**
* delete a service configuration for a specific user
*
* @since 2024.05.25
*
* @param string $userId user id
* @param IService $service service object
*
* @return bool status of delete action
*/
public function deleteService(string $userId, IService $service): bool {

return false;

}

}
Loading

0 comments on commit 20dc43a

Please sign in to comment.