diff options
author | Christoph Wurst <christoph@winzerhof-wurst.at> | 2020-08-11 22:10:49 +0300 |
---|---|---|
committer | Christoph Wurst <christoph@winzerhof-wurst.at> | 2020-08-24 21:21:40 +0300 |
commit | 9498ebac6eccde201526b9a6131a76c02ca5db62 (patch) | |
tree | 7256f1fc6de4dee185f5cc116e45247cf27f28cb /lib | |
parent | 430500712496242526eed2aedb5afc42d60ca1b9 (diff) |
Rework the routing
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Diffstat (limited to 'lib')
29 files changed, 731 insertions, 418 deletions
diff --git a/lib/Attachment.php b/lib/Attachment.php index 33acaace9..1770587be 100644 --- a/lib/Attachment.php +++ b/lib/Attachment.php @@ -32,13 +32,13 @@ class Attachment { /** * @param \Horde_Imap_Client_Socket $conn * @param \Horde_Imap_Client_Mailbox $mailBox - * @param int $messageId + * @param int $messageUid * @param string $attachmentId */ - public function __construct($conn, $mailBox, $messageId, $attachmentId) { + public function __construct($conn, $mailBox, $messageUid, $attachmentId) { $this->conn = $conn; $this->mailBox = $mailBox; - $this->messageId = $messageId; + $this->messageUid = $messageUid; $this->attachmentId = $attachmentId; $this->load(); @@ -53,7 +53,7 @@ class Attachment { * @var \Horde_Imap_Client_Mailbox */ private $mailBox; - private $messageId; + private $messageUid; private $attachmentId; /** @@ -67,13 +67,13 @@ class Attachment { $fetch_query->mimeHeader($this->attachmentId); // $list is an array of Horde_Imap_Client_Data_Fetch objects. - $ids = new \Horde_Imap_Client_Ids($this->messageId); + $ids = new \Horde_Imap_Client_Ids($this->messageUid); $headers = $this->conn->fetch($this->mailBox, $fetch_query, ['ids' => $ids]); /** @var $fetch Horde_Imap_Client_Data_Fetch */ - if (!isset($headers[$this->messageId])) { + if (!isset($headers[$this->messageUid])) { throw new DoesNotExistException('Unable to load the attachment.'); } - $fetch = $headers[$this->messageId]; + $fetch = $headers[$this->messageUid]; /** @var \Horde_Mime_Headers $mimeHeaders */ $mimeHeaders = $fetch->getMimeHeader($this->attachmentId, Horde_Imap_Client_Data_Fetch::HEADER_PARSE); diff --git a/lib/Contracts/IMailManager.php b/lib/Contracts/IMailManager.php index 553aea52b..212aee8a8 100644 --- a/lib/Contracts/IMailManager.php +++ b/lib/Contracts/IMailManager.php @@ -25,16 +25,28 @@ namespace OCA\Mail\Contracts; use OCA\Mail\Account; use OCA\Mail\Db\Mailbox; +use OCA\Mail\Db\Message; use OCA\Mail\Exception\ClientException; use OCA\Mail\Exception\ServiceException; use OCA\Mail\Folder; use OCA\Mail\IMAP\FolderStats; use OCA\Mail\Model\IMAPMessage; use OCA\Mail\Service\Quota; +use OCP\AppFramework\Db\DoesNotExistException; interface IMailManager { /** + * @param string $uid + * @param int $id + * + * @return Mailbox + * + * @throws DoesNotExistException + */ + public function getMailbox(string $uid, int $id): Mailbox; + + /** * @param Account $account * * @return Mailbox[] @@ -47,44 +59,63 @@ interface IMailManager { * @param Account $account * @param string $name * - * @return Folder + * @return Mailbox * * @throws ServiceException */ - public function createFolder(Account $account, string $name): Folder; + public function createMailbox(Account $account, string $name): Mailbox; /** * @param Account $account - * @param string $folderId + * @param Mailbox $mailbox * * @return FolderStats + */ + public function getMailboxStats(Account $account, Mailbox $mailbox): FolderStats; + + /** + * @param Mailbox $mailbox + * @param $uid * - * @throws ServiceException + * @return int|null + */ + public function getMessageIdForUid(Mailbox $mailbox, $uid): ?int; + + /** + * @param string $uid + * @param int $id + * + * @return Mailbox + * + * @throws ClientException */ - public function getFolderStats(Account $account, string $folderId): FolderStats; + public function getMessage(string $uid, int $id): Message; /** * @param Account $account * @param string $mb - * @param int $id + * @param int $uid * * @return string * @throws ClientException * @throws ServiceException */ - public function getSource(Account $account, string $mb, int $id): string; + public function getSource(Account $account, string $mb, int $uid): ?string; /** * @param Account $account - * @param string $mailbox - * @param int $id + * @param Mailbox $mailbox + * @param int $uid * @param bool $loadBody * * @return IMAPMessage * * @throws ServiceException */ - public function getMessage(Account $account, string $mailbox, int $id, bool $loadBody = false): IMAPMessage; + public function getImapMessage(Account $account, + Mailbox $mailbox, + int $uid, + bool $loadBody = false): IMAPMessage; /** * @param Account $account @@ -97,14 +128,17 @@ interface IMailManager { /** * @param Account $sourceAccount * @param string $sourceFolderId - * @param int $messageId + * @param int $uid * @param Account $destinationAccount * @param string $destFolderId * * @throws ServiceException */ - public function moveMessage(Account $sourceAccount, string $sourceFolderId, int $messageId, - Account $destinationAccount, string $destFolderId); + public function moveMessage(Account $sourceAccount, + string $sourceFolderId, + int $uid, + Account $destinationAccount, + string $destFolderId); /** * @param Account $account @@ -119,11 +153,9 @@ interface IMailManager { * Mark all messages of a folder as read * * @param Account $account - * @param string $folderId - * - * @throws ServiceException + * @param Mailbox $mailbox */ - public function markFolderAsRead(Account $account, string $folderId): void; + public function markFolderAsRead(Account $account, Mailbox $mailbox): void; /** * @param Account $account @@ -146,9 +178,9 @@ interface IMailManager { /** * @param Account $account - * @param string $folderId + * @param Mailbox $mailbox * * @throws ServiceException */ - public function deleteMailbox(Account $account, string $folderId): void; + public function deleteMailbox(Account $account, Mailbox $mailbox): void; } diff --git a/lib/Contracts/IMailSearch.php b/lib/Contracts/IMailSearch.php index 9487334b2..c751e2c53 100644 --- a/lib/Contracts/IMailSearch.php +++ b/lib/Contracts/IMailSearch.php @@ -26,6 +26,7 @@ declare(strict_types=1); namespace OCA\Mail\Contracts; use OCA\Mail\Account; +use OCA\Mail\Db\Mailbox; use OCA\Mail\Db\Message; use OCA\Mail\Exception\ClientException; use OCA\Mail\Exception\ServiceException; @@ -43,18 +44,25 @@ interface IMailSearch { * @throws ClientException * @throws ServiceException */ - public function findMessage(Account $account, string $mailboxName, int $uid): Message; + public function findMessage(Account $account, + Mailbox $mailbox, + Message $message): Message; /** * @param Account $account - * @param string $mailboxName + * @param Mailbox $mailbox * @param string|null $filter * @param int|null $cursor + * @param int|null $limit * * @return Message[] * * @throws ClientException * @throws ServiceException */ - public function findMessages(Account $account, string $mailboxName, ?string $filter, ?int $cursor, ?int $limit): array; + public function findMessages(Account $account, + Mailbox $mailbox, + ?string $filter, + ?int $cursor, + ?int $limit): array; } diff --git a/lib/Contracts/IMailTransmission.php b/lib/Contracts/IMailTransmission.php index ff1d04138..d4e4c3c57 100644 --- a/lib/Contracts/IMailTransmission.php +++ b/lib/Contracts/IMailTransmission.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace OCA\Mail\Contracts; use OCA\Mail\Db\Alias; +use OCA\Mail\Db\Message; use OCA\Mail\Exception\ServiceException; use OCA\Mail\Model\NewMessageData; use OCA\Mail\Model\RepliedMessageData; @@ -33,27 +34,27 @@ interface IMailTransmission { /** * Send a new message or reply to an existing one * - * @param string $userId * @param NewMessageData $message - * @param RepliedMessageData $reply + * @param RepliedMessageData|null $reply * @param Alias|null $alias + * @param Message|null $draft * * @throws ServiceException */ public function sendMessage(NewMessageData $message, RepliedMessageData $reply = null, Alias $alias = null, - int $draftUID = null); + Message $draft = null); /** * Save a message draft * * @param NewMessageData $message - * @param int $draftUID + * @param Message|null $previousDraft * - * @return int + * @return array * * @throws ServiceException */ - public function saveDraft(NewMessageData $message, int $draftUID = null): int; + public function saveDraft(NewMessageData $message, Message $previousDraft = null): array; } diff --git a/lib/Controller/AccountsController.php b/lib/Controller/AccountsController.php index e5c8a94bb..94221eb09 100644 --- a/lib/Controller/AccountsController.php +++ b/lib/Controller/AccountsController.php @@ -30,8 +30,10 @@ declare(strict_types=1); namespace OCA\Mail\Controller; use Exception; +use Horde_Imap_Client; use OCA\Mail\Contracts\IMailManager; use OCA\Mail\Contracts\IMailTransmission; +use OCA\Mail\Db\Mailbox; use OCA\Mail\Exception\ClientException; use OCA\Mail\Exception\ServiceException; use OCA\Mail\Http\JsonResponse as MailJsonResponse; @@ -41,6 +43,7 @@ use OCA\Mail\Service\AccountService; use OCA\Mail\Service\AliasesService; use OCA\Mail\Service\GroupsIntegration; use OCA\Mail\Service\SetupService; +use OCA\Mail\Service\Sync\SyncService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; @@ -77,17 +80,37 @@ class AccountsController extends Controller { /** @var IMailManager */ private $mailManager; + /** @var SyncService */ + private $syncService; + + /** + * AccountsController constructor. + * + * @param string $appName + * @param IRequest $request + * @param AccountService $accountService + * @param GroupsIntegration $groupsIntegration + * @param $UserId + * @param ILogger $logger + * @param IL10N $l10n + * @param AliasesService $aliasesService + * @param IMailTransmission $mailTransmission + * @param SetupService $setup + * @param IMailManager $mailManager + * @param SyncService $syncService + */ public function __construct(string $appName, - IRequest $request, - AccountService $accountService, - GroupsIntegration $groupsIntegration, - $UserId, - ILogger $logger, - IL10N $l10n, - AliasesService $aliasesService, - IMailTransmission $mailTransmission, - SetupService $setup, - IMailManager $mailManager + IRequest $request, + AccountService $accountService, + GroupsIntegration $groupsIntegration, + $UserId, + ILogger $logger, + IL10N $l10n, + AliasesService $aliasesService, + IMailTransmission $mailTransmission, + SetupService $setup, + IMailManager $mailManager, + SyncService $syncService ) { parent::__construct($appName, $request); $this->accountService = $accountService; @@ -99,6 +122,7 @@ class AccountsController extends Controller { $this->mailTransmission = $mailTransmission; $this->setup = $setup; $this->mailManager = $mailManager; + $this->syncService = $syncService; } /** @@ -123,13 +147,13 @@ class AccountsController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId + * @param int $id * * @return JSONResponse * @throws ClientException */ - public function show($accountId): JSONResponse { - return new JSONResponse($this->accountService->find($this->currentUserId, $accountId)); + public function show(int $id): JSONResponse { + return new JSONResponse($this->accountService->find($this->currentUserId, $id)); } /** @@ -198,7 +222,7 @@ class AccountsController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId + * @param int $id * @param string|null $editorMode * @param int|null $order * @param bool|null $showSubscribedOnly @@ -207,11 +231,11 @@ class AccountsController extends Controller { * * @throws ClientException */ - public function patchAccount(int $accountId, + public function patchAccount(int $id, string $editorMode = null, int $order = null, bool $showSubscribedOnly = null): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); + $account = $this->accountService->find($this->currentUserId, $id); if ($account === null) { return new JSONResponse(null, Http::STATUS_FORBIDDEN); @@ -236,7 +260,7 @@ class AccountsController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId + * @param int $id * @param string|null $signature * * @return JSONResponse @@ -244,8 +268,8 @@ class AccountsController extends Controller { * @throws ClientException * @throws ServiceException */ - public function updateSignature(int $accountId, string $signature = null): JSONResponse { - $this->accountService->updateSignature($accountId, $this->currentUserId, $signature); + public function updateSignature(int $id, string $signature = null): JSONResponse { + $this->accountService->updateSignature($id, $this->currentUserId, $signature); return new JSONResponse(); } @@ -259,7 +283,7 @@ class AccountsController extends Controller { * * @throws ClientException */ - public function destroy($id): JSONResponse { + public function destroy(int $id): JSONResponse { $this->accountService->delete($this->currentUserId, $id); return new JSONResponse(); } @@ -315,13 +339,13 @@ class AccountsController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId + * @param int $id * @param string $subject * @param string $body * @param string $to * @param string $cc * @param string $bcc - * @param int|null $draftUID + * @param int|null $draftId * @param string|null $folderId * @param int|null $messageId * @param mixed $attachments @@ -332,19 +356,19 @@ class AccountsController extends Controller { * @throws ClientException * @throws ServiceException */ - public function send(int $accountId, + public function send(int $id, string $subject, string $body, string $to, string $cc, string $bcc, bool $isHtml = true, - int $draftUID = null, + int $draftId = null, string $folderId = null, int $messageId = null, array $attachments = [], int $aliasId = null): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); + $account = $this->accountService->find($this->currentUserId, $id); $alias = $aliasId ? $this->aliasesService->find($aliasId, $this->currentUserId) : null; $expandedTo = $this->groupsIntegration->expand($to); @@ -357,8 +381,16 @@ class AccountsController extends Controller { $repliedMessageData = new RepliedMessageData($account, base64_decode($folderId), $messageId); } + $draft = null; + if ($draftId !== null) { + try { + $draft = $this->mailManager->getMessage($this->currentUserId, $draftId); + } catch (ClientException $e) { + $this->logger->info("Draft " . $draftId . " could not be loaded: " . $e->getMessage()); + } + } try { - $this->mailTransmission->sendMessage($messageData, $repliedMessageData, $alias, $draftUID); + $this->mailTransmission->sendMessage($messageData, $repliedMessageData, $alias, $draft); return new JSONResponse(); } catch (ServiceException $ex) { $this->logger->error('Sending mail failed: ' . $ex->getMessage()); @@ -370,7 +402,7 @@ class AccountsController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId + * @param int $id * @param string $subject * @param string $body * @param string $to @@ -382,27 +414,43 @@ class AccountsController extends Controller { * * @throws ClientException */ - public function draft(int $accountId, - string $subject = null, + public function draft(int $id, + string $subject, string $body, string $to, string $cc, string $bcc, bool $isHtml = true, - int $draftUID = null): JSONResponse { - if ($draftUID === null) { - $this->logger->info("Saving a new draft in account <$accountId>"); + int $draftId = null): JSONResponse { + if ($draftId === null) { + $this->logger->info("Saving a new draft in account <$id>"); } else { - $this->logger->info("Updating draft <$draftUID> in account <$accountId>"); + $this->logger->info("Updating draft <$draftId> in account <$id>"); } - $account = $this->accountService->find($this->currentUserId, $accountId); + $account = $this->accountService->find($this->currentUserId, $id); + $previousDraft = null; + if ($draftId !== null) { + try { + $previousDraft = $this->mailManager->getMessage($this->currentUserId, $draftId); + } catch (ClientException $e) { + $this->logger->info("Draft " . $draftId . " could not be loaded: " . $e->getMessage()); + } + } $messageData = NewMessageData::fromRequest($account, $to, $cc, $bcc, $subject, $body, [], $isHtml); try { - $newUID = $this->mailTransmission->saveDraft($messageData, $draftUID); + /** @var Mailbox $draftsMailbox */ + [, $draftsMailbox, $newUID] = $this->mailTransmission->saveDraft($messageData, $previousDraft); + $this->syncService->syncMailbox( + $account, + $draftsMailbox, + Horde_Imap_Client::SYNC_NEWMSGS, + [], + false + ); return new JSONResponse([ - 'uid' => $newUID, + 'id' => $this->mailManager->getMessageIdForUid($draftsMailbox, $newUID) ]); } catch (ServiceException $ex) { $this->logger->error('Saving draft failed: ' . $ex->getMessage()); @@ -413,13 +461,13 @@ class AccountsController extends Controller { /** * @NoAdminRequired * - * @param int $accountId + * @param int $id * * @return JSONResponse * @throws ClientException */ - public function getQuota(int $accountId): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); + public function getQuota(int $id): JSONResponse { + $account = $this->accountService->find($this->currentUserId, $id); $quota = $this->mailManager->getQuota($account); if ($quota === null) { diff --git a/lib/Controller/AliasesController.php b/lib/Controller/AliasesController.php index cda86bd65..ca9d61589 100644 --- a/lib/Controller/AliasesController.php +++ b/lib/Controller/AliasesController.php @@ -54,9 +54,10 @@ class AliasesController extends Controller { * @TrapError * * @param int $accountId + * * @return JSONResponse */ - public function index($accountId): JSONResponse { + public function index(int $accountId): JSONResponse { return new JSONResponse($this->aliasService->findAll($accountId, $this->currentUser->getUID())); } @@ -83,7 +84,7 @@ class AliasesController extends Controller { * @param int $id * @return JSONResponse */ - public function destroy($id): JSONResponse { + public function destroy(int $id): JSONResponse { return new JSONResponse($this->aliasService->delete($id, $this->currentUser->getUID())); } @@ -94,9 +95,10 @@ class AliasesController extends Controller { * @param int $accountId * @param string $alias * @param string $aliasName + * * @return JSONResponse */ - public function create($accountId, $alias, $aliasName): JSONResponse { + public function create(int $accountId, string $alias, string $aliasName): JSONResponse { return new JSONResponse($this->aliasService->create($accountId, $alias, $aliasName), Http::STATUS_CREATED); } } diff --git a/lib/Controller/FoldersController.php b/lib/Controller/MailboxesController.php index 2527658e5..c7ad55007 100644 --- a/lib/Controller/FoldersController.php +++ b/lib/Controller/MailboxesController.php @@ -31,8 +31,6 @@ use OCA\Mail\Exception\IncompleteSyncException; use OCA\Mail\Exception\MailboxNotCachedException; use OCA\Mail\Exception\ServiceException; use OCA\Mail\Service\Sync\SyncService; -use function base64_decode; -use function is_array; use OCA\Mail\Contracts\IMailManager; use OCA\Mail\Exception\NotImplemented; use OCA\Mail\Service\AccountService; @@ -41,7 +39,7 @@ use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCP\IRequest; -class FoldersController extends Controller { +class MailboxesController extends Controller { /** @var AccountService */ private $accountService; @@ -66,7 +64,7 @@ class FoldersController extends Controller { public function __construct(string $appName, IRequest $request, AccountService $accountService, - $UserId, + ?string $UserId, IMailManager $mailManager, SyncService $syncService) { parent::__construct($appName, $request); @@ -94,7 +92,7 @@ class FoldersController extends Controller { return new JSONResponse([ 'id' => $accountId, 'email' => $account->getEmail(), - 'folders' => $mailboxes, + 'mailboxes' => $mailboxes, 'delimiter' => reset($mailboxes)->getDelimiter(), ]); } @@ -103,30 +101,28 @@ class FoldersController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId - * @param string $folderId - * @param string $syncToken - * @param int[] $uids + * @param int $id + * @param int[] $ids + * + * @param bool $init + * @param string|null $query * * @return JSONResponse * @throws ClientException * @throws ServiceException */ - public function sync(int $accountId, string $folderId, array $uids = [], bool $init = false, string $query = null): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); - - if (empty($accountId) || empty($folderId) || !is_array($uids)) { - return new JSONResponse(null, Http::STATUS_BAD_REQUEST); - } + public function sync(int $id, array $ids = [], bool $init = false, string $query = null): JSONResponse { + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $id); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); try { $syncResponse = $this->syncService->syncMailbox( $account, - base64_decode($folderId), + $mailbox, Horde_Imap_Client::SYNC_NEWMSGSUIDS | Horde_Imap_Client::SYNC_FLAGSUIDS | Horde_Imap_Client::SYNC_VANISHEDUIDS, - array_map(function ($uid) { - return (int) $uid; - }, $uids), + array_map(function ($id) { + return (int) $id; + }, $ids), !$init, $query ); @@ -143,21 +139,17 @@ class FoldersController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId - * @param string $folderId + * @param string $id * * @return JSONResponse * @throws ClientException * @throws ServiceException */ - public function clearCache(int $accountId, string $folderId): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); + public function clearCache(int $id): JSONResponse { + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $id); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); - if (empty($accountId) || empty($folderId)) { - return new JSONResponse(null, Http::STATUS_BAD_REQUEST); - } - - $this->syncService->clearCache($account, base64_decode($folderId)); + $this->syncService->clearCache($account, $mailbox); return new JSONResponse(null); } @@ -165,43 +157,37 @@ class FoldersController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId - * @param string $folderId + * @param int $id + * * @return JSONResponse * * @throws ClientException */ - public function markAllAsRead(int $accountId, string $folderId): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); - - if (empty($accountId) || empty($folderId)) { - return new JSONResponse(null, Http::STATUS_BAD_REQUEST); - } + public function markAllAsRead(int $id): JSONResponse { + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $id); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); - $syncResponse = $this->mailManager->markFolderAsRead($account, base64_decode($folderId)); + $this->mailManager->markFolderAsRead($account, $mailbox); - return new JSONResponse($syncResponse); + return new JSONResponse(null); } /** * @NoAdminRequired * @TrapError * - * @param int $accountId - * @param string $folderId + * @param int $id * * @return JSONResponse * * @throws ClientException + * @throws ServiceException */ - public function stats(int $accountId, string $folderId): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); - - if (empty($accountId) || empty($folderId)) { - return new JSONResponse(null, Http::STATUS_BAD_REQUEST); - } + public function stats(int $id): JSONResponse { + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $id); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); - $stats = $this->mailManager->getFolderStats($account, base64_decode($folderId)); + $stats = $this->mailManager->getMailboxStats($account, $mailbox); return new JSONResponse($stats); } @@ -232,19 +218,24 @@ class FoldersController extends Controller { public function create(int $accountId, string $name) { $account = $this->accountService->find($this->currentUserId, $accountId); - return new JSONResponse($this->mailManager->createFolder($account, $name)); + return new JSONResponse($this->mailManager->createMailbox($account, $name)); } /** * @NoAdminRequired * @TrapError - * @param int $accountId - * @param string $folderId + * + * @param int $id + * + * @return JSONResponse + * @throws ClientException * @throws ServiceException */ - public function delete(int $accountId, string $folderId): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); - $this->mailManager->deleteMailbox($account, base64_decode($folderId)); + public function destroy(int $id): JSONResponse { + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $id); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); + + $this->mailManager->deleteMailbox($account, $mailbox); return new JSONResponse(); } } diff --git a/lib/Controller/MessagesController.php b/lib/Controller/MessagesController.php index ef6357835..df602bf94 100755 --- a/lib/Controller/MessagesController.php +++ b/lib/Controller/MessagesController.php @@ -39,7 +39,6 @@ use OCA\Mail\Http\AttachmentDownloadResponse; use OCA\Mail\Http\HtmlResponse; use OCA\Mail\Model\IMAPMessage; use OCA\Mail\Service\AccountService; -use OCA\Mail\Service\IMailBox; use OCA\Mail\Service\ItineraryService; use OCP\AppFramework\Controller; use OCP\AppFramework\Db\DoesNotExistException; @@ -55,7 +54,6 @@ use OCP\ILogger; use OCP\IRequest; use OCP\IURLGenerator; use function array_map; -use function base64_decode; class MessagesController extends Controller { @@ -131,29 +129,33 @@ class MessagesController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId - * @param string $folderId + * @param int $mailboxId * @param int $cursor * @param string $filter + * @param int|null $limit * * @return JSONResponse * * @throws ClientException * @throws ServiceException */ - public function index(int $accountId, string $folderId, int $cursor = null, string $filter = null, int $limit = null): JSONResponse { + public function index(int $mailboxId, + int $cursor = null, + string $filter = null, + int $limit = null): JSONResponse { try { - $account = $this->accountService->find($this->currentUserId, $accountId); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $mailboxId); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); } catch (DoesNotExistException $e) { return new JSONResponse(null, Http::STATUS_FORBIDDEN); } - $this->logger->debug("loading messages of folder <$folderId>"); + $this->logger->debug("loading messages of folder <$mailboxId>"); return new JSONResponse( $this->mailSearch->findMessages( $account, - base64_decode($folderId), + $mailbox, $filter === '' ? null : $filter, $cursor, $limit @@ -174,20 +176,22 @@ class MessagesController extends Controller { * @throws ClientException * @throws ServiceException */ - public function show(int $accountId, string $folderId, int $id): JSONResponse { + public function show(int $id): JSONResponse { try { - $account = $this->accountService->find($this->currentUserId, $accountId); + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); } catch (DoesNotExistException $e) { return new JSONResponse(null, Http::STATUS_FORBIDDEN); } - $this->logger->debug("loading message of folder <$folderId>"); + $this->logger->debug("loading message <$id>"); return new JSONResponse( $this->mailSearch->findMessage( $account, - base64_decode($folderId), - $id + $mailbox, + $message ) ); } @@ -196,40 +200,40 @@ class MessagesController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId - * @param string $folderId - * @param int $messageId + * @param int $id * * @return JSONResponse * * @throws ClientException * @throws ServiceException */ - public function getBody(int $accountId, string $folderId, int $messageId): JSONResponse { + public function getBody(int $id): JSONResponse { try { - $account = $this->accountService->find($this->currentUserId, $accountId); + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); } catch (DoesNotExistException $e) { return new JSONResponse(null, Http::STATUS_FORBIDDEN); } - $json = $this->mailManager->getMessage( + $json = $this->mailManager->getImapMessage( $account, - base64_decode($folderId), - $messageId, + $mailbox, + $message->getUid(), true - )->getFullMessage( - $accountId, - base64_decode($folderId), - $messageId - ); + )->getFullMessage($id); $json['itineraries'] = $this->itineraryService->extract( $account, - base64_decode($folderId), - $messageId + $mailbox->getName(), + $message->getUid() ); - $json['attachments'] = array_map(function ($a) use ($accountId, $folderId, $messageId) { - return $this->enrichDownloadUrl($accountId, $folderId, $messageId, $a); + $json['attachments'] = array_map(function ($a) use ($id) { + return $this->enrichDownloadUrl( + $id, + $a + ); }, $json['attachments']); + $json['databaseId'] = $message->getId(); return new JSONResponse($json); } @@ -239,14 +243,19 @@ class MessagesController extends Controller { * @NoCSRFRequired * @TrapError * - * @param int $accountId - * @param string $folderId + * @param int $id * * @return JSONResponse * @throws ClientException */ - public function getThread(int $accountId, int $id): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); + public function getThread(int $id): JSONResponse { + try { + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); + } catch (DoesNotExistException $e) { + return new JSONResponse(null, Http::STATUS_FORBIDDEN); + } return new JSONResponse($this->mailManager->getThread($account, $id)); } @@ -255,10 +264,7 @@ class MessagesController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId - * @param string $folderId * @param int $id - * @param int $destAccountId * @param string $destFolderId * * @return JSONResponse @@ -266,12 +272,24 @@ class MessagesController extends Controller { * @throws ClientException * @throws ServiceException */ - public function move($accountId, $folderId, $id, $destAccountId, $destFolderId): JSONResponse { - $srcAccount = $this->accountService->find($this->currentUserId, $accountId); - $dstAccount = $this->accountService->find($this->currentUserId, - $destAccountId); - $this->mailManager->moveMessage($srcAccount, base64_decode($folderId), $id, - $dstAccount, base64_decode($destFolderId)); + public function move(int $id, int $destFolderId): JSONResponse { + try { + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $srcMailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $dstMailbox = $this->mailManager->getMailbox($this->currentUserId, $destFolderId); + $srcAccount = $this->accountService->find($this->currentUserId, $srcMailbox->getAccountId()); + $dstAccount = $this->accountService->find($this->currentUserId, $dstMailbox->getAccountId()); + } catch (DoesNotExistException $e) { + return new JSONResponse(null, Http::STATUS_FORBIDDEN); + } + + $this->mailManager->moveMessage( + $srcAccount, + $srcMailbox->getName(), + $message->getUid(), + $dstAccount, + $dstMailbox->getName() + ); return new JSONResponse(); } @@ -284,17 +302,23 @@ class MessagesController extends Controller { * @param string $folderId * @param int $messageId * - * @return HtmlResponse|TemplateResponse + * @return JSONResponse * @throws ServiceException */ - public function getSource(int $accountId, string $folderId, int $messageId): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); + public function getSource(int $id): JSONResponse { + try { + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); + } catch (DoesNotExistException $e) { + return new JSONResponse(null, Http::STATUS_FORBIDDEN); + } $response = new JSONResponse([ 'source' => $this->mailManager->getSource( $account, - base64_decode($folderId), - $messageId + $mailbox->getName(), + $message->getUid() ) ]); @@ -309,18 +333,18 @@ class MessagesController extends Controller { * @NoCSRFRequired * @TrapError * - * @param int $accountId - * @param string $folderId - * @param int $messageId + * @param int $id * * @return HtmlResponse|TemplateResponse * * @throws ClientException */ - public function getHtmlBody(int $accountId, string $folderId, int $messageId): Response { + public function getHtmlBody(int $id): Response { try { try { - $account = $this->accountService->find($this->currentUserId, $accountId); + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); } catch (DoesNotExistException $e) { return new TemplateResponse( $this->appName, @@ -331,12 +355,14 @@ class MessagesController extends Controller { } $htmlResponse = new HtmlResponse( - $this->mailManager->getMessage( + $this->mailManager->getImapMessage( $account, - base64_decode($folderId), - $messageId, + $mailbox, + $message->getUid(), true - )->getHtmlBody($accountId, base64_decode($folderId), $messageId) + )->getHtmlBody( + $id + ) ); // Harden the default security policy @@ -369,19 +395,25 @@ class MessagesController extends Controller { * * @param int $accountId * @param string $folderId - * @param int $messageId + * @param int $id * @param int $attachmentId * - * @return AttachmentDownloadResponse + * @return Response * * @throws ClientException * @throws ServiceException */ - public function downloadAttachment(int $accountId, string $folderId, int $messageId, - string $attachmentId) { - $mailBox = $this->getFolder($accountId, $folderId); - - $attachment = $mailBox->getAttachment($messageId, $attachmentId); + public function downloadAttachment(int $id, + string $attachmentId): Response { + try { + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); + } catch (DoesNotExistException $e) { + return new JSONResponse(null, Http::STATUS_FORBIDDEN); + } + $folder = $account->getMailbox($mailbox->getName()); + $attachment = $folder->getAttachment($message->getUid(), $attachmentId); // Body party and embedded messages do not have a name if ($attachment->getName() === null) { @@ -404,9 +436,7 @@ class MessagesController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId - * @param string $folderId - * @param int $messageId + * @param int $id * @param int $attachmentId * @param string $targetPath * @@ -415,14 +445,22 @@ class MessagesController extends Controller { * @throws ClientException * @throws ServiceException */ - public function saveAttachment(int $accountId, string $folderId, int $messageId, - string $attachmentId, string $targetPath) { - $mailBox = $this->getFolder($accountId, $folderId); + public function saveAttachment(int $id, + string $attachmentId, + string $targetPath) { + try { + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); + } catch (DoesNotExistException $e) { + return new JSONResponse(null, Http::STATUS_FORBIDDEN); + } + $folder = $account->getMailbox($mailbox->getName()); if ($attachmentId === '0') { // Save all attachments /* @var $m IMAPMessage */ - $m = $mailBox->getMessage($messageId); + $m = $folder->getMessage($id); $attachmentIds = array_map(function ($a) { return $a['id']; }, $m->attachments); @@ -430,11 +468,11 @@ class MessagesController extends Controller { $attachmentIds = [$attachmentId]; } - foreach ($attachmentIds as $attachmentId) { - $attachment = $mailBox->getAttachment($messageId, $attachmentId); + foreach ($attachmentIds as $aid) { + $attachment = $folder->getAttachment($message->getUid(), $attachmentId); $fileName = $attachment->getName() ?? $this->l10n->t('Embedded message %s', [ - $attachmentId, + $aid, ]) . '.eml'; $fileParts = pathinfo($fileName); $fileName = $fileParts['filename']; @@ -456,9 +494,7 @@ class MessagesController extends Controller { * @NoAdminRequired * @TrapError * - * @param int $accountId - * @param string $folderId - * @param string $messageId + * @param string $id * @param array $flags * * @return JSONResponse @@ -466,12 +502,18 @@ class MessagesController extends Controller { * @throws ClientException * @throws ServiceException */ - public function setFlags(int $accountId, string $folderId, int $messageId, array $flags): JSONResponse { - $account = $this->accountService->find($this->currentUserId, $accountId); + public function setFlags(int $id, array $flags): JSONResponse { + try { + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); + } catch (DoesNotExistException $e) { + return new JSONResponse(null, Http::STATUS_FORBIDDEN); + } foreach ($flags as $flag => $value) { $value = filter_var($value, FILTER_VALIDATE_BOOLEAN); - $this->mailManager->flagMessage($account, base64_decode($folderId), $messageId, $flag, $value); + $this->mailManager->flagMessage($account, $mailbox->getName(), $message->getUid(), $flag, $value); } return new JSONResponse(); } @@ -489,49 +531,36 @@ class MessagesController extends Controller { * @throws ClientException * @throws ServiceException */ - public function destroy(int $accountId, string $folderId, int $id): JSONResponse { - $this->logger->debug("deleting message <$id> of folder <$folderId>, account <$accountId>"); - + public function destroy(int $id): JSONResponse { try { - $account = $this->accountService->find($this->currentUserId, $accountId); + $message = $this->mailManager->getMessage($this->currentUserId, $id); + $mailbox = $this->mailManager->getMailbox($this->currentUserId, $message->getMailboxId()); + $account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId()); } catch (DoesNotExistException $e) { return new JSONResponse(null, Http::STATUS_FORBIDDEN); } - $this->mailManager->deleteMessage($account, base64_decode($folderId), $id); - return new JSONResponse(); - } + $this->logger->debug("deleting message <$id>"); - /** - * @param int $accountId - * @param string $folderId - * - * @return IMailBox - * @deprecated - * - * @throws ClientException - * @throws ServiceException - */ - private function getFolder(int $accountId, string $folderId): IMailBox { - $account = $this->accountService->find($this->currentUserId, $accountId); - return $account->getMailbox(base64_decode($folderId)); + $this->mailManager->deleteMessage( + $account, + $mailbox->getName(), + $message->getUid() + ); + return new JSONResponse(); } /** - * @param int $accountId - * @param string $folderId - * @param int $messageId + * @param int $id * @param array $attachment * * @return array */ - private function enrichDownloadUrl(int $accountId, string $folderId, int $messageId, + private function enrichDownloadUrl(int $id, array $attachment) { $downloadUrl = $this->urlGenerator->linkToRoute('mail.messages.downloadAttachment', [ - 'accountId' => $accountId, - 'folderId' => $folderId, - 'messageId' => $messageId, + 'id' => $id, 'attachmentId' => $attachment['id'], ]); $downloadUrl = $this->urlGenerator->getAbsoluteURL($downloadUrl); diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 219bb16ff..fe9181760 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -117,13 +117,13 @@ class PageController extends Controller { $this->currentUserId); try { $mailboxes = $this->mailManager->getMailboxes($mailAccount); - $json['folders'] = $mailboxes; + $json['mailboxes'] = $mailboxes; } catch (Exception $ex) { $this->logger->logException($ex, [ - 'message' => 'Could not load account folders: ' . $ex->getMessage(), + 'message' => 'Could not load account mailboxes: ' . $ex->getMessage(), 'level' => ILogger::FATAL, ]); - $json['folders'] = []; + $json['mailboxes'] = []; $json['error'] = true; } $accountsJson[] = $json; @@ -160,6 +160,56 @@ class PageController extends Controller { * @NoAdminRequired * @NoCSRFRequired * + * @return TemplateResponse + */ + public function setup(): TemplateResponse { + return $this->index(); + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + * + * @return TemplateResponse + */ + public function keyboardShortcuts(): TemplateResponse { + return $this->index(); + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + * + * @return TemplateResponse + */ + public function accountSettings(int $id): TemplateResponse { + return $this->index(); + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + * + * @return TemplateResponse + */ + public function mailbox(int $id): TemplateResponse { + return $this->index(); + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + * + * @return TemplateResponse + */ + public function thread(int $mailboxId, int $id): TemplateResponse { + return $this->index(); + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + * * @param string $uri * * @return RedirectResponse diff --git a/lib/Db/MailAccount.php b/lib/Db/MailAccount.php index 36fb54cdf..c63abe27c 100644 --- a/lib/Db/MailAccount.php +++ b/lib/Db/MailAccount.php @@ -153,6 +153,7 @@ class MailAccount extends Entity { */ public function toJson() { $result = [ + 'id' => $this->getId(), 'accountId' => $this->getId(), 'name' => $this->getName(), 'order' => $this->getOrder(), diff --git a/lib/Db/Mailbox.php b/lib/Db/Mailbox.php index 2a877ee64..0da25c85f 100644 --- a/lib/Db/Mailbox.php +++ b/lib/Db/Mailbox.php @@ -121,12 +121,14 @@ class Mailbox extends Entity implements JsonSerializable { return [ 'databaseId' => $this->getId(), 'id' => base64_encode($this->getName()), + 'name' => $this->getName(), 'accountId' => $this->accountId, 'displayName' => $this->getName(), 'attributes' => json_decode($this->attributes ?? '[]', true) ?? [], 'delimiter' => $this->delimiter, 'specialUse' => $specialUse, 'specialRole' => $specialUse[0] ?? 0, + 'mailboxes' => [], ]; } } diff --git a/lib/Db/MailboxMapper.php b/lib/Db/MailboxMapper.php index d2361b927..0b2ae36ce 100644 --- a/lib/Db/MailboxMapper.php +++ b/lib/Db/MailboxMapper.php @@ -85,6 +85,34 @@ class MailboxMapper extends QBMapper { } /** + * @param int $id + * @param string $uid + * + * @return Mailbox + * + * @throws DoesNotExistException + * @throws ServiceException + */ + public function findByUid(int $id, string $uid): Mailbox { + $qb = $this->db->getQueryBuilder(); + + $select = $qb->select('mb.*') + ->from($this->getTableName(), 'mb') + ->join('mb', 'mail_accounts', 'a', $qb->expr()->eq('mb.account_id', 'a.id', IQueryBuilder::PARAM_INT)) + ->where( + $qb->expr()->eq('a.user_id', $qb->createNamedParameter($uid)), + $qb->expr()->eq('mb.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT) + ); + + try { + return $this->findEntity($select); + } catch (MultipleObjectsReturnedException $e) { + // Not possible due to DB constraints + throw new ServiceException("The impossible has happened", 42, $e); + } + } + + /** * @throws DoesNotExistException */ public function findSpecial(Account $account, string $specialUse): Mailbox { diff --git a/lib/Db/Message.php b/lib/Db/Message.php index 8f028a284..95248b132 100644 --- a/lib/Db/Message.php +++ b/lib/Db/Message.php @@ -126,6 +126,7 @@ class Message extends Entity implements JsonSerializable { $this->bcc = new AddressList([]); $this->addType('uid', 'integer'); + $this->addType('mailboxId', 'integer'); $this->addType('sentAt', 'integer'); $this->addType('flagAnswered', 'bool'); $this->addType('flagDeleted', 'bool'); @@ -252,6 +253,7 @@ class Message extends Entity implements JsonSerializable { 'to' => $this->getTo()->jsonSerialize(), 'cc' => $this->getCc()->jsonSerialize(), 'bcc' => $this->getBcc()->jsonSerialize(), + 'mailboxId' => $this->getMailboxId(), ]; } } diff --git a/lib/Db/MessageMapper.php b/lib/Db/MessageMapper.php index e6593bb8c..77d0564ed 100644 --- a/lib/Db/MessageMapper.php +++ b/lib/Db/MessageMapper.php @@ -31,6 +31,7 @@ use OCA\Mail\Address; use OCA\Mail\AddressList; use OCA\Mail\IMAP\Threading\DatabaseMessage; use OCA\Mail\Service\Search\SearchQuery; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -51,6 +52,11 @@ class MessageMapper extends QBMapper { $this->timeFactory = $timeFactory; } + /** + * @param IQueryBuilder $query + * + * @return int[] + */ private function findUids(IQueryBuilder $query): array { $result = $query->execute(); $uids = array_map(function (array $row) { @@ -61,6 +67,21 @@ class MessageMapper extends QBMapper { return $uids; } + /** + * @param IQueryBuilder $query + * + * @return int[] + */ + private function findIds(IQueryBuilder $query): array { + $result = $query->execute(); + $uids = array_map(function (array $row) { + return (int)$row['id']; + }, $result->fetchAll()); + $result->closeCursor(); + + return $uids; + } + public function findHighestUid(Mailbox $mailbox): ?int { $query = $this->db->getQueryBuilder(); @@ -78,12 +99,64 @@ class MessageMapper extends QBMapper { return $max; } + public function findByUserId(string $uid, int $id): Message { + $query = $this->db->getQueryBuilder(); + + $query->select('m.*') + ->from($this->getTableName(), 'm') + ->join('m', 'mail_mailboxes', 'mb', $query->expr()->eq('m.mailbox_id', 'mb.id', IQueryBuilder::PARAM_INT)) + ->join('m', 'mail_accounts', 'a', $query->expr()->eq('mb.account_id', 'a.id', IQueryBuilder::PARAM_INT)) + ->where( + $query->expr()->eq('a.user_id', $query->createNamedParameter($uid)), + $query->expr()->eq('m.id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT) + ); + + $results = $this->findRecipients($this->findEntities($query)); + if (empty($results)) { + throw new DoesNotExistException("Message $id does not exist"); + } + return $results[0]; + } + public function findAllUids(Mailbox $mailbox): array { $query = $this->db->getQueryBuilder(); $query->select('uid') ->from($this->getTableName()) - ->where($query->expr()->eq('mailbox_id', $query->createNamedParameter($mailbox->getId()))); + ->where($query->expr()->eq('mailbox_id', $query->createNamedParameter($mailbox->getId(), IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)); + + return $this->findUids($query); + } + + public function findAllIds(Mailbox $mailbox): array { + $query = $this->db->getQueryBuilder(); + + $query->select('id') + ->from($this->getTableName()) + ->where($query->expr()->eq('mailbox_id', $query->createNamedParameter($mailbox->getId(), IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)); + + return $this->findIds($query); + } + + /** + * @param Mailbox $mailbox + * @param int[] $ids + * + * @return int[] + */ + public function findUidsForIds(Mailbox $mailbox, array $ids) { + if (empty($ids)) { + // Shortcut for empty sets + return []; + } + + $query = $this->db->getQueryBuilder(); + $query->select('uid') + ->from($this->getTableName()) + ->where( + $query->expr()->eq('mailbox_id', $query->createNamedParameter($mailbox->getId(), IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT), + $query->expr()->in('id', $query->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY), IQueryBuilder::PARAM_INT_ARRAY) + ); return $this->findUids($query); } @@ -382,11 +455,11 @@ class MessageMapper extends QBMapper { * * @return int[] */ - public function findUidsByQuery(Mailbox $mailbox, SearchQuery $query, ?int $limit, array $uids = null): array { + public function findIdsByQuery(Mailbox $mailbox, SearchQuery $query, ?int $limit, array $uids = null): array { $qb = $this->db->getQueryBuilder(); $select = $qb - ->selectDistinct('m.uid') + ->selectDistinct('m.id') ->addSelect('m.sent_at') ->from($this->getTableName(), 'm'); @@ -477,7 +550,7 @@ class MessageMapper extends QBMapper { } return array_map(function (Message $message) { - return $message->getUid(); + return $message->getId(); }, $this->findEntities($select)); } @@ -503,6 +576,28 @@ class MessageMapper extends QBMapper { } /** + * @param int[] $ids + * + * @return Message[] + */ + public function findByIds(array $ids): array { + if (empty($ids)) { + return []; + } + $qb = $this->db->getQueryBuilder(); + + $select = $qb + ->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->in('id', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY), IQueryBuilder::PARAM_INT_ARRAY) + ) + ->orderBy('sent_at', 'desc'); + + return $this->findRecipients($this->findEntities($select)); + } + + /** * @param Message[] $messages * * @return Message[] @@ -558,18 +653,26 @@ class MessageMapper extends QBMapper { * * @return Message[] */ - public function findNewUids(Mailbox $mailbox, int $highest): array { + public function findNewIds(Mailbox $mailbox, array $ids): array { $qb = $this->db->getQueryBuilder(); + $sub = $this->db->getQueryBuilder(); + $subSelect = $sub + ->select($sub->func()->max('uid')) + ->from($this->getTableName()) + ->where( + $sub->expr()->eq('mailbox_id', $qb->createNamedParameter($mailbox->getId(), IQueryBuilder::PARAM_INT)), + $sub->expr()->in('id', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY), IQueryBuilder::PARAM_INT_ARRAY) + ); $select = $qb - ->select('uid') + ->select('id') ->from($this->getTableName()) ->where( $qb->expr()->eq('mailbox_id', $qb->createNamedParameter($mailbox->getId(), IQueryBuilder::PARAM_INT)), - $qb->expr()->gt('uid', $qb->createNamedParameter($highest, IQueryBuilder::PARAM_INT)) + $qb->expr()->gt('uid', $qb->createFunction('(' . $subSelect->getSQL() . ')'), IQueryBuilder::PARAM_INT) ); - return $this->findUids($select); + return $this->findIds($select); } public function findChanged(Mailbox $mailbox, int $since): array { @@ -644,4 +747,22 @@ class MessageMapper extends QBMapper { ->where($qb4->expr()->in('id', $qb4->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY), IQueryBuilder::PARAM_INT_ARRAY)); $recipientsQuery->execute(); } + + public function getIdForUid(Mailbox $mailbox, $uid): ?int { + $qb = $this->db->getQueryBuilder(); + + $select = $qb + ->select('m.id') + ->from($this->getTableName(), 'm') + ->where( + $qb->expr()->eq('mailbox_id', $qb->createNamedParameter($mailbox->getId()), IQueryBuilder::PARAM_INT), + $qb->expr()->eq('uid', $qb->createNamedParameter($uid, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT) + ); + $result = $select->execute(); + $rows = $result->fetchAll(); + if (empty($rows)) { + return null; + } + return (int) $rows[0]['id']; + } } diff --git a/lib/Events/DraftSavedEvent.php b/lib/Events/DraftSavedEvent.php index f8b6368da..677b4556c 100644 --- a/lib/Events/DraftSavedEvent.php +++ b/lib/Events/DraftSavedEvent.php @@ -26,6 +26,7 @@ declare(strict_types=1); namespace OCA\Mail\Events; use OCA\Mail\Account; +use OCA\Mail\Db\Message; use OCA\Mail\Model\NewMessageData; use OCP\EventDispatcher\Event; @@ -37,16 +38,16 @@ class DraftSavedEvent extends Event { /** @var NewMessageData */ private $newMessageData; - /** @var int|null */ - private $draftUid; + /** @var Message|null */ + private $draft; public function __construct(Account $account, NewMessageData $newMessageData, - ?int $draftUid) { + ?Message $draft) { parent::__construct(); $this->account = $account; $this->newMessageData = $newMessageData; - $this->draftUid = $draftUid; + $this->draft = $draft; } public function getAccount(): Account { @@ -57,7 +58,7 @@ class DraftSavedEvent extends Event { return $this->newMessageData; } - public function getDraftUid(): ?int { - return $this->draftUid; + public function getDraft(): ?Message { + return $this->draft; } } diff --git a/lib/Events/MessageSentEvent.php b/lib/Events/MessageSentEvent.php index 563467c4c..a2973cb8c 100644 --- a/lib/Events/MessageSentEvent.php +++ b/lib/Events/MessageSentEvent.php @@ -27,6 +27,7 @@ namespace OCA\Mail\Events; use Horde_Mime_Mail; use OCA\Mail\Account; +use OCA\Mail\Db\Message; use OCA\Mail\Model\IMessage; use OCA\Mail\Model\NewMessageData; use OCA\Mail\Model\RepliedMessageData; @@ -43,8 +44,8 @@ class MessageSentEvent extends Event { /** @var null|RepliedMessageData */ private $repliedMessageData; - /** @var int|null */ - private $draftUid; + /** @var Message|null */ + private $draft; /** @var IMessage */ private $message; @@ -55,14 +56,14 @@ class MessageSentEvent extends Event { public function __construct(Account $account, NewMessageData $newMessageData, ?RepliedMessageData $repliedMessageData, - ?int $draftUid, + ?Message $draft, IMessage $message, Horde_Mime_Mail $mail) { parent::__construct(); $this->account = $account; $this->newMessageData = $newMessageData; $this->repliedMessageData = $repliedMessageData; - $this->draftUid = $draftUid; + $this->draft = $draft; $this->message = $message; $this->mail = $mail; } @@ -79,8 +80,8 @@ class MessageSentEvent extends Event { return $this->repliedMessageData; } - public function getDraftUid(): ?int { - return $this->draftUid; + public function getDraft(): ?Message { + return $this->draft; } public function getMessage(): IMessage { diff --git a/lib/Events/SaveDraftEvent.php b/lib/Events/SaveDraftEvent.php index 1f5590281..830bfe976 100644 --- a/lib/Events/SaveDraftEvent.php +++ b/lib/Events/SaveDraftEvent.php @@ -26,6 +26,7 @@ declare(strict_types=1); namespace OCA\Mail\Events; use OCA\Mail\Account; +use OCA\Mail\Db\Message; use OCA\Mail\Model\NewMessageData; use OCP\EventDispatcher\Event; @@ -37,16 +38,16 @@ class SaveDraftEvent extends Event { /** @var NewMessageData */ private $newMessageData; - /** @var int|null */ - private $draftUid; + /** @var Message|null */ + private $draft; public function __construct(Account $account, NewMessageData $newMessageData, - ?int $draftUid) { + ?Message $draft) { parent::__construct(); $this->account = $account; $this->newMessageData = $newMessageData; - $this->draftUid = $draftUid; + $this->draft = $draft; } public function getAccount(): Account { @@ -57,7 +58,7 @@ class SaveDraftEvent extends Event { return $this->newMessageData; } - public function getDraftUid(): ?int { - return $this->draftUid; + public function getDraft(): ?Message { + return $this->draft; } } diff --git a/lib/IMAP/MessageMapper.php b/lib/IMAP/MessageMapper.php index 2d0f3cee8..3b4fde341 100644 --- a/lib/IMAP/MessageMapper.php +++ b/lib/IMAP/MessageMapper.php @@ -364,14 +364,14 @@ class MessageMapper { /** * @param Horde_Imap_Client_Socket $client * @param string $mailbox - * @param int $id + * @param int $uid * * @return string|null * @throws ServiceException */ public function getSource(Horde_Imap_Client_Socket $client, string $mailbox, - int $id): ?string { + int $uid): ?string { $query = new Horde_Imap_Client_Fetch_Query(); $query->uid(); $query->fullText([ @@ -380,7 +380,7 @@ class MessageMapper { try { $result = iterator_to_array($client->fetch($mailbox, $query, [ - 'ids' => new Horde_Imap_Client_Ids($id), + 'ids' => new Horde_Imap_Client_Ids($uid), ]), false); } catch (Horde_Imap_Client_Exception $e) { throw new ServiceException("Could not fetch message source: " . $e->getMessage(), $e->getCode(), $e); @@ -399,13 +399,13 @@ class MessageMapper { public function getHtmlBody(Horde_Imap_Client_Socket $client, string $mailbox, - int $id): ?string { + int $uid): ?string { $messageQuery = new Horde_Imap_Client_Fetch_Query(); $messageQuery->envelope(); $messageQuery->structure(); $result = $client->fetch($mailbox, $messageQuery, [ - 'ids' => new Horde_Imap_Client_Ids([$id]), + 'ids' => new Horde_Imap_Client_Ids([$uid]), ]); if (($message = $result->first()) === null) { @@ -435,7 +435,7 @@ class MessageMapper { } $parts = $client->fetch($mailbox, $partsQuery, [ - 'ids' => new Horde_Imap_Client_Ids([$id]), + 'ids' => new Horde_Imap_Client_Ids([$uid]), ]); foreach ($parts as $part) { @@ -456,12 +456,12 @@ class MessageMapper { public function getRawAttachments(Horde_Imap_Client_Socket $client, string $mailbox, - int $id): array { + int $uid): array { $messageQuery = new Horde_Imap_Client_Fetch_Query(); $messageQuery->structure(); $result = $client->fetch($mailbox, $messageQuery, [ - 'ids' => new Horde_Imap_Client_Ids([$id]), + 'ids' => new Horde_Imap_Client_Ids([$uid]), ]); if (($structureResult = $result->first()) === null) { @@ -488,7 +488,7 @@ class MessageMapper { } $parts = $client->fetch($mailbox, $partsQuery, [ - 'ids' => new Horde_Imap_Client_Ids([$id]), + 'ids' => new Horde_Imap_Client_Ids([$uid]), ]); if (($messageData = $parts->first()) === null) { throw new DoesNotExistException('Message does not exist'); diff --git a/lib/IMAP/Sync/Response.php b/lib/IMAP/Sync/Response.php index 9e95b342e..d12625775 100644 --- a/lib/IMAP/Sync/Response.php +++ b/lib/IMAP/Sync/Response.php @@ -24,14 +24,15 @@ declare(strict_types=1); namespace OCA\Mail\IMAP\Sync; use JsonSerializable; +use OCA\Mail\Db\Message; use OCA\Mail\Model\IMAPMessage; class Response implements JsonSerializable { - /** @var IMAPMessage[] */ + /** @var IMAPMessage|Message[] */ private $newMessages; - /** @var IMAPMessage[] */ + /** @var IMAPMessage|Message[] */ private $changedMessages; /** @var int[] */ @@ -39,8 +40,8 @@ class Response implements JsonSerializable { /** * @param string $syncToken - * @param IMAPMessage[] $newMessages - * @param IMAPMessage[] $changedMessages + * @param IMAPMessage|Message[] $newMessages + * @param IMAPMessage|Message[] $changedMessages * @param int[] $vanishedMessageUids */ public function __construct(array $newMessages = [], @@ -52,14 +53,14 @@ class Response implements JsonSerializable { } /** - * @return IMAPMessage[] + * @return IMAPMessage|Message[] */ public function getNewMessages(): array { return $this->newMessages; } /** - * @return IMAPMessage[] + * @return IMAPMessage|Message[] */ public function getChangedMessages(): array { return $this->changedMessages; diff --git a/lib/IMAP/Threading/ThreadBuilder.php b/lib/IMAP/Threading/ThreadBuilder.php index 72283423f..8053d045d 100644 --- a/lib/IMAP/Threading/ThreadBuilder.php +++ b/lib/IMAP/Threading/ThreadBuilder.php @@ -110,6 +110,9 @@ class ThreadBuilder { // Step 1.C //$parentId = $message->getReferences()[count($message->getReferences()) - 1] ?? null; //$container->setParent($idTable[$parentId] ?? null); + if ($parent === $container) { + throw new \Exception("about to run into a nasty endless loop"); + } if ($parent === null || !$parent->hasAncestor($container)) { $container->setParent($parent); } diff --git a/lib/Listener/DeleteDraftListener.php b/lib/Listener/DeleteDraftListener.php index 0e76adec6..9758a0fa0 100644 --- a/lib/Listener/DeleteDraftListener.php +++ b/lib/Listener/DeleteDraftListener.php @@ -30,6 +30,7 @@ use Horde_Imap_Client_Exception; use OCA\Mail\Account; use OCA\Mail\Db\Mailbox; use OCA\Mail\Db\MailboxMapper; +use OCA\Mail\Db\Message; use OCA\Mail\Events\DraftSavedEvent; use OCA\Mail\Events\MessageSentEvent; use OCA\Mail\IMAP\IMAPClientFactory; @@ -70,17 +71,18 @@ class DeleteDraftListener implements IEventListener { } public function handle(Event $event): void { - if ($event instanceof DraftSavedEvent && $event->getDraftUid() !== null) { - $this->deleteDraft($event->getAccount(), $event->getDraftUid()); - } elseif ($event instanceof MessageSentEvent && $event->getDraftUid() !== null) { - $this->deleteDraft($event->getAccount(), $event->getDraftUid()); + if ($event instanceof DraftSavedEvent && $event->getDraft() !== null) { + $this->deleteDraft($event->getAccount(), $event->getDraft()); + } elseif ($event instanceof MessageSentEvent && $event->getDraft() !== null) { + $this->deleteDraft($event->getAccount(), $event->getDraft()); } } /** - * @param DraftSavedEvent $event + * @param Account $account + * @param Message $draft */ - private function deleteDraft(Account $account, int $draftUid): void { + private function deleteDraft(Account $account, Message $draft): void { $client = $this->imapClientFactory->getClient($account); $draftsMailbox = $this->getDraftsMailbox($account); @@ -88,7 +90,7 @@ class DeleteDraftListener implements IEventListener { $this->messageMapper->addFlag( $client, $draftsMailbox, - $draftUid, + $draft->getUid(), // TODO: the UID could be from another mailbox Horde_Imap_Client::FLAG_DELETED ); } catch (Horde_Imap_Client_Exception $e) { diff --git a/lib/Mailbox.php b/lib/Mailbox.php index 09daef680..5aecf8330 100644 --- a/lib/Mailbox.php +++ b/lib/Mailbox.php @@ -295,12 +295,13 @@ class Mailbox implements IMailBox { } /** - * @param int $messageId + * @param int $messageUid * @param string $attachmentId + * * @return Attachment */ - public function getAttachment(int $messageId, string $attachmentId): Attachment { - return new Attachment($this->conn, $this->mailBox, $messageId, $attachmentId); + public function getAttachment(int $messageUid, string $attachmentId): Attachment { + return new Attachment($this->conn, $this->mailBox, $messageUid, $attachmentId); } /** diff --git a/lib/Model/IMAPMessage.php b/lib/Model/IMAPMessage.php index 1dae7a1d0..8b7ae12a3 100644 --- a/lib/Model/IMAPMessage.php +++ b/lib/Model/IMAPMessage.php @@ -48,7 +48,6 @@ use OCA\Mail\Service\Html; use OCP\AppFramework\Db\DoesNotExistException; use OCP\Files\File; use OCP\Files\SimpleFS\ISimpleFile; -use function base64_encode; use function in_array; use function mb_convert_encoding; @@ -422,15 +421,17 @@ class IMAPMessage implements IMessage, JsonSerializable { } /** + * @param int $id + * * @return array */ - public function getFullMessage(int $accountId, string $mailbox, int $id): array { + public function getFullMessage(int $id): array { $mailBody = $this->plainMessage; $data = $this->jsonSerialize(); if ($this->hasHtmlMessage) { $data['hasHtmlBody'] = true; - $data['body'] = $this->getHtmlBody($accountId, $mailbox, $id); + $data['body'] = $this->getHtmlBody($id); } else { $mailBody = $this->htmlService->convertLinks($mailBody); list($mailBody, $signature) = $this->htmlService->parseMailBody($mailBody); @@ -462,17 +463,13 @@ class IMAPMessage implements IMessage, JsonSerializable { } /** - * @param int $accountId - * @param string $folderId - * @param int $messageId + * @param int $id * * @return string */ - public function getHtmlBody(int $accountId, string $folderId, int $messageId): string { + public function getHtmlBody(int $id): string { return $this->htmlService->sanitizeHtmlMailBody($this->htmlMessage, [ - 'accountId' => $accountId, - 'folderId' => base64_encode($folderId), - 'messageId' => $messageId, + 'id' => $id, ], function ($cid) { $match = array_filter($this->attachments, function ($a) use ($cid) { diff --git a/lib/Service/AccountService.php b/lib/Service/AccountService.php index 83a4a6977..36fbf11a1 100644 --- a/lib/Service/AccountService.php +++ b/lib/Service/AccountService.php @@ -89,25 +89,25 @@ class AccountService { /** * @param string $uid - * @param int $accountId + * @param int $id * * @return Account * @throws ClientException */ - public function find(string $uid, int $accountId): Account { + public function find(string $uid, int $id): Account { if ($this->accounts !== null) { foreach ($this->accounts as $account) { - if ($account->getId() === $accountId) { + if ($account->getId() === $id) { return $account; } } - throw new ClientException("Account $accountId does not exist or you don\'t have permission to access it"); + throw new ClientException("Account $id does not exist or you don\'t have permission to access it"); } try { - return new Account($this->mapper->find($uid, $accountId)); + return new Account($this->mapper->find($uid, $id)); } catch (DoesNotExistException $e) { - throw new ClientException("Account $accountId does not exist or you don\'t have permission to access it"); + throw new ClientException("Account $id does not exist or you don\'t have permission to access it"); } } diff --git a/lib/Service/IMailBox.php b/lib/Service/IMailBox.php index 093e3d7f1..d75c5c562 100644 --- a/lib/Service/IMailBox.php +++ b/lib/Service/IMailBox.php @@ -47,11 +47,12 @@ interface IMailBox { public function getMessage(int $id, bool $loadHtmlMessageBody = false); /** - * @param int $messageId + * @param int $messageUid * @param string $attachmentId + * * @return Attachment */ - public function getAttachment(int $messageId, string $attachmentId): Attachment; + public function getAttachment(int $messageUid, string $attachmentId): Attachment; /** * @param int $flags diff --git a/lib/Service/MailManager.php b/lib/Service/MailManager.php index 08cceb812..09d04dcc3 100644 --- a/lib/Service/MailManager.php +++ b/lib/Service/MailManager.php @@ -30,6 +30,7 @@ use OCA\Mail\Account; use OCA\Mail\Contracts\IMailManager; use OCA\Mail\Db\Mailbox; use OCA\Mail\Db\MailboxMapper; +use OCA\Mail\Db\Message; use OCA\Mail\Db\MessageMapper as DbMessageMapper; use OCA\Mail\Events\BeforeMessageDeletedEvent; use OCA\Mail\Events\MessageDeletedEvent; @@ -100,6 +101,14 @@ class MailManager implements IMailManager { $this->eventDispatcher = $eventDispatcher; } + public function getMailbox(string $uid, int $id): Mailbox { + try { + return $this->mailboxMapper->findByUid($id, $uid); + } catch (DoesNotExistException $e) { + throw new ClientException("Mailbox $id does not exist", 0, $e); + } + } + /** * @param Account $account * @@ -116,10 +125,10 @@ class MailManager implements IMailManager { * @param Account $account * @param string $name * - * @return Folder + * @return Mailbox * @throws ServiceException */ - public function createFolder(Account $account, string $name): Folder { + public function createMailbox(Account $account, string $name): Mailbox { $client = $this->imapClientFactory->getClient($account); $folder = $this->folderMapper->createFolder($client, $account, $name); @@ -132,30 +141,33 @@ class MailManager implements IMailManager { $this->mailboxSync->sync($account, true); - return $folder; + return $this->mailboxMapper->find($account, $name); } /** * @param Account $account - * @param string $folderId + * @param Mailbox $mailbox * * @return FolderStats + * @throws Horde_Imap_Client_Exception */ - public function getFolderStats(Account $account, string $folderId): FolderStats { + public function getMailboxStats(Account $account, Mailbox $mailbox): FolderStats { $client = $this->imapClientFactory->getClient($account); - return $this->folderMapper->getFoldersStatusAsObject($client, $folderId); + return $this->folderMapper->getFoldersStatusAsObject($client, $mailbox->getName()); } - public function getMessage(Account $account, string $mailbox, int $id, bool $loadBody = false): IMAPMessage { + public function getImapMessage(Account $account, + Mailbox $mailbox, + int $uid, + bool $loadBody = false): IMAPMessage { $client = $this->imapClientFactory->getClient($account); - $mailbox = $this->mailboxMapper->find($account, $mailbox); try { return $this->imapMessageMapper->find( $client, $mailbox->getName(), - $id, + $uid, $loadBody ); } catch (Horde_Imap_Client_Exception|DoesNotExistException $e) { @@ -167,24 +179,32 @@ class MailManager implements IMailManager { return $this->dbMessageMapper->findThread($account, $messageId); } + public function getMessageIdForUid(Mailbox $mailbox, $uid): ?int { + return $this->dbMessageMapper->getIdForUid($mailbox, $uid); + } + + public function getMessage(string $uid, int $id): Message { + return $this->dbMessageMapper->findByUserId($uid, $id); + } + /** * @param Account $account * @param string $mb - * @param int $id + * @param int $uid * * @return string * * @throws ClientException * @throws ServiceException */ - public function getSource(Account $account, string $mailbox, int $id): string { + public function getSource(Account $account, string $mailbox, int $uid): ?string { $client = $this->imapClientFactory->getClient($account); try { return $this->imapMessageMapper->getSource( $client, $mailbox, - $id + $uid ); } catch (Horde_Imap_Client_Exception|DoesNotExistException $e) { throw new ServiceException("Could not load message", 0, $e); @@ -194,7 +214,7 @@ class MailManager implements IMailManager { /** * @param Account $sourceAccount * @param string $sourceFolderId - * @param int $messageId + * @param int $uid * @param Account $destinationAccount * @param string $destFolderId * @@ -204,7 +224,7 @@ class MailManager implements IMailManager { */ public function moveMessage(Account $sourceAccount, string $sourceFolderId, - int $messageId, + int $uid, Account $destinationAccount, string $destFolderId) { if ($sourceAccount->getId() === $destinationAccount->getId()) { @@ -212,7 +232,7 @@ class MailManager implements IMailManager { $sourceAccount, $sourceFolderId, $destFolderId, - $messageId + $uid ); } else { throw new ServiceException('It is not possible to move across accounts yet'); @@ -284,10 +304,10 @@ class MailManager implements IMailManager { $this->imapMessageMapper->move($client, $sourceFolderId, $messageId, $destFolderId); } - public function markFolderAsRead(Account $account, string $folderId): void { + public function markFolderAsRead(Account $account, Mailbox $mailbox): void { $client = $this->imapClientFactory->getClient($account); - $this->imapMessageMapper->markAllRead($client, $folderId); + $this->imapMessageMapper->markAllRead($client, $mailbox->getName()); } public function flagMessage(Account $account, string $mailbox, int $uid, string $flag, bool $value): void { @@ -371,18 +391,14 @@ class MailManager implements IMailManager { /** * @param Account $account - * @param string $folderId + * @param Mailbox $mailbox + * * @throws ServiceException */ public function deleteMailbox(Account $account, - string $folderId): void { - try { - $mailbox = $this->mailboxMapper->find($account, $folderId); - } catch (DoesNotExistException $e) { - throw new ServiceException("Source mailbox $folderId does not exist", 0, $e); - } + Mailbox $mailbox): void { $client = $this->imapClientFactory->getClient($account); - $this->folderMapper->delete($client, $folderId); + $this->folderMapper->delete($client, $mailbox->getName()); $this->mailboxMapper->delete($mailbox); } } diff --git a/lib/Service/MailTransmission.php b/lib/Service/MailTransmission.php index 9db9c1d4a..e8ff9d135 100644 --- a/lib/Service/MailTransmission.php +++ b/lib/Service/MailTransmission.php @@ -39,6 +39,7 @@ use OCA\Mail\Contracts\IAttachmentService; use OCA\Mail\Contracts\IMailTransmission; use OCA\Mail\Db\Alias; use OCA\Mail\Db\MailboxMapper; +use OCA\Mail\Db\Message; use OCA\Mail\Events\DraftSavedEvent; use OCA\Mail\Events\MessageSentEvent; use OCA\Mail\Events\SaveDraftEvent; @@ -102,22 +103,10 @@ class MailTransmission implements IMailTransmission { $this->logger = $logger; } - /** - * Send a new message or reply to an existing one - * - * @param NewMessageData $messageData - * @param RepliedMessageData $replyData - * @param Alias|null $alias - * @param int|null $draftUID - * - * @throws ServiceException - * - * @return void - */ public function sendMessage(NewMessageData $messageData, RepliedMessageData $replyData = null, Alias $alias = null, - int $draftUID = null) { + Message $draft = null) { $account = $messageData->getAccount(); if ($replyData !== null) { @@ -178,17 +167,22 @@ class MailTransmission implements IMailTransmission { $this->eventDispatcher->dispatch( MessageSentEvent::class, - new MessageSentEvent($account, $messageData, $replyData, $draftUID, $message, $mail) + new MessageSentEvent($account, $messageData, $replyData, $draft, $message, $mail) ); } /** + * @param NewMessageData $message + * @param Message|null $previousDraft + * + * @return array + * * @throws ServiceException */ - public function saveDraft(NewMessageData $message, int $draftUID = null): int { + public function saveDraft(NewMessageData $message, Message $previousDraft = null): array { $this->eventDispatcher->dispatch( SaveDraftEvent::class, - new SaveDraftEvent($message->getAccount(), $message, $draftUID) + new SaveDraftEvent($message->getAccount(), $message, $previousDraft) ); $account = $message->getAccount(); @@ -243,10 +237,10 @@ class MailTransmission implements IMailTransmission { $this->eventDispatcher->dispatch( DraftSavedEvent::class, - new DraftSavedEvent($account, $message, $draftUID) + new DraftSavedEvent($account, $message, $previousDraft) ); - return $newUid; + return [$account, $draftsMailbox, $newUid]; } private function buildReplyMessage(Account $account, diff --git a/lib/Service/Search/MailSearch.php b/lib/Service/Search/MailSearch.php index cead97321..85b021704 100644 --- a/lib/Service/Search/MailSearch.php +++ b/lib/Service/Search/MailSearch.php @@ -75,32 +75,26 @@ class MailSearch implements IMailSearch { $this->logger = $logger; } - public function findMessage(Account $account, string $mailboxName, int $uid): Message { - try { - $mailbox = $this->mailboxMapper->find($account, $mailboxName); - } catch (DoesNotExistException $e) { - throw new ServiceException('Mailbox does not exist', 0, $e); - } - - $messages = $this->previewEnhancer->process( + public function findMessage(Account $account, + Mailbox $mailbox, + Message $message): Message { + $processed = $this->previewEnhancer->process( $account, $mailbox, - $this->messageMapper->findByUids( - $mailbox, - [$uid] - ) + [$message] ); - if (empty($messages)) { + if (empty($processed)) { throw new DoesNotExistException("Message does not exist"); } - return $messages[0]; + return $processed[0]; } /** * @param Account $account - * @param string $mailboxName + * @param Mailbox $mailbox * @param string|null $filter * @param int|null $cursor + * @param int|null $limit * * @return Message[] * @@ -108,16 +102,10 @@ class MailSearch implements IMailSearch { * @throws ServiceException */ public function findMessages(Account $account, - string $mailboxName, + Mailbox $mailbox, ?string $filter, ?int $cursor, ?int $limit): array { - try { - $mailbox = $this->mailboxMapper->find($account, $mailboxName); - } catch (DoesNotExistException $e) { - throw new ServiceException('Mailbox does not exist', 0, $e); - } - if ($mailbox->hasLocks()) { throw MailboxLockedException::from($mailbox); } @@ -141,9 +129,8 @@ class MailSearch implements IMailSearch { return $this->previewEnhancer->process( $account, $mailbox, - $this->messageMapper->findByUids( - $mailbox, - $this->getUids($account, $mailbox, $query, $limit) + $this->messageMapper->findByIds( + $this->getIds($account, $mailbox, $query, $limit) ) ); } @@ -153,9 +140,9 @@ class MailSearch implements IMailSearch { * * @throws ServiceException */ - private function getUids(Account $account, Mailbox $mailbox, SearchQuery $query, ?int $limit): array { + private function getIds(Account $account, Mailbox $mailbox, SearchQuery $query, ?int $limit): array { if (empty($query->getTextTokens())) { - return $this->messageMapper->findUidsByQuery($mailbox, $query, $limit); + return $this->messageMapper->findIdsByQuery($mailbox, $query, $limit); } $fromImap = $this->imapSearchProvider->findMatches( @@ -163,6 +150,6 @@ class MailSearch implements IMailSearch { $mailbox, $query ); - return $this->messageMapper->findUidsByQuery($mailbox, $query, $limit, $fromImap); + return $this->messageMapper->findIdsByQuery($mailbox, $query, $limit, $fromImap); } } diff --git a/lib/Service/Sync/SyncService.php b/lib/Service/Sync/SyncService.php index 484ce597b..385a66b84 100644 --- a/lib/Service/Sync/SyncService.php +++ b/lib/Service/Sync/SyncService.php @@ -38,10 +38,8 @@ use OCA\Mail\IMAP\PreviewEnhancer; use OCA\Mail\IMAP\Sync\Response; use OCA\Mail\Service\Search\FilterStringParser; use OCA\Mail\Service\Search\SearchQuery; -use OCP\AppFramework\Db\DoesNotExistException; use function array_diff; use function array_map; -use function end; class SyncService { @@ -74,46 +72,36 @@ class SyncService { /** * @param Account $account - * @param string $mailboxId + * @param Mailbox $mailbox * * @throws MailboxLockedException * @throws ServiceException */ public function clearCache(Account $account, - string $mailboxId): void { - try { - $mailbox = $this->mailboxMapper->find($account, $mailboxId); - } catch (DoesNotExistException $e) { - throw new ServiceException('Mailbox to sync does not exist in the database', 0, $e); - } - + Mailbox $mailbox): void { $this->synchronizer->clearCache($account, $mailbox); } /** * @param Account $account - * @param string $mailboxId + * @param Mailbox $mailbox * @param int $criteria - * @param array $knownUids + * @param int[] $knownIds * @param bool $partialOnly * + * @param string|null $filter + * * @return Response * @throws ClientException * @throws MailboxNotCachedException * @throws ServiceException */ public function syncMailbox(Account $account, - string $mailboxId, + Mailbox $mailbox, int $criteria, - array $knownUids, + array $knownIds, bool $partialOnly, string $filter = null): Response { - try { - $mailbox = $this->mailboxMapper->find($account, $mailboxId); - } catch (DoesNotExistException $e) { - throw new ServiceException('Mailbox to sync does not exist in the database', 0, $e); - } - if ($partialOnly && !$mailbox->isCached()) { throw MailboxNotCachedException::from($mailbox); } @@ -122,52 +110,57 @@ class SyncService { $account, $mailbox, $criteria, - $knownUids, + $this->messageMapper->findUidsForIds($mailbox, $knownIds), !$partialOnly ); $query = $filter === null ? null : $this->filterStringParser->parse($filter); - - return $this->getDatabaseSyncChanges($account, $mailbox, $knownUids, $query); + return $this->getDatabaseSyncChanges( + $account, + $mailbox, + $knownIds, + $query + ); } /** * @param Account $account * @param Mailbox $mailbox - * @param array $knownUids + * @param int[] $knownIds * @param SearchQuery $query * * @return Response * @todo does not work with text token search queries * */ - private function getDatabaseSyncChanges(Account $account, Mailbox $mailbox, array $knownUids, ?SearchQuery $query): Response { - if (empty($knownUids)) { - $newUids = $this->messageMapper->findAllUids($mailbox); + private function getDatabaseSyncChanges(Account $account, + Mailbox $mailbox, + array $knownIds, + ?SearchQuery $query): Response { + if (empty($knownIds)) { + $newIds = $this->messageMapper->findAllIds($mailbox); } else { - sort($knownUids, SORT_NUMERIC); - $last = end($knownUids); - $newUids = $this->messageMapper->findNewUids($mailbox, $last); + $newIds = $this->messageMapper->findNewIds($mailbox, $knownIds); } if ($query !== null) { // Filter new messages to those that also match the current filter - $newUids = $this->messageMapper->findUidsByQuery($mailbox, $query, null, $newUids); + $newIds = $this->messageMapper->findIdsByQuery($mailbox, $query, null, $newIds); } - $new = $this->messageMapper->findByUids($mailbox, $newUids); + $new = $this->messageMapper->findByIds($newIds); // TODO: $changed = $this->messageMapper->findChanged($mailbox, $uids); if ($query !== null) { - $changedUids = $this->messageMapper->findUidsByQuery($mailbox, $query, null, $knownUids); + $changedIds = $this->messageMapper->findIdsByQuery($mailbox, $query, null, $knownIds); } else { - $changedUids = $knownUids; + $changedIds = $knownIds; } - $changed = $this->messageMapper->findByUids($mailbox, $changedUids); + $changed = $this->messageMapper->findByIds($changedIds); - $stillKnownUids = array_map(static function (Message $msg) { - return $msg->getUid(); + $stillKnownIds = array_map(static function (Message $msg) { + return $msg->getId(); }, $changed); - $vanished = array_values(array_diff($knownUids, $stillKnownUids)); + $vanished = array_values(array_diff($knownIds, $stillKnownIds)); return new Response( $this->previewEnhancer->process($account, $mailbox, $new), |