diff --git a/lib/Contracts/IMailManager.php b/lib/Contracts/IMailManager.php index d7ee53837b..27124b246f 100644 --- a/lib/Contracts/IMailManager.php +++ b/lib/Contracts/IMailManager.php @@ -22,8 +22,22 @@ namespace OCA\Mail\Contracts; use OCA\Mail\Account; +use OCA\Mail\Folder; +use OCA\Mail\IMAP\Sync\Request as SyncRequest; +use OCA\Mail\IMAP\Sync\Response as SyncResponse; interface IMailManager { + /** + * @param Account $account + * @return Folder[] + */ public function getFolders(Account $account); + + /** + * @param Account + * @param SyncRequest $syncRequest + * @return SyncResponse + */ + public function syncMessages(Account $account, SyncRequest $syncRequest); } diff --git a/lib/IMAP/MessageMapper.php b/lib/IMAP/MessageMapper.php new file mode 100644 index 0000000000..61b62ed406 --- /dev/null +++ b/lib/IMAP/MessageMapper.php @@ -0,0 +1,41 @@ + + * + * Mail + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Mail\IMAP; + +use Exception; +use Horde_Imap_Client_Base; +use OCA\Mail\Folder; +use OCA\Mail\Model\IMAPMessage; + +class MessageMapper { + + /** + * @param Horde_Imap_Client_Base $imapClient + * @param Folder $mailbox + * @param array $ids + * @return IMAPMessage[] + */ + public function findByIds(Horde_Imap_Client_Base $imapClient, $mailbox, array $ids) { + throw new Exception('not implemented'); + } + +} diff --git a/lib/IMAP/Sync/Request.php b/lib/IMAP/Sync/Request.php new file mode 100644 index 0000000000..59de0d65cc --- /dev/null +++ b/lib/IMAP/Sync/Request.php @@ -0,0 +1,57 @@ + + * + * Mail + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Mail\IMAP\Sync; + +class Request { + + /** @var string */ + private $mailbox; + + /** @var string */ + private $syncToken; + + /** + * @param string $mailbox + * @param string $syncToken + */ + public function __construct($mailbox, $syncToken) { + $this->mailbox = $mailbox; + $this->syncToken = $syncToken; + } + + /** + * Get the mailbox name + * + * @return string + */ + public function getMailbox() { + return $this->mailbox; + } + + /** + * @return string the Horde sync token + */ + public function getToken() { + return $this->syncToken; + } + +} diff --git a/lib/IMAP/Sync/Response.php b/lib/IMAP/Sync/Response.php new file mode 100644 index 0000000000..a267b84e8f --- /dev/null +++ b/lib/IMAP/Sync/Response.php @@ -0,0 +1,68 @@ + + * + * Mail + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Mail\IMAP\Sync; + +use Exception; +use JsonSerializable; +use OCA\Mail\Model\IMAPMessage; + +class Response implements JsonSerializable { + + /** @var string */ + private $syncToken; + + /** @var IMAPMessage[] */ + private $newMessages; + + /** @var IMAPMessage[] */ + private $changedMessages; + + /** @var array */ + private $vanishedMessages; + + /** + * @param string $syncToken + * @param IMAPMessage[] $newMessages + * @param IMAPMessage[] $changedMessages + * @param array $vanishedMessages + */ + public function __construct($syncToken, array $newMessages = [], array $changedMessages = [], + array $vanishedMessages = []) { + $this->syncToken = $syncToken; + $this->newMessages = $newMessages; + $this->changedMessages = $changedMessages; + $this->vanishedMessages = $vanishedMessages; + } + + /** + * @return array + */ + public function jsonSerialize() { + return [ + 'newMessages' => $this->newMessages, + 'changedMessages' => $this->changedMessages, + 'vanishedMessages' => $this->vanishedMessages, + 'token' => $this->syncToken, + ]; + } + +} diff --git a/lib/IMAP/Sync/Synchronizer.php b/lib/IMAP/Sync/Synchronizer.php new file mode 100644 index 0000000000..840d8ec592 --- /dev/null +++ b/lib/IMAP/Sync/Synchronizer.php @@ -0,0 +1,57 @@ + + * + * Mail + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Mail\IMAP\Sync; + +use Horde_Imap_Client_Base; +use OCA\Mail\IMAP\MessageMapper; +use OCA\Mail\IMAP\Sync\Request; +use OCA\Mail\IMAP\Sync\Response; + +class Synchronizer { + + /** @var MessageMapper */ + private $messageMapper; + + /** + * @param MessageMapper $messageMapper + */ + public function __construct(MessageMapper $messageMapper) { + $this->messageMapper = $messageMapper; + } + + /** + * @param Horde_Imap_Client_Base $imapClient + * @param Request $request + * @return Response + */ + public function sync(Horde_Imap_Client_Base $imapClient, Request $request) { + $hordeSync = $imapClient->sync($request->getMailbox(), $request->getToken()); + + $newMessages = $this->messageMapper->findByIds($imapClient, $request->getMailbox(), $hordeSync->newmsgsuids->ids); + $changedMessages = $this->messageMapper->findByIds($imapClient, $request->getMailbox(), $hordeSync->flagsuids->ids); + $vanishedMessages = $hordeSync->vanisheduids->ids; + + $newSyncToken = $imapClient->getSyncToken($request->getMailbox()); + return new Response($newSyncToken, $newMessages, $changedMessages, $vanishedMessages); + } + +} diff --git a/lib/Service/MailManager.php b/lib/Service/MailManager.php index f04022074d..906bae0bfd 100644 --- a/lib/Service/MailManager.php +++ b/lib/Service/MailManager.php @@ -1,5 +1,6 @@ * @@ -24,7 +25,12 @@ use OCA\Mail\Account; use OCA\Mail\Contracts\IMailManager; use OCA\Mail\Folder; -use OCA\Mail\Service\IMAP\IMAPClientFactory; +use OCA\Mail\IMAP\IMAPClientFactory; +use OCA\Mail\IMAP\Sync\Request; +use OCA\Mail\IMAP\Sync\Response; +use OCA\Mail\IMAP\Sync\Synchronizer; +use OCA\Mail\Service\FolderMapper; +use OCA\Mail\Service\FolderNameTranslator; class MailManager implements IMailManager { @@ -37,16 +43,21 @@ class MailManager implements IMailManager { /** @var FolderNameTranslator */ private $folderNameTranslator; + /** @var Synchronizer */ + private $synchronizer; + /** * @param IMAPClientFactory $imapClientFactory * @param FolderMapper $folderMapper - * @param FolderNameTranslator + * @param FolderNameTranslator $folderNameTranslator + * @param Synchronizer $synchronizer */ - public function __construct(IMAPClientFactory $imapClientFactory, - FolderMapper $folderMapper, FolderNameTranslator $folderNameTranslator) { + public function __construct(IMAPClientFactory $imapClientFactory, FolderMapper $folderMapper, + FolderNameTranslator $folderNameTranslator, Synchronizer $synchronizer) { $this->imapClientFactory = $imapClientFactory; $this->folderMapper = $folderMapper; $this->folderNameTranslator = $folderNameTranslator; + $this->synchronizer = $synchronizer; } /** @@ -64,4 +75,15 @@ public function getFolders(Account $account) { return $this->folderMapper->buildFolderHierarchy($folders); } + /** + * @param Account $account + * @param Request $syncRequest + * @return Response + */ + public function syncMessages(Account $account, Request $syncRequest) { + $client = $this->imapClientFactory->getClient($account); + + return $this->synchronizer->sync($client, $syncRequest); + } + } diff --git a/tests/IMAP/Sync/RequestTest.php b/tests/IMAP/Sync/RequestTest.php new file mode 100644 index 0000000000..e54d5703fa --- /dev/null +++ b/tests/IMAP/Sync/RequestTest.php @@ -0,0 +1,55 @@ + + * + * Mail + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Mail\Tests\IMAP\Sync; + +use OCA\Mail\IMAP\Sync\Request; +use PHPUnit_Framework_TestCase; + +class RequestTest extends PHPUnit_Framework_TestCase { + + /** @var string */ + private $mailbox; + + /** @var string */ + private $syncToken; + + /** @var Request */ + private $request; + + protected function setUp() { + parent::setUp(); + + $this->mailbox = 'inbox'; + $this->syncToken = 'ab123'; + + $this->request = new Request($this->mailbox, $this->syncToken); + } + + public function testGetMailbox() { + $this->assertEquals($this->mailbox, $this->request->getMailbox()); + } + + public function testGetSyncToken() { + $this->assertEquals($this->syncToken, $this->request->getToken()); + } + +} diff --git a/tests/IMAP/Sync/ResponseTest.php b/tests/IMAP/Sync/ResponseTest.php new file mode 100644 index 0000000000..d9b4741ce9 --- /dev/null +++ b/tests/IMAP/Sync/ResponseTest.php @@ -0,0 +1,47 @@ + + * + * Mail + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Mail\Tests\IMAP\Sync; + +use OCA\Mail\IMAP\Sync\Response; +use PHPUnit_Framework_TestCase; + +class ResponseTest extends PHPUnit_Framework_TestCase { + + public function testJsonSerialize() { + $newMessages = []; + $changedMessages = []; + $vanishedMessages = []; + $syncToken = 'bc4564'; + $response = new Response($syncToken, $newMessages, $changedMessages, $vanishedMessages); + $expected = [ + 'newMessages' => [], + 'changedMessages' => [], + 'vanishedMessages' => [], + 'token' => $syncToken, + ]; + + $json = $response->jsonSerialize(); + + $this->assertEquals($expected, $json); + } + +} diff --git a/tests/IMAP/Sync/SynchronizerTest.php b/tests/IMAP/Sync/SynchronizerTest.php new file mode 100644 index 0000000000..a6381c0b15 --- /dev/null +++ b/tests/IMAP/Sync/SynchronizerTest.php @@ -0,0 +1,91 @@ + + * + * Mail + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Mail\Tests\IMAP\Sync; + +use Horde_Imap_Client_Base; +use Horde_Imap_Client_Data_Sync; +use Horde_Imap_Client_Ids; +use OCA\Mail\IMAP\MessageMapper; +use OCA\Mail\IMAP\Sync\Request; +use OCA\Mail\IMAP\Sync\Response; +use OCA\Mail\IMAP\Sync\Synchronizer; +use PHPUnit_Framework_MockObject_MockObject; +use PHPUnit_Framework_TestCase; + +class SynchronizerTest extends PHPUnit_Framework_TestCase { + + /** @var MessageMapper|PHPUnit_Framework_MockObject_MockObject */ + private $messageMapper; + + /** @var Synchronizer */ + private $synchronizer; + + protected function setUp() { + parent::setUp(); + + $this->messageMapper = $this->createMock(MessageMapper::class); + + $this->synchronizer = new Synchronizer($this->messageMapper); + } + + private function getHordeMessageIdMock(array $ids) { + $hordeIds = $this->createMock(Horde_Imap_Client_Ids::class); + $hordeIds->ids = $ids; + return $hordeIds; + } + + public function testSync() { + $imapClient = $this->createMock(Horde_Imap_Client_Base::class); + $request = $this->createMock(Request::class); + $request->expects($this->any()) + ->method('getMailbox') + ->willReturn('inbox'); + $request->expects($this->once()) + ->method('getToken') + ->willReturn('123456'); + $hordeSync = $this->createMock(Horde_Imap_Client_Data_Sync::class); + $imapClient->expects($this->once()) + ->method('sync') + ->with($this->equalTo('inbox'), $this->equalTo('123456')) + ->willReturn($hordeSync); + $newMessages = []; + $changedMessages = []; + $vanishedMessages = [4, 5]; + $hordeSync->newmsgsuids = $this->getHordeMessageIdMock($newMessages); + $hordeSync->flagsuids = $this->getHordeMessageIdMock($changedMessages); + $hordeSync->vanisheduids = $this->getHordeMessageIdMock($vanishedMessages); + $this->messageMapper->expects($this->exactly(2)) + ->method('findByIds') + ->with($imapClient, $this->equalTo('inbox'), $this->equalTo([])) + ->willReturn([]); + $imapClient->expects($this->once()) + ->method('getSyncToken') + ->with($this->equalTo('inbox')) + ->willReturn('54321'); + $expected = new Response('54321', $newMessages, $changedMessages, $vanishedMessages); + + $response = $this->synchronizer->sync($imapClient, $request); + + $this->assertEquals($expected, $response); + } + +}