diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-01-23 17:05:10 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-01-23 17:25:25 +0100 |
commit | 03fd475039b418f2f16b2890554fb59403d708dd (patch) | |
tree | 1db5b8d8aa4d5f30ed2881bb68a3044aab25011b | |
parent | 48c0f01320981319a9ac0f34a7ce117325bfbc13 (diff) | |
download | sink-03fd475039b418f2f16b2890554fb59403d708dd.tar.gz sink-03fd475039b418f2f16b2890554fb59403d708dd.zip |
Track uidvalidity to detect changes behind our back.
-rw-r--r-- | examples/imapresource/imapresource.cpp | 19 | ||||
-rw-r--r-- | examples/imapresource/imapserverproxy.cpp | 11 | ||||
-rw-r--r-- | examples/imapresource/imapserverproxy.h | 1 | ||||
-rw-r--r-- | tests/mailsynctest.cpp | 65 | ||||
-rw-r--r-- | tests/mailsynctest.h | 1 |
5 files changed, 84 insertions, 13 deletions
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 6607578..e8bc903 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp | |||
@@ -246,10 +246,23 @@ public: | |||
246 | SinkWarningCtx(mLogCtx) << "Invalid folder " << folderRemoteId << folder.path(); | 246 | SinkWarningCtx(mLogCtx) << "Invalid folder " << folderRemoteId << folder.path(); |
247 | return KAsync::error<void>("Invalid folder"); | 247 | return KAsync::error<void>("Invalid folder"); |
248 | } | 248 | } |
249 | // auto capabilities = imap->getCapabilities(); | ||
250 | 249 | ||
251 | //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. | 250 | //Start by checking if UIDVALIDITY is still correct |
252 | return KAsync::start<void>([=]() { | 251 | return KAsync::start([=] { |
252 | bool ok = false; | ||
253 | const auto uidvalidity = syncStore().readValue(folderRemoteId, "uidvalidity").toLongLong(&ok); | ||
254 | return imap->select(folder) | ||
255 | .then([=](const SelectResult &selectResult) { | ||
256 | SinkLogCtx(mLogCtx) << "Checking UIDVALIDITY. Local" << uidvalidity << "remote " << selectResult.uidValidity; | ||
257 | if (ok && selectResult.uidValidity != uidvalidity) { | ||
258 | SinkWarningCtx(mLogCtx) << "UIDVALIDITY changed " << selectResult.uidValidity << uidvalidity; | ||
259 | syncStore().removePrefix(folderRemoteId); | ||
260 | } | ||
261 | syncStore().writeValue(folderRemoteId, "uidvalidity", QByteArray::number(selectResult.uidValidity)); | ||
262 | }); | ||
263 | }) | ||
264 | // //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. | ||
265 | .then([=] { | ||
253 | auto uidNext = syncStore().readValue(folderRemoteId, "uidnext").toLongLong(); | 266 | auto uidNext = syncStore().readValue(folderRemoteId, "uidnext").toLongLong(); |
254 | bool ok = false; | 267 | bool ok = false; |
255 | const auto changedsince = syncStore().readValue(folderRemoteId, "changedsince").toLongLong(&ok); | 268 | const auto changedsince = syncStore().readValue(folderRemoteId, "changedsince").toLongLong(&ok); |
diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index b27ffb0..a5a64db 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp | |||
@@ -244,6 +244,11 @@ KAsync::Job<SelectResult> ImapServerProxy::select(const QString &mailbox) | |||
244 | }); | 244 | }); |
245 | } | 245 | } |
246 | 246 | ||
247 | KAsync::Job<SelectResult> ImapServerProxy::select(const Folder &folder) | ||
248 | { | ||
249 | return select(mailboxFromFolder(folder)); | ||
250 | } | ||
251 | |||
247 | KAsync::Job<qint64> ImapServerProxy::append(const QString &mailbox, const QByteArray &content, const QList<QByteArray> &flags, const QDateTime &internalDate) | 252 | KAsync::Job<qint64> ImapServerProxy::append(const QString &mailbox, const QByteArray &content, const QList<QByteArray> &flags, const QDateTime &internalDate) |
248 | { | 253 | { |
249 | auto append = new KIMAP2::AppendJob(mSession); | 254 | auto append = new KIMAP2::AppendJob(mSession); |
@@ -577,7 +582,7 @@ QString ImapServerProxy::mailboxFromFolder(const Folder &folder) const | |||
577 | KAsync::Job<SelectResult> ImapServerProxy::fetchFlags(const Folder &folder, const KIMAP2::ImapSet &set, qint64 changedsince, std::function<void(const Message &)> callback) | 582 | KAsync::Job<SelectResult> ImapServerProxy::fetchFlags(const Folder &folder, const KIMAP2::ImapSet &set, qint64 changedsince, std::function<void(const Message &)> callback) |
578 | { | 583 | { |
579 | SinkTrace() << "Fetching flags " << folder.path(); | 584 | SinkTrace() << "Fetching flags " << folder.path(); |
580 | return select(mailboxFromFolder(folder)).then<SelectResult, SelectResult>([=](const SelectResult &selectResult) -> KAsync::Job<SelectResult> { | 585 | return select(folder).then<SelectResult, SelectResult>([=](const SelectResult &selectResult) -> KAsync::Job<SelectResult> { |
581 | SinkTrace() << "Modeseq " << folder.path() << selectResult.highestModSequence << changedsince; | 586 | SinkTrace() << "Modeseq " << folder.path() << selectResult.highestModSequence << changedsince; |
582 | 587 | ||
583 | if (selectResult.highestModSequence == static_cast<quint64>(changedsince)) { | 588 | if (selectResult.highestModSequence == static_cast<quint64>(changedsince)) { |
@@ -601,7 +606,7 @@ KAsync::Job<void> ImapServerProxy::fetchMessages(const Folder &folder, qint64 ui | |||
601 | { | 606 | { |
602 | auto time = QSharedPointer<QTime>::create(); | 607 | auto time = QSharedPointer<QTime>::create(); |
603 | time->start(); | 608 | time->start(); |
604 | return select(mailboxFromFolder(folder)).then<void, SelectResult>([this, callback, folder, time, progress, uidNext](const SelectResult &selectResult) -> KAsync::Job<void> { | 609 | return select(folder).then<void, SelectResult>([this, callback, folder, time, progress, uidNext](const SelectResult &selectResult) -> KAsync::Job<void> { |
605 | SinkTrace() << "UIDNEXT " << folder.path() << selectResult.uidNext << uidNext; | 610 | SinkTrace() << "UIDNEXT " << folder.path() << selectResult.uidNext << uidNext; |
606 | if (selectResult.uidNext == (uidNext + 1)) { | 611 | if (selectResult.uidNext == (uidNext + 1)) { |
607 | SinkTrace()<< folder.path() << "Uidnext didn't change, nothing to do."; | 612 | SinkTrace()<< folder.path() << "Uidnext didn't change, nothing to do."; |
@@ -624,7 +629,7 @@ KAsync::Job<void> ImapServerProxy::fetchMessages(const Folder &folder, const QVe | |||
624 | { | 629 | { |
625 | auto time = QSharedPointer<QTime>::create(); | 630 | auto time = QSharedPointer<QTime>::create(); |
626 | time->start(); | 631 | time->start(); |
627 | return select(mailboxFromFolder(folder)).then<void, SelectResult>([this, callback, folder, time, progress, uidsToFetch, headersOnly](const SelectResult &selectResult) -> KAsync::Job<void> { | 632 | return select(folder).then<void, SelectResult>([this, callback, folder, time, progress, uidsToFetch, headersOnly](const SelectResult &selectResult) -> KAsync::Job<void> { |
628 | 633 | ||
629 | SinkTrace() << "Fetching messages" << folder.path(); | 634 | SinkTrace() << "Fetching messages" << folder.path(); |
630 | SinkTrace() << " Total: " << uidsToFetch.size(); | 635 | SinkTrace() << " Total: " << uidsToFetch.size(); |
diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h index 57b252d..c69fe67 100644 --- a/examples/imapresource/imapserverproxy.h +++ b/examples/imapresource/imapserverproxy.h | |||
@@ -261,6 +261,7 @@ public: | |||
261 | KAsync::Job<void> login(const QString &username, const QString &password); | 261 | KAsync::Job<void> login(const QString &username, const QString &password); |
262 | KAsync::Job<void> logout(); | 262 | KAsync::Job<void> logout(); |
263 | KAsync::Job<SelectResult> select(const QString &mailbox); | 263 | KAsync::Job<SelectResult> select(const QString &mailbox); |
264 | KAsync::Job<SelectResult> select(const Folder &mailbox); | ||
264 | KAsync::Job<qint64> append(const QString &mailbox, const QByteArray &content, const QList<QByteArray> &flags = QList<QByteArray>(), const QDateTime &internalDate = QDateTime()); | 265 | KAsync::Job<qint64> append(const QString &mailbox, const QByteArray &content, const QList<QByteArray> &flags = QList<QByteArray>(), const QDateTime &internalDate = QDateTime()); |
265 | KAsync::Job<void> store(const KIMAP2::ImapSet &set, const QList<QByteArray> &flags); | 266 | KAsync::Job<void> store(const KIMAP2::ImapSet &set, const QList<QByteArray> &flags); |
266 | KAsync::Job<void> storeFlags(const KIMAP2::ImapSet &set, const QList<QByteArray> &flags); | 267 | KAsync::Job<void> storeFlags(const KIMAP2::ImapSet &set, const QList<QByteArray> &flags); |
diff --git a/tests/mailsynctest.cpp b/tests/mailsynctest.cpp index 058cae4..69541e1 100644 --- a/tests/mailsynctest.cpp +++ b/tests/mailsynctest.cpp | |||
@@ -33,6 +33,16 @@ | |||
33 | using namespace Sink; | 33 | using namespace Sink; |
34 | using namespace Sink::ApplicationDomain; | 34 | using namespace Sink::ApplicationDomain; |
35 | 35 | ||
36 | static QByteArray newMessage(const QString &subject) | ||
37 | { | ||
38 | auto msg = KMime::Message::Ptr::create(); | ||
39 | msg->subject(true)->fromUnicodeString(subject, "utf8"); | ||
40 | msg->date(true)->setDateTime(QDateTime::currentDateTimeUtc()); | ||
41 | msg->assemble(); | ||
42 | return msg->encodedContent(true); | ||
43 | } | ||
44 | |||
45 | |||
36 | void MailSyncTest::initTestCase() | 46 | void MailSyncTest::initTestCase() |
37 | { | 47 | { |
38 | Test::initTest(); | 48 | Test::initTest(); |
@@ -261,11 +271,7 @@ void MailSyncTest::testListRemovedSubFolder() | |||
261 | 271 | ||
262 | void MailSyncTest::testListMails() | 272 | void MailSyncTest::testListMails() |
263 | { | 273 | { |
264 | auto msg = KMime::Message::Ptr::create(); | 274 | createMessage(QStringList() << "test", newMessage("This is a Subject.")); |
265 | msg->subject(true)->fromUnicodeString("This is a Subject.", "utf8"); | ||
266 | msg->date(true)->setDateTime(QDateTime::currentDateTimeUtc()); | ||
267 | msg->assemble(); | ||
268 | createMessage(QStringList() << "test", msg->encodedContent(true)); | ||
269 | 275 | ||
270 | Sink::Query query; | 276 | Sink::Query query; |
271 | query.resourceFilter(mResourceInstanceIdentifier); | 277 | query.resourceFilter(mResourceInstanceIdentifier); |
@@ -420,8 +426,6 @@ void MailSyncTest::testSyncSingleFolder() | |||
420 | // Ensure all local data is processed | 426 | // Ensure all local data is processed |
421 | VERIFYEXEC(Store::synchronize(syncScope)); | 427 | VERIFYEXEC(Store::synchronize(syncScope)); |
422 | VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | 428 | VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); |
423 | |||
424 | |||
425 | } | 429 | } |
426 | 430 | ||
427 | void MailSyncTest::testSyncSingleMail() | 431 | void MailSyncTest::testSyncSingleMail() |
@@ -490,3 +494,50 @@ void MailSyncTest::testFailingSync() | |||
490 | // Ensure sync fails if resource is misconfigured | 494 | // Ensure sync fails if resource is misconfigured |
491 | QTRY_VERIFY(errorReceived); | 495 | QTRY_VERIFY(errorReceived); |
492 | } | 496 | } |
497 | |||
498 | void MailSyncTest::testSyncUidvalidity() | ||
499 | { | ||
500 | createFolder({"uidvalidity"}); | ||
501 | createMessage({"uidvalidity"}, newMessage("old")); | ||
502 | |||
503 | VERIFYEXEC(Store::synchronize(SyncScope{ApplicationDomain::getTypeName<Folder>()}.resourceFilter(mResourceInstanceIdentifier))); | ||
504 | VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
505 | |||
506 | auto folder = Store::readOne<Folder>(Query{}.resourceFilter(mResourceInstanceIdentifier).filter<Folder::Name>("uidvalidity")); | ||
507 | |||
508 | auto folderSyncScope = SyncScope{ApplicationDomain::getTypeName<Mail>()}; | ||
509 | folderSyncScope.resourceFilter(mResourceInstanceIdentifier); | ||
510 | folderSyncScope.filter<Mail::Folder>(QVariant::fromValue(folder.identifier())); | ||
511 | VERIFYEXEC(Store::synchronize(folderSyncScope)); | ||
512 | VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
513 | |||
514 | |||
515 | { | ||
516 | Sink::Query query; | ||
517 | query.resourceFilter(mResourceInstanceIdentifier); | ||
518 | query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>(); | ||
519 | query.filter<Mail::Folder>(folder); | ||
520 | auto mails = Store::read<Mail>(query); | ||
521 | QCOMPARE(mails.size(), 1); | ||
522 | } | ||
523 | |||
524 | resetTestEnvironment(); | ||
525 | |||
526 | createFolder({"uidvalidity"}); | ||
527 | createMessage({"uidvalidity"}, newMessage("new")); | ||
528 | |||
529 | // Ensure all local data is processed | ||
530 | VERIFYEXEC(Store::synchronize(folderSyncScope)); | ||
531 | VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); | ||
532 | |||
533 | //Now we should have one message | ||
534 | auto folder2 = Store::readOne<Folder>(Query{}.resourceFilter(mResourceInstanceIdentifier).filter<Folder::Name>("uidvalidity")); | ||
535 | Sink::Query query; | ||
536 | query.resourceFilter(mResourceInstanceIdentifier); | ||
537 | query.request<Mail::Subject>().request<Mail::MimeMessage>().request<Mail::Folder>().request<Mail::Date>(); | ||
538 | query.filter<Mail::Folder>(folder2); | ||
539 | auto mails = Store::read<Mail>(query); | ||
540 | QCOMPARE(mails.size(), 1); | ||
541 | QCOMPARE(mails.first().getSubject(), {"new"}); | ||
542 | } | ||
543 | |||
diff --git a/tests/mailsynctest.h b/tests/mailsynctest.h index 0decf00..f197833 100644 --- a/tests/mailsynctest.h +++ b/tests/mailsynctest.h | |||
@@ -74,6 +74,7 @@ private slots: | |||
74 | void testSyncSingleMailWithBogusId(); | 74 | void testSyncSingleMailWithBogusId(); |
75 | 75 | ||
76 | void testFailingSync(); | 76 | void testFailingSync(); |
77 | void testSyncUidvalidity(); | ||
77 | }; | 78 | }; |
78 | 79 | ||
79 | } | 80 | } |