From ac73ca1d2a23d3b62cca20545e019355f9d00035 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 20 Apr 2017 14:59:59 +0200 Subject: Handle host not found --- examples/imapresource/imapserverproxy.cpp | 10 +++++++--- examples/imapresource/imapserverproxy.h | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index 0cc43b8..3305f60 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp @@ -161,12 +161,16 @@ KAsync::Job ImapServerProxy::login(const QString &username, const QString // SinkTrace() << "Found user namespaces: " << mNamespaces.user; }).then([=] (const KAsync::Error &error) { if (error) { - if (error.errorCode == KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT) { + switch (error.errorCode) { + case KIMAP2::LoginJob::ErrorCode::ERR_HOST_NOT_FOUND: + return KAsync::error(HostNotFoundError, "Host not found: " + error.errorMessage); + case KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT: return KAsync::error(CouldNotConnectError, "Failed to connect: " + error.errorMessage); - } else if (error.errorCode == KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED) { + case KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED: return KAsync::error(SslHandshakeError, "Ssl handshake failed: " + error.errorMessage); + default: + return KAsync::error(error); } - return KAsync::error(error); } return KAsync::null(); }); diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h index 872f032..58c49a2 100644 --- a/examples/imapresource/imapserverproxy.h +++ b/examples/imapresource/imapserverproxy.h @@ -31,6 +31,7 @@ namespace Imap { enum ErrorCode { NoError, + HostNotFoundError, CouldNotConnectError, SslHandshakeError }; -- cgit v1.2.3 From 8d20128618b427270c6c7db49a1716f65b8ff840 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Fri, 21 Apr 2017 12:06:49 +0200 Subject: Gmail support. For the time being we hardcode a list of folders that we synchronize that we know are not duplicating messages. --- examples/imapresource/imapresource.cpp | 36 ++++++++++++++++++++++++++----- examples/imapresource/imapserverproxy.cpp | 31 +++++++++++++++++++++++++- examples/imapresource/imapserverproxy.h | 13 ++++++----- 3 files changed, 69 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 0579dae..0b3dcf5 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -87,6 +87,25 @@ static QByteArray parentRid(const Imap::Folder &folder) return folder.parentPath().toUtf8(); } +static QByteArray getSpecialPurposeType(const QByteArrayList &flags) +{ + if (Imap::flagsContain(Imap::FolderFlags::Trash, flags)) { + return ApplicationDomain::SpecialPurpose::Mail::trash; + } + if (Imap::flagsContain(Imap::FolderFlags::Drafts, flags)) { + return ApplicationDomain::SpecialPurpose::Mail::drafts; + } + if (Imap::flagsContain(Imap::FolderFlags::Sent, flags)) { + return ApplicationDomain::SpecialPurpose::Mail::sent; + } + return {}; +} + +static bool hasSpecialPurposeFlag(const QByteArrayList &flags) +{ + return !getSpecialPurposeType(flags).isEmpty(); +} + class ImapSynchronizer : public Sink::Synchronizer { Q_OBJECT @@ -100,7 +119,7 @@ public: QByteArray createFolder(const Imap::Folder &f) { const auto parentFolderRid = parentRid(f); - SinkTraceCtx(mLogCtx) << "Creating folder: " << f.name() << parentFolderRid; + SinkTraceCtx(mLogCtx) << "Creating folder: " << f.name() << parentFolderRid << f.flags; const auto remoteId = folderRid(f); Sink::ApplicationDomain::Folder folder; @@ -108,10 +127,17 @@ public: folder.setIcon("folder"); folder.setEnabled(f.subscribed); QHash mergeCriteria; - if (SpecialPurpose::isSpecialPurposeFolderName(f.name()) && parentFolderRid.isEmpty()) { - auto type = SpecialPurpose::getSpecialPurposeType(f.name()); - folder.setSpecialPurpose(QByteArrayList() << type); - mergeCriteria.insert(ApplicationDomain::Folder::SpecialPurpose::name, Query::Comparator(type, Query::Comparator::Contains)); + auto specialPurpose = [&] { + if (hasSpecialPurposeFlag(f.flags)) { + return getSpecialPurposeType(f.flags); + } else if (SpecialPurpose::isSpecialPurposeFolderName(f.name()) && parentFolderRid.isEmpty()) { + return SpecialPurpose::getSpecialPurposeType(f.name()); + } + return QByteArray{}; + }(); + if (!specialPurpose.isEmpty()) { + folder.setSpecialPurpose(QByteArrayList() << specialPurpose); + mergeCriteria.insert(ApplicationDomain::Folder::SpecialPurpose::name, Query::Comparator(specialPurpose, Query::Comparator::Contains)); } if (!parentFolderRid.isEmpty()) { diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index 3305f60..36dbcf5 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp @@ -57,6 +57,7 @@ const char* Imap::FolderFlags::Trash = "\\Trash"; const char* Imap::FolderFlags::Archive = "\\Archive"; const char* Imap::FolderFlags::Junk = "\\Junk"; const char* Imap::FolderFlags::Flagged = "\\Flagged"; +const char* Imap::FolderFlags::Drafts = "\\Drafts"; const char* Imap::Capabilities::Namespace = "NAMESPACE"; const char* Imap::Capabilities::Uidplus = "UIDPLUS"; @@ -192,6 +193,12 @@ KAsync::Job ImapServerProxy::logout() } } +bool ImapServerProxy::isGmail() const +{ + //Magic capability that only gmail has + return mCapabilities.contains("X-GM-EXT-1"); +} + KAsync::Job ImapServerProxy::select(const QString &mailbox) { auto select = new KIMAP2::SelectJob(mSession); @@ -441,6 +448,15 @@ QString ImapServerProxy::getNamespace(const QString &name) return ns.name; } +static bool caseInsensitiveContains(const QByteArray &f, const QByteArrayList &list) { + return list.contains(f) || list.contains(f.toLower()); +} + +bool Imap::flagsContain(const QByteArray &f, const QByteArrayList &flags) +{ + return caseInsensitiveContains(f, flags); +} + KAsync::Job ImapServerProxy::fetchFolders(std::function callback) { SinkTrace() << "Fetching folders"; @@ -448,8 +464,21 @@ KAsync::Job ImapServerProxy::fetchFolders(std::function &){ *subscribedList << mailbox.name; }).then(list(KIMAP2::ListJob::IncludeUnsubscribed, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList &flags) { - bool noselect = flags.contains(QByteArray(FolderFlags::Noselect).toLower()) || flags.contains(QByteArray(FolderFlags::Noselect)); + bool noselect = caseInsensitiveContains(FolderFlags::Noselect, flags); bool subscribed = subscribedList->contains(mailbox.name); + if (isGmail()) { + bool inbox = mailbox.name.toLower() == "inbox"; + bool sent = caseInsensitiveContains(FolderFlags::Sent, flags); + bool drafts = caseInsensitiveContains(FolderFlags::Drafts, flags); + bool trash = caseInsensitiveContains(FolderFlags::Trash, flags); + bool isgmailParent = mailbox.name.toLower() == "[gmail]"; + /** + * Because gmail duplicates messages all over the place we only support a few selected folders for now that should be mostly exclusive. + */ + if (!(inbox || sent || drafts || trash || isgmailParent)) { + return; + } + } SinkLog() << "Found mailbox: " << mailbox.name << flags << FolderFlags::Noselect << noselect << " sub: " << subscribed; auto ns = getNamespace(mailbox.name); callback(Folder{mailbox.name, ns, mailbox.separator, noselect, subscribed, flags}); diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h index 58c49a2..0d70ba5 100644 --- a/examples/imapresource/imapserverproxy.h +++ b/examples/imapresource/imapserverproxy.h @@ -61,6 +61,7 @@ namespace FolderFlags extern const char* Junk; extern const char* Flagged; extern const char* All; + extern const char* Drafts; } namespace Capabilities @@ -79,6 +80,8 @@ struct Message { bool fullPayload; }; +bool flagsContain(const QByteArray &f, const QByteArrayList &flags); + struct Folder { Folder() = default; Folder(const QString &path, const QString &ns, const QChar &separator, bool noselect_, bool subscribed_, const QByteArrayList &flags_) @@ -226,11 +229,6 @@ private: }; class ImapServerProxy { - KIMAP2::Session *mSession; - QStringList mCapabilities; - Namespaces mNamespaces; - - public: ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache = nullptr); @@ -280,9 +278,14 @@ public: KAsync::Job> fetchUids(const Folder &folder); private: + bool isGmail() const; + QString getNamespace(const QString &name); QObject mGuard; SessionCache *mSessionCache; + KIMAP2::Session *mSession; + QStringList mCapabilities; + Namespaces mNamespaces; }; } -- cgit v1.2.3 From 0d9d054be6a1716abff20a49e44d94b9b79ab107 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Fri, 21 Apr 2017 13:46:45 +0200 Subject: Apparently this folder can have various names. --- examples/imapresource/imapserverproxy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index 36dbcf5..08a0081 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp @@ -471,7 +471,7 @@ KAsync::Job ImapServerProxy::fetchFolders(std::function Date: Fri, 21 Apr 2017 17:31:22 +0200 Subject: Make sure we always have a complete hierarchy from the IMAP server --- examples/imapresource/imapresource.cpp | 10 +++++----- examples/imapresource/imapserverproxy.cpp | 26 +++++++++++++++++++++++--- examples/imapresource/imapserverproxy.h | 9 +++++++++ 3 files changed, 37 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 0b3dcf5..8577b8c 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -119,6 +119,8 @@ public: QByteArray createFolder(const Imap::Folder &f) { const auto parentFolderRid = parentRid(f); + bool isToplevel = parentFolderRid.isEmpty(); + SinkTraceCtx(mLogCtx) << "Creating folder: " << f.name() << parentFolderRid << f.flags; const auto remoteId = folderRid(f); @@ -126,22 +128,20 @@ public: folder.setName(f.name()); folder.setIcon("folder"); folder.setEnabled(f.subscribed); - QHash mergeCriteria; auto specialPurpose = [&] { if (hasSpecialPurposeFlag(f.flags)) { return getSpecialPurposeType(f.flags); - } else if (SpecialPurpose::isSpecialPurposeFolderName(f.name()) && parentFolderRid.isEmpty()) { + } else if (SpecialPurpose::isSpecialPurposeFolderName(f.name()) && isToplevel) { return SpecialPurpose::getSpecialPurposeType(f.name()); } return QByteArray{}; }(); if (!specialPurpose.isEmpty()) { folder.setSpecialPurpose(QByteArrayList() << specialPurpose); - mergeCriteria.insert(ApplicationDomain::Folder::SpecialPurpose::name, Query::Comparator(specialPurpose, Query::Comparator::Contains)); } - if (!parentFolderRid.isEmpty()) { - folder.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, parentFolderRid)); + if (!isToplevel) { + folder.setParent(syncStore().resolveRemoteId(ApplicationDomain::Folder::name, parentFolderRid)); } createOrModify(ApplicationDomain::getTypeName(), remoteId, folder); return remoteId; diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index 08a0081..d3ad2d4 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp @@ -457,10 +457,23 @@ bool Imap::flagsContain(const QByteArray &f, const QByteArrayList &flags) return caseInsensitiveContains(f, flags); } +static void reportFolder(const Folder &f, QSharedPointer> reportedList, std::function callback) { + if (!reportedList->contains(f.path())) { + reportedList->insert(f.path()); + auto c = f; + c.noselect = true; + callback(c); + if (!f.parentPath().isEmpty()){ + reportFolder(f.parentFolder(), reportedList, callback); + } + } +} + KAsync::Job ImapServerProxy::fetchFolders(std::function callback) { SinkTrace() << "Fetching folders"; auto subscribedList = QSharedPointer>::create() ; + auto reportedList = QSharedPointer>::create() ; return list(KIMAP2::ListJob::NoOption, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList &){ *subscribedList << mailbox.name; }).then(list(KIMAP2::ListJob::IncludeUnsubscribed, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList &flags) { @@ -471,17 +484,24 @@ KAsync::Job ImapServerProxy::fetchFolders(std::functioncontains(folder.parentPath())) { + reportFolder(folder.parentFolder(), reportedList, callback); + } + reportedList->insert(folder.path()); + callback(folder); })); } diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h index 0d70ba5..2d90f39 100644 --- a/examples/imapresource/imapserverproxy.h +++ b/examples/imapresource/imapserverproxy.h @@ -118,6 +118,15 @@ struct Folder { return parentPath; } + Folder parentFolder() const + { + Folder parent; + parent.mPath = parentPath(); + parent.mNamespace = mNamespace; + parent.mSeparator = mSeparator; + return parent; + } + QString name() const { auto pathParts = mPath.split(mSeparator); -- cgit v1.2.3 From 350824b0ecacabab7c7bb565df165d8c108ea43f Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 26 Apr 2017 16:50:42 +0200 Subject: Only create one session And not one for every imap proxy --- examples/imapresource/imapserverproxy.cpp | 22 +++++++++++++++------- examples/imapresource/imapserverproxy.h | 7 ++++++- 2 files changed, 21 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index d3ad2d4..7fa0b5a 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp @@ -99,17 +99,25 @@ static KAsync::Job runJob(KJob *job) }); } -ImapServerProxy::ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache) : mSession(new KIMAP2::Session(serverUrl, qint16(port))), mSessionCache(sessionCache) +KIMAP2::Session *createNewSession(const QString &serverUrl, int port) { - QObject::connect(mSession, &KIMAP2::Session::sslErrors, [this](const QList &errors) { + auto newSession = new KIMAP2::Session(serverUrl, qint16(port)); + if (Sink::Test::testModeEnabled()) { + newSession->setTimeout(1); + } else { + newSession->setTimeout(40); + } + QObject::connect(newSession, &KIMAP2::Session::sslErrors, [=](const QList &errors) { SinkLog() << "Received ssl error: " << errors; - mSession->ignoreErrors(errors); + newSession->ignoreErrors(errors); }); + return newSession; +} - if (Sink::Test::testModeEnabled()) { - mSession->setTimeout(1); - } else { - mSession->setTimeout(40); +ImapServerProxy::ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache) : mSessionCache(sessionCache), mSession(nullptr) +{ + if (!mSessionCache || mSessionCache->isEmpty()) { + mSession = createNewSession(serverUrl, port); } } diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h index 2d90f39..82f4f58 100644 --- a/examples/imapresource/imapserverproxy.h +++ b/examples/imapresource/imapserverproxy.h @@ -231,7 +231,12 @@ public: return session; } } - return CachedSession{}; + return {}; + } + + bool isEmpty() const + { + return mSessions.isEmpty(); } private: QList mSessions; -- cgit v1.2.3 From 3017e10e7f06f41f1d64996a8191407c7a0a8f69 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 26 Apr 2017 17:54:21 +0200 Subject: We require at leaset 0.1.1 --- examples/imapresource/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/imapresource/CMakeLists.txt b/examples/imapresource/CMakeLists.txt index 46a8b08..15ff135 100644 --- a/examples/imapresource/CMakeLists.txt +++ b/examples/imapresource/CMakeLists.txt @@ -4,7 +4,7 @@ add_definitions(-DQT_PLUGIN) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) find_package(KF5 COMPONENTS REQUIRED Mime) -find_package(KIMAP2 0.0.1 REQUIRED) +find_package(KIMAP2 0.1.1 REQUIRED) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) -- cgit v1.2.3 From a08984c450b1cd2584272b0d57a2f95ae3d074c3 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Fri, 28 Apr 2017 15:25:47 +0200 Subject: Removed the resource mapper --- examples/dummyresource/domainadaptor.cpp | 3 --- examples/dummyresource/domainadaptor.h | 8 +++----- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/dummyresource/domainadaptor.cpp b/examples/dummyresource/domainadaptor.cpp index dcc08c7..e7a20da 100644 --- a/examples/dummyresource/domainadaptor.cpp +++ b/examples/dummyresource/domainadaptor.cpp @@ -28,9 +28,6 @@ using namespace flatbuffers; DummyEventAdaptorFactory::DummyEventAdaptorFactory() : DomainTypeAdaptorFactory() { - //TODO turn this into initializeReadPropertyMapper as well? - mResourceMapper->addMapping(&DummyEvent::summary); - mResourceWriteMapper->addMapping(&DummyEventBuilder::add_summary); } DummyMailAdaptorFactory::DummyMailAdaptorFactory() diff --git a/examples/dummyresource/domainadaptor.h b/examples/dummyresource/domainadaptor.h index e7098e9..3faaa63 100644 --- a/examples/dummyresource/domainadaptor.h +++ b/examples/dummyresource/domainadaptor.h @@ -22,25 +22,23 @@ #include "event_generated.h" #include "mail_generated.h" #include "folder_generated.h" -#include "dummy_generated.h" -#include "dummycalendar_generated.h" #include "entity_generated.h" -class DummyEventAdaptorFactory : public DomainTypeAdaptorFactory +class DummyEventAdaptorFactory : public DomainTypeAdaptorFactory { public: DummyEventAdaptorFactory(); virtual ~DummyEventAdaptorFactory() {}; }; -class DummyMailAdaptorFactory : public DomainTypeAdaptorFactory +class DummyMailAdaptorFactory : public DomainTypeAdaptorFactory { public: DummyMailAdaptorFactory(); virtual ~DummyMailAdaptorFactory() {}; }; -class DummyFolderAdaptorFactory : public DomainTypeAdaptorFactory +class DummyFolderAdaptorFactory : public DomainTypeAdaptorFactory { public: DummyFolderAdaptorFactory(); -- cgit v1.2.3 From a2ecf2a77a38025442da8150156e5b59f107b897 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 4 May 2017 07:53:36 +0200 Subject: Only sync subscribed folders --- examples/imapresource/imapresource.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 8577b8c..8e6d2b1 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -584,6 +584,10 @@ public: //Synchronize folders return KAsync::value(folders) .serialEach([=](const Folder &folder) { + //Skip unsubscribed folders + if (!folder.subscribed) { + return KAsync::null(); + } SinkLog() << "Syncing folder " << folder.path(); //Emit notification that the folder is being synced. //The synchronizer can't do that because it has no concept of the folder filter on a mail sync scope meaning that the folder is being synchronized. -- cgit v1.2.3 From 6adf9a4734f15a2c0fa199897f76ded4659b83b7 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 4 May 2017 11:40:24 +0200 Subject: Added progress notification --- examples/imapresource/imapresource.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 8e6d2b1..5aa18dd 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -324,7 +324,7 @@ public: synchronizeMails(folderRemoteId, m); }, [this, maxUid, folder](int progress, int total) { - SinkLog() << "Progress: " << progress << " out of " << total; + reportProgress(progress, total); //commit every 10 messages if ((progress % 10) == 0) { commit(); @@ -365,7 +365,7 @@ public: synchronizeMails(folderRemoteId, m); }, [=](int progress, int total) { - SinkLogCtx(mLogCtx) << "Progress: " << progress << " out of " << total; + reportProgress(progress, total); //commit every 100 messages if ((progress % 100) == 0) { commit(); -- cgit v1.2.3 From 4bc39b1b771aaf9ae5f7c96aeaddb06f2bd101ac Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 4 May 2017 17:33:32 +0200 Subject: Fixed folder sync When explicitly listing the folder we can't rely on the subscription state, nor should we. --- examples/imapresource/imapresource.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 5aa18dd..e7458f6 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -492,7 +492,7 @@ public: //Otherwise fetch full payload for daterange auto folderList = QSharedPointer>::create(); return imap->fetchFolders([folderList](const Folder &folder) { - if (!folder.noselect) { + if (!folder.noselect && folder.subscribed) { *folderList << folder; } }) @@ -580,14 +580,7 @@ public: bool syncHeaders = query.hasFilter(); //FIXME If we were able to to flush in between we could just query the local store for the folder list. return getFolderList(imap, query) - .then([=] (const QVector &folders) { - //Synchronize folders - return KAsync::value(folders) .serialEach([=](const Folder &folder) { - //Skip unsubscribed folders - if (!folder.subscribed) { - return KAsync::null(); - } SinkLog() << "Syncing folder " << folder.path(); //Emit notification that the folder is being synced. //The synchronizer can't do that because it has no concept of the folder filter on a mail sync scope meaning that the folder is being synchronized. @@ -602,7 +595,6 @@ public: SinkWarning() << "Failed to sync folder: " << folder.path() << "Error: " << error.errorMessage; }); }); - }); } }) .then([=] (const KAsync::Error &error) { -- cgit v1.2.3 From 4da825f0429f1f125abe5f1843cd4e3ac5387346 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Tue, 9 May 2017 14:30:29 +0200 Subject: Translate the host not found error --- examples/imapresource/imapresource.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index e7458f6..90383e0 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -506,12 +506,16 @@ public: KAsync::Error getError(const KAsync::Error &error) { if (error) { - if (error.errorCode == Imap::CouldNotConnectError) { - return {ApplicationDomain::ConnectionError, error.errorMessage}; - } else if (error.errorCode == Imap::SslHandshakeError) { - return {ApplicationDomain::LoginError, error.errorMessage}; + switch(error.errorCode) { + case Imap::CouldNotConnectError: + return {ApplicationDomain::ConnectionError, error.errorMessage}; + case Imap::SslHandshakeError: + return {ApplicationDomain::LoginError, error.errorMessage}; + case Imap::HostNotFoundError: + return {ApplicationDomain::NoServerError, error.errorMessage}; + default: + return {ApplicationDomain::UnknownError, error.errorMessage}; } - return {ApplicationDomain::UnknownError, error.errorMessage}; } return {}; } -- cgit v1.2.3 From 380ecbf2866a322de01b28e90dafc1a24e4d948c Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 10 May 2017 21:32:41 +0200 Subject: Don't create folders on invalid config. --- examples/maildirresource/maildirresource.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp index 40bab37..24681b5 100644 --- a/examples/maildirresource/maildirresource.cpp +++ b/examples/maildirresource/maildirresource.cpp @@ -555,18 +555,20 @@ MaildirResource::MaildirResource(const Sink::ResourceContext &resourceContext) setupPreprocessors(ENTITY_TYPE_FOLDER, QVector() << new FolderPreprocessor(mMaildirPath)); KPIM::Maildir dir(mMaildirPath, true); - SinkTrace() << "Started maildir resource for maildir: " << mMaildirPath; - { - auto draftsFolder = dir.addSubFolder("Drafts"); - auto remoteId = synchronizer->createFolder(draftsFolder, "folder", QByteArrayList() << "drafts"); - auto draftsFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); - } - { - auto trashFolder = dir.addSubFolder("Trash"); - auto remoteId = synchronizer->createFolder(trashFolder, "folder", QByteArrayList() << "trash"); - auto trashFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); + if (dir.isValid(false)) { + { + auto draftsFolder = dir.addSubFolder("Drafts"); + auto remoteId = synchronizer->createFolder(draftsFolder, "folder", QByteArrayList() << "drafts"); + auto draftsFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); + } + { + auto trashFolder = dir.addSubFolder("Trash"); + auto remoteId = synchronizer->createFolder(trashFolder, "folder", QByteArrayList() << "trash"); + auto trashFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); + } + synchronizer->commit(); } - synchronizer->commit(); + SinkTrace() << "Started maildir resource for maildir: " << mMaildirPath; } -- cgit v1.2.3 From 41f8f9bc05c059cafa780ac8aec27a56cba2c278 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Fri, 12 May 2017 08:20:43 +0200 Subject: No more SINK_DEBUG_AREA --- examples/davresource/davresource.cpp | 2 -- examples/dummyresource/resourcefactory.cpp | 2 -- examples/imapresource/imapresource.cpp | 2 -- examples/imapresource/imapserverproxy.cpp | 2 -- examples/imapresource/tests/imapmailsyncbenchmark.cpp | 2 -- examples/imapresource/tests/imapserverproxytest.cpp | 2 -- examples/maildirresource/maildirresource.cpp | 2 -- examples/mailtransportresource/mailtransport.cpp | 2 -- examples/mailtransportresource/mailtransportresource.cpp | 2 -- 9 files changed, 18 deletions(-) (limited to 'examples') diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp index 50471ed..6631148 100644 --- a/examples/davresource/davresource.cpp +++ b/examples/davresource/davresource.cpp @@ -42,8 +42,6 @@ #define ENTITY_TYPE_CONTACT "contact" #define ENTITY_TYPE_ADDRESSBOOK "addressbook" -SINK_DEBUG_AREA("davresource") - using namespace Sink; static KAsync::Job runJob(KJob *job) diff --git a/examples/dummyresource/resourcefactory.cpp b/examples/dummyresource/resourcefactory.cpp index c1f536e..dffdfc9 100644 --- a/examples/dummyresource/resourcefactory.cpp +++ b/examples/dummyresource/resourcefactory.cpp @@ -42,8 +42,6 @@ #define ENTITY_TYPE_MAIL "mail" #define ENTITY_TYPE_FOLDER "folder" -SINK_DEBUG_AREA("dummyresource") - using namespace Sink; class DummySynchronizer : public Sink::Synchronizer { diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 90383e0..94ca27a 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -46,8 +46,6 @@ #define ENTITY_TYPE_MAIL "mail" #define ENTITY_TYPE_FOLDER "folder" -SINK_DEBUG_AREA("imapresource") - Q_DECLARE_METATYPE(QSharedPointer) using namespace Imap; diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index 7fa0b5a..a52c5eb 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp @@ -37,8 +37,6 @@ #include "log.h" #include "test.h" -SINK_DEBUG_AREA("imapserverproxy") - using namespace Imap; const char* Imap::Flags::Seen = "\\Seen"; diff --git a/examples/imapresource/tests/imapmailsyncbenchmark.cpp b/examples/imapresource/tests/imapmailsyncbenchmark.cpp index a53c148..814e325 100644 --- a/examples/imapresource/tests/imapmailsyncbenchmark.cpp +++ b/examples/imapresource/tests/imapmailsyncbenchmark.cpp @@ -31,8 +31,6 @@ using namespace Sink; using namespace Sink::ApplicationDomain; -SINK_DEBUG_AREA("ImapMailSyncBenchmark") - /** * Test of complete system using the imap resource. * diff --git a/examples/imapresource/tests/imapserverproxytest.cpp b/examples/imapresource/tests/imapserverproxytest.cpp index 476066d..271b3d9 100644 --- a/examples/imapresource/tests/imapserverproxytest.cpp +++ b/examples/imapresource/tests/imapserverproxytest.cpp @@ -12,8 +12,6 @@ using namespace Imap; -// SINK_DEBUG_AREA("imapserverproxytest") - /** */ class ImapServerProxyTest : public QObject diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp index 24681b5..b406f63 100644 --- a/examples/maildirresource/maildirresource.cpp +++ b/examples/maildirresource/maildirresource.cpp @@ -43,8 +43,6 @@ #define ENTITY_TYPE_MAIL "mail" #define ENTITY_TYPE_FOLDER "folder" -SINK_DEBUG_AREA("maildirresource") - using namespace Sink; static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) diff --git a/examples/mailtransportresource/mailtransport.cpp b/examples/mailtransportresource/mailtransport.cpp index 84c1556..afe0257 100644 --- a/examples/mailtransportresource/mailtransport.cpp +++ b/examples/mailtransportresource/mailtransport.cpp @@ -23,8 +23,6 @@ #include #include -SINK_DEBUG_AREA("mailtransport") - extern "C" { #include diff --git a/examples/mailtransportresource/mailtransportresource.cpp b/examples/mailtransportresource/mailtransportresource.cpp index c73219f..3d6f8e4 100644 --- a/examples/mailtransportresource/mailtransportresource.cpp +++ b/examples/mailtransportresource/mailtransportresource.cpp @@ -39,8 +39,6 @@ #define ENTITY_TYPE_MAIL "mail" -SINK_DEBUG_AREA("mailtransportresource") - using namespace Sink; class MailtransportPreprocessor : public Sink::Preprocessor -- cgit v1.2.3 From 524e405f645edb6231f9b16fafc1f9ca36af8237 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sat, 20 May 2017 11:28:02 +0200 Subject: Avoid notifcations for requests that do nothing, progress with folderid --- examples/imapresource/imapresource.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 94ca27a..533dea3 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -184,14 +184,12 @@ public: return flags; } - void synchronizeMails(const QByteArray &folderRid, const Message &message) + void synchronizeMails(const QByteArray &folderRid, const QByteArray &folderLocalId, const Message &message) { auto time = QSharedPointer::create(); time->start(); SinkTraceCtx(mLogCtx) << "Importing new mail." << folderRid; - const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRid); - const auto remoteId = assembleMailRid(folderLocalId, message.uid); Q_ASSERT(message.msg); @@ -315,14 +313,15 @@ public: SinkTraceCtx(mLogCtx) << "Uids to fetch: " << filteredAndSorted; bool headersOnly = false; + const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); return imap->fetchMessages(folder, filteredAndSorted, headersOnly, [=](const Message &m) { if (*maxUid < m.uid) { *maxUid = m.uid; } - synchronizeMails(folderRemoteId, m); + synchronizeMails(folderRemoteId, folderLocalId, m); }, - [this, maxUid, folder](int progress, int total) { - reportProgress(progress, total); + [=](int progress, int total) { + reportProgress(progress, total, QByteArrayList{} << folderLocalId); //commit every 10 messages if ((progress % 10) == 0) { commit(); @@ -359,11 +358,12 @@ public: SinkLogCtx(mLogCtx) << "Fetching headers for: " << toFetch; bool headersOnly = true; + const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); return imap->fetchMessages(folder, toFetch, headersOnly, [=](const Message &m) { - synchronizeMails(folderRemoteId, m); + synchronizeMails(folderRemoteId, folderLocalId, m); }, [=](int progress, int total) { - reportProgress(progress, total); + reportProgress(progress, total, QByteArrayList{} << folderLocalId); //commit every 100 messages if ((progress % 100) == 0) { commit(); @@ -567,11 +567,12 @@ public: } SinkLog() << "Fetching messages: " << toFetch << folderRemoteId; bool headersOnly = false; + const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); return imap->fetchMessages(Folder{folderRemoteId}, toFetch, headersOnly, [=](const Message &m) { - synchronizeMails(folderRemoteId, m); + synchronizeMails(folderRemoteId, folderLocalId, m); }, [=](int progress, int total) { - reportProgress(progress, total); + reportProgress(progress, total, QByteArrayList{} << folderLocalId); //commit every 100 messages if ((progress % 100) == 0) { commit(); -- cgit v1.2.3 From cdca3e36eddea7f43787fa6bbc2ad6b669c89185 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sat, 20 May 2017 16:53:53 +0200 Subject: Subscribe to mailboxes in imaptest --- examples/imapresource/tests/populatemailbox.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'examples') diff --git a/examples/imapresource/tests/populatemailbox.sh b/examples/imapresource/tests/populatemailbox.sh index a435df7..800e2e7 100644 --- a/examples/imapresource/tests/populatemailbox.sh +++ b/examples/imapresource/tests/populatemailbox.sh @@ -1,12 +1,23 @@ #!/bin/bash sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost +#Delete all mailboxes sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost +#Create mailboxes sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost sudo echo "cm user.doe.Trash" | cyradm --auth PLAIN -u cyrus -w admin localhost + +#Set acls so we can create in INBOX sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost +#Subscribe to mailboxes +sudo echo "sub INBOX" | cyradm --auth PLAIN -u doe -w doe localhost +sudo echo "sub INBOX.test" | cyradm --auth PLAIN -u doe -w doe localhost +sudo echo "sub INBOX.Drafts" | cyradm --auth PLAIN -u doe -w doe localhost +sudo echo "sub INBOX.Trash" | cyradm --auth PLAIN -u doe -w doe localhost + +#Create a bunch of test messages in the test folder # for i in `seq 1 5000`; # do # # sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/$i. -- cgit v1.2.3 From d67b886dd2cf51264e4d6da680e268015a0a7031 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sat, 20 May 2017 16:54:34 +0200 Subject: Don't try to replay modifications on nothing. --- examples/imapresource/imapresource.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 533dea3..bfe43bf 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -689,6 +689,12 @@ public: KAsync::Job replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE { + if (operation != Sink::Operation_Creation) { + if(oldRemoteId.isEmpty()) { + Q_ASSERT(false); + return KAsync::error("Tried to replay modification without old remoteId."); + } + } auto imap = QSharedPointer::create(mServer, mPort, &mSessionCache); auto login = imap->login(mUser, mPassword); if (operation == Sink::Operation_Creation) { -- cgit v1.2.3 From 227610b7399905fca38e0f09053d78e2e866f32e Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sat, 20 May 2017 16:55:21 +0200 Subject: Ensure change-replay errors make it through to the correct error handling and are appropriately dealt with. --- examples/imapresource/imapresource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index bfe43bf..29ff03b 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -680,7 +680,7 @@ public: if (error) { SinkWarning() << "Error during changereplay: " << error.errorMessage; return imap->logout() - .then(KAsync::error(error)); + .then(KAsync::error(getError(error))); } return imap->logout() .then(KAsync::value(remoteId)); -- cgit v1.2.3 From e97fb6d6de7c9bda6e8f911c5f471f4059ae7470 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sun, 11 Jun 2017 22:55:50 +0200 Subject: Disabled automatic syncing of folders I triggers a lot of work, and as we currently can't abort sync tasks and have no priority lane for requests it's rather intrusive. A sync will still be triggered when a folder is selected, so we're not loosing a lot except the "pull everything offline" case. --- examples/imapresource/imapresource.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 29ff03b..a6c0343 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -415,8 +415,9 @@ public: list << Synchronizer::SyncRequest{query}; } else { list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName())}; + //TODO automatic syncing off all folders is disabled for the time being //This request depends on the previous one so we flush first. - list << Synchronizer::SyncRequest{applyMailDefaults(Sink::QueryBase(ApplicationDomain::getTypeName())), QByteArray{}, Synchronizer::SyncRequest::RequestFlush}; + //list << Synchronizer::SyncRequest{applyMailDefaults(Sink::QueryBase(ApplicationDomain::getTypeName())), QByteArray{}, Synchronizer::SyncRequest::RequestFlush}; } return list; } -- cgit v1.2.3 From e179ec896bb5b55e33419da8bea2e891517697f6 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Mon, 12 Jun 2017 11:48:34 +0200 Subject: Avoid the extra parsing step. We only need the content, we'll parse later on when processing the pipeline. --- examples/imapresource/CMakeLists.txt | 2 +- examples/imapresource/imapresource.cpp | 2 +- examples/imapresource/imapserverproxy.cpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/CMakeLists.txt b/examples/imapresource/CMakeLists.txt index 15ff135..5d2d38b 100644 --- a/examples/imapresource/CMakeLists.txt +++ b/examples/imapresource/CMakeLists.txt @@ -4,7 +4,7 @@ add_definitions(-DQT_PLUGIN) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) find_package(KF5 COMPONENTS REQUIRED Mime) -find_package(KIMAP2 0.1.1 REQUIRED) +find_package(KIMAP2 0.2 REQUIRED) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index a6c0343..5a9c36f 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -193,7 +193,7 @@ public: const auto remoteId = assembleMailRid(folderLocalId, message.uid); Q_ASSERT(message.msg); - SinkTraceCtx(mLogCtx) << "Found a mail " << remoteId << message.msg->subject(true)->asUnicodeString() << message.flags; + SinkTraceCtx(mLogCtx) << "Found a mail " << remoteId << message.flags; auto mail = Sink::ApplicationDomain::Mail::create(mResourceInstanceIdentifier); mail.setFolder(folderLocalId); diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index a52c5eb..538105c 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp @@ -314,6 +314,7 @@ KAsync::Job ImapServerProxy::fetch(const KIMAP2::ImapSet &set, KIMAP2::Fet fetch->setSequenceSet(set); fetch->setUidBased(true); fetch->setScope(scope); + fetch->setAvoidParsing(true); QObject::connect(fetch, &KIMAP2::FetchJob::resultReceived, callback); return runJob(fetch); } -- cgit v1.2.3 From 7dc97cc6e338e1d756734e1620a062cdb08635ca Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Mon, 12 Jun 2017 16:09:29 +0200 Subject: There shouldn't be any conversions necessary at this point. --- examples/imapresource/imapresource.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 5a9c36f..945962f 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -615,10 +615,10 @@ public: auto login = imap->login(mUser, mPassword); KAsync::Job job = KAsync::null(); if (operation == Sink::Operation_Creation) { - QString mailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); - QByteArray content = KMime::LFtoCRLF(mail.getMimeMessage()); - auto flags = getFlags(mail); - QDateTime internalDate = mail.getDate(); + const QString mailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); + const QByteArray content = mail.getMimeMessage(); + const auto flags = getFlags(mail); + const QDateTime internalDate = mail.getDate(); job = login.then(imap->append(mailbox, content, flags, internalDate)) .addToContext(imap) .then([mail](qint64 uid) { @@ -649,11 +649,11 @@ public: const bool messageMoved = changedProperties.contains(ApplicationDomain::Mail::Folder::name); const bool messageChanged = changedProperties.contains(ApplicationDomain::Mail::MimeMessage::name); if (messageChanged || messageMoved) { - SinkTrace() << "Replacing message."; const auto folderId = folderIdFromMailRid(oldRemoteId); const QString oldMailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, folderId); - QByteArray content = KMime::LFtoCRLF(mail.getMimeMessage()); - QDateTime internalDate = mail.getDate(); + const QByteArray content = mail.getMimeMessage(); + const QDateTime internalDate = mail.getDate(); + SinkTrace() << "Replacing message. Old mailbox: " << oldMailbox << "New mailbox: " << mailbox << "Flags: " << flags << "Content: " << content; KIMAP2::ImapSet set; set.add(uid); job = login.then(imap->append(mailbox, content, flags, internalDate)) -- cgit v1.2.3 From ae1c5a0a53d1fd351b6fd33e8a46ad1034874489 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 14 Jun 2017 12:39:29 +0200 Subject: Deal with both CRLF and LF mime messages. IMAP always requires CRLF, and so does the MIME standard, KMIME expects LF-only. We now just try to always use CRLF on disk, but convert LF-only messages should we have to (e.g. because copied over from maildir or so). --- examples/imapresource/imapresource.cpp | 15 ++++++++++++--- .../mailtransportresource/tests/mailtransporttest.cpp | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 945962f..25d9534 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -197,7 +197,7 @@ public: auto mail = Sink::ApplicationDomain::Mail::create(mResourceInstanceIdentifier); mail.setFolder(folderLocalId); - mail.setMimeMessage(message.msg->encodedContent()); + mail.setMimeMessage(message.msg->encodedContent(true)); mail.setExtractedFullPayloadAvailable(message.fullPayload); setFlags(mail, message.flags); @@ -608,6 +608,15 @@ public: } return KAsync::error("Nothing to do"); } + static QByteArray ensureCRLF(const QByteArray &data) { + auto index = data.indexOf('\n'); + if (index > 0 && data.at(index - 1) == '\r') { //First line is LF-only terminated + //Convert back and forth in case there's a mix. We don't want to expand CRLF into CRCRLF. + return KMime::LFtoCRLF(KMime::CRLFtoLF(data)); + } else { + return data; + } + } KAsync::Job replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE { @@ -616,7 +625,7 @@ public: KAsync::Job job = KAsync::null(); if (operation == Sink::Operation_Creation) { const QString mailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); - const QByteArray content = mail.getMimeMessage(); + const auto content = ensureCRLF(mail.getMimeMessage()); const auto flags = getFlags(mail); const QDateTime internalDate = mail.getDate(); job = login.then(imap->append(mailbox, content, flags, internalDate)) @@ -651,7 +660,7 @@ public: if (messageChanged || messageMoved) { const auto folderId = folderIdFromMailRid(oldRemoteId); const QString oldMailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, folderId); - const QByteArray content = mail.getMimeMessage(); + const auto content = ensureCRLF(mail.getMimeMessage()); const QDateTime internalDate = mail.getDate(); SinkTrace() << "Replacing message. Old mailbox: " << oldMailbox << "New mailbox: " << mailbox << "Flags: " << flags << "Content: " << content; KIMAP2::ImapSet set; diff --git a/examples/mailtransportresource/tests/mailtransporttest.cpp b/examples/mailtransportresource/tests/mailtransporttest.cpp index e4cc447..2a831ed 100644 --- a/examples/mailtransportresource/tests/mailtransporttest.cpp +++ b/examples/mailtransportresource/tests/mailtransporttest.cpp @@ -64,7 +64,7 @@ private slots: message->assemble(); auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); - mail.setMimeMessage(message->encodedContent()); + mail.setMimeMessage(message->encodedContent(true)); VERIFYEXEC(Store::create(mail)); VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); @@ -92,7 +92,7 @@ private slots: message->assemble(); auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); - mail.setMimeMessage(message->encodedContent()); + mail.setMimeMessage(message->encodedContent(true)); VERIFYEXEC(Store::create(mail)); VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); -- cgit v1.2.3 From c6422cbbb24542a48e26b5f2448def3fcfc8694d Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 14 Jun 2017 13:23:48 +0200 Subject: We now have to manually parse for inspections ..since we turn of parsing for regular fetching. --- examples/imapresource/imapresource.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 25d9534..9fb83ab 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -834,6 +834,10 @@ protected: .then(imap->select(folderRemoteId)) .then([](Imap::SelectResult){}) .then(imap->fetch(set, scope, [imap, messageByUid](const Imap::Message &message) { + //We avoid parsing normally, so we have to do it explicitly here + if (message.msg) { + message.msg->parse(); + } messageByUid->insert(message.uid, message); })); -- cgit v1.2.3 From 71ee6fc9b75bfb158ba507d9bd42b64d750029e0 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 14 Jun 2017 14:20:02 +0200 Subject: Cleanup --- examples/imapresource/imapresource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 9fb83ab..b28ecf1 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -584,7 +584,7 @@ public: bool syncHeaders = query.hasFilter(); //FIXME If we were able to to flush in between we could just query the local store for the folder list. return getFolderList(imap, query) - .serialEach([=](const Folder &folder) { + .serialEach([=](const Folder &folder) { SinkLog() << "Syncing folder " << folder.path(); //Emit notification that the folder is being synced. //The synchronizer can't do that because it has no concept of the folder filter on a mail sync scope meaning that the folder is being synchronized. -- cgit v1.2.3 From 78ff8574c3f481f8b0be248b153d55fe8c33eda5 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 14 Jun 2017 14:20:09 +0200 Subject: The mailsynctest relies on subscribed mailboxes --- examples/imapresource/tests/resetmailbox.sh | 3 +++ 1 file changed, 3 insertions(+) (limited to 'examples') diff --git a/examples/imapresource/tests/resetmailbox.sh b/examples/imapresource/tests/resetmailbox.sh index 6ed198e..63d3478 100644 --- a/examples/imapresource/tests/resetmailbox.sh +++ b/examples/imapresource/tests/resetmailbox.sh @@ -3,8 +3,11 @@ sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost +sudo echo "subscribe INBOX.test" | cyradm --auth PLAIN -u doe -w doe localhost sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost +sudo echo "subscribe INBOX.Drafts" | cyradm --auth PLAIN -u doe -w doe localhost sudo echo "cm user.doe.Trash" | cyradm --auth PLAIN -u cyrus -w admin localhost +sudo echo "subscribe INBOX.Trash" | cyradm --auth PLAIN -u doe -w doe localhost sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/1. sudo chown cyrus:mail /var/spool/imap/d/user/doe/test/1. -- cgit v1.2.3 From 01d470b8a94c2e022eb1ca620064732bf2ddece3 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 15 Jun 2017 10:28:50 +0200 Subject: Revert this change, we can just only sync folders in kube. --- examples/imapresource/imapresource.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index b28ecf1..81c808b 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -415,9 +415,8 @@ public: list << Synchronizer::SyncRequest{query}; } else { list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName())}; - //TODO automatic syncing off all folders is disabled for the time being //This request depends on the previous one so we flush first. - //list << Synchronizer::SyncRequest{applyMailDefaults(Sink::QueryBase(ApplicationDomain::getTypeName())), QByteArray{}, Synchronizer::SyncRequest::RequestFlush}; + list << Synchronizer::SyncRequest{applyMailDefaults(Sink::QueryBase(ApplicationDomain::getTypeName())), QByteArray{}, Synchronizer::SyncRequest::RequestFlush}; } return list; } -- cgit v1.2.3 From 3357b1473efa0ccfa06cb4c2f3ab387c33038274 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sun, 25 Jun 2017 16:10:38 +0200 Subject: Register the right facade. --- examples/davresource/davresource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp index 6631148..88b7338 100644 --- a/examples/davresource/davresource.cpp +++ b/examples/davresource/davresource.cpp @@ -269,7 +269,7 @@ Sink::Resource *DavResourceFactory::createResource(const ResourceContext &contex void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory) { factory.registerFacade>(name); - factory.registerFacade>(name); + factory.registerFacade>(name); } void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry ®istry) -- cgit v1.2.3 From 26c27fd619857d0945b6c110a863bdc8b4a5e073 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 29 Jun 2017 12:09:11 +0200 Subject: Ported to KDAV2, enabled the dav resource by default. --- examples/davresource/CMakeLists.txt | 4 ++-- examples/davresource/davresource.cpp | 38 ++++++++++++++++++------------------ examples/davresource/davresource.h | 4 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) (limited to 'examples') diff --git a/examples/davresource/CMakeLists.txt b/examples/davresource/CMakeLists.txt index 28829d5..7091edc 100644 --- a/examples/davresource/CMakeLists.txt +++ b/examples/davresource/CMakeLists.txt @@ -3,10 +3,10 @@ project(sink_resource_dav) add_definitions(-DQT_PLUGIN) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) -find_package(KPimKDAV REQUIRED) +find_package(KPimKDAV2 REQUIRED) add_library(${PROJECT_NAME} SHARED davresource.cpp) qt5_use_modules(${PROJECT_NAME} Core Network) -target_link_libraries(${PROJECT_NAME} sink KPim::KDAV) +target_link_libraries(${PROJECT_NAME} sink KPim::KDAV2) install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp index 88b7338..1d45f20 100644 --- a/examples/davresource/davresource.cpp +++ b/examples/davresource/davresource.cpp @@ -31,12 +31,12 @@ #include "contactpreprocessor.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include //This is the resources entity type, and not the domain type #define ENTITY_TYPE_CONTACT "contact" @@ -85,7 +85,7 @@ public: return remoteId; } - void synchronizeAddressbooks(const KDAV::DavCollection::List &addressbookList) + void synchronizeAddressbooks(const KDAV2::DavCollection::List &addressbookList) { const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK; SinkTrace() << "Found addressbooks " << addressbookList.size(); @@ -119,12 +119,12 @@ public: return list; } - static QByteArray getRid(const KDAV::DavItem &item) + static QByteArray getRid(const KDAV2::DavItem &item) { return item.url().toDisplayString().toUtf8(); } - static QByteArray getRid(const KDAV::DavCollection &item) + static QByteArray getRid(const KDAV2::DavCollection &item) { return item.url().toDisplayString().toUtf8(); } @@ -133,7 +133,7 @@ public: { if (query.type() == ApplicationDomain::getTypeName()) { SinkLogCtx(mLogCtx) << "Synchronizing addressbooks:" << mResourceUrl.url(); - auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl); + auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(mResourceUrl); auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] (const KAsync::Error &error) { if (error) { SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString(); @@ -145,29 +145,29 @@ public: } else if (query.type() == ApplicationDomain::getTypeName()) { SinkLogCtx(mLogCtx) << "Synchronizing contacts."; auto ridList = QSharedPointer::create(); - auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl); + auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(mResourceUrl); auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] { synchronizeAddressbooks(collectionsFetchJob ->collections()); return collectionsFetchJob->collections(); }) - .serialEach([this, ridList](const KDAV::DavCollection &collection) { + .serialEach([this, ridList](const KDAV2::DavCollection &collection) { auto collId = getRid(collection); const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId); auto ctag = collection.CTag().toLatin1(); if (ctag != syncStore().readValue(collId + "_ctagXX")) { SinkTraceCtx(mLogCtx) << "Syncing " << collId; - auto cache = std::shared_ptr(new KDAV::EtagCache()); - auto davItemsListJob = new KDAV::DavItemsListJob(collection.url(), cache); + auto cache = std::shared_ptr(new KDAV2::EtagCache()); + auto davItemsListJob = new KDAV2::DavItemsListJob(collection.url(), cache); const QByteArray bufferType = ENTITY_TYPE_CONTACT; QHash mergeCriteria; auto colljob = runJob(davItemsListJob).then([davItemsListJob] { return KAsync::value(davItemsListJob->items()); }) - .serialEach([=] (const KDAV::DavItem &item) { + .serialEach([=] (const KDAV2::DavItem &item) { QByteArray rid = getRid(item); if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){ SinkTrace() << "Updating " << rid; - auto davItemFetchJob = new KDAV::DavItemFetchJob(item); + auto davItemFetchJob = new KDAV2::DavItemFetchJob(item); auto itemjob = runJob(davItemFetchJob) .then([=] { const auto item = davItemFetchJob->item(); @@ -178,7 +178,7 @@ public: createOrModify(bufferType, rid, contact, mergeCriteria); return item; }) - .then([this, ridList] (const KDAV::DavItem &item) { + .then([this, ridList] (const KDAV2::DavItem &item) { const auto rid = getRid(item); syncStore().writeValue(rid + "_etag", item.etag().toLatin1()); ridList->append(rid); @@ -225,7 +225,7 @@ KAsync::Job replay(const ApplicationDomain::Contact &contact, Sink:: } public: - KDAV::DavUrl mResourceUrl; + KDAV2::DavUrl mResourceUrl; }; @@ -242,7 +242,7 @@ DavResource::DavResource(const Sink::ResourceContext &resourceContext) resourceUrl.setUserName(config.value("username").toString()); resourceUrl.setPassword(config.value("password").toString()); - mResourceUrl = KDAV::DavUrl(resourceUrl, KDAV::CardDav); + mResourceUrl = KDAV2::DavUrl(resourceUrl, KDAV2::CardDav); auto synchronizer = QSharedPointer::create(resourceContext); synchronizer->mResourceUrl = mResourceUrl; diff --git a/examples/davresource/davresource.h b/examples/davresource/davresource.h index 1ce66ea..db175a4 100644 --- a/examples/davresource/davresource.h +++ b/examples/davresource/davresource.h @@ -21,7 +21,7 @@ #include "common/genericresource.h" -#include +#include #include #include @@ -48,7 +48,7 @@ public: private: QStringList listAvailableFolders(); - KDAV::DavUrl mResourceUrl; + KDAV2::DavUrl mResourceUrl; }; class DavResourceFactory : public Sink::ResourceFactory -- cgit v1.2.3 From 682b90cc4138ce7d4c2a4eb4b28c8a17b08cd6de Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 29 Jun 2017 12:13:22 +0200 Subject: KDE_FORK_SLAVES is no longer necessary --- examples/davresource/davresource.cpp | 5 ----- 1 file changed, 5 deletions(-) (limited to 'examples') diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp index 1d45f20..465220f 100644 --- a/examples/davresource/davresource.cpp +++ b/examples/davresource/davresource.cpp @@ -232,11 +232,6 @@ public: DavResource::DavResource(const Sink::ResourceContext &resourceContext) : Sink::GenericResource(resourceContext) { - /* - * Fork KIO slaves (used in kdav), instead of starting them via klauncher. - * Otherwise we have yet another runtime dependency that will i.e. not work in the docker container. - */ - qputenv("KDE_FORK_SLAVES", "TRUE"); auto config = ResourceConfig::getConfiguration(resourceContext.instanceId()); auto resourceUrl = QUrl::fromUserInput(config.value("server").toString()); resourceUrl.setUserName(config.value("username").toString()); -- cgit v1.2.3