diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-03-02 11:58:18 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-03-02 11:58:18 +0100 |
commit | db36ceabeb698b1988f7f6ecfe694c226eb156d7 (patch) | |
tree | 5bdadc074e98de7f9bcf14338b46068ae17f5856 | |
parent | 54fc031c2a1632a69f4d6effa56dc9ba75448937 (diff) | |
download | sink-db36ceabeb698b1988f7f6ecfe694c226eb156d7.tar.gz sink-db36ceabeb698b1988f7f6ecfe694c226eb156d7.zip |
Fixed synchronization with new mail notifications
-rw-r--r-- | examples/imapresource/imapresource.cpp | 44 | ||||
-rw-r--r-- | examples/imapresource/tests/imapmailsynctest.cpp | 25 |
2 files changed, 50 insertions, 19 deletions
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index f87c5ff..811e560 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp | |||
@@ -267,13 +267,13 @@ public: | |||
267 | }) | 267 | }) |
268 | // //First we fetch flag changes for all messages. Since we don't know which messages are locally available we just get everything and only apply to what we have. | 268 | // //First we fetch flag changes for all messages. Since we don't know which messages are locally available we just get everything and only apply to what we have. |
269 | .then([=] { | 269 | .then([=] { |
270 | auto uidNext = syncStore().readValue(folderRemoteId, "uidnext").toLongLong(); | 270 | auto lastSeenUid = syncStore().readValue(folderRemoteId, "uidnext").toLongLong(); |
271 | bool ok = false; | 271 | bool ok = false; |
272 | const auto changedsince = syncStore().readValue(folderRemoteId, "changedsince").toLongLong(&ok); | 272 | const auto changedsince = syncStore().readValue(folderRemoteId, "changedsince").toLongLong(&ok); |
273 | SinkLogCtx(mLogCtx) << "About to update flags" << folder.path() << "changedsince: " << changedsince; | 273 | SinkLogCtx(mLogCtx) << "About to update flags" << folder.path() << "changedsince: " << changedsince; |
274 | //If we have any mails so far we start off by updating any changed flags using changedsince | 274 | //If we have any mails so far we start off by updating any changed flags using changedsince |
275 | if (ok) { | 275 | if (ok) { |
276 | return imap->fetchFlags(folder, KIMAP2::ImapSet(1, qMax(uidNext, qint64(1))), changedsince, [=](const Message &message) { | 276 | return imap->fetchFlags(folder, KIMAP2::ImapSet(1, qMax(lastSeenUid, qint64(1))), changedsince, [=](const Message &message) { |
277 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); | 277 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); |
278 | const auto remoteId = assembleMailRid(folderLocalId, message.uid); | 278 | const auto remoteId = assembleMailRid(folderLocalId, message.uid); |
279 | 279 | ||
@@ -315,12 +315,12 @@ public: | |||
315 | return job.then([=](const QVector<qint64> &uidsToFetch) { | 315 | return job.then([=](const QVector<qint64> &uidsToFetch) { |
316 | SinkTraceCtx(mLogCtx) << "Received result set " << uidsToFetch; | 316 | SinkTraceCtx(mLogCtx) << "Received result set " << uidsToFetch; |
317 | SinkTraceCtx(mLogCtx) << "About to fetch mail" << folder.path(); | 317 | SinkTraceCtx(mLogCtx) << "About to fetch mail" << folder.path(); |
318 | const auto uidNext = syncStore().readValue(folderRemoteId, "uidnext").toLongLong(); | 318 | const auto lastSeenUid = syncStore().readValue(folderRemoteId, "uidnext").toLongLong(); |
319 | 319 | ||
320 | //Make sure the uids are sorted in reverse order and drop everything below uidNext (so we don't refetch what we already have | 320 | //Make sure the uids are sorted in reverse order and drop everything below lastSeenUid (so we don't refetch what we already have |
321 | QVector<qint64> filteredAndSorted = uidsToFetch; | 321 | QVector<qint64> filteredAndSorted = uidsToFetch; |
322 | qSort(filteredAndSorted.begin(), filteredAndSorted.end(), qGreater<qint64>()); | 322 | qSort(filteredAndSorted.begin(), filteredAndSorted.end(), qGreater<qint64>()); |
323 | auto lowerBound = qLowerBound(filteredAndSorted.begin(), filteredAndSorted.end(), uidNext, qGreater<qint64>()); | 323 | auto lowerBound = qLowerBound(filteredAndSorted.begin(), filteredAndSorted.end(), lastSeenUid, qGreater<qint64>()); |
324 | if (lowerBound != filteredAndSorted.end()) { | 324 | if (lowerBound != filteredAndSorted.end()) { |
325 | filteredAndSorted.erase(lowerBound, filteredAndSorted.end()); | 325 | filteredAndSorted.erase(lowerBound, filteredAndSorted.end()); |
326 | } | 326 | } |
@@ -348,13 +348,14 @@ public: | |||
348 | } | 348 | } |
349 | }) | 349 | }) |
350 | .then([=] { | 350 | .then([=] { |
351 | SinkLogCtx(mLogCtx) << "UIDMAX: " << *maxUid << folder.path(); | 351 | SinkLogCtx(mLogCtx) << "Highest found uid: " << *maxUid << folder.path(); |
352 | if (*maxUid > 0) { | 352 | if (*maxUid > 0) { |
353 | syncStore().writeValue(folderRemoteId, "uidnext", QByteArray::number(*maxUid)); | 353 | syncStore().writeValue(folderRemoteId, "uidnext", QByteArray::number(*maxUid)); |
354 | } else { | 354 | } else { |
355 | if (serverUidNext) { | 355 | if (serverUidNext) { |
356 | SinkLogCtx(mLogCtx) << "Storing the server side uidnext: " << serverUidNext << folder.path(); | ||
356 | //If we don't receive a mail we should still record the updated uidnext value. | 357 | //If we don't receive a mail we should still record the updated uidnext value. |
357 | syncStore().writeValue(folderRemoteId, "uidnext", QByteArray::number(serverUidNext)); | 358 | syncStore().writeValue(folderRemoteId, "uidnext", QByteArray::number(serverUidNext - 1)); |
358 | } | 359 | } |
359 | } | 360 | } |
360 | syncStore().writeValue(folderRemoteId, "fullsetLowerbound", QByteArray::number(lowerBoundUid)); | 361 | syncStore().writeValue(folderRemoteId, "fullsetLowerbound", QByteArray::number(lowerBoundUid)); |
@@ -567,17 +568,26 @@ public: | |||
567 | synchronizeFolders(*folderList); | 568 | synchronizeFolders(*folderList); |
568 | return *folderList; | 569 | return *folderList; |
569 | }) | 570 | }) |
571 | //The rest is only to check for new messages. | ||
570 | .each([=](const Imap::Folder &folder) { | 572 | .each([=](const Imap::Folder &folder) { |
571 | //TODO examine instead | 573 | if (!folder.noselect && folder.subscribed) { |
572 | return imap->select(folder) | 574 | return imap->examine(folder) |
573 | .then([=](const SelectResult &result) { | 575 | .then([=](const SelectResult &result) { |
574 | const auto folderRemoteId = folderRid(folder); | 576 | const auto folderRemoteId = folderRid(folder); |
575 | auto localUidNext = syncStore().readValue(folderRemoteId, "uidnext").toLongLong(); | 577 | auto lastSeenUid = syncStore().readValue(folderRemoteId, "uidnext").toLongLong(); |
576 | if (result.uidNext > localUidNext) { | 578 | SinkTraceCtx(mLogCtx) << "Checking for new messages." << folderRemoteId << " Last seen uid: " << lastSeenUid << " Uidnext: " << result.uidNext; |
577 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); | 579 | if (result.uidNext > (lastSeenUid + 1)) { |
578 | emitNotification(Notification::Info, ApplicationDomain::NewContentAvailable, {}, {}, {{folderLocalId}}); | 580 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); |
579 | } | 581 | emitNotification(Notification::Info, ApplicationDomain::NewContentAvailable, {}, {}, {{folderLocalId}}); |
580 | }); | 582 | } |
583 | }).then([=] (const KAsync::Error &error) { | ||
584 | if (error) { | ||
585 | //Ignore the error because we don't want to fail the synchronization here | ||
586 | SinkWarningCtx(mLogCtx) << "Examine failed: " << error.errorMessage; | ||
587 | } | ||
588 | }); | ||
589 | } | ||
590 | return KAsync::null(); | ||
581 | }); | 591 | }); |
582 | }) | 592 | }) |
583 | .then([=] (const KAsync::Error &error) { | 593 | .then([=] (const KAsync::Error &error) { |
diff --git a/examples/imapresource/tests/imapmailsynctest.cpp b/examples/imapresource/tests/imapmailsynctest.cpp index 1d62a80..06369f3 100644 --- a/examples/imapresource/tests/imapmailsynctest.cpp +++ b/examples/imapresource/tests/imapmailsynctest.cpp | |||
@@ -135,12 +135,15 @@ protected: | |||
135 | private slots: | 135 | private slots: |
136 | void testNewMailNotification() | 136 | void testNewMailNotification() |
137 | { | 137 | { |
138 | createFolder(QStringList() << "testNewMailNotification"); | ||
139 | createMessage(QStringList() << "testNewMailNotification", newMessage("Foobar")); | ||
140 | |||
138 | const auto syncFolders = Sink::SyncScope{ApplicationDomain::getTypeName<Folder>()}.resourceFilter(mResourceInstanceIdentifier); | 141 | const auto syncFolders = Sink::SyncScope{ApplicationDomain::getTypeName<Folder>()}.resourceFilter(mResourceInstanceIdentifier); |
139 | //Fetch folders initially | 142 | //Fetch folders initially |
140 | VERIFYEXEC(Store::synchronize(syncFolders)); | 143 | VERIFYEXEC(Store::synchronize(syncFolders)); |
141 | VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 144 | VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
142 | 145 | ||
143 | auto folder = Store::readOne<Folder>(Sink::Query{}.resourceFilter(mResourceInstanceIdentifier).filter<Folder::Name>("test")); | 146 | auto folder = Store::readOne<Folder>(Sink::Query{}.resourceFilter(mResourceInstanceIdentifier).filter<Folder::Name>("testNewMailNotification")); |
144 | Q_ASSERT(!folder.identifier().isEmpty()); | 147 | Q_ASSERT(!folder.identifier().isEmpty()); |
145 | 148 | ||
146 | const auto syncTestMails = Sink::SyncScope{ApplicationDomain::getTypeName<Mail>()}.resourceFilter(mResourceInstanceIdentifier).filter<Mail::Folder>(QVariant::fromValue(folder.identifier())); | 149 | const auto syncTestMails = Sink::SyncScope{ApplicationDomain::getTypeName<Mail>()}.resourceFilter(mResourceInstanceIdentifier).filter<Mail::Folder>(QVariant::fromValue(folder.identifier())); |
@@ -172,7 +175,7 @@ private slots: | |||
172 | QVERIFY(!notificationReceived); | 175 | QVERIFY(!notificationReceived); |
173 | 176 | ||
174 | //Create message and retry | 177 | //Create message and retry |
175 | createMessage(QStringList() << "test", newMessage("This is a Subject.")); | 178 | createMessage(QStringList() << "testNewMailNotification", newMessage("This is a Subject.")); |
176 | 179 | ||
177 | //Should result in change notification | 180 | //Should result in change notification |
178 | VERIFYEXEC(Store::synchronize(syncFolders)); | 181 | VERIFYEXEC(Store::synchronize(syncFolders)); |
@@ -180,6 +183,24 @@ private slots: | |||
180 | 183 | ||
181 | QTRY_VERIFY(notificationReceived); | 184 | QTRY_VERIFY(notificationReceived); |
182 | } | 185 | } |
186 | |||
187 | void testSyncFolderBeforeFetchingNewMessages() | ||
188 | { | ||
189 | const auto syncScope = Sink::Query{}.resourceFilter(mResourceInstanceIdentifier); | ||
190 | |||
191 | createFolder(QStringList() << "test3"); | ||
192 | |||
193 | VERIFYEXEC(Store::synchronize(syncScope)); | ||
194 | VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
195 | |||
196 | createMessage(QStringList() << "test3", newMessage("Foobar")); | ||
197 | |||
198 | VERIFYEXEC(Store::synchronize(syncScope)); | ||
199 | VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
200 | |||
201 | auto mailQuery = Sink::Query{}.resourceFilter(mResourceInstanceIdentifier).request<Mail::Subject>().filter<Mail::Folder>(Sink::Query{}.filter<Folder::Name>("test3")); | ||
202 | QCOMPARE(Store::read<Mail>(mailQuery).size(), 1); | ||
203 | } | ||
183 | }; | 204 | }; |
184 | 205 | ||
185 | QTEST_MAIN(ImapMailSyncTest) | 206 | QTEST_MAIN(ImapMailSyncTest) |