1<?php 2 3declare(strict_types=1); 4 5/** 6 * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> 7 * 8 * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> 9 * 10 * @license GNU AGPL version 3 or any later version 11 * 12 * This program is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU Affero General Public License as 14 * published by the Free Software Foundation, either version 3 of the 15 * License, or (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU Affero General Public License for more details. 21 * 22 * You should have received a copy of the GNU Affero General Public License 23 * along with this program. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26namespace OCA\Mail\Service\Sync; 27 28use OCA\Mail\Account; 29use OCA\Mail\Db\Mailbox; 30use OCA\Mail\Db\MailboxMapper; 31use OCA\Mail\Db\Message; 32use OCA\Mail\Db\MessageMapper; 33use OCA\Mail\Exception\ClientException; 34use OCA\Mail\Exception\MailboxLockedException; 35use OCA\Mail\Exception\MailboxNotCachedException; 36use OCA\Mail\Exception\ServiceException; 37use OCA\Mail\IMAP\MailboxSync; 38use OCA\Mail\IMAP\PreviewEnhancer; 39use OCA\Mail\IMAP\Sync\Response; 40use OCA\Mail\Service\Search\FilterStringParser; 41use OCA\Mail\Service\Search\SearchQuery; 42use Psr\Log\LoggerInterface; 43use function array_diff; 44use function array_map; 45 46class SyncService { 47 48 /** @var ImapToDbSynchronizer */ 49 private $synchronizer; 50 51 /** @var FilterStringParser */ 52 private $filterStringParser; 53 54 /** @var MailboxMapper */ 55 private $mailboxMapper; 56 57 /** @var MessageMapper */ 58 private $messageMapper; 59 60 /** @var PreviewEnhancer */ 61 private $previewEnhancer; 62 63 /** @var LoggerInterface */ 64 private $logger; 65 66 /** @var MailboxSync */ 67 private $mailboxSync; 68 69 public function __construct(ImapToDbSynchronizer $synchronizer, 70 FilterStringParser $filterStringParser, 71 MailboxMapper $mailboxMapper, 72 MessageMapper $messageMapper, 73 PreviewEnhancer $previewEnhancer, 74 LoggerInterface $logger, 75 MailboxSync $mailboxSync) { 76 $this->synchronizer = $synchronizer; 77 $this->filterStringParser = $filterStringParser; 78 $this->mailboxMapper = $mailboxMapper; 79 $this->messageMapper = $messageMapper; 80 $this->previewEnhancer = $previewEnhancer; 81 $this->logger = $logger; 82 $this->mailboxSync = $mailboxSync; 83 } 84 85 /** 86 * @param Account $account 87 * @param Mailbox $mailbox 88 * 89 * @throws MailboxLockedException 90 * @throws ServiceException 91 */ 92 public function clearCache(Account $account, 93 Mailbox $mailbox): void { 94 $this->synchronizer->clearCache($account, $mailbox); 95 } 96 97 /** 98 * @param Account $account 99 * @param Mailbox $mailbox 100 * @param int $criteria 101 * @param int[] $knownIds 102 * @param bool $partialOnly 103 * 104 * @param string|null $filter 105 * 106 * @return Response 107 * @throws ClientException 108 * @throws MailboxNotCachedException 109 * @throws ServiceException 110 */ 111 public function syncMailbox(Account $account, 112 Mailbox $mailbox, 113 int $criteria, 114 array $knownIds = null, 115 bool $partialOnly, 116 string $filter = null): Response { 117 if ($partialOnly && !$mailbox->isCached()) { 118 throw MailboxNotCachedException::from($mailbox); 119 } 120 121 $this->synchronizer->sync( 122 $account, 123 $mailbox, 124 $this->logger, 125 $criteria, 126 $knownIds === null ? null : $this->messageMapper->findUidsForIds($mailbox, $knownIds), 127 !$partialOnly 128 ); 129 130 $this->mailboxSync->syncStats($account, $mailbox); 131 132 $query = $filter === null ? null : $this->filterStringParser->parse($filter); 133 return $this->getDatabaseSyncChanges( 134 $account, 135 $mailbox, 136 $knownIds ?? [], 137 $query 138 ); 139 } 140 141 /** 142 * @param Account $account 143 * @param Mailbox $mailbox 144 * @param int[] $knownIds 145 * @param SearchQuery $query 146 * 147 * @return Response 148 * @todo does not work with text token search queries 149 * 150 */ 151 private function getDatabaseSyncChanges(Account $account, 152 Mailbox $mailbox, 153 array $knownIds, 154 ?SearchQuery $query): Response { 155 if (empty($knownIds)) { 156 $newIds = $this->messageMapper->findAllIds($mailbox); 157 } else { 158 $newIds = $this->messageMapper->findNewIds($mailbox, $knownIds); 159 } 160 161 if ($query !== null) { 162 // Filter new messages to those that also match the current filter 163 $newUids = $this->messageMapper->findUidsForIds($mailbox, $newIds); 164 $newIds = $this->messageMapper->findIdsByQuery($mailbox, $query, null, $newUids); 165 } 166 $new = $this->messageMapper->findByIds($account->getUserId(), $newIds); 167 168 // TODO: $changed = $this->messageMapper->findChanged($account, $mailbox, $uids); 169 if ($query !== null) { 170 $changedUids = $this->messageMapper->findUidsForIds($mailbox, $knownIds); 171 $changedIds = $this->messageMapper->findIdsByQuery($mailbox, $query, null, $changedUids); 172 } else { 173 $changedIds = $knownIds; 174 } 175 $changed = $this->messageMapper->findByIds($account->getUserId(), $changedIds); 176 177 $stillKnownIds = array_map(static function (Message $msg) { 178 return $msg->getId(); 179 }, $changed); 180 $vanished = array_values(array_diff($knownIds, $stillKnownIds)); 181 182 return new Response( 183 $this->previewEnhancer->process($account, $mailbox, $new), 184 $changed, 185 $vanished, 186 $mailbox->getStats() 187 ); 188 } 189} 190