From 07d9eaaa97dfa63bb3ff9767c6b2e152ad8a04e0 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 21 Jul 2016 20:39:11 +0200 Subject: Prepare incremental syncing. --- examples/imapresource/imapresource.cpp | 67 +++++++++++++++++++------------ examples/imapresource/imapserverproxy.cpp | 50 ++++++++++++++++++++--- examples/imapresource/imapserverproxy.h | 14 ++++++- 3 files changed, 99 insertions(+), 32 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 24fd426..3ad80f9 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -211,14 +211,49 @@ public: SinkLog() << "Removed " << count << " mails in " << path << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]"; } + KAsync::Job synchronizeFolder(QSharedPointer imap, const Imap::Folder &folder) + { + QSet uids; + SinkLog() << "Synchronizing mails" << folder.normalizedPath(); + auto capabilities = imap->getCapabilities(); + bool canDoIncrementalRemovals = false; + return KAsync::start([=]() { + //TODO update flags + }) + .then>([=]() { + //TODO Remove what's no longer existing + if (canDoIncrementalRemovals) { + } else { + return imap->fetchUids(folder).then>([this, folder](const QVector &uids) { + SinkTrace() << "Syncing removals"; + synchronizeRemovals(folder.normalizedPath(), uids.toList().toSet()); + commit(); + }).then([](){}); + } + return KAsync::null(); + }) + .then>([this, folder, imap]() { + auto uidNext = 0; + return imap->fetchMessages(folder, uidNext, [this, folder](const QVector &messages) { + SinkTrace() << "Got mail"; + synchronizeMails(folder.normalizedPath(), messages); + }, + [](int progress, int total) { + SinkTrace() << "Progress: " << progress << " out of " << total; + }); + }); + + + } + KAsync::Job synchronizeWithSource() Q_DECL_OVERRIDE { SinkLog() << " Synchronizing"; return KAsync::start([this](KAsync::Future future) { SinkTrace() << "Connecting to:" << mServer << mPort; SinkTrace() << "as:" << mUser; - ImapServerProxy imap(mServer, mPort); - auto loginFuture = imap.login(mUser, mPassword).exec(); + auto imap = QSharedPointer::create(mServer, mPort); + auto loginFuture = imap->login(mUser, mPassword).exec(); loginFuture.waitForFinished(); if (loginFuture.errorCode()) { SinkWarning() << "Login failed."; @@ -229,7 +264,7 @@ public: } QVector folderList; - auto folderFuture = imap.fetchFolders([this, &imap, &folderList](const QVector &folders) { + auto folderFuture = imap->fetchFolders([this, &imap, &folderList](const QVector &folders) { synchronizeFolders(folders); commit(); folderList << folders; @@ -248,27 +283,7 @@ public: if (folder.noselect) { continue; } - QSet uids; - SinkLog() << "Synchronizing mails" << folder.normalizedPath(); - auto messagesFuture = imap.fetchMessages(folder, [this, folder, &uids](const QVector &messages) { - for (const auto &msg : messages) { - uids << msg.uid; - } - synchronizeMails(folder.normalizedPath(), messages); - }, - [](int progress, int total) { - SinkTrace() << "Progress: " << progress << " out of " << total; - }).exec(); - messagesFuture.waitForFinished(); - commit(); - if (messagesFuture.errorCode()) { - SinkWarning() << "Folder sync failed: " << folder.normalizedPath(); - continue; - } - //Remove what there is to remove - synchronizeRemovals(folder.normalizedPath(), uids); - commit(); - SinkTrace() << "Folder synchronized: " << folder.normalizedPath(); + synchronizeFolder(imap, folder).exec().waitForFinished(); } future.setFinished(); }); @@ -541,7 +556,7 @@ KAsync::Job ImapResource::inspect(int inspectionType, const QByteArray &in SinkTrace() << "Connecting to:" << mServer << mPort; SinkTrace() << "as:" << mUser; auto inspectionJob = imap->login(mUser, mPassword) - .then(imap->select(folderRemoteId)) + .then(imap->select(folderRemoteId).then([](){})) .then(imap->fetch(set, scope, [imap, messageByUid](const QVector &messages) { for (const auto &m : messages) { messageByUid->insert(m.uid, m); @@ -604,7 +619,7 @@ KAsync::Job ImapResource::inspect(int inspectionType, const QByteArray &in auto imap = QSharedPointer::create(mServer, mPort); auto messageByUid = QSharedPointer>::create(); auto inspectionJob = imap->login(mUser, mPassword) - .then(imap->select(remoteId)) + .then(imap->select(remoteId).then([](){})) .then(imap->fetch(set, scope, [=](const QVector &messages) { for (const auto &m : messages) { messageByUid->insert(m.uid, m); diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index 3406315..5fffb36 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -188,12 +189,14 @@ KAsync::Job ImapServerProxy::login(const QString &username, const QString }); } -KAsync::Job ImapServerProxy::select(const QString &mailbox) +KAsync::Job ImapServerProxy::select(const QString &mailbox) { auto select = new KIMAP::SelectJob(mSession); select->setMailBox(mailbox); - // select->setCondstoreEnabled(serverSupportsCondstore()); - return runJob(select); + select->setCondstoreEnabled(mCapabilities.contains("CONDSTORE")); + return runJob(select, [select](KJob* job) -> SelectResult { + return {select->uidValidity(), select->nextUid(), select->highestModSequence()}; + }); } KAsync::Job ImapServerProxy::append(const QString &mailbox, const QByteArray &content, const QList &flags, const QDateTime &internalDate) @@ -303,6 +306,16 @@ KAsync::Job ImapServerProxy::fetch(const KIMAP::ImapSet &set, KIMAP::Fetch return runJob(fetch); } +KAsync::Job> ImapServerProxy::search(const KIMAP::ImapSet &set) +{ + auto search = new KIMAP::SearchJob(mSession); + search->setTerm(KIMAP::Term(KIMAP::Term::Uid, set)); + search->setUidBased(true); + return runJob>(search, [](KJob *job) -> QVector { + return static_cast(job)->results(); + }); +} + KAsync::Job ImapServerProxy::fetch(const KIMAP::ImapSet &set, KIMAP::FetchJob::FetchScope scope, const std::function &)> &callback) { return fetch(set, scope, @@ -320,6 +333,11 @@ KAsync::Job ImapServerProxy::fetch(const KIMAP::ImapSet &set, KIMAP::Fetch }); } +QStringList ImapServerProxy::getCapabilities() const +{ + return mCapabilities; +} + KAsync::Job> ImapServerProxy::fetchHeaders(const QString &mailbox) { auto list = QSharedPointer>::create(); @@ -351,6 +369,11 @@ KAsync::Job> ImapServerProxy::fetchHeaders(const QString &mailbox) }); } +KAsync::Job> ImapServerProxy::fetchUids(const QString &mailbox) +{ + return select(mailbox).then>(search(KIMAP::ImapSet(1, 0))); +} + KAsync::Job ImapServerProxy::list(KIMAP::ListJob::Option option, const std::function &mailboxes,const QList > &flags)> &callback) { auto listJob = new KIMAP::ListJob(mSession); @@ -434,12 +457,19 @@ QString ImapServerProxy::mailboxFromFolder(const Folder &folder) const return folder.pathParts.join(mPersonalNamespaceSeparator); } -KAsync::Job ImapServerProxy::fetchMessages(const Folder &folder, std::function &)> callback, std::function progress) +KAsync::Job ImapServerProxy::fetchMessages(const Folder &folder, qint64 uidNext, std::function &)> callback, std::function progress) { + Q_UNUSED(uidNext); auto time = QSharedPointer::create(); time->start(); Q_ASSERT(!mPersonalNamespaceSeparator.isNull()); - return select(mailboxFromFolder(folder)).then>([this, callback, folder, time, progress]() -> KAsync::Job { + return select(mailboxFromFolder(folder)).then, SelectResult>([this, callback, folder, time, progress, uidNext](const SelectResult &selectResult) -> KAsync::Job { + + if (selectResult.uidNext == uidNext) { + SinkTrace() << "Uidnext didn't change, nothing to do."; + return KAsync::null(); + } + return fetchHeaders(mailboxFromFolder(folder)).then, QList>([this, callback, time, progress](const QList &uidsToFetch){ SinkTrace() << "Fetched headers"; SinkTrace() << " Total: " << uidsToFetch.size(); @@ -472,3 +502,13 @@ KAsync::Job ImapServerProxy::fetchMessages(const Folder &folder, std::func }); } + +KAsync::Job ImapServerProxy::fetchMessages(const Folder &folder, std::function &)> callback, std::function progress) +{ + return fetchMessages(folder, 0, callback, progress); +} + +KAsync::Job> ImapServerProxy::fetchUids(const Folder &folder) +{ + return fetchUids(mailboxFromFolder(folder)); +} diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h index f3e1794..9baa52c 100644 --- a/examples/imapresource/imapserverproxy.h +++ b/examples/imapresource/imapserverproxy.h @@ -75,6 +75,12 @@ struct Folder { bool noselect; }; +struct SelectResult { + qint64 uidValidity; + qint64 uidNext; + quint64 highestModSequence; +}; + class ImapServerProxy { KIMAP::Session *mSession; QStringList mCapabilities; @@ -91,7 +97,7 @@ public: //Standard IMAP calls KAsync::Job login(const QString &username, const QString &password); - KAsync::Job select(const QString &mailbox); + KAsync::Job select(const QString &mailbox); KAsync::Job append(const QString &mailbox, const QByteArray &content, const QList &flags = QList(), const QDateTime &internalDate = QDateTime()); KAsync::Job store(const KIMAP::ImapSet &set, const QList &flags); KAsync::Job storeFlags(const KIMAP::ImapSet &set, const QList &flags); @@ -103,6 +109,7 @@ public: KAsync::Job expunge(); KAsync::Job expunge(const KIMAP::ImapSet &set); KAsync::Job copy(const KIMAP::ImapSet &set, const QString &newMailbox); + KAsync::Job> search(const KIMAP::ImapSet &set); typedef std::function &, @@ -115,6 +122,8 @@ public: KAsync::Job fetch(const KIMAP::ImapSet &set, KIMAP::FetchJob::FetchScope scope, const std::function &)> &callback); KAsync::Job list(KIMAP::ListJob::Option option, const std::function &mailboxes,const QList > &flags)> &callback); + QStringList getCapabilities() const; + //Composed calls that do login etc. KAsync::Job> fetchHeaders(const QString &mailbox); KAsync::Job remove(const QString &mailbox, const KIMAP::ImapSet &set); @@ -122,11 +131,14 @@ public: KAsync::Job move(const QString &mailbox, const KIMAP::ImapSet &set, const QString &newMailbox); KAsync::Job createSubfolder(const QString &parentMailbox, const QString &folderName); KAsync::Job renameSubfolder(const QString &mailbox, const QString &newName); + KAsync::Job> fetchUids(const QString &mailbox); QString mailboxFromFolder(const Folder &) const; KAsync::Job fetchFolders(std::function &)> callback); KAsync::Job fetchMessages(const Folder &folder, std::function &)> callback, std::function progress = std::function()); + KAsync::Job fetchMessages(const Folder &folder, qint64 uidNext, std::function &)> callback, std::function progress = std::function()); + KAsync::Job> fetchUids(const Folder &folder); private: KAsync::Job ping(); -- cgit v1.2.3