diff options
Diffstat (limited to 'examples')
18 files changed, 240 insertions, 126 deletions
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) | |||
3 | add_definitions(-DQT_PLUGIN) | 3 | add_definitions(-DQT_PLUGIN) |
4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) | 4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) |
5 | 5 | ||
6 | find_package(KPimKDAV REQUIRED) | 6 | find_package(KPimKDAV2 REQUIRED) |
7 | 7 | ||
8 | add_library(${PROJECT_NAME} SHARED davresource.cpp) | 8 | add_library(${PROJECT_NAME} SHARED davresource.cpp) |
9 | qt5_use_modules(${PROJECT_NAME} Core Network) | 9 | qt5_use_modules(${PROJECT_NAME} Core Network) |
10 | target_link_libraries(${PROJECT_NAME} sink KPim::KDAV) | 10 | target_link_libraries(${PROJECT_NAME} sink KPim::KDAV2) |
11 | 11 | ||
12 | install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) | 12 | install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) |
diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp index 50471ed..465220f 100644 --- a/examples/davresource/davresource.cpp +++ b/examples/davresource/davresource.cpp | |||
@@ -31,19 +31,17 @@ | |||
31 | 31 | ||
32 | #include "contactpreprocessor.h" | 32 | #include "contactpreprocessor.h" |
33 | 33 | ||
34 | #include <KDAV/DavCollection> | 34 | #include <KDAV2/DavCollection> |
35 | #include <KDAV/DavCollectionsFetchJob> | 35 | #include <KDAV2/DavCollectionsFetchJob> |
36 | #include <KDAV/DavItem> | 36 | #include <KDAV2/DavItem> |
37 | #include <KDAV/DavItemsListJob> | 37 | #include <KDAV2/DavItemsListJob> |
38 | #include <KDAV/DavItemFetchJob> | 38 | #include <KDAV2/DavItemFetchJob> |
39 | #include <KDAV/EtagCache> | 39 | #include <KDAV2/EtagCache> |
40 | 40 | ||
41 | //This is the resources entity type, and not the domain type | 41 | //This is the resources entity type, and not the domain type |
42 | #define ENTITY_TYPE_CONTACT "contact" | 42 | #define ENTITY_TYPE_CONTACT "contact" |
43 | #define ENTITY_TYPE_ADDRESSBOOK "addressbook" | 43 | #define ENTITY_TYPE_ADDRESSBOOK "addressbook" |
44 | 44 | ||
45 | SINK_DEBUG_AREA("davresource") | ||
46 | |||
47 | using namespace Sink; | 45 | using namespace Sink; |
48 | 46 | ||
49 | static KAsync::Job<void> runJob(KJob *job) | 47 | static KAsync::Job<void> runJob(KJob *job) |
@@ -87,7 +85,7 @@ public: | |||
87 | return remoteId; | 85 | return remoteId; |
88 | } | 86 | } |
89 | 87 | ||
90 | void synchronizeAddressbooks(const KDAV::DavCollection::List &addressbookList) | 88 | void synchronizeAddressbooks(const KDAV2::DavCollection::List &addressbookList) |
91 | { | 89 | { |
92 | const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK; | 90 | const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK; |
93 | SinkTrace() << "Found addressbooks " << addressbookList.size(); | 91 | SinkTrace() << "Found addressbooks " << addressbookList.size(); |
@@ -121,12 +119,12 @@ public: | |||
121 | return list; | 119 | return list; |
122 | } | 120 | } |
123 | 121 | ||
124 | static QByteArray getRid(const KDAV::DavItem &item) | 122 | static QByteArray getRid(const KDAV2::DavItem &item) |
125 | { | 123 | { |
126 | return item.url().toDisplayString().toUtf8(); | 124 | return item.url().toDisplayString().toUtf8(); |
127 | } | 125 | } |
128 | 126 | ||
129 | static QByteArray getRid(const KDAV::DavCollection &item) | 127 | static QByteArray getRid(const KDAV2::DavCollection &item) |
130 | { | 128 | { |
131 | return item.url().toDisplayString().toUtf8(); | 129 | return item.url().toDisplayString().toUtf8(); |
132 | } | 130 | } |
@@ -135,7 +133,7 @@ public: | |||
135 | { | 133 | { |
136 | if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) { | 134 | if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) { |
137 | SinkLogCtx(mLogCtx) << "Synchronizing addressbooks:" << mResourceUrl.url(); | 135 | SinkLogCtx(mLogCtx) << "Synchronizing addressbooks:" << mResourceUrl.url(); |
138 | auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl); | 136 | auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(mResourceUrl); |
139 | auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] (const KAsync::Error &error) { | 137 | auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] (const KAsync::Error &error) { |
140 | if (error) { | 138 | if (error) { |
141 | SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString(); | 139 | SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString(); |
@@ -147,29 +145,29 @@ public: | |||
147 | } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) { | 145 | } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) { |
148 | SinkLogCtx(mLogCtx) << "Synchronizing contacts."; | 146 | SinkLogCtx(mLogCtx) << "Synchronizing contacts."; |
149 | auto ridList = QSharedPointer<QByteArrayList>::create(); | 147 | auto ridList = QSharedPointer<QByteArrayList>::create(); |
150 | auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl); | 148 | auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(mResourceUrl); |
151 | auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] { | 149 | auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] { |
152 | synchronizeAddressbooks(collectionsFetchJob ->collections()); | 150 | synchronizeAddressbooks(collectionsFetchJob ->collections()); |
153 | return collectionsFetchJob->collections(); | 151 | return collectionsFetchJob->collections(); |
154 | }) | 152 | }) |
155 | .serialEach([this, ridList](const KDAV::DavCollection &collection) { | 153 | .serialEach([this, ridList](const KDAV2::DavCollection &collection) { |
156 | auto collId = getRid(collection); | 154 | auto collId = getRid(collection); |
157 | const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId); | 155 | const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId); |
158 | auto ctag = collection.CTag().toLatin1(); | 156 | auto ctag = collection.CTag().toLatin1(); |
159 | if (ctag != syncStore().readValue(collId + "_ctagXX")) { | 157 | if (ctag != syncStore().readValue(collId + "_ctagXX")) { |
160 | SinkTraceCtx(mLogCtx) << "Syncing " << collId; | 158 | SinkTraceCtx(mLogCtx) << "Syncing " << collId; |
161 | auto cache = std::shared_ptr<KDAV::EtagCache>(new KDAV::EtagCache()); | 159 | auto cache = std::shared_ptr<KDAV2::EtagCache>(new KDAV2::EtagCache()); |
162 | auto davItemsListJob = new KDAV::DavItemsListJob(collection.url(), cache); | 160 | auto davItemsListJob = new KDAV2::DavItemsListJob(collection.url(), cache); |
163 | const QByteArray bufferType = ENTITY_TYPE_CONTACT; | 161 | const QByteArray bufferType = ENTITY_TYPE_CONTACT; |
164 | QHash<QByteArray, Query::Comparator> mergeCriteria; | 162 | QHash<QByteArray, Query::Comparator> mergeCriteria; |
165 | auto colljob = runJob(davItemsListJob).then([davItemsListJob] { | 163 | auto colljob = runJob(davItemsListJob).then([davItemsListJob] { |
166 | return KAsync::value(davItemsListJob->items()); | 164 | return KAsync::value(davItemsListJob->items()); |
167 | }) | 165 | }) |
168 | .serialEach([=] (const KDAV::DavItem &item) { | 166 | .serialEach([=] (const KDAV2::DavItem &item) { |
169 | QByteArray rid = getRid(item); | 167 | QByteArray rid = getRid(item); |
170 | if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){ | 168 | if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){ |
171 | SinkTrace() << "Updating " << rid; | 169 | SinkTrace() << "Updating " << rid; |
172 | auto davItemFetchJob = new KDAV::DavItemFetchJob(item); | 170 | auto davItemFetchJob = new KDAV2::DavItemFetchJob(item); |
173 | auto itemjob = runJob(davItemFetchJob) | 171 | auto itemjob = runJob(davItemFetchJob) |
174 | .then([=] { | 172 | .then([=] { |
175 | const auto item = davItemFetchJob->item(); | 173 | const auto item = davItemFetchJob->item(); |
@@ -180,7 +178,7 @@ public: | |||
180 | createOrModify(bufferType, rid, contact, mergeCriteria); | 178 | createOrModify(bufferType, rid, contact, mergeCriteria); |
181 | return item; | 179 | return item; |
182 | }) | 180 | }) |
183 | .then([this, ridList] (const KDAV::DavItem &item) { | 181 | .then([this, ridList] (const KDAV2::DavItem &item) { |
184 | const auto rid = getRid(item); | 182 | const auto rid = getRid(item); |
185 | syncStore().writeValue(rid + "_etag", item.etag().toLatin1()); | 183 | syncStore().writeValue(rid + "_etag", item.etag().toLatin1()); |
186 | ridList->append(rid); | 184 | ridList->append(rid); |
@@ -227,24 +225,19 @@ KAsync::Job<QByteArray> replay(const ApplicationDomain::Contact &contact, Sink:: | |||
227 | } | 225 | } |
228 | 226 | ||
229 | public: | 227 | public: |
230 | KDAV::DavUrl mResourceUrl; | 228 | KDAV2::DavUrl mResourceUrl; |
231 | }; | 229 | }; |
232 | 230 | ||
233 | 231 | ||
234 | DavResource::DavResource(const Sink::ResourceContext &resourceContext) | 232 | DavResource::DavResource(const Sink::ResourceContext &resourceContext) |
235 | : Sink::GenericResource(resourceContext) | 233 | : Sink::GenericResource(resourceContext) |
236 | { | 234 | { |
237 | /* | ||
238 | * Fork KIO slaves (used in kdav), instead of starting them via klauncher. | ||
239 | * Otherwise we have yet another runtime dependency that will i.e. not work in the docker container. | ||
240 | */ | ||
241 | qputenv("KDE_FORK_SLAVES", "TRUE"); | ||
242 | auto config = ResourceConfig::getConfiguration(resourceContext.instanceId()); | 235 | auto config = ResourceConfig::getConfiguration(resourceContext.instanceId()); |
243 | auto resourceUrl = QUrl::fromUserInput(config.value("server").toString()); | 236 | auto resourceUrl = QUrl::fromUserInput(config.value("server").toString()); |
244 | resourceUrl.setUserName(config.value("username").toString()); | 237 | resourceUrl.setUserName(config.value("username").toString()); |
245 | resourceUrl.setPassword(config.value("password").toString()); | 238 | resourceUrl.setPassword(config.value("password").toString()); |
246 | 239 | ||
247 | mResourceUrl = KDAV::DavUrl(resourceUrl, KDAV::CardDav); | 240 | mResourceUrl = KDAV2::DavUrl(resourceUrl, KDAV2::CardDav); |
248 | 241 | ||
249 | auto synchronizer = QSharedPointer<ContactSynchronizer>::create(resourceContext); | 242 | auto synchronizer = QSharedPointer<ContactSynchronizer>::create(resourceContext); |
250 | synchronizer->mResourceUrl = mResourceUrl; | 243 | synchronizer->mResourceUrl = mResourceUrl; |
@@ -271,7 +264,7 @@ Sink::Resource *DavResourceFactory::createResource(const ResourceContext &contex | |||
271 | void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory) | 264 | void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory) |
272 | { | 265 | { |
273 | factory.registerFacade<ApplicationDomain::Contact, DefaultFacade<ApplicationDomain::Contact>>(name); | 266 | factory.registerFacade<ApplicationDomain::Contact, DefaultFacade<ApplicationDomain::Contact>>(name); |
274 | factory.registerFacade<ApplicationDomain::Addressbook, DefaultFacade<ApplicationDomain::Contact>>(name); | 267 | factory.registerFacade<ApplicationDomain::Addressbook, DefaultFacade<ApplicationDomain::Addressbook>>(name); |
275 | } | 268 | } |
276 | 269 | ||
277 | void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry ®istry) | 270 | void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry ®istry) |
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 @@ | |||
21 | 21 | ||
22 | #include "common/genericresource.h" | 22 | #include "common/genericresource.h" |
23 | 23 | ||
24 | #include <KDAV/DavUrl> | 24 | #include <KDAV2/DavUrl> |
25 | #include <KAsync/Async> | 25 | #include <KAsync/Async> |
26 | 26 | ||
27 | #include <flatbuffers/flatbuffers.h> | 27 | #include <flatbuffers/flatbuffers.h> |
@@ -48,7 +48,7 @@ public: | |||
48 | private: | 48 | private: |
49 | QStringList listAvailableFolders(); | 49 | QStringList listAvailableFolders(); |
50 | 50 | ||
51 | KDAV::DavUrl mResourceUrl; | 51 | KDAV2::DavUrl mResourceUrl; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | class DavResourceFactory : public Sink::ResourceFactory | 54 | class DavResourceFactory : public Sink::ResourceFactory |
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; | |||
28 | DummyEventAdaptorFactory::DummyEventAdaptorFactory() | 28 | DummyEventAdaptorFactory::DummyEventAdaptorFactory() |
29 | : DomainTypeAdaptorFactory() | 29 | : DomainTypeAdaptorFactory() |
30 | { | 30 | { |
31 | //TODO turn this into initializeReadPropertyMapper as well? | ||
32 | mResourceMapper->addMapping<Sink::ApplicationDomain::Event::Summary, DummyEvent>(&DummyEvent::summary); | ||
33 | mResourceWriteMapper->addMapping<Sink::ApplicationDomain::Event::Summary>(&DummyEventBuilder::add_summary); | ||
34 | } | 31 | } |
35 | 32 | ||
36 | DummyMailAdaptorFactory::DummyMailAdaptorFactory() | 33 | 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 @@ | |||
22 | #include "event_generated.h" | 22 | #include "event_generated.h" |
23 | #include "mail_generated.h" | 23 | #include "mail_generated.h" |
24 | #include "folder_generated.h" | 24 | #include "folder_generated.h" |
25 | #include "dummy_generated.h" | ||
26 | #include "dummycalendar_generated.h" | ||
27 | #include "entity_generated.h" | 25 | #include "entity_generated.h" |
28 | 26 | ||
29 | class DummyEventAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Event, DummyCalendar::DummyEvent, DummyCalendar::DummyEventBuilder> | 27 | class DummyEventAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Event> |
30 | { | 28 | { |
31 | public: | 29 | public: |
32 | DummyEventAdaptorFactory(); | 30 | DummyEventAdaptorFactory(); |
33 | virtual ~DummyEventAdaptorFactory() {}; | 31 | virtual ~DummyEventAdaptorFactory() {}; |
34 | }; | 32 | }; |
35 | 33 | ||
36 | class DummyMailAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail, Sink::ApplicationDomain::Buffer::Dummy, Sink::ApplicationDomain::Buffer::DummyBuilder> | 34 | class DummyMailAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail> |
37 | { | 35 | { |
38 | public: | 36 | public: |
39 | DummyMailAdaptorFactory(); | 37 | DummyMailAdaptorFactory(); |
40 | virtual ~DummyMailAdaptorFactory() {}; | 38 | virtual ~DummyMailAdaptorFactory() {}; |
41 | }; | 39 | }; |
42 | 40 | ||
43 | class DummyFolderAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Folder, Sink::ApplicationDomain::Buffer::Dummy, Sink::ApplicationDomain::Buffer::DummyBuilder> | 41 | class DummyFolderAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Folder> |
44 | { | 42 | { |
45 | public: | 43 | public: |
46 | DummyFolderAdaptorFactory(); | 44 | DummyFolderAdaptorFactory(); |
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 @@ | |||
42 | #define ENTITY_TYPE_MAIL "mail" | 42 | #define ENTITY_TYPE_MAIL "mail" |
43 | #define ENTITY_TYPE_FOLDER "folder" | 43 | #define ENTITY_TYPE_FOLDER "folder" |
44 | 44 | ||
45 | SINK_DEBUG_AREA("dummyresource") | ||
46 | |||
47 | using namespace Sink; | 45 | using namespace Sink; |
48 | 46 | ||
49 | class DummySynchronizer : public Sink::Synchronizer { | 47 | class DummySynchronizer : public Sink::Synchronizer { |
diff --git a/examples/imapresource/CMakeLists.txt b/examples/imapresource/CMakeLists.txt index 46a8b08..5d2d38b 100644 --- a/examples/imapresource/CMakeLists.txt +++ b/examples/imapresource/CMakeLists.txt | |||
@@ -4,7 +4,7 @@ add_definitions(-DQT_PLUGIN) | |||
4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) | 4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) |
5 | 5 | ||
6 | find_package(KF5 COMPONENTS REQUIRED Mime) | 6 | find_package(KF5 COMPONENTS REQUIRED Mime) |
7 | find_package(KIMAP2 0.0.1 REQUIRED) | 7 | find_package(KIMAP2 0.2 REQUIRED) |
8 | 8 | ||
9 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) | 9 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) |
10 | 10 | ||
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 0579dae..81c808b 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp | |||
@@ -46,8 +46,6 @@ | |||
46 | #define ENTITY_TYPE_MAIL "mail" | 46 | #define ENTITY_TYPE_MAIL "mail" |
47 | #define ENTITY_TYPE_FOLDER "folder" | 47 | #define ENTITY_TYPE_FOLDER "folder" |
48 | 48 | ||
49 | SINK_DEBUG_AREA("imapresource") | ||
50 | |||
51 | Q_DECLARE_METATYPE(QSharedPointer<Imap::ImapServerProxy>) | 49 | Q_DECLARE_METATYPE(QSharedPointer<Imap::ImapServerProxy>) |
52 | 50 | ||
53 | using namespace Imap; | 51 | using namespace Imap; |
@@ -87,6 +85,25 @@ static QByteArray parentRid(const Imap::Folder &folder) | |||
87 | return folder.parentPath().toUtf8(); | 85 | return folder.parentPath().toUtf8(); |
88 | } | 86 | } |
89 | 87 | ||
88 | static QByteArray getSpecialPurposeType(const QByteArrayList &flags) | ||
89 | { | ||
90 | if (Imap::flagsContain(Imap::FolderFlags::Trash, flags)) { | ||
91 | return ApplicationDomain::SpecialPurpose::Mail::trash; | ||
92 | } | ||
93 | if (Imap::flagsContain(Imap::FolderFlags::Drafts, flags)) { | ||
94 | return ApplicationDomain::SpecialPurpose::Mail::drafts; | ||
95 | } | ||
96 | if (Imap::flagsContain(Imap::FolderFlags::Sent, flags)) { | ||
97 | return ApplicationDomain::SpecialPurpose::Mail::sent; | ||
98 | } | ||
99 | return {}; | ||
100 | } | ||
101 | |||
102 | static bool hasSpecialPurposeFlag(const QByteArrayList &flags) | ||
103 | { | ||
104 | return !getSpecialPurposeType(flags).isEmpty(); | ||
105 | } | ||
106 | |||
90 | 107 | ||
91 | class ImapSynchronizer : public Sink::Synchronizer { | 108 | class ImapSynchronizer : public Sink::Synchronizer { |
92 | Q_OBJECT | 109 | Q_OBJECT |
@@ -100,22 +117,29 @@ public: | |||
100 | QByteArray createFolder(const Imap::Folder &f) | 117 | QByteArray createFolder(const Imap::Folder &f) |
101 | { | 118 | { |
102 | const auto parentFolderRid = parentRid(f); | 119 | const auto parentFolderRid = parentRid(f); |
103 | SinkTraceCtx(mLogCtx) << "Creating folder: " << f.name() << parentFolderRid; | 120 | bool isToplevel = parentFolderRid.isEmpty(); |
121 | |||
122 | SinkTraceCtx(mLogCtx) << "Creating folder: " << f.name() << parentFolderRid << f.flags; | ||
104 | 123 | ||
105 | const auto remoteId = folderRid(f); | 124 | const auto remoteId = folderRid(f); |
106 | Sink::ApplicationDomain::Folder folder; | 125 | Sink::ApplicationDomain::Folder folder; |
107 | folder.setName(f.name()); | 126 | folder.setName(f.name()); |
108 | folder.setIcon("folder"); | 127 | folder.setIcon("folder"); |
109 | folder.setEnabled(f.subscribed); | 128 | folder.setEnabled(f.subscribed); |
110 | QHash<QByteArray, Query::Comparator> mergeCriteria; | 129 | auto specialPurpose = [&] { |
111 | if (SpecialPurpose::isSpecialPurposeFolderName(f.name()) && parentFolderRid.isEmpty()) { | 130 | if (hasSpecialPurposeFlag(f.flags)) { |
112 | auto type = SpecialPurpose::getSpecialPurposeType(f.name()); | 131 | return getSpecialPurposeType(f.flags); |
113 | folder.setSpecialPurpose(QByteArrayList() << type); | 132 | } else if (SpecialPurpose::isSpecialPurposeFolderName(f.name()) && isToplevel) { |
114 | mergeCriteria.insert(ApplicationDomain::Folder::SpecialPurpose::name, Query::Comparator(type, Query::Comparator::Contains)); | 133 | return SpecialPurpose::getSpecialPurposeType(f.name()); |
134 | } | ||
135 | return QByteArray{}; | ||
136 | }(); | ||
137 | if (!specialPurpose.isEmpty()) { | ||
138 | folder.setSpecialPurpose(QByteArrayList() << specialPurpose); | ||
115 | } | 139 | } |
116 | 140 | ||
117 | if (!parentFolderRid.isEmpty()) { | 141 | if (!isToplevel) { |
118 | folder.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, parentFolderRid)); | 142 | folder.setParent(syncStore().resolveRemoteId(ApplicationDomain::Folder::name, parentFolderRid)); |
119 | } | 143 | } |
120 | createOrModify(ApplicationDomain::getTypeName<ApplicationDomain::Folder>(), remoteId, folder); | 144 | createOrModify(ApplicationDomain::getTypeName<ApplicationDomain::Folder>(), remoteId, folder); |
121 | return remoteId; | 145 | return remoteId; |
@@ -160,22 +184,20 @@ public: | |||
160 | return flags; | 184 | return flags; |
161 | } | 185 | } |
162 | 186 | ||
163 | void synchronizeMails(const QByteArray &folderRid, const Message &message) | 187 | void synchronizeMails(const QByteArray &folderRid, const QByteArray &folderLocalId, const Message &message) |
164 | { | 188 | { |
165 | auto time = QSharedPointer<QTime>::create(); | 189 | auto time = QSharedPointer<QTime>::create(); |
166 | time->start(); | 190 | time->start(); |
167 | SinkTraceCtx(mLogCtx) << "Importing new mail." << folderRid; | 191 | SinkTraceCtx(mLogCtx) << "Importing new mail." << folderRid; |
168 | 192 | ||
169 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRid); | ||
170 | |||
171 | const auto remoteId = assembleMailRid(folderLocalId, message.uid); | 193 | const auto remoteId = assembleMailRid(folderLocalId, message.uid); |
172 | 194 | ||
173 | Q_ASSERT(message.msg); | 195 | Q_ASSERT(message.msg); |
174 | SinkTraceCtx(mLogCtx) << "Found a mail " << remoteId << message.msg->subject(true)->asUnicodeString() << message.flags; | 196 | SinkTraceCtx(mLogCtx) << "Found a mail " << remoteId << message.flags; |
175 | 197 | ||
176 | auto mail = Sink::ApplicationDomain::Mail::create(mResourceInstanceIdentifier); | 198 | auto mail = Sink::ApplicationDomain::Mail::create(mResourceInstanceIdentifier); |
177 | mail.setFolder(folderLocalId); | 199 | mail.setFolder(folderLocalId); |
178 | mail.setMimeMessage(message.msg->encodedContent()); | 200 | mail.setMimeMessage(message.msg->encodedContent(true)); |
179 | mail.setExtractedFullPayloadAvailable(message.fullPayload); | 201 | mail.setExtractedFullPayloadAvailable(message.fullPayload); |
180 | setFlags(mail, message.flags); | 202 | setFlags(mail, message.flags); |
181 | 203 | ||
@@ -291,14 +313,15 @@ public: | |||
291 | SinkTraceCtx(mLogCtx) << "Uids to fetch: " << filteredAndSorted; | 313 | SinkTraceCtx(mLogCtx) << "Uids to fetch: " << filteredAndSorted; |
292 | 314 | ||
293 | bool headersOnly = false; | 315 | bool headersOnly = false; |
316 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); | ||
294 | return imap->fetchMessages(folder, filteredAndSorted, headersOnly, [=](const Message &m) { | 317 | return imap->fetchMessages(folder, filteredAndSorted, headersOnly, [=](const Message &m) { |
295 | if (*maxUid < m.uid) { | 318 | if (*maxUid < m.uid) { |
296 | *maxUid = m.uid; | 319 | *maxUid = m.uid; |
297 | } | 320 | } |
298 | synchronizeMails(folderRemoteId, m); | 321 | synchronizeMails(folderRemoteId, folderLocalId, m); |
299 | }, | 322 | }, |
300 | [this, maxUid, folder](int progress, int total) { | 323 | [=](int progress, int total) { |
301 | SinkLog() << "Progress: " << progress << " out of " << total; | 324 | reportProgress(progress, total, QByteArrayList{} << folderLocalId); |
302 | //commit every 10 messages | 325 | //commit every 10 messages |
303 | if ((progress % 10) == 0) { | 326 | if ((progress % 10) == 0) { |
304 | commit(); | 327 | commit(); |
@@ -335,11 +358,12 @@ public: | |||
335 | SinkLogCtx(mLogCtx) << "Fetching headers for: " << toFetch; | 358 | SinkLogCtx(mLogCtx) << "Fetching headers for: " << toFetch; |
336 | 359 | ||
337 | bool headersOnly = true; | 360 | bool headersOnly = true; |
361 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); | ||
338 | return imap->fetchMessages(folder, toFetch, headersOnly, [=](const Message &m) { | 362 | return imap->fetchMessages(folder, toFetch, headersOnly, [=](const Message &m) { |
339 | synchronizeMails(folderRemoteId, m); | 363 | synchronizeMails(folderRemoteId, folderLocalId, m); |
340 | }, | 364 | }, |
341 | [=](int progress, int total) { | 365 | [=](int progress, int total) { |
342 | SinkLogCtx(mLogCtx) << "Progress: " << progress << " out of " << total; | 366 | reportProgress(progress, total, QByteArrayList{} << folderLocalId); |
343 | //commit every 100 messages | 367 | //commit every 100 messages |
344 | if ((progress % 100) == 0) { | 368 | if ((progress % 100) == 0) { |
345 | commit(); | 369 | commit(); |
@@ -466,7 +490,7 @@ public: | |||
466 | //Otherwise fetch full payload for daterange | 490 | //Otherwise fetch full payload for daterange |
467 | auto folderList = QSharedPointer<QVector<Folder>>::create(); | 491 | auto folderList = QSharedPointer<QVector<Folder>>::create(); |
468 | return imap->fetchFolders([folderList](const Folder &folder) { | 492 | return imap->fetchFolders([folderList](const Folder &folder) { |
469 | if (!folder.noselect) { | 493 | if (!folder.noselect && folder.subscribed) { |
470 | *folderList << folder; | 494 | *folderList << folder; |
471 | } | 495 | } |
472 | }) | 496 | }) |
@@ -480,12 +504,16 @@ public: | |||
480 | KAsync::Error getError(const KAsync::Error &error) | 504 | KAsync::Error getError(const KAsync::Error &error) |
481 | { | 505 | { |
482 | if (error) { | 506 | if (error) { |
483 | if (error.errorCode == Imap::CouldNotConnectError) { | 507 | switch(error.errorCode) { |
484 | return {ApplicationDomain::ConnectionError, error.errorMessage}; | 508 | case Imap::CouldNotConnectError: |
485 | } else if (error.errorCode == Imap::SslHandshakeError) { | 509 | return {ApplicationDomain::ConnectionError, error.errorMessage}; |
486 | return {ApplicationDomain::LoginError, error.errorMessage}; | 510 | case Imap::SslHandshakeError: |
511 | return {ApplicationDomain::LoginError, error.errorMessage}; | ||
512 | case Imap::HostNotFoundError: | ||
513 | return {ApplicationDomain::NoServerError, error.errorMessage}; | ||
514 | default: | ||
515 | return {ApplicationDomain::UnknownError, error.errorMessage}; | ||
487 | } | 516 | } |
488 | return {ApplicationDomain::UnknownError, error.errorMessage}; | ||
489 | } | 517 | } |
490 | return {}; | 518 | return {}; |
491 | } | 519 | } |
@@ -539,11 +567,12 @@ public: | |||
539 | } | 567 | } |
540 | SinkLog() << "Fetching messages: " << toFetch << folderRemoteId; | 568 | SinkLog() << "Fetching messages: " << toFetch << folderRemoteId; |
541 | bool headersOnly = false; | 569 | bool headersOnly = false; |
570 | const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRemoteId); | ||
542 | return imap->fetchMessages(Folder{folderRemoteId}, toFetch, headersOnly, [=](const Message &m) { | 571 | return imap->fetchMessages(Folder{folderRemoteId}, toFetch, headersOnly, [=](const Message &m) { |
543 | synchronizeMails(folderRemoteId, m); | 572 | synchronizeMails(folderRemoteId, folderLocalId, m); |
544 | }, | 573 | }, |
545 | [=](int progress, int total) { | 574 | [=](int progress, int total) { |
546 | reportProgress(progress, total); | 575 | reportProgress(progress, total, QByteArrayList{} << folderLocalId); |
547 | //commit every 100 messages | 576 | //commit every 100 messages |
548 | if ((progress % 100) == 0) { | 577 | if ((progress % 100) == 0) { |
549 | commit(); | 578 | commit(); |
@@ -554,10 +583,7 @@ public: | |||
554 | bool syncHeaders = query.hasFilter<ApplicationDomain::Mail::Folder>(); | 583 | bool syncHeaders = query.hasFilter<ApplicationDomain::Mail::Folder>(); |
555 | //FIXME If we were able to to flush in between we could just query the local store for the folder list. | 584 | //FIXME If we were able to to flush in between we could just query the local store for the folder list. |
556 | return getFolderList(imap, query) | 585 | return getFolderList(imap, query) |
557 | .then([=] (const QVector<Folder> &folders) { | 586 | .serialEach([=](const Folder &folder) { |
558 | //Synchronize folders | ||
559 | return KAsync::value(folders) | ||
560 | .serialEach<void>([=](const Folder &folder) { | ||
561 | SinkLog() << "Syncing folder " << folder.path(); | 587 | SinkLog() << "Syncing folder " << folder.path(); |
562 | //Emit notification that the folder is being synced. | 588 | //Emit notification that the folder is being synced. |
563 | //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. | 589 | //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. |
@@ -572,7 +598,6 @@ public: | |||
572 | SinkWarning() << "Failed to sync folder: " << folder.path() << "Error: " << error.errorMessage; | 598 | SinkWarning() << "Failed to sync folder: " << folder.path() << "Error: " << error.errorMessage; |
573 | }); | 599 | }); |
574 | }); | 600 | }); |
575 | }); | ||
576 | } | 601 | } |
577 | }) | 602 | }) |
578 | .then([=] (const KAsync::Error &error) { | 603 | .then([=] (const KAsync::Error &error) { |
@@ -582,6 +607,15 @@ public: | |||
582 | } | 607 | } |
583 | return KAsync::error<void>("Nothing to do"); | 608 | return KAsync::error<void>("Nothing to do"); |
584 | } | 609 | } |
610 | static QByteArray ensureCRLF(const QByteArray &data) { | ||
611 | auto index = data.indexOf('\n'); | ||
612 | if (index > 0 && data.at(index - 1) == '\r') { //First line is LF-only terminated | ||
613 | //Convert back and forth in case there's a mix. We don't want to expand CRLF into CRCRLF. | ||
614 | return KMime::LFtoCRLF(KMime::CRLFtoLF(data)); | ||
615 | } else { | ||
616 | return data; | ||
617 | } | ||
618 | } | ||
585 | 619 | ||
586 | KAsync::Job<QByteArray> replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE | 620 | KAsync::Job<QByteArray> replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE |
587 | { | 621 | { |
@@ -589,10 +623,10 @@ public: | |||
589 | auto login = imap->login(mUser, mPassword); | 623 | auto login = imap->login(mUser, mPassword); |
590 | KAsync::Job<QByteArray> job = KAsync::null<QByteArray>(); | 624 | KAsync::Job<QByteArray> job = KAsync::null<QByteArray>(); |
591 | if (operation == Sink::Operation_Creation) { | 625 | if (operation == Sink::Operation_Creation) { |
592 | QString mailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); | 626 | const QString mailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); |
593 | QByteArray content = KMime::LFtoCRLF(mail.getMimeMessage()); | 627 | const auto content = ensureCRLF(mail.getMimeMessage()); |
594 | auto flags = getFlags(mail); | 628 | const auto flags = getFlags(mail); |
595 | QDateTime internalDate = mail.getDate(); | 629 | const QDateTime internalDate = mail.getDate(); |
596 | job = login.then(imap->append(mailbox, content, flags, internalDate)) | 630 | job = login.then(imap->append(mailbox, content, flags, internalDate)) |
597 | .addToContext(imap) | 631 | .addToContext(imap) |
598 | .then([mail](qint64 uid) { | 632 | .then([mail](qint64 uid) { |
@@ -623,11 +657,11 @@ public: | |||
623 | const bool messageMoved = changedProperties.contains(ApplicationDomain::Mail::Folder::name); | 657 | const bool messageMoved = changedProperties.contains(ApplicationDomain::Mail::Folder::name); |
624 | const bool messageChanged = changedProperties.contains(ApplicationDomain::Mail::MimeMessage::name); | 658 | const bool messageChanged = changedProperties.contains(ApplicationDomain::Mail::MimeMessage::name); |
625 | if (messageChanged || messageMoved) { | 659 | if (messageChanged || messageMoved) { |
626 | SinkTrace() << "Replacing message."; | ||
627 | const auto folderId = folderIdFromMailRid(oldRemoteId); | 660 | const auto folderId = folderIdFromMailRid(oldRemoteId); |
628 | const QString oldMailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, folderId); | 661 | const QString oldMailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, folderId); |
629 | QByteArray content = KMime::LFtoCRLF(mail.getMimeMessage()); | 662 | const auto content = ensureCRLF(mail.getMimeMessage()); |
630 | QDateTime internalDate = mail.getDate(); | 663 | const QDateTime internalDate = mail.getDate(); |
664 | SinkTrace() << "Replacing message. Old mailbox: " << oldMailbox << "New mailbox: " << mailbox << "Flags: " << flags << "Content: " << content; | ||
631 | KIMAP2::ImapSet set; | 665 | KIMAP2::ImapSet set; |
632 | set.add(uid); | 666 | set.add(uid); |
633 | job = login.then(imap->append(mailbox, content, flags, internalDate)) | 667 | job = login.then(imap->append(mailbox, content, flags, internalDate)) |
@@ -655,7 +689,7 @@ public: | |||
655 | if (error) { | 689 | if (error) { |
656 | SinkWarning() << "Error during changereplay: " << error.errorMessage; | 690 | SinkWarning() << "Error during changereplay: " << error.errorMessage; |
657 | return imap->logout() | 691 | return imap->logout() |
658 | .then(KAsync::error<QByteArray>(error)); | 692 | .then(KAsync::error<QByteArray>(getError(error))); |
659 | } | 693 | } |
660 | return imap->logout() | 694 | return imap->logout() |
661 | .then(KAsync::value(remoteId)); | 695 | .then(KAsync::value(remoteId)); |
@@ -664,6 +698,12 @@ public: | |||
664 | 698 | ||
665 | KAsync::Job<QByteArray> replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE | 699 | KAsync::Job<QByteArray> replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE |
666 | { | 700 | { |
701 | if (operation != Sink::Operation_Creation) { | ||
702 | if(oldRemoteId.isEmpty()) { | ||
703 | Q_ASSERT(false); | ||
704 | return KAsync::error<QByteArray>("Tried to replay modification without old remoteId."); | ||
705 | } | ||
706 | } | ||
667 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort, &mSessionCache); | 707 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort, &mSessionCache); |
668 | auto login = imap->login(mUser, mPassword); | 708 | auto login = imap->login(mUser, mPassword); |
669 | if (operation == Sink::Operation_Creation) { | 709 | if (operation == Sink::Operation_Creation) { |
@@ -793,6 +833,10 @@ protected: | |||
793 | .then(imap->select(folderRemoteId)) | 833 | .then(imap->select(folderRemoteId)) |
794 | .then([](Imap::SelectResult){}) | 834 | .then([](Imap::SelectResult){}) |
795 | .then(imap->fetch(set, scope, [imap, messageByUid](const Imap::Message &message) { | 835 | .then(imap->fetch(set, scope, [imap, messageByUid](const Imap::Message &message) { |
836 | //We avoid parsing normally, so we have to do it explicitly here | ||
837 | if (message.msg) { | ||
838 | message.msg->parse(); | ||
839 | } | ||
796 | messageByUid->insert(message.uid, message); | 840 | messageByUid->insert(message.uid, message); |
797 | })); | 841 | })); |
798 | 842 | ||
diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index 0cc43b8..538105c 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp | |||
@@ -37,8 +37,6 @@ | |||
37 | #include "log.h" | 37 | #include "log.h" |
38 | #include "test.h" | 38 | #include "test.h" |
39 | 39 | ||
40 | SINK_DEBUG_AREA("imapserverproxy") | ||
41 | |||
42 | using namespace Imap; | 40 | using namespace Imap; |
43 | 41 | ||
44 | const char* Imap::Flags::Seen = "\\Seen"; | 42 | const char* Imap::Flags::Seen = "\\Seen"; |
@@ -57,6 +55,7 @@ const char* Imap::FolderFlags::Trash = "\\Trash"; | |||
57 | const char* Imap::FolderFlags::Archive = "\\Archive"; | 55 | const char* Imap::FolderFlags::Archive = "\\Archive"; |
58 | const char* Imap::FolderFlags::Junk = "\\Junk"; | 56 | const char* Imap::FolderFlags::Junk = "\\Junk"; |
59 | const char* Imap::FolderFlags::Flagged = "\\Flagged"; | 57 | const char* Imap::FolderFlags::Flagged = "\\Flagged"; |
58 | const char* Imap::FolderFlags::Drafts = "\\Drafts"; | ||
60 | 59 | ||
61 | const char* Imap::Capabilities::Namespace = "NAMESPACE"; | 60 | const char* Imap::Capabilities::Namespace = "NAMESPACE"; |
62 | const char* Imap::Capabilities::Uidplus = "UIDPLUS"; | 61 | const char* Imap::Capabilities::Uidplus = "UIDPLUS"; |
@@ -98,17 +97,25 @@ static KAsync::Job<void> runJob(KJob *job) | |||
98 | }); | 97 | }); |
99 | } | 98 | } |
100 | 99 | ||
101 | ImapServerProxy::ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache) : mSession(new KIMAP2::Session(serverUrl, qint16(port))), mSessionCache(sessionCache) | 100 | KIMAP2::Session *createNewSession(const QString &serverUrl, int port) |
102 | { | 101 | { |
103 | QObject::connect(mSession, &KIMAP2::Session::sslErrors, [this](const QList<QSslError> &errors) { | 102 | auto newSession = new KIMAP2::Session(serverUrl, qint16(port)); |
103 | if (Sink::Test::testModeEnabled()) { | ||
104 | newSession->setTimeout(1); | ||
105 | } else { | ||
106 | newSession->setTimeout(40); | ||
107 | } | ||
108 | QObject::connect(newSession, &KIMAP2::Session::sslErrors, [=](const QList<QSslError> &errors) { | ||
104 | SinkLog() << "Received ssl error: " << errors; | 109 | SinkLog() << "Received ssl error: " << errors; |
105 | mSession->ignoreErrors(errors); | 110 | newSession->ignoreErrors(errors); |
106 | }); | 111 | }); |
112 | return newSession; | ||
113 | } | ||
107 | 114 | ||
108 | if (Sink::Test::testModeEnabled()) { | 115 | ImapServerProxy::ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache) : mSessionCache(sessionCache), mSession(nullptr) |
109 | mSession->setTimeout(1); | 116 | { |
110 | } else { | 117 | if (!mSessionCache || mSessionCache->isEmpty()) { |
111 | mSession->setTimeout(40); | 118 | mSession = createNewSession(serverUrl, port); |
112 | } | 119 | } |
113 | } | 120 | } |
114 | 121 | ||
@@ -161,12 +168,16 @@ KAsync::Job<void> ImapServerProxy::login(const QString &username, const QString | |||
161 | // SinkTrace() << "Found user namespaces: " << mNamespaces.user; | 168 | // SinkTrace() << "Found user namespaces: " << mNamespaces.user; |
162 | }).then([=] (const KAsync::Error &error) { | 169 | }).then([=] (const KAsync::Error &error) { |
163 | if (error) { | 170 | if (error) { |
164 | if (error.errorCode == KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT) { | 171 | switch (error.errorCode) { |
172 | case KIMAP2::LoginJob::ErrorCode::ERR_HOST_NOT_FOUND: | ||
173 | return KAsync::error(HostNotFoundError, "Host not found: " + error.errorMessage); | ||
174 | case KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT: | ||
165 | return KAsync::error(CouldNotConnectError, "Failed to connect: " + error.errorMessage); | 175 | return KAsync::error(CouldNotConnectError, "Failed to connect: " + error.errorMessage); |
166 | } else if (error.errorCode == KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED) { | 176 | case KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED: |
167 | return KAsync::error(SslHandshakeError, "Ssl handshake failed: " + error.errorMessage); | 177 | return KAsync::error(SslHandshakeError, "Ssl handshake failed: " + error.errorMessage); |
178 | default: | ||
179 | return KAsync::error(error); | ||
168 | } | 180 | } |
169 | return KAsync::error(error); | ||
170 | } | 181 | } |
171 | return KAsync::null(); | 182 | return KAsync::null(); |
172 | }); | 183 | }); |
@@ -188,6 +199,12 @@ KAsync::Job<void> ImapServerProxy::logout() | |||
188 | } | 199 | } |
189 | } | 200 | } |
190 | 201 | ||
202 | bool ImapServerProxy::isGmail() const | ||
203 | { | ||
204 | //Magic capability that only gmail has | ||
205 | return mCapabilities.contains("X-GM-EXT-1"); | ||
206 | } | ||
207 | |||
191 | KAsync::Job<SelectResult> ImapServerProxy::select(const QString &mailbox) | 208 | KAsync::Job<SelectResult> ImapServerProxy::select(const QString &mailbox) |
192 | { | 209 | { |
193 | auto select = new KIMAP2::SelectJob(mSession); | 210 | auto select = new KIMAP2::SelectJob(mSession); |
@@ -297,6 +314,7 @@ KAsync::Job<void> ImapServerProxy::fetch(const KIMAP2::ImapSet &set, KIMAP2::Fet | |||
297 | fetch->setSequenceSet(set); | 314 | fetch->setSequenceSet(set); |
298 | fetch->setUidBased(true); | 315 | fetch->setUidBased(true); |
299 | fetch->setScope(scope); | 316 | fetch->setScope(scope); |
317 | fetch->setAvoidParsing(true); | ||
300 | QObject::connect(fetch, &KIMAP2::FetchJob::resultReceived, callback); | 318 | QObject::connect(fetch, &KIMAP2::FetchJob::resultReceived, callback); |
301 | return runJob(fetch); | 319 | return runJob(fetch); |
302 | } | 320 | } |
@@ -437,18 +455,60 @@ QString ImapServerProxy::getNamespace(const QString &name) | |||
437 | return ns.name; | 455 | return ns.name; |
438 | } | 456 | } |
439 | 457 | ||
458 | static bool caseInsensitiveContains(const QByteArray &f, const QByteArrayList &list) { | ||
459 | return list.contains(f) || list.contains(f.toLower()); | ||
460 | } | ||
461 | |||
462 | bool Imap::flagsContain(const QByteArray &f, const QByteArrayList &flags) | ||
463 | { | ||
464 | return caseInsensitiveContains(f, flags); | ||
465 | } | ||
466 | |||
467 | static void reportFolder(const Folder &f, QSharedPointer<QSet<QString>> reportedList, std::function<void(const Folder &)> callback) { | ||
468 | if (!reportedList->contains(f.path())) { | ||
469 | reportedList->insert(f.path()); | ||
470 | auto c = f; | ||
471 | c.noselect = true; | ||
472 | callback(c); | ||
473 | if (!f.parentPath().isEmpty()){ | ||
474 | reportFolder(f.parentFolder(), reportedList, callback); | ||
475 | } | ||
476 | } | ||
477 | } | ||
478 | |||
440 | KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const Folder &)> callback) | 479 | KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const Folder &)> callback) |
441 | { | 480 | { |
442 | SinkTrace() << "Fetching folders"; | 481 | SinkTrace() << "Fetching folders"; |
443 | auto subscribedList = QSharedPointer<QSet<QString>>::create() ; | 482 | auto subscribedList = QSharedPointer<QSet<QString>>::create() ; |
483 | auto reportedList = QSharedPointer<QSet<QString>>::create() ; | ||
444 | return list(KIMAP2::ListJob::NoOption, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &){ | 484 | return list(KIMAP2::ListJob::NoOption, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &){ |
445 | *subscribedList << mailbox.name; | 485 | *subscribedList << mailbox.name; |
446 | }).then(list(KIMAP2::ListJob::IncludeUnsubscribed, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &flags) { | 486 | }).then(list(KIMAP2::ListJob::IncludeUnsubscribed, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &flags) { |
447 | bool noselect = flags.contains(QByteArray(FolderFlags::Noselect).toLower()) || flags.contains(QByteArray(FolderFlags::Noselect)); | 487 | bool noselect = caseInsensitiveContains(FolderFlags::Noselect, flags); |
448 | bool subscribed = subscribedList->contains(mailbox.name); | 488 | bool subscribed = subscribedList->contains(mailbox.name); |
489 | if (isGmail()) { | ||
490 | bool inbox = mailbox.name.toLower() == "inbox"; | ||
491 | bool sent = caseInsensitiveContains(FolderFlags::Sent, flags); | ||
492 | bool drafts = caseInsensitiveContains(FolderFlags::Drafts, flags); | ||
493 | bool trash = caseInsensitiveContains(FolderFlags::Trash, flags); | ||
494 | /** | ||
495 | * Because gmail duplicates messages all over the place we only support a few selected folders for now that should be mostly exclusive. | ||
496 | */ | ||
497 | if (!(inbox || sent || drafts || trash)) { | ||
498 | return; | ||
499 | } | ||
500 | } | ||
449 | SinkLog() << "Found mailbox: " << mailbox.name << flags << FolderFlags::Noselect << noselect << " sub: " << subscribed; | 501 | SinkLog() << "Found mailbox: " << mailbox.name << flags << FolderFlags::Noselect << noselect << " sub: " << subscribed; |
450 | auto ns = getNamespace(mailbox.name); | 502 | auto ns = getNamespace(mailbox.name); |
451 | callback(Folder{mailbox.name, ns, mailbox.separator, noselect, subscribed, flags}); | 503 | auto folder = Folder{mailbox.name, ns, mailbox.separator, noselect, subscribed, flags}; |
504 | |||
505 | //call callback for parents if that didn't already happen. | ||
506 | //This is necessary because we can have missing bits in the hierarchy in IMAP, but this will not work in sink because we'd end up with an incomplete tree. | ||
507 | if (!folder.parentPath().isEmpty() && !reportedList->contains(folder.parentPath())) { | ||
508 | reportFolder(folder.parentFolder(), reportedList, callback); | ||
509 | } | ||
510 | reportedList->insert(folder.path()); | ||
511 | callback(folder); | ||
452 | })); | 512 | })); |
453 | } | 513 | } |
454 | 514 | ||
diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h index 872f032..82f4f58 100644 --- a/examples/imapresource/imapserverproxy.h +++ b/examples/imapresource/imapserverproxy.h | |||
@@ -31,6 +31,7 @@ namespace Imap { | |||
31 | 31 | ||
32 | enum ErrorCode { | 32 | enum ErrorCode { |
33 | NoError, | 33 | NoError, |
34 | HostNotFoundError, | ||
34 | CouldNotConnectError, | 35 | CouldNotConnectError, |
35 | SslHandshakeError | 36 | SslHandshakeError |
36 | }; | 37 | }; |
@@ -60,6 +61,7 @@ namespace FolderFlags | |||
60 | extern const char* Junk; | 61 | extern const char* Junk; |
61 | extern const char* Flagged; | 62 | extern const char* Flagged; |
62 | extern const char* All; | 63 | extern const char* All; |
64 | extern const char* Drafts; | ||
63 | } | 65 | } |
64 | 66 | ||
65 | namespace Capabilities | 67 | namespace Capabilities |
@@ -78,6 +80,8 @@ struct Message { | |||
78 | bool fullPayload; | 80 | bool fullPayload; |
79 | }; | 81 | }; |
80 | 82 | ||
83 | bool flagsContain(const QByteArray &f, const QByteArrayList &flags); | ||
84 | |||
81 | struct Folder { | 85 | struct Folder { |
82 | Folder() = default; | 86 | Folder() = default; |
83 | Folder(const QString &path, const QString &ns, const QChar &separator, bool noselect_, bool subscribed_, const QByteArrayList &flags_) | 87 | Folder(const QString &path, const QString &ns, const QChar &separator, bool noselect_, bool subscribed_, const QByteArrayList &flags_) |
@@ -114,6 +118,15 @@ struct Folder { | |||
114 | return parentPath; | 118 | return parentPath; |
115 | } | 119 | } |
116 | 120 | ||
121 | Folder parentFolder() const | ||
122 | { | ||
123 | Folder parent; | ||
124 | parent.mPath = parentPath(); | ||
125 | parent.mNamespace = mNamespace; | ||
126 | parent.mSeparator = mSeparator; | ||
127 | return parent; | ||
128 | } | ||
129 | |||
117 | QString name() const | 130 | QString name() const |
118 | { | 131 | { |
119 | auto pathParts = mPath.split(mSeparator); | 132 | auto pathParts = mPath.split(mSeparator); |
@@ -218,18 +231,18 @@ public: | |||
218 | return session; | 231 | return session; |
219 | } | 232 | } |
220 | } | 233 | } |
221 | return CachedSession{}; | 234 | return {}; |
235 | } | ||
236 | |||
237 | bool isEmpty() const | ||
238 | { | ||
239 | return mSessions.isEmpty(); | ||
222 | } | 240 | } |
223 | private: | 241 | private: |
224 | QList<CachedSession> mSessions; | 242 | QList<CachedSession> mSessions; |
225 | }; | 243 | }; |
226 | 244 | ||
227 | class ImapServerProxy { | 245 | class ImapServerProxy { |
228 | KIMAP2::Session *mSession; | ||
229 | QStringList mCapabilities; | ||
230 | Namespaces mNamespaces; | ||
231 | |||
232 | |||
233 | public: | 246 | public: |
234 | ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache = nullptr); | 247 | ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache = nullptr); |
235 | 248 | ||
@@ -279,9 +292,14 @@ public: | |||
279 | KAsync::Job<QVector<qint64>> fetchUids(const Folder &folder); | 292 | KAsync::Job<QVector<qint64>> fetchUids(const Folder &folder); |
280 | 293 | ||
281 | private: | 294 | private: |
295 | bool isGmail() const; | ||
296 | |||
282 | QString getNamespace(const QString &name); | 297 | QString getNamespace(const QString &name); |
283 | QObject mGuard; | 298 | QObject mGuard; |
284 | SessionCache *mSessionCache; | 299 | SessionCache *mSessionCache; |
300 | KIMAP2::Session *mSession; | ||
301 | QStringList mCapabilities; | ||
302 | Namespaces mNamespaces; | ||
285 | }; | 303 | }; |
286 | 304 | ||
287 | } | 305 | } |
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 @@ | |||
31 | using namespace Sink; | 31 | using namespace Sink; |
32 | using namespace Sink::ApplicationDomain; | 32 | using namespace Sink::ApplicationDomain; |
33 | 33 | ||
34 | SINK_DEBUG_AREA("ImapMailSyncBenchmark") | ||
35 | |||
36 | /** | 34 | /** |
37 | * Test of complete system using the imap resource. | 35 | * Test of complete system using the imap resource. |
38 | * | 36 | * |
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 @@ | |||
12 | 12 | ||
13 | using namespace Imap; | 13 | using namespace Imap; |
14 | 14 | ||
15 | // SINK_DEBUG_AREA("imapserverproxytest") | ||
16 | |||
17 | /** | 15 | /** |
18 | */ | 16 | */ |
19 | class ImapServerProxyTest : public QObject | 17 | class ImapServerProxyTest : public QObject |
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 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | 2 | ||
3 | sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost | 3 | sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost |
4 | #Delete all mailboxes | ||
4 | sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost | 5 | sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost |
6 | #Create mailboxes | ||
5 | sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost | 7 | sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost |
6 | sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost | 8 | sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost |
7 | sudo echo "cm user.doe.Trash" | cyradm --auth PLAIN -u cyrus -w admin localhost | 9 | sudo echo "cm user.doe.Trash" | cyradm --auth PLAIN -u cyrus -w admin localhost |
10 | |||
11 | #Set acls so we can create in INBOX | ||
8 | sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost | 12 | sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost |
9 | 13 | ||
14 | #Subscribe to mailboxes | ||
15 | sudo echo "sub INBOX" | cyradm --auth PLAIN -u doe -w doe localhost | ||
16 | sudo echo "sub INBOX.test" | cyradm --auth PLAIN -u doe -w doe localhost | ||
17 | sudo echo "sub INBOX.Drafts" | cyradm --auth PLAIN -u doe -w doe localhost | ||
18 | sudo echo "sub INBOX.Trash" | cyradm --auth PLAIN -u doe -w doe localhost | ||
19 | |||
20 | #Create a bunch of test messages in the test folder | ||
10 | # for i in `seq 1 5000`; | 21 | # for i in `seq 1 5000`; |
11 | # do | 22 | # do |
12 | # # sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/$i. | 23 | # # sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/$i. |
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 @@ | |||
3 | sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost | 3 | sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost |
4 | sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost | 4 | sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost |
5 | sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost | 5 | sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost |
6 | sudo echo "subscribe INBOX.test" | cyradm --auth PLAIN -u doe -w doe localhost | ||
6 | sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost | 7 | sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost |
8 | sudo echo "subscribe INBOX.Drafts" | cyradm --auth PLAIN -u doe -w doe localhost | ||
7 | sudo echo "cm user.doe.Trash" | cyradm --auth PLAIN -u cyrus -w admin localhost | 9 | sudo echo "cm user.doe.Trash" | cyradm --auth PLAIN -u cyrus -w admin localhost |
10 | sudo echo "subscribe INBOX.Trash" | cyradm --auth PLAIN -u doe -w doe localhost | ||
8 | sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost | 11 | sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost |
9 | sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/1. | 12 | sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/1. |
10 | sudo chown cyrus:mail /var/spool/imap/d/user/doe/test/1. | 13 | sudo chown cyrus:mail /var/spool/imap/d/user/doe/test/1. |
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp index 40bab37..b406f63 100644 --- a/examples/maildirresource/maildirresource.cpp +++ b/examples/maildirresource/maildirresource.cpp | |||
@@ -43,8 +43,6 @@ | |||
43 | #define ENTITY_TYPE_MAIL "mail" | 43 | #define ENTITY_TYPE_MAIL "mail" |
44 | #define ENTITY_TYPE_FOLDER "folder" | 44 | #define ENTITY_TYPE_FOLDER "folder" |
45 | 45 | ||
46 | SINK_DEBUG_AREA("maildirresource") | ||
47 | |||
48 | using namespace Sink; | 46 | using namespace Sink; |
49 | 47 | ||
50 | static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) | 48 | static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) |
@@ -555,18 +553,20 @@ MaildirResource::MaildirResource(const Sink::ResourceContext &resourceContext) | |||
555 | setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << new FolderPreprocessor(mMaildirPath)); | 553 | setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << new FolderPreprocessor(mMaildirPath)); |
556 | 554 | ||
557 | KPIM::Maildir dir(mMaildirPath, true); | 555 | KPIM::Maildir dir(mMaildirPath, true); |
558 | SinkTrace() << "Started maildir resource for maildir: " << mMaildirPath; | 556 | if (dir.isValid(false)) { |
559 | { | 557 | { |
560 | auto draftsFolder = dir.addSubFolder("Drafts"); | 558 | auto draftsFolder = dir.addSubFolder("Drafts"); |
561 | auto remoteId = synchronizer->createFolder(draftsFolder, "folder", QByteArrayList() << "drafts"); | 559 | auto remoteId = synchronizer->createFolder(draftsFolder, "folder", QByteArrayList() << "drafts"); |
562 | auto draftsFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); | 560 | auto draftsFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); |
563 | } | 561 | } |
564 | { | 562 | { |
565 | auto trashFolder = dir.addSubFolder("Trash"); | 563 | auto trashFolder = dir.addSubFolder("Trash"); |
566 | auto remoteId = synchronizer->createFolder(trashFolder, "folder", QByteArrayList() << "trash"); | 564 | auto remoteId = synchronizer->createFolder(trashFolder, "folder", QByteArrayList() << "trash"); |
567 | auto trashFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); | 565 | auto trashFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId); |
566 | } | ||
567 | synchronizer->commit(); | ||
568 | } | 568 | } |
569 | synchronizer->commit(); | 569 | SinkTrace() << "Started maildir resource for maildir: " << mMaildirPath; |
570 | } | 570 | } |
571 | 571 | ||
572 | 572 | ||
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 @@ | |||
23 | #include <QDebug> | 23 | #include <QDebug> |
24 | #include <common/log.h> | 24 | #include <common/log.h> |
25 | 25 | ||
26 | SINK_DEBUG_AREA("mailtransport") | ||
27 | |||
28 | extern "C" { | 26 | extern "C" { |
29 | 27 | ||
30 | #include <stdio.h> | 28 | #include <stdio.h> |
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 @@ | |||
39 | 39 | ||
40 | #define ENTITY_TYPE_MAIL "mail" | 40 | #define ENTITY_TYPE_MAIL "mail" |
41 | 41 | ||
42 | SINK_DEBUG_AREA("mailtransportresource") | ||
43 | |||
44 | using namespace Sink; | 42 | using namespace Sink; |
45 | 43 | ||
46 | class MailtransportPreprocessor : public Sink::Preprocessor | 44 | class MailtransportPreprocessor : public Sink::Preprocessor |
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: | |||
64 | message->assemble(); | 64 | message->assemble(); |
65 | 65 | ||
66 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); | 66 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); |
67 | mail.setMimeMessage(message->encodedContent()); | 67 | mail.setMimeMessage(message->encodedContent(true)); |
68 | 68 | ||
69 | VERIFYEXEC(Store::create(mail)); | 69 | VERIFYEXEC(Store::create(mail)); |
70 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); | 70 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); |
@@ -92,7 +92,7 @@ private slots: | |||
92 | message->assemble(); | 92 | message->assemble(); |
93 | 93 | ||
94 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); | 94 | auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); |
95 | mail.setMimeMessage(message->encodedContent()); | 95 | mail.setMimeMessage(message->encodedContent(true)); |
96 | 96 | ||
97 | VERIFYEXEC(Store::create(mail)); | 97 | VERIFYEXEC(Store::create(mail)); |
98 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); | 98 | VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); |