From 03fd475039b418f2f16b2890554fb59403d708dd Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Tue, 23 Jan 2018 17:05:10 +0100 Subject: Track uidvalidity to detect changes behind our back. --- examples/imapresource/imapresource.cpp | 19 +++++++-- examples/imapresource/imapserverproxy.cpp | 11 ++++-- examples/imapresource/imapserverproxy.h | 1 + tests/mailsynctest.cpp | 65 +++++++++++++++++++++++++++---- 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: SinkWarningCtx(mLogCtx) << "Invalid folder " << folderRemoteId << folder.path(); return KAsync::error("Invalid folder"); } - // auto capabilities = imap->getCapabilities(); - //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. - return KAsync::start([=]() { + //Start by checking if UIDVALIDITY is still correct + return KAsync::start([=] { + bool ok = false; + const auto uidvalidity = syncStore().readValue(folderRemoteId, "uidvalidity").toLongLong(&ok); + return imap->select(folder) + .then([=](const SelectResult &selectResult) { + SinkLogCtx(mLogCtx) << "Checking UIDVALIDITY. Local" << uidvalidity << "remote " << selectResult.uidValidity; + if (ok && selectResult.uidValidity != uidvalidity) { + SinkWarningCtx(mLogCtx) << "UIDVALIDITY changed " << selectResult.uidValidity << uidvalidity; + syncStore().removePrefix(folderRemoteId); + } + syncStore().writeValue(folderRemoteId, "uidvalidity", QByteArray::number(selectResult.uidValidity)); + }); + }) + // //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. + .then([=] { auto uidNext = syncStore().readValue(folderRemoteId, "uidnext").toLongLong(); bool ok = false; 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 ImapServerProxy::select(const QString &mailbox) }); } +KAsync::Job ImapServerProxy::select(const Folder &folder) +{ + return select(mailboxFromFolder(folder)); +} + KAsync::Job ImapServerProxy::append(const QString &mailbox, const QByteArray &content, const QList &flags, const QDateTime &internalDate) { auto append = new KIMAP2::AppendJob(mSession); @@ -577,7 +582,7 @@ QString ImapServerProxy::mailboxFromFolder(const Folder &folder) const KAsync::Job ImapServerProxy::fetchFlags(const Folder &folder, const KIMAP2::ImapSet &set, qint64 changedsince, std::function callback) { SinkTrace() << "Fetching flags " << folder.path(); - return select(mailboxFromFolder(folder)).then([=](const SelectResult &selectResult) -> KAsync::Job { + return select(folder).then([=](const SelectResult &selectResult) -> KAsync::Job { SinkTrace() << "Modeseq " << folder.path() << selectResult.highestModSequence << changedsince; if (selectResult.highestModSequence == static_cast(changedsince)) { @@ -601,7 +606,7 @@ KAsync::Job ImapServerProxy::fetchMessages(const Folder &folder, qint64 ui { auto time = QSharedPointer::create(); time->start(); - return select(mailboxFromFolder(folder)).then([this, callback, folder, time, progress, uidNext](const SelectResult &selectResult) -> KAsync::Job { + return select(folder).then([this, callback, folder, time, progress, uidNext](const SelectResult &selectResult) -> KAsync::Job { SinkTrace() << "UIDNEXT " << folder.path() << selectResult.uidNext << uidNext; if (selectResult.uidNext == (uidNext + 1)) { SinkTrace()<< folder.path() << "Uidnext didn't change, nothing to do."; @@ -624,7 +629,7 @@ KAsync::Job ImapServerProxy::fetchMessages(const Folder &folder, const QVe { auto time = QSharedPointer::create(); time->start(); - return select(mailboxFromFolder(folder)).then([this, callback, folder, time, progress, uidsToFetch, headersOnly](const SelectResult &selectResult) -> KAsync::Job { + return select(folder).then([this, callback, folder, time, progress, uidsToFetch, headersOnly](const SelectResult &selectResult) -> KAsync::Job { SinkTrace() << "Fetching messages" << folder.path(); 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: KAsync::Job login(const QString &username, const QString &password); KAsync::Job logout(); KAsync::Job select(const QString &mailbox); + KAsync::Job select(const Folder &mailbox); KAsync::Job append(const QString &mailbox, const QByteArray &content, const QList &flags = QList(), const QDateTime &internalDate = QDateTime()); KAsync::Job store(const KIMAP2::ImapSet &set, const QList &flags); KAsync::Job storeFlags(const KIMAP2::ImapSet &set, const QList &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 @@ using namespace Sink; using namespace Sink::ApplicationDomain; +static QByteArray newMessage(const QString &subject) +{ + auto msg = KMime::Message::Ptr::create(); + msg->subject(true)->fromUnicodeString(subject, "utf8"); + msg->date(true)->setDateTime(QDateTime::currentDateTimeUtc()); + msg->assemble(); + return msg->encodedContent(true); +} + + void MailSyncTest::initTestCase() { Test::initTest(); @@ -261,11 +271,7 @@ void MailSyncTest::testListRemovedSubFolder() void MailSyncTest::testListMails() { - auto msg = KMime::Message::Ptr::create(); - msg->subject(true)->fromUnicodeString("This is a Subject.", "utf8"); - msg->date(true)->setDateTime(QDateTime::currentDateTimeUtc()); - msg->assemble(); - createMessage(QStringList() << "test", msg->encodedContent(true)); + createMessage(QStringList() << "test", newMessage("This is a Subject.")); Sink::Query query; query.resourceFilter(mResourceInstanceIdentifier); @@ -420,8 +426,6 @@ void MailSyncTest::testSyncSingleFolder() // Ensure all local data is processed VERIFYEXEC(Store::synchronize(syncScope)); VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); - - } void MailSyncTest::testSyncSingleMail() @@ -490,3 +494,50 @@ void MailSyncTest::testFailingSync() // Ensure sync fails if resource is misconfigured QTRY_VERIFY(errorReceived); } + +void MailSyncTest::testSyncUidvalidity() +{ + createFolder({"uidvalidity"}); + createMessage({"uidvalidity"}, newMessage("old")); + + VERIFYEXEC(Store::synchronize(SyncScope{ApplicationDomain::getTypeName()}.resourceFilter(mResourceInstanceIdentifier))); + VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); + + auto folder = Store::readOne(Query{}.resourceFilter(mResourceInstanceIdentifier).filter("uidvalidity")); + + auto folderSyncScope = SyncScope{ApplicationDomain::getTypeName()}; + folderSyncScope.resourceFilter(mResourceInstanceIdentifier); + folderSyncScope.filter(QVariant::fromValue(folder.identifier())); + VERIFYEXEC(Store::synchronize(folderSyncScope)); + VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); + + + { + Sink::Query query; + query.resourceFilter(mResourceInstanceIdentifier); + query.request().request().request().request(); + query.filter(folder); + auto mails = Store::read(query); + QCOMPARE(mails.size(), 1); + } + + resetTestEnvironment(); + + createFolder({"uidvalidity"}); + createMessage({"uidvalidity"}, newMessage("new")); + + // Ensure all local data is processed + VERIFYEXEC(Store::synchronize(folderSyncScope)); + VERIFYEXEC(ResourceControl::flushMessageQueue(mResourceInstanceIdentifier)); + + //Now we should have one message + auto folder2 = Store::readOne(Query{}.resourceFilter(mResourceInstanceIdentifier).filter("uidvalidity")); + Sink::Query query; + query.resourceFilter(mResourceInstanceIdentifier); + query.request().request().request().request(); + query.filter(folder2); + auto mails = Store::read(query); + QCOMPARE(mails.size(), 1); + QCOMPARE(mails.first().getSubject(), {"new"}); +} + 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: void testSyncSingleMailWithBogusId(); void testFailingSync(); + void testSyncUidvalidity(); }; } -- cgit v1.2.3