summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/davresource/CMakeLists.txt8
-rw-r--r--examples/davresource/davresource.cpp402
-rw-r--r--examples/davresource/davresource.h2
-rw-r--r--examples/davresource/domainadaptor.cpp35
-rw-r--r--examples/davresource/domainadaptor.h38
-rw-r--r--examples/davresource/facade.cpp44
-rw-r--r--examples/davresource/facade.h36
-rw-r--r--examples/dummyresource/CMakeLists.txt2
-rw-r--r--examples/dummyresource/facade.cpp51
-rw-r--r--examples/dummyresource/facade.h44
-rw-r--r--examples/dummyresource/resourcefactory.cpp13
-rw-r--r--examples/imapresource/CMakeLists.txt2
-rw-r--r--examples/imapresource/domainadaptor.cpp35
-rw-r--r--examples/imapresource/domainadaptor.h38
-rw-r--r--examples/imapresource/facade.cpp44
-rw-r--r--examples/imapresource/facade.h36
-rw-r--r--examples/imapresource/imapresource.cpp139
-rw-r--r--examples/imapresource/imapserverproxy.cpp14
-rw-r--r--examples/imapresource/imapserverproxy.h6
-rw-r--r--examples/maildirresource/CMakeLists.txt5
-rw-r--r--examples/maildirresource/domainadaptor.cpp35
-rw-r--r--examples/maildirresource/domainadaptor.h38
-rw-r--r--examples/maildirresource/libmaildir/CMakeLists.txt8
-rw-r--r--examples/maildirresource/libmaildir/maildir.cpp2
-rw-r--r--examples/maildirresource/libmaildir/maildir.h6
-rw-r--r--examples/maildirresource/libmaildir/maildir_export.h43
-rw-r--r--examples/maildirresource/maildirresource.cpp14
-rw-r--r--examples/mailtransportresource/mailtransport.cpp60
-rw-r--r--examples/mailtransportresource/mailtransport.h4
-rw-r--r--examples/mailtransportresource/mailtransportresource.cpp67
-rw-r--r--examples/mailtransportresource/tests/mailtransporttest.cpp43
31 files changed, 345 insertions, 969 deletions
diff --git a/examples/davresource/CMakeLists.txt b/examples/davresource/CMakeLists.txt
index c7899eb..28829d5 100644
--- a/examples/davresource/CMakeLists.txt
+++ b/examples/davresource/CMakeLists.txt
@@ -3,16 +3,10 @@ project(sink_resource_dav)
3add_definitions(-DQT_PLUGIN) 3add_definitions(-DQT_PLUGIN)
4include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 4include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
5 5
6find_package(KF5 COMPONENTS REQUIRED Mime)
7find_package(KPimKDAV REQUIRED) 6find_package(KPimKDAV REQUIRED)
8 7
9add_library(${PROJECT_NAME} SHARED facade.cpp davresource.cpp domainadaptor.cpp) 8add_library(${PROJECT_NAME} SHARED davresource.cpp)
10qt5_use_modules(${PROJECT_NAME} Core Network) 9qt5_use_modules(${PROJECT_NAME} Core Network)
11target_link_libraries(${PROJECT_NAME} sink KPim::KDAV) 10target_link_libraries(${PROJECT_NAME} sink KPim::KDAV)
12 11
13install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) 12install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH})
14
15#add_definitions(-DTESTDATAPATH="${CMAKE_CURRENT_SOURCE_DIR}/tests/data")
16
17#add_subdirectory(libmaildir)
18#add_subdirectory(tests)
diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp
index de4c0b0..50471ed 100644
--- a/examples/davresource/davresource.cpp
+++ b/examples/davresource/davresource.cpp
@@ -21,10 +21,8 @@
21 21
22#include "facade.h" 22#include "facade.h"
23#include "resourceconfig.h" 23#include "resourceconfig.h"
24#include "index.h"
25#include "log.h" 24#include "log.h"
26#include "definitions.h" 25#include "definitions.h"
27#include "inspection.h"
28#include "synchronizer.h" 26#include "synchronizer.h"
29#include "inspector.h" 27#include "inspector.h"
30 28
@@ -40,181 +38,14 @@
40#include <KDAV/DavItemFetchJob> 38#include <KDAV/DavItemFetchJob>
41#include <KDAV/EtagCache> 39#include <KDAV/EtagCache>
42 40
43#include <QDir>
44#include <QDirIterator>
45
46//This is the resources entity type, and not the domain type 41//This is the resources entity type, and not the domain type
47#define ENTITY_TYPE_CONTACT "contact" 42#define ENTITY_TYPE_CONTACT "contact"
48#define ENTITY_TYPE_ADDRESSBOOK "folder" 43#define ENTITY_TYPE_ADDRESSBOOK "addressbook"
49 44
50SINK_DEBUG_AREA("davresource") 45SINK_DEBUG_AREA("davresource")
51 46
52using namespace Sink; 47using namespace Sink;
53 48
54/*static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath)
55{
56 auto parts = mimeMessagePath.split('/');
57 const auto key = parts.takeLast();
58 const auto path = parts.join("/") + "/cur/";
59
60 QDir dir(path);
61 const QFileInfoList list = dir.entryInfoList(QStringList() << (key+"*"), QDir::Files);
62 if (list.size() != 1) {
63 SinkWarning() << "Failed to find message " << mimeMessagePath;
64 SinkWarning() << "Failed to find message " << path;
65 return QString();
66 }
67 return list.first().filePath();
68}
69
70class MaildirMailPropertyExtractor : public MailPropertyExtractor
71{
72protected:
73 virtual QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) const Q_DECL_OVERRIDE
74 {
75 return ::getFilePathFromMimeMessagePath(mimeMessagePath);
76 }
77};
78
79class MaildirMimeMessageMover : public Sink::Preprocessor
80{
81public:
82 MaildirMimeMessageMover(const QByteArray &resourceInstanceIdentifier, const QString &maildirPath) : mResourceInstanceIdentifier(resourceInstanceIdentifier), mMaildirPath(maildirPath) {}
83
84 QString getPath(const QByteArray &folderIdentifier)
85 {
86 if (folderIdentifier.isEmpty()) {
87 return mMaildirPath;
88 }
89 QString folderPath;
90 const auto folder = entityStore().readLatest<ApplicationDomain::Folder>(folderIdentifier);
91 if (mMaildirPath.endsWith(folder.getName())) {
92 folderPath = mMaildirPath;
93 } else {
94 auto folderName = folder.getName();
95 //FIXME handle non toplevel folders
96 folderPath = mMaildirPath + "/" + folderName;
97 }
98 return folderPath;
99 }
100
101 QString moveMessage(const QString &oldPath, const QByteArray &folder)
102 {
103 if (oldPath.startsWith(Sink::temporaryFileLocation())) {
104 const auto path = getPath(folder);
105 KPIM::Contactdir maildir(path, false);
106 if (!maildir.isValid(true)) {
107 SinkWarning() << "Maildir is not existing: " << path;
108 }
109 auto identifier = maildir.addEntryFromPath(oldPath);
110 return path + "/" + identifier;
111 } else {
112 //Handle moves
113 const auto path = getPath(folder);
114 KPIM::Contactdir maildir(path, false);
115 if (!maildir.isValid(true)) {
116 SinkWarning() << "Maildir is not existing: " << path;
117 }
118 auto oldIdentifier = KPIM::Contactdir::getKeyFromFile(oldPath);
119 auto pathParts = oldPath.split('/');
120 pathParts.takeLast();
121 auto oldDirectory = pathParts.join('/');
122 if (oldDirectory == path) {
123 return oldPath;
124 }
125 KPIM::Contactdir oldMaildir(oldDirectory, false);
126 if (!oldMaildir.isValid(false)) {
127 SinkWarning() << "Maildir is not existing: " << path;
128 }
129 auto identifier = oldMaildir.moveEntryTo(oldIdentifier, maildir);
130 return path + "/" + identifier;
131 }
132 }
133
134 void newEntity(Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
135 {
136 auto mail = newEntity.cast<ApplicationDomain::Contact>();
137 const auto mimeMessage = mail.getMimeMessagePath();
138 if (!mimeMessage.isNull()) {
139 const auto path = moveMessage(mimeMessage, mail.getFolder());
140 auto blob = ApplicationDomain::BLOB{path};
141 blob.isExternal = false;
142 mail.setProperty(ApplicationDomain::Contact::MimeMessage::name, QVariant::fromValue(blob));
143 }
144 }
145
146 void modifiedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity, Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
147 {
148 auto newMail = newEntity.cast<ApplicationDomain::Contact>();
149 const ApplicationDomain::Contact oldMail{oldEntity};
150 const auto mimeMessage = newMail.getMimeMessagePath();
151 const auto newFolder = newMail.getFolder();
152 const bool mimeMessageChanged = !mimeMessage.isNull() && mimeMessage != oldMail.getMimeMessagePath();
153 const bool folderChanged = !newFolder.isNull() && newFolder != oldMail.getFolder();
154 if (mimeMessageChanged || folderChanged) {
155 SinkTrace() << "Moving mime message: " << mimeMessageChanged << folderChanged;
156 auto newPath = moveMessage(mimeMessage, newMail.getFolder());
157 if (newPath != oldMail.getMimeMessagePath()) {
158 const auto oldPath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath());
159 auto blob = ApplicationDomain::BLOB{newPath};
160 blob.isExternal = false;
161 newMail.setProperty(ApplicationDomain::Contact::MimeMessage::name, QVariant::fromValue(blob));
162 //Remove the olde mime message if there is a new one
163 QFile::remove(oldPath);
164 }
165 }
166
167 auto mimeMessagePath = newMail.getMimeMessagePath();
168 const auto maildirPath = getPath(newMail.getFolder());
169 KPIM::Contactdir maildir(maildirPath, false);
170 const auto file = getFilePathFromMimeMessagePath(mimeMessagePath);
171 QString identifier = KPIM::Contactdir::getKeyFromFile(file);
172
173 //get flags from
174 KPIM::Contactdir::Flags flags;
175 if (!newMail.getUnread()) {
176 flags |= KPIM::Contactdir::Seen;
177 }
178 if (newMail.getImportant()) {
179 flags |= KPIM::Contactdir::Flagged;
180 }
181
182 maildir.changeEntryFlags(identifier, flags);
183 }
184
185 void deletedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity) Q_DECL_OVERRIDE
186 {
187 const ApplicationDomain::Contact oldMail{oldEntity};
188 const auto filePath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath());
189 QFile::remove(filePath);
190 }
191 QByteArray mResourceInstanceIdentifier;
192 QString mMaildirPath;
193};
194
195class FolderPreprocessor : public Sink::Preprocessor
196{
197public:
198 FolderPreprocessor(const QString maildirPath) : mMaildirPath(maildirPath) {}
199
200 void newEntity(Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
201 {
202 auto folderName = Sink::ApplicationDomain::Folder{newEntity}.getName();
203 const auto path = mMaildirPath + "/" + folderName;
204 KPIM::Contactdir maildir(path, false);
205 maildir.create();
206 }
207
208 void modifiedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity, Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
209 {
210 }
211
212 void deletedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity) Q_DECL_OVERRIDE
213 {
214 }
215 QString mMaildirPath;
216};*/
217
218static KAsync::Job<void> runJob(KJob *job) 49static KAsync::Job<void> runJob(KJob *job)
219{ 50{
220 return KAsync::start<void>([job](KAsync::Future<void> &future) { 51 return KAsync::start<void>([job](KAsync::Future<void> &future) {
@@ -240,33 +71,33 @@ public:
240 71
241 } 72 }
242 73
243 QByteArray createAddressbook(const QString &folderName, const QString &folderPath, const QString &parentFolderRid, const QByteArray &icon) 74 QByteArray createAddressbook(const QString &addressbookName, const QString &addressbookPath, const QString &parentAddressbookRid)
244 { 75 {
245 SinkTrace() << "Creating addressbook: " << folderName << parentFolderRid; 76 SinkTrace() << "Creating addressbook: " << addressbookName << parentAddressbookRid;
246 const auto remoteId = folderPath.toUtf8(); 77 const auto remoteId = addressbookPath.toUtf8();
247 const auto bufferType = ENTITY_TYPE_ADDRESSBOOK; 78 const auto bufferType = ENTITY_TYPE_ADDRESSBOOK;
248 Sink::ApplicationDomain::Folder folder; 79 Sink::ApplicationDomain::Addressbook addressbook;
249 folder.setName(folderName); 80 addressbook.setName(addressbookName);
250 folder.setIcon(icon);
251 QHash<QByteArray, Query::Comparator> mergeCriteria; 81 QHash<QByteArray, Query::Comparator> mergeCriteria;
252 82
253 if (!parentFolderRid.isEmpty()) { 83 if (!parentAddressbookRid.isEmpty()) {
254 folder.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, parentFolderRid.toUtf8())); 84 addressbook.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, parentAddressbookRid.toUtf8()));
255 } 85 }
256 createOrModify(bufferType, remoteId, folder, mergeCriteria); 86 createOrModify(bufferType, remoteId, addressbook, mergeCriteria);
257 return remoteId; 87 return remoteId;
258 } 88 }
259 89
260 void synchronizeAddressbooks(const KDAV::DavCollection::List &folderList) 90 void synchronizeAddressbooks(const KDAV::DavCollection::List &addressbookList)
261 { 91 {
262 const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK; 92 const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK;
263 SinkTrace() << "Found addressbooks " << folderList.size(); 93 SinkTrace() << "Found addressbooks " << addressbookList.size();
264 94
265 QVector<QByteArray> ridList; 95 QVector<QByteArray> ridList;
266 for(const auto &f : folderList) { 96 for(const auto &f : addressbookList) {
267 const auto &rid = f.url().toDisplayString(); 97 const auto &rid = getRid(f);
268 ridList.append(rid.toUtf8()); 98 SinkTrace() << "Found addressbook:" << rid;
269 createAddressbook(f.displayName(), rid, "", "addressbook"); 99 ridList.append(rid);
100 createAddressbook(f.displayName(), rid, "");
270 } 101 }
271 102
272 scanForRemovals(bufferType, 103 scanForRemovals(bufferType,
@@ -284,54 +115,73 @@ public:
284 list << Synchronizer::SyncRequest{query}; 115 list << Synchronizer::SyncRequest{query};
285 } else { 116 } else {
286 //We want to synchronize everything 117 //We want to synchronize everything
118 list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>())};
287 list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName<ApplicationDomain::Contact>())}; 119 list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName<ApplicationDomain::Contact>())};
288 } 120 }
289 return list; 121 return list;
290 } 122 }
291 123
124 static QByteArray getRid(const KDAV::DavItem &item)
125 {
126 return item.url().toDisplayString().toUtf8();
127 }
128
129 static QByteArray getRid(const KDAV::DavCollection &item)
130 {
131 return item.url().toDisplayString().toUtf8();
132 }
133
292 KAsync::Job<void> synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE 134 KAsync::Job<void> synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE
293 { 135 {
294 if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) { 136 if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) {
137 SinkLogCtx(mLogCtx) << "Synchronizing addressbooks:" << mResourceUrl.url();
295 auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl); 138 auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl);
296 auto job = runJob(collectionsFetchJob).then<void>([this, collectionsFetchJob] { 139 auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] (const KAsync::Error &error) {
297 synchronizeAddressbooks(collectionsFetchJob ->collections()); 140 if (error) {
141 SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString();
142 } else {
143 synchronizeAddressbooks(collectionsFetchJob ->collections());
144 }
298 }); 145 });
299 return job; 146 return job;
300 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) { 147 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) {
148 SinkLogCtx(mLogCtx) << "Synchronizing contacts.";
301 auto ridList = QSharedPointer<QByteArrayList>::create(); 149 auto ridList = QSharedPointer<QByteArrayList>::create();
302 auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl); 150 auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl);
303 auto job = runJob(collectionsFetchJob).then<KDAV::DavCollection::List>([this, collectionsFetchJob] { 151 auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] {
304 synchronizeAddressbooks(collectionsFetchJob ->collections()); 152 synchronizeAddressbooks(collectionsFetchJob ->collections());
305 return collectionsFetchJob->collections(); 153 return collectionsFetchJob->collections();
306 }) 154 })
307 .serialEach<void>([this, ridList](const KDAV::DavCollection &collection) { 155 .serialEach([this, ridList](const KDAV::DavCollection &collection) {
308 auto collId = collection.url().toDisplayString().toLatin1(); 156 auto collId = getRid(collection);
157 const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId);
309 auto ctag = collection.CTag().toLatin1(); 158 auto ctag = collection.CTag().toLatin1();
310 if (ctag != syncStore().readValue(collId + "_ctagXX")) { 159 if (ctag != syncStore().readValue(collId + "_ctagXX")) {
311 SinkTrace() << "Syncing " << collId; 160 SinkTraceCtx(mLogCtx) << "Syncing " << collId;
312 auto cache = std::shared_ptr<KDAV::EtagCache>(new KDAV::EtagCache()); 161 auto cache = std::shared_ptr<KDAV::EtagCache>(new KDAV::EtagCache());
313 auto davItemsListJob = new KDAV::DavItemsListJob(collection.url(), cache); 162 auto davItemsListJob = new KDAV::DavItemsListJob(collection.url(), cache);
314 const QByteArray bufferType = ENTITY_TYPE_CONTACT; 163 const QByteArray bufferType = ENTITY_TYPE_CONTACT;
315 QHash<QByteArray, Query::Comparator> mergeCriteria; 164 QHash<QByteArray, Query::Comparator> mergeCriteria;
316 auto colljob = runJob(davItemsListJob).then<KDAV::DavItem::List>([davItemsListJob] { 165 auto colljob = runJob(davItemsListJob).then([davItemsListJob] {
317 return KAsync::value(davItemsListJob->items()); 166 return KAsync::value(davItemsListJob->items());
318 }) 167 })
319 .serialEach<QByteArray>([this, ridList, bufferType, mergeCriteria] (const KDAV::DavItem &item) { 168 .serialEach([=] (const KDAV::DavItem &item) {
320 QByteArray rid = item.url().toDisplayString().toUtf8(); 169 QByteArray rid = getRid(item);
321 if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){ 170 if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){
322 SinkTrace() << "Updating " << rid; 171 SinkTrace() << "Updating " << rid;
323 auto davItemFetchJob = new KDAV::DavItemFetchJob(item); 172 auto davItemFetchJob = new KDAV::DavItemFetchJob(item);
324 auto itemjob = runJob(davItemFetchJob) 173 auto itemjob = runJob(davItemFetchJob)
325 .then<KDAV::DavItem>([this, davItemFetchJob, bufferType, mergeCriteria] { 174 .then([=] {
326 const auto item = davItemFetchJob->item(); 175 const auto item = davItemFetchJob->item();
327 const auto rid = item.url().toDisplayString().toUtf8(); 176 const auto rid = getRid(item);
328 Sink::ApplicationDomain::Contact contact; 177 Sink::ApplicationDomain::Contact contact;
329 contact.setVcard(item.data()); 178 contact.setVcard(item.data());
179 contact.setAddressbook(addressbookLocalId);
330 createOrModify(bufferType, rid, contact, mergeCriteria); 180 createOrModify(bufferType, rid, contact, mergeCriteria);
331 return item; 181 return item;
332 }) 182 })
333 .then<QByteArray>([this, ridList] (const KDAV::DavItem &item) { 183 .then([this, ridList] (const KDAV::DavItem &item) {
334 const auto rid = item.url().toDisplayString().toUtf8(); 184 const auto rid = getRid(item);
335 syncStore().writeValue(rid + "_etag", item.etag().toLatin1()); 185 syncStore().writeValue(rid + "_etag", item.etag().toLatin1());
336 ridList->append(rid); 186 ridList->append(rid);
337 return rid; 187 return rid;
@@ -342,11 +192,12 @@ public:
342 return KAsync::value(rid); 192 return KAsync::value(rid);
343 } 193 }
344 }) 194 })
345 .then<void>([this, collId, ctag] () { 195 .then([this, collId, ctag] () {
346 syncStore().writeValue(collId + "_ctag", ctag); 196 syncStore().writeValue(collId + "_ctag", ctag);
347 }); 197 });
348 return colljob; 198 return colljob;
349 } else { 199 } else {
200 SinkTraceCtx(mLogCtx) << "Collection unchanged: " << ctag;
350 // for(const auto &item : addressbook) { 201 // for(const auto &item : addressbook) {
351 // ridList->append(rid); 202 // ridList->append(rid);
352 // } 203 // }
@@ -367,43 +218,11 @@ public:
367 218
368KAsync::Job<QByteArray> replay(const ApplicationDomain::Contact &contact, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE 219KAsync::Job<QByteArray> replay(const ApplicationDomain::Contact &contact, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
369 { 220 {
370 /*
371 if (operation == Sink::Operation_Creation) {
372 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
373 SinkTrace() << "Contact created: " << remoteId;
374 return KAsync::value(remoteId.toUtf8());
375 } else if (operation == Sink::Operation_Removal) {
376 SinkTrace() << "Removing a contact " << oldRemoteId;
377 return KAsync::null<QByteArray>();
378 } else if (operation == Sink::Operation_Modification) {
379 SinkTrace() << "Modifying a contact: " << oldRemoteId;
380 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
381 return KAsync::value(remoteId.toUtf8());
382 }*/
383 return KAsync::null<QByteArray>(); 221 return KAsync::null<QByteArray>();
384 } 222 }
385 223
386 KAsync::Job<QByteArray> replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE 224 KAsync::Job<QByteArray> replay(const ApplicationDomain::Addressbook &addressbook, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
387 { 225 {
388 /*
389 if (operation == Sink::Operation_Creation) {
390 auto folderName = folder.getName();
391 //FIXME handle non toplevel folders
392 auto path = mMaildirPath + "/" + folderName;
393 SinkTrace() << "Creating a new folder: " << path;
394 KPIM::Contactdir maildir(path, false);
395 maildir.create();
396 return KAsync::value(path.toUtf8());
397 } else if (operation == Sink::Operation_Removal) {
398 const auto path = oldRemoteId;
399 SinkTrace() << "Removing a folder: " << path;
400 KPIM::Contactdir maildir(path, false);
401 maildir.remove();
402 return KAsync::null<QByteArray>();
403 } else if (operation == Sink::Operation_Modification) {
404 SinkWarning() << "Folder modifications are not implemented";
405 return KAsync::value(oldRemoteId);
406 }*/
407 return KAsync::null<QByteArray>(); 226 return KAsync::null<QByteArray>();
408 } 227 }
409 228
@@ -411,109 +230,17 @@ public:
411 KDAV::DavUrl mResourceUrl; 230 KDAV::DavUrl mResourceUrl;
412}; 231};
413 232
414/*
415class MaildirInspector : public Sink::Inspector {
416public:
417 MaildirInspector(const Sink::ResourceContext &resourceContext)
418 : Sink::Inspector(resourceContext)
419 {
420
421 }
422protected:
423
424 KAsync::Job<void> inspect(int inspectionType, const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue) Q_DECL_OVERRIDE {
425 auto synchronizationStore = QSharedPointer<Sink::Storage::DataStore>::create(Sink::storageLocation(), mResourceContext.instanceId() + ".synchronization", Sink::Storage::DataStore::ReadOnly);
426 auto synchronizationTransaction = synchronizationStore->createTransaction(Sink::Storage::DataStore::ReadOnly);
427
428 auto mainStore = QSharedPointer<Sink::Storage::DataStore>::create(Sink::storageLocation(), mResourceContext.instanceId(), Sink::Storage::DataStore::ReadOnly);
429 auto transaction = mainStore->createTransaction(Sink::Storage::DataStore::ReadOnly);
430
431 Sink::Storage::EntityStore entityStore(mResourceContext, {"maildirresource"});
432 auto syncStore = QSharedPointer<SynchronizerStore>::create(synchronizationTransaction);
433
434 SinkTrace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue;
435
436 if (domainType == ENTITY_TYPE_MAIL) {
437 auto mail = entityStore.readLatest<Sink::ApplicationDomain::Contact>(entityId);
438 const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
439
440 if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) {
441 if (property == "unread") {
442 const auto flags = KPIM::Contactdir::readEntryFlags(filePath.split('/').last());
443 if (expectedValue.toBool() && (flags & KPIM::Contactdir::Seen)) {
444 return KAsync::error<void>(1, "Expected unread but couldn't find it.");
445 }
446 if (!expectedValue.toBool() && !(flags & KPIM::Contactdir::Seen)) {
447 return KAsync::error<void>(1, "Expected read but couldn't find it.");
448 }
449 return KAsync::null<void>();
450 }
451 if (property == "subject") {
452 KMime::Message *msg = new KMime::Message;
453 msg->setHead(KMime::CRLFtoLF(KPIM::Contactdir::readEntryHeadersFromFile(filePath)));
454 msg->parse();
455
456 if (msg->subject(true)->asUnicodeString() != expectedValue.toString()) {
457 return KAsync::error<void>(1, "Subject not as expected: " + msg->subject(true)->asUnicodeString());
458 }
459 return KAsync::null<void>();
460 }
461 }
462 if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) {
463 if (QFileInfo(filePath).exists() != expectedValue.toBool()) {
464 return KAsync::error<void>(1, "Wrong file existence: " + filePath);
465 }
466 }
467 }
468 if (domainType == ENTITY_TYPE_FOLDER) {
469 const auto remoteId = syncStore->resolveLocalId(ENTITY_TYPE_FOLDER, entityId);
470 auto folder = entityStore.readLatest<Sink::ApplicationDomain::Folder>(entityId);
471
472 if (inspectionType == Sink::ResourceControl::Inspection::CacheIntegrityInspectionType) {
473 SinkTrace() << "Inspecting cache integrity" << remoteId;
474 if (!QDir(remoteId).exists()) {
475 return KAsync::error<void>(1, "The directory is not existing: " + remoteId);
476 }
477
478 int expectedCount = 0;
479 Index index("mail.index.folder", transaction);
480 index.lookup(entityId, [&](const QByteArray &sinkId) {
481 expectedCount++;
482 },
483 [&](const Index::Error &error) {
484 SinkWarning() << "Error in index: " << error.message << property;
485 });
486
487 QDir dir(remoteId + "/cur");
488 const QFileInfoList list = dir.entryInfoList(QDir::Files);
489 if (list.size() != expectedCount) {
490 for (const auto &fileInfo : list) {
491 SinkWarning() << "Found in cache: " << fileInfo.fileName();
492 }
493 return KAsync::error<void>(1, QString("Wrong number of files; found %1 instead of %2.").arg(list.size()).arg(expectedCount));
494 }
495 }
496 if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) {
497 if (!remoteId.endsWith(folder.getName().toUtf8())) {
498 return KAsync::error<void>(1, "Wrong folder name: " + remoteId);
499 }
500 //TODO we shouldn't use the remoteId here to figure out the path, it could be gone/changed already
501 if (QDir(remoteId).exists() != expectedValue.toBool()) {
502 return KAsync::error<void>(1, "Wrong folder existence: " + remoteId);
503 }
504 }
505
506 }
507 return KAsync::null<void>();
508 }
509};*/
510
511 233
512DavResource::DavResource(const Sink::ResourceContext &resourceContext) 234DavResource::DavResource(const Sink::ResourceContext &resourceContext)
513 : Sink::GenericResource(resourceContext) 235 : Sink::GenericResource(resourceContext)
514{ 236{
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");
515 auto config = ResourceConfig::getConfiguration(resourceContext.instanceId()); 242 auto config = ResourceConfig::getConfiguration(resourceContext.instanceId());
516 auto resourceUrl = QUrl::fromUserInput(config.value("resourceUrl").toString()); 243 auto resourceUrl = QUrl::fromUserInput(config.value("server").toString());
517 resourceUrl.setUserName(config.value("username").toString()); 244 resourceUrl.setUserName(config.value("username").toString());
518 resourceUrl.setPassword(config.value("password").toString()); 245 resourceUrl.setPassword(config.value("password").toString());
519 246
@@ -522,7 +249,6 @@ DavResource::DavResource(const Sink::ResourceContext &resourceContext)
522 auto synchronizer = QSharedPointer<ContactSynchronizer>::create(resourceContext); 249 auto synchronizer = QSharedPointer<ContactSynchronizer>::create(resourceContext);
523 synchronizer->mResourceUrl = mResourceUrl; 250 synchronizer->mResourceUrl = mResourceUrl;
524 setupSynchronizer(synchronizer); 251 setupSynchronizer(synchronizer);
525 //setupInspector(QSharedPointer<MaildirInspector>::create(resourceContext));
526 252
527 setupPreprocessors(ENTITY_TYPE_CONTACT, QVector<Sink::Preprocessor*>() << new ContactPropertyExtractor); 253 setupPreprocessors(ENTITY_TYPE_CONTACT, QVector<Sink::Preprocessor*>() << new ContactPropertyExtractor);
528} 254}
@@ -530,7 +256,9 @@ DavResource::DavResource(const Sink::ResourceContext &resourceContext)
530 256
531DavResourceFactory::DavResourceFactory(QObject *parent) 257DavResourceFactory::DavResourceFactory(QObject *parent)
532 : Sink::ResourceFactory(parent, 258 : Sink::ResourceFactory(parent,
533 {"-folder.rename"} 259 {Sink::ApplicationDomain::ResourceCapabilities::Contact::contact,
260 Sink::ApplicationDomain::ResourceCapabilities::Contact::addressbook,
261 }
534 ) 262 )
535{ 263{
536} 264}
@@ -542,14 +270,14 @@ Sink::Resource *DavResourceFactory::createResource(const ResourceContext &contex
542 270
543void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory) 271void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory)
544{ 272{
545 factory.registerFacade<Sink::ApplicationDomain::Contact, DavResourceContactFacade>(name); 273 factory.registerFacade<ApplicationDomain::Contact, DefaultFacade<ApplicationDomain::Contact>>(name);
546 factory.registerFacade<Sink::ApplicationDomain::Folder, DavResourceFolderFacade>(name); 274 factory.registerFacade<ApplicationDomain::Addressbook, DefaultFacade<ApplicationDomain::Contact>>(name);
547} 275}
548 276
549void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry &registry) 277void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry &registry)
550{ 278{
551 registry.registerFactory<Sink::ApplicationDomain::Contact, ContactAdaptorFactory>(name); 279 registry.registerFactory<ApplicationDomain::Contact, DefaultAdaptorFactory<ApplicationDomain::Contact>>(name);
552 registry.registerFactory<Sink::ApplicationDomain::Folder, AddressbookAdaptorFactory>(name); 280 registry.registerFactory<ApplicationDomain::Addressbook, DefaultAdaptorFactory<ApplicationDomain::Addressbook>>(name);
553} 281}
554 282
555void DavResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier) 283void DavResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier)
diff --git a/examples/davresource/davresource.h b/examples/davresource/davresource.h
index 415527a..1ce66ea 100644
--- a/examples/davresource/davresource.h
+++ b/examples/davresource/davresource.h
@@ -54,7 +54,7 @@ private:
54class DavResourceFactory : public Sink::ResourceFactory 54class DavResourceFactory : public Sink::ResourceFactory
55{ 55{
56 Q_OBJECT 56 Q_OBJECT
57 Q_PLUGIN_METADATA(IID "sink.davresource") 57 Q_PLUGIN_METADATA(IID "sink.dav")
58 Q_INTERFACES(Sink::ResourceFactory) 58 Q_INTERFACES(Sink::ResourceFactory)
59 59
60public: 60public:
diff --git a/examples/davresource/domainadaptor.cpp b/examples/davresource/domainadaptor.cpp
deleted file mode 100644
index 861a10e..0000000
--- a/examples/davresource/domainadaptor.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "domainadaptor.h"
21
22using namespace flatbuffers;
23
24ContactAdaptorFactory::ContactAdaptorFactory()
25 : DomainTypeAdaptorFactory()
26{
27
28}
29
30AddressbookAdaptorFactory::AddressbookAdaptorFactory()
31 : DomainTypeAdaptorFactory()
32{
33
34}
35
diff --git a/examples/davresource/domainadaptor.h b/examples/davresource/domainadaptor.h
deleted file mode 100644
index 7e3c723..0000000
--- a/examples/davresource/domainadaptor.h
+++ /dev/null
@@ -1,38 +0,0 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#pragma once
20
21#include <common/domainadaptor.h>
22#include "contact_generated.h"
23#include "folder_generated.h"
24#include "dummy_generated.h"
25
26class ContactAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Contact, Sink::ApplicationDomain::Buffer::Dummy, Sink::ApplicationDomain::Buffer::DummyBuilder>
27{
28public:
29 ContactAdaptorFactory();
30 virtual ~ContactAdaptorFactory() {};
31};
32
33class AddressbookAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Folder, Sink::ApplicationDomain::Buffer::Dummy, Sink::ApplicationDomain::Buffer::DummyBuilder>
34{
35public:
36 AddressbookAdaptorFactory();
37 virtual ~AddressbookAdaptorFactory() {};
38};
diff --git a/examples/davresource/facade.cpp b/examples/davresource/facade.cpp
deleted file mode 100644
index b56815a..0000000
--- a/examples/davresource/facade.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "facade.h"
21
22#include <QDir>
23#include <QFileInfo>
24
25#include "query.h"
26
27DavResourceContactFacade::DavResourceContactFacade(const Sink::ResourceContext &context)
28 : Sink::GenericFacade<Sink::ApplicationDomain::Contact>(context)
29{
30}
31
32DavResourceContactFacade::~DavResourceContactFacade()
33{
34}
35
36
37DavResourceFolderFacade::DavResourceFolderFacade(const Sink::ResourceContext &context)
38 : Sink::GenericFacade<Sink::ApplicationDomain::Folder>(context)
39{
40}
41
42DavResourceFolderFacade::~DavResourceFolderFacade()
43{
44}
diff --git a/examples/davresource/facade.h b/examples/davresource/facade.h
deleted file mode 100644
index 02bd5dc..0000000
--- a/examples/davresource/facade.h
+++ /dev/null
@@ -1,36 +0,0 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include "common/facade.h"
23
24class DavResourceContactFacade : public Sink::GenericFacade<Sink::ApplicationDomain::Contact>
25{
26public:
27 DavResourceContactFacade(const Sink::ResourceContext &context);
28 virtual ~DavResourceContactFacade();
29};
30
31class DavResourceFolderFacade : public Sink::GenericFacade<Sink::ApplicationDomain::Folder>
32{
33public:
34 DavResourceFolderFacade(const Sink::ResourceContext &context);
35 virtual ~DavResourceFolderFacade();
36};
diff --git a/examples/dummyresource/CMakeLists.txt b/examples/dummyresource/CMakeLists.txt
index 6400f0c..2bbaa47 100644
--- a/examples/dummyresource/CMakeLists.txt
+++ b/examples/dummyresource/CMakeLists.txt
@@ -4,7 +4,7 @@ add_definitions(-DQT_PLUGIN)
4include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 4include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
5 5
6 6
7add_library(${PROJECT_NAME} SHARED facade.cpp resourcefactory.cpp domainadaptor.cpp dummystore.cpp) 7add_library(${PROJECT_NAME} SHARED resourcefactory.cpp domainadaptor.cpp dummystore.cpp)
8generate_flatbuffers(${PROJECT_NAME} dummycalendar) 8generate_flatbuffers(${PROJECT_NAME} dummycalendar)
9qt5_use_modules(${PROJECT_NAME} Core Network) 9qt5_use_modules(${PROJECT_NAME} Core Network)
10target_link_libraries(${PROJECT_NAME} sink) 10target_link_libraries(${PROJECT_NAME} sink)
diff --git a/examples/dummyresource/facade.cpp b/examples/dummyresource/facade.cpp
deleted file mode 100644
index 4343eba..0000000
--- a/examples/dummyresource/facade.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "facade.h"
21
22#include "domainadaptor.h"
23
24DummyResourceFacade::DummyResourceFacade(const Sink::ResourceContext &context)
25 : Sink::GenericFacade<Sink::ApplicationDomain::Event>(context)
26{
27}
28
29DummyResourceFacade::~DummyResourceFacade()
30{
31}
32
33
34DummyResourceMailFacade::DummyResourceMailFacade(const Sink::ResourceContext &context)
35 : Sink::GenericFacade<Sink::ApplicationDomain::Mail>(context)
36{
37}
38
39DummyResourceMailFacade::~DummyResourceMailFacade()
40{
41}
42
43
44DummyResourceFolderFacade::DummyResourceFolderFacade(const Sink::ResourceContext &context)
45 : Sink::GenericFacade<Sink::ApplicationDomain::Folder>(context)
46{
47}
48
49DummyResourceFolderFacade::~DummyResourceFolderFacade()
50{
51}
diff --git a/examples/dummyresource/facade.h b/examples/dummyresource/facade.h
deleted file mode 100644
index 1bb45fd..0000000
--- a/examples/dummyresource/facade.h
+++ /dev/null
@@ -1,44 +0,0 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include "common/facade.h"
23#include "common/domain/event.h"
24
25class DummyResourceFacade : public Sink::GenericFacade<Sink::ApplicationDomain::Event>
26{
27public:
28 DummyResourceFacade(const Sink::ResourceContext &context);
29 virtual ~DummyResourceFacade();
30};
31
32class DummyResourceMailFacade : public Sink::GenericFacade<Sink::ApplicationDomain::Mail>
33{
34public:
35 DummyResourceMailFacade(const Sink::ResourceContext &context);
36 virtual ~DummyResourceMailFacade();
37};
38
39class DummyResourceFolderFacade : public Sink::GenericFacade<Sink::ApplicationDomain::Folder>
40{
41public:
42 DummyResourceFolderFacade(const Sink::ResourceContext &context);
43 virtual ~DummyResourceFolderFacade();
44};
diff --git a/examples/dummyresource/resourcefactory.cpp b/examples/dummyresource/resourcefactory.cpp
index e915710..c1f536e 100644
--- a/examples/dummyresource/resourcefactory.cpp
+++ b/examples/dummyresource/resourcefactory.cpp
@@ -26,8 +26,6 @@
26#include "mail_generated.h" 26#include "mail_generated.h"
27#include "domainadaptor.h" 27#include "domainadaptor.h"
28#include "log.h" 28#include "log.h"
29#include "domain/event.h"
30#include "domain/mail.h"
31#include "dummystore.h" 29#include "dummystore.h"
32#include "definitions.h" 30#include "definitions.h"
33#include "facadefactory.h" 31#include "facadefactory.h"
@@ -46,6 +44,8 @@
46 44
47SINK_DEBUG_AREA("dummyresource") 45SINK_DEBUG_AREA("dummyresource")
48 46
47using namespace Sink;
48
49class DummySynchronizer : public Sink::Synchronizer { 49class DummySynchronizer : public Sink::Synchronizer {
50 public: 50 public:
51 51
@@ -69,6 +69,7 @@ class DummySynchronizer : public Sink::Synchronizer {
69 Sink::ApplicationDomain::Mail::Ptr createMail(const QByteArray &ridBuffer, const QMap<QString, QVariant> &data) 69 Sink::ApplicationDomain::Mail::Ptr createMail(const QByteArray &ridBuffer, const QMap<QString, QVariant> &data)
70 { 70 {
71 auto mail = Sink::ApplicationDomain::Mail::Ptr::create(); 71 auto mail = Sink::ApplicationDomain::Mail::Ptr::create();
72 mail->setExtractedMessageId(ridBuffer);
72 mail->setExtractedSubject(data.value("subject").toString()); 73 mail->setExtractedSubject(data.value("subject").toString());
73 mail->setExtractedSender(Sink::ApplicationDomain::Mail::Contact{data.value("senderName").toString(), data.value("senderEmail").toString()}); 74 mail->setExtractedSender(Sink::ApplicationDomain::Mail::Contact{data.value("senderName").toString(), data.value("senderEmail").toString()});
74 mail->setExtractedDate(data.value("date").toDateTime()); 75 mail->setExtractedDate(data.value("date").toDateTime());
@@ -163,7 +164,7 @@ DummyResource::DummyResource(const Sink::ResourceContext &resourceContext, const
163 setupSynchronizer(QSharedPointer<DummySynchronizer>::create(resourceContext)); 164 setupSynchronizer(QSharedPointer<DummySynchronizer>::create(resourceContext));
164 setupInspector(QSharedPointer<DummyInspector>::create(resourceContext)); 165 setupInspector(QSharedPointer<DummyInspector>::create(resourceContext));
165 setupPreprocessors(ENTITY_TYPE_MAIL, 166 setupPreprocessors(ENTITY_TYPE_MAIL,
166 QVector<Sink::Preprocessor*>() << new MailPropertyExtractor << new SpecialPurposeProcessor{resourceContext.resourceType, resourceContext.instanceId()}); 167 QVector<Sink::Preprocessor*>() << new MailPropertyExtractor << new SpecialPurposeProcessor);
167 setupPreprocessors(ENTITY_TYPE_FOLDER, 168 setupPreprocessors(ENTITY_TYPE_FOLDER,
168 QVector<Sink::Preprocessor*>()); 169 QVector<Sink::Preprocessor*>());
169 setupPreprocessors(ENTITY_TYPE_EVENT, 170 setupPreprocessors(ENTITY_TYPE_EVENT,
@@ -194,9 +195,9 @@ Sink::Resource *DummyResourceFactory::createResource(const Sink::ResourceContext
194 195
195void DummyResourceFactory::registerFacades(const QByteArray &resourceName, Sink::FacadeFactory &factory) 196void DummyResourceFactory::registerFacades(const QByteArray &resourceName, Sink::FacadeFactory &factory)
196{ 197{
197 factory.registerFacade<Sink::ApplicationDomain::Event, DummyResourceFacade>(resourceName); 198 factory.registerFacade<ApplicationDomain::Event, DefaultFacade<ApplicationDomain::Event>>(resourceName);
198 factory.registerFacade<Sink::ApplicationDomain::Mail, DummyResourceMailFacade>(resourceName); 199 factory.registerFacade<ApplicationDomain::Mail, DefaultFacade<ApplicationDomain::Mail>>(resourceName);
199 factory.registerFacade<Sink::ApplicationDomain::Folder, DummyResourceFolderFacade>(resourceName); 200 factory.registerFacade<ApplicationDomain::Folder, DefaultFacade<ApplicationDomain::Folder>>(resourceName);
200} 201}
201 202
202void DummyResourceFactory::registerAdaptorFactories(const QByteArray &resourceName, Sink::AdaptorFactoryRegistry &registry) 203void DummyResourceFactory::registerAdaptorFactories(const QByteArray &resourceName, Sink::AdaptorFactoryRegistry &registry)
diff --git a/examples/imapresource/CMakeLists.txt b/examples/imapresource/CMakeLists.txt
index 15a720d..46a8b08 100644
--- a/examples/imapresource/CMakeLists.txt
+++ b/examples/imapresource/CMakeLists.txt
@@ -8,7 +8,7 @@ find_package(KIMAP2 0.0.1 REQUIRED)
8 8
9include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 9include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
10 10
11add_library(${PROJECT_NAME} SHARED facade.cpp imapresource.cpp domainadaptor.cpp imapserverproxy.cpp) 11add_library(${PROJECT_NAME} SHARED imapresource.cpp imapserverproxy.cpp)
12qt5_use_modules(${PROJECT_NAME} Core Network) 12qt5_use_modules(${PROJECT_NAME} Core Network)
13target_link_libraries(${PROJECT_NAME} sink KF5::Mime KIMAP2) 13target_link_libraries(${PROJECT_NAME} sink KF5::Mime KIMAP2)
14 14
diff --git a/examples/imapresource/domainadaptor.cpp b/examples/imapresource/domainadaptor.cpp
deleted file mode 100644
index 4e74ad2..0000000
--- a/examples/imapresource/domainadaptor.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "domainadaptor.h"
21
22using namespace flatbuffers;
23
24ImapMailAdaptorFactory::ImapMailAdaptorFactory()
25 : DomainTypeAdaptorFactory()
26{
27
28}
29
30ImapFolderAdaptorFactory::ImapFolderAdaptorFactory()
31 : DomainTypeAdaptorFactory()
32{
33
34}
35
diff --git a/examples/imapresource/domainadaptor.h b/examples/imapresource/domainadaptor.h
deleted file mode 100644
index 06a513c..0000000
--- a/examples/imapresource/domainadaptor.h
+++ /dev/null
@@ -1,38 +0,0 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#pragma once
20
21#include <common/domainadaptor.h>
22#include "mail_generated.h"
23#include "folder_generated.h"
24#include "dummy_generated.h"
25
26class ImapMailAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail, Sink::ApplicationDomain::Buffer::Dummy, Sink::ApplicationDomain::Buffer::DummyBuilder>
27{
28public:
29 ImapMailAdaptorFactory();
30 virtual ~ImapMailAdaptorFactory() {};
31};
32
33class ImapFolderAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Folder, Sink::ApplicationDomain::Buffer::Dummy, Sink::ApplicationDomain::Buffer::DummyBuilder>
34{
35public:
36 ImapFolderAdaptorFactory();
37 virtual ~ImapFolderAdaptorFactory() {};
38};
diff --git a/examples/imapresource/facade.cpp b/examples/imapresource/facade.cpp
deleted file mode 100644
index 2829bb1..0000000
--- a/examples/imapresource/facade.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "facade.h"
21
22#include <QDir>
23#include <QFileInfo>
24
25#include "domainadaptor.h"
26#include "queryrunner.h"
27
28ImapResourceMailFacade::ImapResourceMailFacade(const Sink::ResourceContext &context)
29 : Sink::GenericFacade<Sink::ApplicationDomain::Mail>(context)
30{
31}
32
33ImapResourceMailFacade::~ImapResourceMailFacade()
34{
35}
36
37ImapResourceFolderFacade::ImapResourceFolderFacade(const Sink::ResourceContext &context)
38 : Sink::GenericFacade<Sink::ApplicationDomain::Folder>(context)
39{
40}
41
42ImapResourceFolderFacade::~ImapResourceFolderFacade()
43{
44}
diff --git a/examples/imapresource/facade.h b/examples/imapresource/facade.h
deleted file mode 100644
index 1d24856..0000000
--- a/examples/imapresource/facade.h
+++ /dev/null
@@ -1,36 +0,0 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include "common/facade.h"
23
24class ImapResourceMailFacade : public Sink::GenericFacade<Sink::ApplicationDomain::Mail>
25{
26public:
27 ImapResourceMailFacade(const Sink::ResourceContext &context);
28 virtual ~ImapResourceMailFacade();
29};
30
31class ImapResourceFolderFacade : public Sink::GenericFacade<Sink::ApplicationDomain::Folder>
32{
33public:
34 ImapResourceFolderFacade(const Sink::ResourceContext &context);
35 virtual ~ImapResourceFolderFacade();
36};
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp
index 09f57d5..0579dae 100644
--- a/examples/imapresource/imapresource.cpp
+++ b/examples/imapresource/imapresource.cpp
@@ -100,7 +100,7 @@ public:
100 QByteArray createFolder(const Imap::Folder &f) 100 QByteArray createFolder(const Imap::Folder &f)
101 { 101 {
102 const auto parentFolderRid = parentRid(f); 102 const auto parentFolderRid = parentRid(f);
103 SinkTrace() << "Creating folder: " << f.name() << parentFolderRid; 103 SinkTraceCtx(mLogCtx) << "Creating folder: " << f.name() << parentFolderRid;
104 104
105 const auto remoteId = folderRid(f); 105 const auto remoteId = folderRid(f);
106 Sink::ApplicationDomain::Folder folder; 106 Sink::ApplicationDomain::Folder folder;
@@ -123,7 +123,7 @@ public:
123 123
124 void synchronizeFolders(const QVector<Folder> &folderList) 124 void synchronizeFolders(const QVector<Folder> &folderList)
125 { 125 {
126 SinkTrace() << "Found folders " << folderList.size(); 126 SinkTraceCtx(mLogCtx) << "Found folders " << folderList.size();
127 127
128 scanForRemovals(ENTITY_TYPE_FOLDER, 128 scanForRemovals(ENTITY_TYPE_FOLDER,
129 [&folderList](const QByteArray &remoteId) -> bool { 129 [&folderList](const QByteArray &remoteId) -> bool {
@@ -164,14 +164,14 @@ public:
164 { 164 {
165 auto time = QSharedPointer<QTime>::create(); 165 auto time = QSharedPointer<QTime>::create();
166 time->start(); 166 time->start();
167 SinkTrace() << "Importing new mail." << folderRid; 167 SinkTraceCtx(mLogCtx) << "Importing new mail." << folderRid;
168 168
169 const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRid); 169 const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderRid);
170 170
171 const auto remoteId = assembleMailRid(folderLocalId, message.uid); 171 const auto remoteId = assembleMailRid(folderLocalId, message.uid);
172 172
173 Q_ASSERT(message.msg); 173 Q_ASSERT(message.msg);
174 SinkTrace() << "Found a mail " << remoteId << message.msg->subject(true)->asUnicodeString() << message.flags; 174 SinkTraceCtx(mLogCtx) << "Found a mail " << remoteId << message.msg->subject(true)->asUnicodeString() << message.flags;
175 175
176 auto mail = Sink::ApplicationDomain::Mail::create(mResourceInstanceIdentifier); 176 auto mail = Sink::ApplicationDomain::Mail::create(mResourceInstanceIdentifier);
177 mail.setFolder(folderLocalId); 177 mail.setFolder(folderLocalId);
@@ -181,7 +181,7 @@ public:
181 181
182 createOrModify(ENTITY_TYPE_MAIL, remoteId, mail); 182 createOrModify(ENTITY_TYPE_MAIL, remoteId, mail);
183 // const auto elapsed = time->elapsed(); 183 // const auto elapsed = time->elapsed();
184 // SinkTrace() << "Synchronized " << count << " mails in " << folderRid << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]"; 184 // SinkTraceCtx(mLogCtx) << "Synchronized " << count << " mails in " << folderRid << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
185 } 185 }
186 186
187 void synchronizeRemovals(const QByteArray &folderRid, const QSet<qint64> &messages) 187 void synchronizeRemovals(const QByteArray &folderRid, const QSet<qint64> &messages)
@@ -194,7 +194,7 @@ public:
194 return; 194 return;
195 } 195 }
196 196
197 SinkTrace() << "Finding removed mail: " << folderLocalId << " remoteId: " << folderRid; 197 SinkTraceCtx(mLogCtx) << "Finding removed mail: " << folderLocalId << " remoteId: " << folderRid;
198 198
199 int count = 0; 199 int count = 0;
200 200
@@ -370,7 +370,7 @@ public:
370 370
371 Sink::QueryBase applyMailDefaults(const Sink::QueryBase &query) 371 Sink::QueryBase applyMailDefaults(const Sink::QueryBase &query)
372 { 372 {
373 auto defaultDateFilter = QDate::currentDate().addDays(-14); 373 auto defaultDateFilter = QDate::currentDate().addDays(0 - mDaysToSync);
374 auto queryWithDefaults = query; 374 auto queryWithDefaults = query;
375 if (!queryWithDefaults.hasFilter<ApplicationDomain::Mail::Date>()) { 375 if (!queryWithDefaults.hasFilter<ApplicationDomain::Mail::Date>()) {
376 queryWithDefaults.filter(ApplicationDomain::Mail::Date::name, QVariant::fromValue(defaultDateFilter)); 376 queryWithDefaults.filter(ApplicationDomain::Mail::Date::name, QVariant::fromValue(defaultDateFilter));
@@ -382,7 +382,11 @@ public:
382 { 382 {
383 QList<Synchronizer::SyncRequest> list; 383 QList<Synchronizer::SyncRequest> list;
384 if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Mail>()) { 384 if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Mail>()) {
385 list << Synchronizer::SyncRequest{applyMailDefaults(query)}; 385 auto request = Synchronizer::SyncRequest{applyMailDefaults(query)};
386 if (query.hasFilter(ApplicationDomain::Mail::Folder::name)) {
387 request.applicableEntities << query.getFilter(ApplicationDomain::Mail::Folder::name).value.toByteArray();
388 }
389 list << request;
386 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) { 390 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) {
387 list << Synchronizer::SyncRequest{query}; 391 list << Synchronizer::SyncRequest{query};
388 } else { 392 } else {
@@ -393,15 +397,57 @@ public:
393 return list; 397 return list;
394 } 398 }
395 399
400 QByteArray getFolderFromLocalId(const QByteArray &id)
401 {
402 auto mailRemoteId = syncStore().resolveLocalId(ApplicationDomain::getTypeName<ApplicationDomain::Mail>(), id);
403 if (mailRemoteId.isEmpty()) {
404 return {};
405 }
406 return folderIdFromMailRid(mailRemoteId);
407 }
408
409 void mergeIntoQueue(const Synchronizer::SyncRequest &request, QList<Synchronizer::SyncRequest> &queue) Q_DECL_OVERRIDE
410 {
411 auto isIndividualMailSync = [](const Synchronizer::SyncRequest &request) {
412 if (request.requestType == SyncRequest::Synchronization) {
413 const auto query = request.query;
414 if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Mail>()) {
415 return !query.ids().isEmpty();
416 }
417 }
418 return false;
419
420 };
421
422 if (isIndividualMailSync(request)) {
423 auto newId = request.query.ids().first();
424 auto requestFolder = getFolderFromLocalId(newId);
425 if (requestFolder.isEmpty()) {
426 SinkWarningCtx(mLogCtx) << "Failed to find folder for local id. Ignoring request: " << request.query;
427 return;
428 }
429 for (auto &r : queue) {
430 if (isIndividualMailSync(r)) {
431 auto queueFolder = getFolderFromLocalId(r.query.ids().first());
432 if (requestFolder == queueFolder) {
433 //Merge
434 r.query.filter(newId);
435 SinkTrace() << "Merging request " << request.query;
436 SinkTrace() << " to " << r.query;
437 return;
438 }
439 }
440 }
441 }
442 queue << request;
443 }
444
396 KAsync::Job<void> login(QSharedPointer<ImapServerProxy> imap) 445 KAsync::Job<void> login(QSharedPointer<ImapServerProxy> imap)
397 { 446 {
398 SinkTrace() << "Connecting to:" << mServer << mPort; 447 SinkTrace() << "Connecting to:" << mServer << mPort;
399 SinkTrace() << "as:" << mUser; 448 SinkTrace() << "as:" << mUser;
400 return imap->login(mUser, mPassword) 449 return imap->login(mUser, mPassword)
401 .addToContext(imap) 450 .addToContext(imap);
402 .onError([](const KAsync::Error &error) {
403 SinkWarning() << "Login failed.";
404 });
405 } 451 }
406 452
407 KAsync::Job<QVector<Folder>> getFolderList(QSharedPointer<ImapServerProxy> imap, const Sink::QueryBase &query) 453 KAsync::Job<QVector<Folder>> getFolderList(QSharedPointer<ImapServerProxy> imap, const Sink::QueryBase &query)
@@ -431,6 +477,19 @@ public:
431 } 477 }
432 } 478 }
433 479
480 KAsync::Error getError(const KAsync::Error &error)
481 {
482 if (error) {
483 if (error.errorCode == Imap::CouldNotConnectError) {
484 return {ApplicationDomain::ConnectionError, error.errorMessage};
485 } else if (error.errorCode == Imap::SslHandshakeError) {
486 return {ApplicationDomain::LoginError, error.errorMessage};
487 }
488 return {ApplicationDomain::UnknownError, error.errorMessage};
489 }
490 return {};
491 }
492
434 KAsync::Job<void> synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE 493 KAsync::Job<void> synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE
435 { 494 {
436 auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort, &mSessionCache); 495 auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort, &mSessionCache);
@@ -446,11 +505,8 @@ public:
446 }); 505 });
447 }) 506 })
448 .then([=] (const KAsync::Error &error) { 507 .then([=] (const KAsync::Error &error) {
449 if (error) {
450 SinkWarning() << "Error during folder sync: " << error.errorMessage;
451 }
452 return imap->logout() 508 return imap->logout()
453 .then(KAsync::error(error)); 509 .then(KAsync::error(getError(error)));
454 }); 510 });
455 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Mail>()) { 511 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Mail>()) {
456 //TODO 512 //TODO
@@ -487,7 +543,7 @@ public:
487 synchronizeMails(folderRemoteId, m); 543 synchronizeMails(folderRemoteId, m);
488 }, 544 },
489 [=](int progress, int total) { 545 [=](int progress, int total) {
490 SinkLogCtx(mLogCtx) << "Progress: " << progress << " out of " << total; 546 reportProgress(progress, total);
491 //commit every 100 messages 547 //commit every 100 messages
492 if ((progress % 100) == 0) { 548 if ((progress % 100) == 0) {
493 commit(); 549 commit();
@@ -503,6 +559,8 @@ public:
503 return KAsync::value(folders) 559 return KAsync::value(folders)
504 .serialEach<void>([=](const Folder &folder) { 560 .serialEach<void>([=](const Folder &folder) {
505 SinkLog() << "Syncing folder " << folder.path(); 561 SinkLog() << "Syncing folder " << folder.path();
562 //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.
506 QDate dateFilter; 564 QDate dateFilter;
507 auto filter = query.getFilter<ApplicationDomain::Mail::Date>(); 565 auto filter = query.getFilter<ApplicationDomain::Mail::Date>();
508 if (filter.value.canConvert<QDate>()) { 566 if (filter.value.canConvert<QDate>()) {
@@ -510,7 +568,7 @@ public:
510 SinkLog() << " with date-range " << dateFilter; 568 SinkLog() << " with date-range " << dateFilter;
511 } 569 }
512 return synchronizeFolder(imap, folder, dateFilter, syncHeaders) 570 return synchronizeFolder(imap, folder, dateFilter, syncHeaders)
513 .onError([folder](const KAsync::Error &error) { 571 .onError([=](const KAsync::Error &error) {
514 SinkWarning() << "Failed to sync folder: " << folder.path() << "Error: " << error.errorMessage; 572 SinkWarning() << "Failed to sync folder: " << folder.path() << "Error: " << error.errorMessage;
515 }); 573 });
516 }); 574 });
@@ -518,11 +576,8 @@ public:
518 } 576 }
519 }) 577 })
520 .then([=] (const KAsync::Error &error) { 578 .then([=] (const KAsync::Error &error) {
521 if (error) {
522 SinkWarning() << "Error during sync: " << error.errorMessage;
523 }
524 return imap->logout() 579 return imap->logout()
525 .then(KAsync::error(error)); 580 .then(KAsync::error(getError(error)));
526 }); 581 });
527 } 582 }
528 return KAsync::error<void>("Nothing to do"); 583 return KAsync::error<void>("Nothing to do");
@@ -616,11 +671,11 @@ public:
616 if (!folder.getParent().isEmpty()) { 671 if (!folder.getParent().isEmpty()) {
617 parentFolder = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, folder.getParent()); 672 parentFolder = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, folder.getParent());
618 } 673 }
619 SinkTrace() << "Creating a new folder: " << parentFolder << folder.getName(); 674 SinkTraceCtx(mLogCtx) << "Creating a new folder: " << parentFolder << folder.getName();
620 auto rid = QSharedPointer<QByteArray>::create(); 675 auto rid = QSharedPointer<QByteArray>::create();
621 auto createFolder = login.then(imap->createSubfolder(parentFolder, folder.getName())) 676 auto createFolder = login.then(imap->createSubfolder(parentFolder, folder.getName()))
622 .then([imap, rid](const QString &createdFolder) { 677 .then([this, imap, rid](const QString &createdFolder) {
623 SinkTrace() << "Finished creating a new folder: " << createdFolder; 678 SinkTraceCtx(mLogCtx) << "Finished creating a new folder: " << createdFolder;
624 *rid = createdFolder.toUtf8(); 679 *rid = createdFolder.toUtf8();
625 }); 680 });
626 if (folder.getSpecialPurpose().isEmpty()) { 681 if (folder.getSpecialPurpose().isEmpty()) {
@@ -636,19 +691,19 @@ public:
636 specialPurposeFolders->insert(SpecialPurpose::getSpecialPurposeType(folder.name()), folder.path()); 691 specialPurposeFolders->insert(SpecialPurpose::getSpecialPurposeType(folder.name()), folder.path());
637 }; 692 };
638 })) 693 }))
639 .then([specialPurposeFolders, folder, imap, parentFolder, rid]() -> KAsync::Job<void> { 694 .then([this, specialPurposeFolders, folder, imap, parentFolder, rid]() -> KAsync::Job<void> {
640 for (const auto &purpose : folder.getSpecialPurpose()) { 695 for (const auto &purpose : folder.getSpecialPurpose()) {
641 if (specialPurposeFolders->contains(purpose)) { 696 if (specialPurposeFolders->contains(purpose)) {
642 auto f = specialPurposeFolders->value(purpose); 697 auto f = specialPurposeFolders->value(purpose);
643 SinkTrace() << "Merging specialpurpose folder with: " << f << " with purpose: " << purpose; 698 SinkTraceCtx(mLogCtx) << "Merging specialpurpose folder with: " << f << " with purpose: " << purpose;
644 *rid = f.toUtf8(); 699 *rid = f.toUtf8();
645 return KAsync::null<void>(); 700 return KAsync::null<void>();
646 } 701 }
647 } 702 }
648 SinkTrace() << "No match found for merging, creating a new folder"; 703 SinkTraceCtx(mLogCtx) << "No match found for merging, creating a new folder";
649 return imap->createSubfolder(parentFolder, folder.getName()) 704 return imap->createSubfolder(parentFolder, folder.getName())
650 .then([imap, rid](const QString &createdFolder) { 705 .then([this, imap, rid](const QString &createdFolder) {
651 SinkTrace() << "Finished creating a new folder: " << createdFolder; 706 SinkTraceCtx(mLogCtx) << "Finished creating a new folder: " << createdFolder;
652 *rid = createdFolder.toUtf8(); 707 *rid = createdFolder.toUtf8();
653 }); 708 });
654 709
@@ -659,18 +714,18 @@ public:
659 return mergeJob; 714 return mergeJob;
660 } 715 }
661 } else if (operation == Sink::Operation_Removal) { 716 } else if (operation == Sink::Operation_Removal) {
662 SinkTrace() << "Removing a folder: " << oldRemoteId; 717 SinkTraceCtx(mLogCtx) << "Removing a folder: " << oldRemoteId;
663 return login.then(imap->remove(oldRemoteId)) 718 return login.then(imap->remove(oldRemoteId))
664 .then([oldRemoteId, imap] { 719 .then([this, oldRemoteId, imap] {
665 SinkTrace() << "Finished removing a folder: " << oldRemoteId; 720 SinkTraceCtx(mLogCtx) << "Finished removing a folder: " << oldRemoteId;
666 return QByteArray(); 721 return QByteArray();
667 }); 722 });
668 } else if (operation == Sink::Operation_Modification) { 723 } else if (operation == Sink::Operation_Modification) {
669 SinkTrace() << "Renaming a folder: " << oldRemoteId << folder.getName(); 724 SinkTraceCtx(mLogCtx) << "Renaming a folder: " << oldRemoteId << folder.getName();
670 auto rid = QSharedPointer<QByteArray>::create(); 725 auto rid = QSharedPointer<QByteArray>::create();
671 return login.then(imap->renameSubfolder(oldRemoteId, folder.getName())) 726 return login.then(imap->renameSubfolder(oldRemoteId, folder.getName()))
672 .then([imap, rid](const QString &createdFolder) { 727 .then([this, imap, rid](const QString &createdFolder) {
673 SinkTrace() << "Finished renaming a folder: " << createdFolder; 728 SinkTraceCtx(mLogCtx) << "Finished renaming a folder: " << createdFolder;
674 *rid = createdFolder.toUtf8(); 729 *rid = createdFolder.toUtf8();
675 }) 730 })
676 .then([rid] { 731 .then([rid] {
@@ -685,6 +740,7 @@ public:
685 int mPort; 740 int mPort;
686 QString mUser; 741 QString mUser;
687 QString mPassword; 742 QString mPassword;
743 int mDaysToSync = 0;
688 QByteArray mResourceInstanceIdentifier; 744 QByteArray mResourceInstanceIdentifier;
689 Imap::SessionCache mSessionCache; 745 Imap::SessionCache mSessionCache;
690}; 746};
@@ -864,6 +920,7 @@ ImapResource::ImapResource(const ResourceContext &resourceContext)
864 synchronizer->mPort = port; 920 synchronizer->mPort = port;
865 synchronizer->mUser = user; 921 synchronizer->mUser = user;
866 synchronizer->mPassword = password; 922 synchronizer->mPassword = password;
923 synchronizer->mDaysToSync = 14;
867 setupSynchronizer(synchronizer); 924 setupSynchronizer(synchronizer);
868 925
869 auto inspector = QSharedPointer<ImapInspector>::create(resourceContext); 926 auto inspector = QSharedPointer<ImapInspector>::create(resourceContext);
@@ -873,7 +930,7 @@ ImapResource::ImapResource(const ResourceContext &resourceContext)
873 inspector->mPassword = password; 930 inspector->mPassword = password;
874 setupInspector(inspector); 931 setupInspector(inspector);
875 932
876 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new SpecialPurposeProcessor(resourceContext.resourceType, resourceContext.instanceId()) << new MailPropertyExtractor); 933 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new SpecialPurposeProcessor << new MailPropertyExtractor);
877 setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>()); 934 setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>());
878} 935}
879 936
@@ -898,14 +955,14 @@ Sink::Resource *ImapResourceFactory::createResource(const ResourceContext &conte
898 955
899void ImapResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory) 956void ImapResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory)
900{ 957{
901 factory.registerFacade<Sink::ApplicationDomain::Mail, ImapResourceMailFacade>(name); 958 factory.registerFacade<ApplicationDomain::Mail, DefaultFacade<ApplicationDomain::Mail>>(name);
902 factory.registerFacade<Sink::ApplicationDomain::Folder, ImapResourceFolderFacade>(name); 959 factory.registerFacade<ApplicationDomain::Folder, DefaultFacade<ApplicationDomain::Folder>>(name);
903} 960}
904 961
905void ImapResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry &registry) 962void ImapResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry &registry)
906{ 963{
907 registry.registerFactory<Sink::ApplicationDomain::Mail, ImapMailAdaptorFactory>(name); 964 registry.registerFactory<ApplicationDomain::Mail, DefaultAdaptorFactory<ApplicationDomain::Mail>>(name);
908 registry.registerFactory<Sink::ApplicationDomain::Folder, ImapFolderAdaptorFactory>(name); 965 registry.registerFactory<ApplicationDomain::Folder, DefaultAdaptorFactory<ApplicationDomain::Folder>>(name);
909} 966}
910 967
911void ImapResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier) 968void ImapResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier)
diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp
index dabdd8e..0cc43b8 100644
--- a/examples/imapresource/imapserverproxy.cpp
+++ b/examples/imapresource/imapserverproxy.cpp
@@ -69,7 +69,7 @@ static KAsync::Job<T> runJob(KJob *job, const std::function<T(KJob*)> &f)
69 QObject::connect(job, &KJob::result, [&future, f](KJob *job) { 69 QObject::connect(job, &KJob::result, [&future, f](KJob *job) {
70 SinkTrace() << "Job done: " << job->metaObject()->className(); 70 SinkTrace() << "Job done: " << job->metaObject()->className();
71 if (job->error()) { 71 if (job->error()) {
72 SinkWarning() << "Job failed: " << job->errorString(); 72 SinkWarning() << "Job failed: " << job->errorString() << job->metaObject()->className();
73 future.setError(job->error(), job->errorString()); 73 future.setError(job->error(), job->errorString());
74 } else { 74 } else {
75 future.setValue(f(job)); 75 future.setValue(f(job));
@@ -87,7 +87,7 @@ static KAsync::Job<void> runJob(KJob *job)
87 QObject::connect(job, &KJob::result, [&future](KJob *job) { 87 QObject::connect(job, &KJob::result, [&future](KJob *job) {
88 SinkTrace() << "Job done: " << job->metaObject()->className(); 88 SinkTrace() << "Job done: " << job->metaObject()->className();
89 if (job->error()) { 89 if (job->error()) {
90 SinkWarning() << "Job failed: " << job->errorString(); 90 SinkWarning() << "Job failed: " << job->errorString() << job->metaObject()->className();
91 future.setError(job->error(), job->errorString()); 91 future.setError(job->error(), job->errorString());
92 } else { 92 } else {
93 future.setFinished(); 93 future.setFinished();
@@ -159,6 +159,16 @@ KAsync::Job<void> ImapServerProxy::login(const QString &username, const QString
159 // SinkTrace() << "Found personal namespaces: " << mNamespaces.personal; 159 // SinkTrace() << "Found personal namespaces: " << mNamespaces.personal;
160 // SinkTrace() << "Found shared namespaces: " << mNamespaces.shared; 160 // SinkTrace() << "Found shared namespaces: " << mNamespaces.shared;
161 // SinkTrace() << "Found user namespaces: " << mNamespaces.user; 161 // SinkTrace() << "Found user namespaces: " << mNamespaces.user;
162 }).then([=] (const KAsync::Error &error) {
163 if (error) {
164 if (error.errorCode == KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT) {
165 return KAsync::error(CouldNotConnectError, "Failed to connect: " + error.errorMessage);
166 } else if (error.errorCode == KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED) {
167 return KAsync::error(SslHandshakeError, "Ssl handshake failed: " + error.errorMessage);
168 }
169 return KAsync::error(error);
170 }
171 return KAsync::null();
162 }); 172 });
163} 173}
164 174
diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h
index 6eb47ee..872f032 100644
--- a/examples/imapresource/imapserverproxy.h
+++ b/examples/imapresource/imapserverproxy.h
@@ -29,6 +29,12 @@
29 29
30namespace Imap { 30namespace Imap {
31 31
32enum ErrorCode {
33 NoError,
34 CouldNotConnectError,
35 SslHandshakeError
36};
37
32namespace Flags 38namespace Flags
33{ 39{
34 /// The flag for a message being seen (i.e. opened by user). 40 /// The flag for a message being seen (i.e. opened by user).
diff --git a/examples/maildirresource/CMakeLists.txt b/examples/maildirresource/CMakeLists.txt
index e4d113c..a8f0359 100644
--- a/examples/maildirresource/CMakeLists.txt
+++ b/examples/maildirresource/CMakeLists.txt
@@ -5,13 +5,12 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
5 5
6find_package(KF5 COMPONENTS REQUIRED Mime) 6find_package(KF5 COMPONENTS REQUIRED Mime)
7 7
8add_library(${PROJECT_NAME} SHARED facade.cpp maildirresource.cpp domainadaptor.cpp) 8add_library(${PROJECT_NAME} SHARED facade.cpp maildirresource.cpp libmaildir/maildir.cpp libmaildir/keycache.cpp)
9qt5_use_modules(${PROJECT_NAME} Core Network) 9qt5_use_modules(${PROJECT_NAME} Core Network)
10target_link_libraries(${PROJECT_NAME} sink maildir KF5::Mime) 10target_link_libraries(${PROJECT_NAME} sink KF5::Mime)
11 11
12install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) 12install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH})
13 13
14add_definitions(-DTESTDATAPATH="${CMAKE_CURRENT_SOURCE_DIR}/tests/data") 14add_definitions(-DTESTDATAPATH="${CMAKE_CURRENT_SOURCE_DIR}/tests/data")
15 15
16add_subdirectory(libmaildir)
17add_subdirectory(tests) 16add_subdirectory(tests)
diff --git a/examples/maildirresource/domainadaptor.cpp b/examples/maildirresource/domainadaptor.cpp
deleted file mode 100644
index 71b2354..0000000
--- a/examples/maildirresource/domainadaptor.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "domainadaptor.h"
21
22using namespace flatbuffers;
23
24MaildirMailAdaptorFactory::MaildirMailAdaptorFactory()
25 : DomainTypeAdaptorFactory()
26{
27
28}
29
30MaildirFolderAdaptorFactory::MaildirFolderAdaptorFactory()
31 : DomainTypeAdaptorFactory()
32{
33
34}
35
diff --git a/examples/maildirresource/domainadaptor.h b/examples/maildirresource/domainadaptor.h
deleted file mode 100644
index 700d2e5..0000000
--- a/examples/maildirresource/domainadaptor.h
+++ /dev/null
@@ -1,38 +0,0 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#pragma once
20
21#include <common/domainadaptor.h>
22#include "mail_generated.h"
23#include "folder_generated.h"
24#include "dummy_generated.h"
25
26class MaildirMailAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Mail, Sink::ApplicationDomain::Buffer::Dummy, Sink::ApplicationDomain::Buffer::DummyBuilder>
27{
28public:
29 MaildirMailAdaptorFactory();
30 virtual ~MaildirMailAdaptorFactory() {};
31};
32
33class MaildirFolderAdaptorFactory : public DomainTypeAdaptorFactory<Sink::ApplicationDomain::Folder, Sink::ApplicationDomain::Buffer::Dummy, Sink::ApplicationDomain::Buffer::DummyBuilder>
34{
35public:
36 MaildirFolderAdaptorFactory();
37 virtual ~MaildirFolderAdaptorFactory() {};
38};
diff --git a/examples/maildirresource/libmaildir/CMakeLists.txt b/examples/maildirresource/libmaildir/CMakeLists.txt
deleted file mode 100644
index e7803f5..0000000
--- a/examples/maildirresource/libmaildir/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
1# add_subdirectory( tests )
2
3set(maildir_LIB_SRCS keycache.cpp maildir.cpp)
4
5add_library(maildir ${LIBRARY_TYPE} ${maildir_LIB_SRCS})
6qt5_use_modules(maildir Core Network)
7# set_target_properties(maildir PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
8install(TARGETS maildir ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/examples/maildirresource/libmaildir/maildir.cpp b/examples/maildirresource/libmaildir/maildir.cpp
index a889ea2..203f6a6 100644
--- a/examples/maildirresource/libmaildir/maildir.cpp
+++ b/examples/maildirresource/libmaildir/maildir.cpp
@@ -1,5 +1,6 @@
1/* 1/*
2 Copyright (c) 2007 Till Adam <adam@kde.org> 2 Copyright (c) 2007 Till Adam <adam@kde.org>
3 Copyright (c) 2017 Christian Mollekopf <mollekopf@kolabsys.com>
3 4
4 This library is free software; you can redistribute it and/or modify it 5 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by 6 under the terms of the GNU Library General Public License as published by
@@ -24,7 +25,6 @@
24#include <QDirIterator> 25#include <QDirIterator>
25#include <QFileInfo> 26#include <QFileInfo>
26#include <QHostInfo> 27#include <QHostInfo>
27#include <QUuid>
28#include <QLoggingCategory> 28#include <QLoggingCategory>
29 29
30Q_LOGGING_CATEGORY(log, "maildir"); 30Q_LOGGING_CATEGORY(log, "maildir");
diff --git a/examples/maildirresource/libmaildir/maildir.h b/examples/maildirresource/libmaildir/maildir.h
index c10b046..a72f2bc 100644
--- a/examples/maildirresource/libmaildir/maildir.h
+++ b/examples/maildirresource/libmaildir/maildir.h
@@ -1,5 +1,6 @@
1/* 1/*
2 Copyright (c) 2007 Till Adam <adam@kde.org> 2 Copyright (c) 2007 Till Adam <adam@kde.org>
3 Copyright (c) 2017 Christian Mollekopf <mollekopf@kolabsys.com>
3 4
4 This library is free software; you can redistribute it and/or modify it 5 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by 6 under the terms of the GNU Library General Public License as published by
@@ -20,9 +21,6 @@
20#ifndef MAILDIR_H 21#ifndef MAILDIR_H
21#define MAILDIR_H 22#define MAILDIR_H
22 23
23
24#include "maildir_export.h"
25
26#include <QString> 24#include <QString>
27#include <QStringList> 25#include <QStringList>
28 26
@@ -30,7 +28,7 @@ class QDateTime;
30 28
31namespace KPIM { 29namespace KPIM {
32 30
33class MAILDIR_EXPORT Maildir 31class Maildir
34{ 32{
35public: 33public:
36 /** 34 /**
diff --git a/examples/maildirresource/libmaildir/maildir_export.h b/examples/maildirresource/libmaildir/maildir_export.h
deleted file mode 100644
index b330fd0..0000000
--- a/examples/maildirresource/libmaildir/maildir_export.h
+++ /dev/null
@@ -1,43 +0,0 @@
1/* This file is part of the KDE project
2 Copyright (C) 2007 David Faure <faure@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#ifndef MAILDIR_EXPORT_H
21#define MAILDIR_EXPORT_H
22
23/* needed for KDE_EXPORT and KDE_IMPORT macros */
24// #include <kdemacros.h>
25
26#ifndef MAILDIR_EXPORT
27// # if defined(KDEPIM_STATIC_LIBS)
28// #<{(| No export/import for static libraries |)}>#
29# define MAILDIR_EXPORT
30// # elif defined(MAKE_MAILDIR_LIB)
31// #<{(| We are building this library |)}>#
32// # define MAILDIR_EXPORT KDE_EXPORT
33// # else
34// #<{(| We are using this library |)}>#
35// # define MAILDIR_EXPORT KDE_IMPORT
36// # endif
37#endif
38//
39// # ifndef MAILDIR_EXPORT_DEPRECATED
40// # define MAILDIR_EXPORT_DEPRECATED KDE_DEPRECATED MAILDIR_EXPORT
41// # endif
42
43#endif
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp
index 813d84f..40bab37 100644
--- a/examples/maildirresource/maildirresource.cpp
+++ b/examples/maildirresource/maildirresource.cpp
@@ -37,7 +37,7 @@
37 37
38#include <QDir> 38#include <QDir>
39#include <QDirIterator> 39#include <QDirIterator>
40#include <KMime/KMime/KMimeMessage> 40#include <KMime/KMimeMessage>
41 41
42//This is the resources entity type, and not the domain type 42//This is the resources entity type, and not the domain type
43#define ENTITY_TYPE_MAIL "mail" 43#define ENTITY_TYPE_MAIL "mail"
@@ -360,12 +360,12 @@ public:
360 360
361 KAsync::Job<void> synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE 361 KAsync::Job<void> synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE
362 { 362 {
363 auto job = KAsync::start<void>([this] { 363 auto job = KAsync::start([this] {
364 KPIM::Maildir maildir(mMaildirPath, true); 364 KPIM::Maildir maildir(mMaildirPath, true);
365 if (!maildir.isValid(false)) { 365 if (!maildir.isValid(false)) {
366 return KAsync::error<void>(1, "Maildir path doesn't point to a valid maildir: " + mMaildirPath); 366 return KAsync::error(ApplicationDomain::ConfigurationError, "Maildir path doesn't point to a valid maildir: " + mMaildirPath);
367 } 367 }
368 return KAsync::null<void>(); 368 return KAsync::null();
369 }); 369 });
370 370
371 if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) { 371 if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) {
@@ -551,7 +551,7 @@ MaildirResource::MaildirResource(const Sink::ResourceContext &resourceContext)
551 setupSynchronizer(synchronizer); 551 setupSynchronizer(synchronizer);
552 setupInspector(QSharedPointer<MaildirInspector>::create(resourceContext)); 552 setupInspector(QSharedPointer<MaildirInspector>::create(resourceContext));
553 553
554 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new SpecialPurposeProcessor(resourceContext.resourceType, resourceContext.instanceId()) << new MaildirMimeMessageMover(resourceContext.instanceId(), mMaildirPath) << new MaildirMailPropertyExtractor); 554 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new SpecialPurposeProcessor << new MaildirMimeMessageMover(resourceContext.instanceId(), mMaildirPath) << new MaildirMailPropertyExtractor);
555 setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << new FolderPreprocessor(mMaildirPath)); 555 setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << new FolderPreprocessor(mMaildirPath));
556 556
557 KPIM::Maildir dir(mMaildirPath, true); 557 KPIM::Maildir dir(mMaildirPath, true);
@@ -596,8 +596,8 @@ void MaildirResourceFactory::registerFacades(const QByteArray &name, Sink::Facad
596 596
597void MaildirResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry &registry) 597void MaildirResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry &registry)
598{ 598{
599 registry.registerFactory<Sink::ApplicationDomain::Mail, MaildirMailAdaptorFactory>(name); 599 registry.registerFactory<ApplicationDomain::Mail, DefaultAdaptorFactory<ApplicationDomain::Mail>>(name);
600 registry.registerFactory<Sink::ApplicationDomain::Folder, MaildirFolderAdaptorFactory>(name); 600 registry.registerFactory<ApplicationDomain::Folder, DefaultAdaptorFactory<ApplicationDomain::Folder>>(name);
601} 601}
602 602
603void MaildirResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier) 603void MaildirResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier)
diff --git a/examples/mailtransportresource/mailtransport.cpp b/examples/mailtransportresource/mailtransport.cpp
index 3d56af9..84c1556 100644
--- a/examples/mailtransportresource/mailtransport.cpp
+++ b/examples/mailtransportresource/mailtransport.cpp
@@ -41,17 +41,17 @@ static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)
41 struct upload_status *upload_ctx = (struct upload_status *)userp; 41 struct upload_status *upload_ctx = (struct upload_status *)userp;
42 const char *data; 42 const char *data;
43 43
44 if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { 44 if ((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
45 return 0; 45 return 0;
46 } 46 }
47 47
48 data = &upload_ctx->data[upload_ctx->offset]; 48 data = &upload_ctx->data[upload_ctx->offset];
49 if(data) { 49 if (data) {
50 size_t len = strlen(data); 50 size_t len = strlen(data);
51 if (len > size * nmemb) { 51 if (len > size * nmemb) {
52 len = size * nmemb; 52 len = size * nmemb;
53 } 53 }
54 fprintf(stderr, "read n bytes: %d\n", int(len)); 54 fprintf(stdout, "read n bytes: %d\n", int(len));
55 memcpy(ptr, data, len); 55 memcpy(ptr, data, len);
56 upload_ctx->offset += len; 56 upload_ctx->offset += len;
57 return len; 57 return len;
@@ -69,8 +69,17 @@ static int progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow
69 return 0; 69 return 0;
70} 70}
71 71
72static int debug_callback(CURL *handle,
73 curl_infotype type,
74 char *data,
75 size_t size,
76 void *userptr)
77{
78 fprintf(stdout, "CURL_DEBUG: %s", data);
79 return 0;
80}
72 81
73bool sendMessageCurl(const char *to[], int numTos, const char *cc[], int numCcs, const char *msg, bool useTls, const char* from, const char *username, const char *password, const char *server, bool verifyPeer, const QByteArray &cacert) 82bool sendMessageCurl(const char *to[], int numTos, const char *cc[], int numCcs, const char *msg, bool useTls, const char* from, const char *username, const char *password, const char *server, bool verifyPeer, const QByteArray &cacert, QByteArray &errorMessage)
74{ 83{
75 CURL *curl; 84 CURL *curl;
76 CURLcode res = CURLE_OK; 85 CURLcode res = CURLE_OK;
@@ -88,7 +97,7 @@ bool sendMessageCurl(const char *to[], int numTos, const char *cc[], int numCcs,
88 curl_easy_setopt(curl, CURLOPT_URL, server); 97 curl_easy_setopt(curl, CURLOPT_URL, server);
89 98
90 if (useTls) { 99 if (useTls) {
91 curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_TRY); 100 curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
92 } 101 }
93 102
94 if (!verifyPeer) { 103 if (!verifyPeer) {
@@ -121,19 +130,29 @@ bool sendMessageCurl(const char *to[], int numTos, const char *cc[], int numCcs,
121 /* Since the traffic will be encrypted, it is very useful to turn on debug 130 /* Since the traffic will be encrypted, it is very useful to turn on debug
122 * information within libcurl to see what is happening during the transfer. 131 * information within libcurl to see what is happening during the transfer.
123 */ 132 */
124 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); 133 // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
134 curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, debug_callback);
125 135
126 // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); 136 // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L);
127 //Connection timeout of 10s 137 //Connection timeout of 40s
128 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); 138 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 40L);
129 139
130 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); 140 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
131 curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); 141 curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
142 char errorBuffer[CURL_ERROR_SIZE];
143 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);
132 144
133 res = curl_easy_perform(curl); 145 res = curl_easy_perform(curl);
134 if(res != CURLE_OK) { 146 if(res != CURLE_OK) {
135 fprintf(stderr, "curl_easy_perform() failed: %s\n", 147 errorMessage += curl_easy_strerror(res);
136 curl_easy_strerror(res)); 148 errorMessage += "; ";
149 }
150 long http_code = 0;
151 curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
152 if (http_code == 200 && res != CURLE_ABORTED_BY_CALLBACK) {
153 //Succeeded
154 } else {
155 errorMessage += errorBuffer;
137 } 156 }
138 curl_slist_free_all(recipients); 157 curl_slist_free_all(recipients);
139 curl_easy_cleanup(curl); 158 curl_easy_cleanup(curl);
@@ -147,8 +166,6 @@ bool sendMessageCurl(const char *to[], int numTos, const char *cc[], int numCcs,
147bool MailTransport::sendMessage(const KMime::Message::Ptr &message, const QByteArray &server, const QByteArray &username, const QByteArray &password, const QByteArray &cacert, MailTransport::Options options) 166bool MailTransport::sendMessage(const KMime::Message::Ptr &message, const QByteArray &server, const QByteArray &username, const QByteArray &password, const QByteArray &cacert, MailTransport::Options options)
148{ 167{
149 QByteArray msg = message->encodedContent(); 168 QByteArray msg = message->encodedContent();
150 SinkLog() << "Sending message " << server << username << password << cacert;
151 SinkTrace() << "Sending message " << msg;
152 169
153 QByteArray from(message->from(true)->mailboxes().isEmpty() ? QByteArray() : message->from(true)->mailboxes().first().address()); 170 QByteArray from(message->from(true)->mailboxes().isEmpty() ? QByteArray() : message->from(true)->mailboxes().first().address());
154 QList<QByteArray> toList; 171 QList<QByteArray> toList;
@@ -159,8 +176,11 @@ bool MailTransport::sendMessage(const KMime::Message::Ptr &message, const QByteA
159 for (const auto &mb : message->cc(true)->mailboxes()) { 176 for (const auto &mb : message->cc(true)->mailboxes()) {
160 ccList << mb.address(); 177 ccList << mb.address();
161 } 178 }
162 bool verifyPeer = options & VerifyPeers; 179 const bool verifyPeer = options.testFlag(VerifyPeers);
163 bool useTls = options & UseTls; 180 const bool useTls = options.testFlag(UseTls);
181
182 SinkLog() << "Sending message " << server << username << password << "CaCert: " << cacert << "Use tls: " << useTls << " Verify peer: " << verifyPeer;
183 SinkTrace() << "Sending message " << msg;
164 184
165 const int numTos = toList.size(); 185 const int numTos = toList.size();
166 const char* to[numTos]; 186 const char* to[numTos];
@@ -173,6 +193,14 @@ bool MailTransport::sendMessage(const KMime::Message::Ptr &message, const QByteA
173 for (int i = 0; i < numCcs; i++) { 193 for (int i = 0; i < numCcs; i++) {
174 cc[i] = ccList.at(i); 194 cc[i] = ccList.at(i);
175 } 195 }
176 196 //Because curl will fail with smtps, but it won't tell you why.
177 return sendMessageCurl(to, numTos, cc, numCcs, msg, useTls, from.isEmpty() ? nullptr : from, username, password, server, verifyPeer, cacert); 197 auto serverAddress = server;
198 serverAddress.replace("smtps://", "smtp://");
199
200 QByteArray errorMessage;
201 auto ret = sendMessageCurl(to, numTos, cc, numCcs, msg, useTls, from.isEmpty() ? nullptr : from, username, password, serverAddress, verifyPeer, cacert, errorMessage);
202 if (!ret) {
203 SinkWarning() << "Failed to send message: " << errorMessage;
204 }
205 return ret;
178} 206}
diff --git a/examples/mailtransportresource/mailtransport.h b/examples/mailtransportresource/mailtransport.h
index 3ef4a6d..662fdc9 100644
--- a/examples/mailtransportresource/mailtransport.h
+++ b/examples/mailtransportresource/mailtransport.h
@@ -26,8 +26,8 @@
26namespace MailTransport 26namespace MailTransport
27{ 27{
28 enum Option { 28 enum Option {
29 UseTls, 29 UseTls = 1,
30 VerifyPeers 30 VerifyPeers = 2
31 }; 31 };
32 Q_DECLARE_FLAGS(Options, Option); 32 Q_DECLARE_FLAGS(Options, Option);
33 33
diff --git a/examples/mailtransportresource/mailtransportresource.cpp b/examples/mailtransportresource/mailtransportresource.cpp
index 88a90c6..8a4ef92 100644
--- a/examples/mailtransportresource/mailtransportresource.cpp
+++ b/examples/mailtransportresource/mailtransportresource.cpp
@@ -30,7 +30,6 @@
30#include <KMime/Message> 30#include <KMime/Message>
31 31
32#include "mailtransport.h" 32#include "mailtransport.h"
33#include "mail_generated.h"
34#include "inspection.h" 33#include "inspection.h"
35#include <synchronizer.h> 34#include <synchronizer.h>
36#include <log.h> 35#include <log.h>
@@ -44,6 +43,47 @@ SINK_DEBUG_AREA("mailtransportresource")
44 43
45using namespace Sink; 44using namespace Sink;
46 45
46class MailtransportPreprocessor : public Sink::Preprocessor
47{
48public:
49 MailtransportPreprocessor() : Sink::Preprocessor() {}
50
51 QByteArray getTargetResource()
52 {
53 using namespace Sink::ApplicationDomain;
54
55 auto resource = Store::readOne<ApplicationDomain::SinkResource>(Query{}.filter(resourceInstanceIdentifier()).request<ApplicationDomain::SinkResource::Account>());
56 if (resource.identifier().isEmpty()) {
57 SinkWarning() << "Failed to retrieve this resource: " << resourceInstanceIdentifier();
58 }
59 Query query;
60 query.containsFilter<ApplicationDomain::SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::sent);
61 query.filter<ApplicationDomain::SinkResource::Account>(resource.getAccount());
62 auto targetResource = Store::readOne<ApplicationDomain::SinkResource>(query);
63 if (targetResource.identifier().isEmpty()) {
64 SinkWarning() << "Failed to find target resource: " << targetResource.identifier();
65 }
66 return targetResource.identifier();
67 }
68
69 virtual Result processModification(Type type, const ApplicationDomain::ApplicationDomainType &current, ApplicationDomain::ApplicationDomainType &diff) Q_DECL_OVERRIDE
70 {
71 if (type == Preprocessor::Modification) {
72 using namespace Sink::ApplicationDomain;
73 if (diff.changedProperties().contains(Mail::Trash::name)) {
74 //Move back to regular resource
75 diff.setResource(getTargetResource());
76 return {MoveToResource};
77 } else if (diff.changedProperties().contains(Mail::Draft::name)) {
78 //Move back to regular resource
79 diff.setResource(getTargetResource());
80 return {MoveToResource};
81 }
82 }
83 return {NoAction};
84 }
85};
86
47class MailtransportSynchronizer : public Sink::Synchronizer { 87class MailtransportSynchronizer : public Sink::Synchronizer {
48public: 88public:
49 MailtransportSynchronizer(const Sink::ResourceContext &resourceContext) 89 MailtransportSynchronizer(const Sink::ResourceContext &resourceContext)
@@ -55,17 +95,22 @@ public:
55 95
56 KAsync::Job<void> send(const ApplicationDomain::Mail &mail, const MailtransportResource::Settings &settings) 96 KAsync::Job<void> send(const ApplicationDomain::Mail &mail, const MailtransportResource::Settings &settings)
57 { 97 {
58 return KAsync::start<void>([=] { 98 return KAsync::start([=] {
59 if (!syncStore().readValue(mail.identifier()).isEmpty()) { 99 if (!syncStore().readValue(mail.identifier()).isEmpty()) {
60 SinkLog() << "Mail is already sent: " << mail.identifier(); 100 SinkLog() << "Mail is already sent: " << mail.identifier();
61 return KAsync::null(); 101 return KAsync::null();
62 } 102 }
103 emitNotification(Notification::Info, ApplicationDomain::SyncInProgress, "Sending message.", {}, {mail.identifier()});
63 const auto data = mail.getMimeMessage(); 104 const auto data = mail.getMimeMessage();
64 auto msg = KMime::Message::Ptr::create(); 105 auto msg = KMime::Message::Ptr::create();
65 msg->setHead(KMime::CRLFtoLF(data)); 106 msg->setHead(KMime::CRLFtoLF(data));
66 msg->parse(); 107 msg->parse();
67 if (settings.testMode) { 108 if (settings.testMode) {
68 SinkLog() << "I would totally send that mail, but I'm in test mode." << mail.identifier(); 109 auto subject = msg->subject(true)->asUnicodeString();
110 SinkLog() << "I would totally send that mail, but I'm in test mode." << mail.identifier() << subject;
111 if (!subject.contains("send")) {
112 return KAsync::error("Failed to send the message.");
113 }
69 auto path = resourceStorageLocation(mResourceInstanceIdentifier) + "/test/"; 114 auto path = resourceStorageLocation(mResourceInstanceIdentifier) + "/test/";
70 SinkTrace() << path; 115 SinkTrace() << path;
71 QDir dir; 116 QDir dir;
@@ -77,11 +122,16 @@ public:
77 } else { 122 } else {
78 MailTransport::Options options; 123 MailTransport::Options options;
79 if (settings.server.contains("smtps")) { 124 if (settings.server.contains("smtps")) {
80 options &= MailTransport::UseTls; 125 options |= MailTransport::UseTls;
81 } 126 }
82 if (!MailTransport::sendMessage(msg, settings.server.toUtf8(), settings.username.toUtf8(), settings.password.toUtf8(), settings.cacert.toUtf8(), options)) { 127 if (!MailTransport::sendMessage(msg, settings.server.toUtf8(), settings.username.toUtf8(), settings.password.toUtf8(), settings.cacert.toUtf8(), options)) {
83 SinkWarning() << "Failed to send message: " << mail; 128 SinkWarning() << "Failed to send message: " << mail;
129 emitNotification(Notification::Warning, ApplicationDomain::SyncError, "Failed to send message.", {}, {mail.identifier()});
130 emitNotification(Notification::Warning, ApplicationDomain::TransmissionError, "Failed to send message.", {}, {mail.identifier()});
84 return KAsync::error("Failed to send the message."); 131 return KAsync::error("Failed to send the message.");
132 } else {
133 emitNotification(Notification::Info, ApplicationDomain::SyncSuccess, "Message successfully sent.", {}, {mail.identifier()});
134 emitNotification(Notification::Info, ApplicationDomain::TransmissionSuccess, "Message successfully sent.", {}, {mail.identifier()});
85 } 135 }
86 } 136 }
87 syncStore().writeValue(mail.identifier(), "sent"); 137 syncStore().writeValue(mail.identifier(), "sent");
@@ -100,9 +150,8 @@ public:
100 query.filter<ApplicationDomain::SinkResource::Account>(resource.getAccount()); 150 query.filter<ApplicationDomain::SinkResource::Account>(resource.getAccount());
101 return Store::fetchOne<ApplicationDomain::SinkResource>(query) 151 return Store::fetchOne<ApplicationDomain::SinkResource>(query)
102 .then([this, modifiedMail](const ApplicationDomain::SinkResource &resource) { 152 .then([this, modifiedMail](const ApplicationDomain::SinkResource &resource) {
103 //First modify the mail to have the sent property set to true 153 //Modify the mail to have the sent property set to true, and move it to the new resource.
104 modify(modifiedMail, resource.identifier(), true); 154 modify(modifiedMail, resource.identifier(), true);
105 return KAsync::null<void>();
106 }); 155 });
107 }); 156 });
108 } 157 }
@@ -112,12 +161,10 @@ public:
112 return KAsync::start<void>([this]() { 161 return KAsync::start<void>([this]() {
113 QList<ApplicationDomain::Mail> toSend; 162 QList<ApplicationDomain::Mail> toSend;
114 SinkLog() << "Looking for mails to send."; 163 SinkLog() << "Looking for mails to send.";
115 store().readAll<ApplicationDomain::Mail>([&](const ApplicationDomain::Mail &mail) -> bool { 164 store().readAll<ApplicationDomain::Mail>([&](const ApplicationDomain::Mail &mail) {
116 SinkTrace() << "Found mail: " << mail.identifier();
117 if (!mail.getSent()) { 165 if (!mail.getSent()) {
118 toSend << mail; 166 toSend << mail;
119 } 167 }
120 return true;
121 }); 168 });
122 SinkLog() << "Found " << toSend.size() << " mails to send"; 169 SinkLog() << "Found " << toSend.size() << " mails to send";
123 auto job = KAsync::null<void>(); 170 auto job = KAsync::null<void>();
@@ -192,7 +239,7 @@ MailtransportResource::MailtransportResource(const Sink::ResourceContext &resour
192 setupSynchronizer(synchronizer); 239 setupSynchronizer(synchronizer);
193 setupInspector(QSharedPointer<MailtransportInspector>::create(resourceContext)); 240 setupInspector(QSharedPointer<MailtransportInspector>::create(resourceContext));
194 241
195 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new MailPropertyExtractor); 242 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new MailPropertyExtractor << new MailtransportPreprocessor);
196} 243}
197 244
198MailtransportResourceFactory::MailtransportResourceFactory(QObject *parent) 245MailtransportResourceFactory::MailtransportResourceFactory(QObject *parent)
diff --git a/examples/mailtransportresource/tests/mailtransporttest.cpp b/examples/mailtransportresource/tests/mailtransporttest.cpp
index 3b848b3..e4cc447 100644
--- a/examples/mailtransportresource/tests/mailtransporttest.cpp
+++ b/examples/mailtransportresource/tests/mailtransporttest.cpp
@@ -47,7 +47,8 @@ private slots:
47 47
48 void cleanup() 48 void cleanup()
49 { 49 {
50 VERIFYEXEC(ResourceControl::shutdown(mResourceInstanceIdentifier)); 50 VERIFYEXEC(Store::removeDataFromDisk(mResourceInstanceIdentifier));
51 VERIFYEXEC(Store::removeDataFromDisk(mStorageResource));
51 } 52 }
52 53
53 void init() 54 void init()
@@ -58,7 +59,8 @@ private slots:
58 void testSendMail() 59 void testSendMail()
59 { 60 {
60 auto message = KMime::Message::Ptr::create(); 61 auto message = KMime::Message::Ptr::create();
61 message->subject(true)->fromUnicodeString(QString::fromLatin1("Foobar"), "utf8"); 62 message->messageID(true)->generate("foo.com");
63 message->subject(true)->fromUnicodeString(QString::fromLatin1("send: Foobar"), "utf8");
62 message->assemble(); 64 message->assemble();
63 65
64 auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier); 66 auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier);
@@ -67,9 +69,10 @@ private slots:
67 VERIFYEXEC(Store::create(mail)); 69 VERIFYEXEC(Store::create(mail));
68 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier)); 70 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier));
69 71
72 //FIXME the email is sent already because changereplay kicks of automatically
70 //Ensure the mail is queryable in the outbox 73 //Ensure the mail is queryable in the outbox
71 auto mailInOutbox = Store::readOne<ApplicationDomain::Mail>(Query().resourceFilter(mResourceInstanceIdentifier).filter<Mail::Sent>(false).request<Mail::Subject>().request<Mail::Folder>().request<Mail::MimeMessage>().request<Mail::Sent>()); 74 // auto mailInOutbox = Store::readOne<ApplicationDomain::Mail>(Query().resourceFilter(mResourceInstanceIdentifier).filter<Mail::Sent>(false).request<Mail::Subject>().request<Mail::Folder>().request<Mail::MimeMessage>().request<Mail::Sent>());
72 QVERIFY(!mailInOutbox.identifier().isEmpty()); 75 // QVERIFY(!mailInOutbox.identifier().isEmpty());
73 76
74 //Ensure the mail is sent and moved to the sent mail folder on sync 77 //Ensure the mail is sent and moved to the sent mail folder on sync
75 VERIFYEXEC(Store::synchronize(Query().resourceFilter(mResourceInstanceIdentifier))); 78 VERIFYEXEC(Store::synchronize(Query().resourceFilter(mResourceInstanceIdentifier)));
@@ -81,7 +84,37 @@ private slots:
81 QVERIFY(!mailInSentMailFolder.getSubject().isEmpty()); 84 QVERIFY(!mailInSentMailFolder.getSubject().isEmpty());
82 } 85 }
83 86
84 //TODO test mail that fails to be sent. add a special header to the mail and have the resource fail sending. Ensure we can modify the mail to fix sending of the message. 87 void testSendFailure()
88 {
89 auto message = KMime::Message::Ptr::create();
90 message->messageID(true)->generate("foo.com");
91 message->subject(true)->fromUnicodeString(QString::fromLatin1("error: Foobar"), "utf8");
92 message->assemble();
93
94 auto mail = ApplicationDomain::Mail::create(mResourceInstanceIdentifier);
95 mail.setMimeMessage(message->encodedContent());
96
97 VERIFYEXEC(Store::create(mail));
98 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier));
99
100 //Ensure the mail is queryable in the outbox
101 auto mailInOutbox = Store::readOne<ApplicationDomain::Mail>(Query().resourceFilter(mResourceInstanceIdentifier).filter<Mail::Sent>(false));
102 QVERIFY(!mailInOutbox.identifier().isEmpty());
103
104 //Modify back to drafts
105 auto modifiedMail = mailInOutbox;
106 modifiedMail.setDraft(true);
107 VERIFYEXEC(Store::modify(modifiedMail));
108 VERIFYEXEC(ResourceControl::flushMessageQueue(QByteArrayList() << mResourceInstanceIdentifier));
109
110 QTest::qWait(100);
111 auto mailsInOutbox = Store::read<ApplicationDomain::Mail>(Query().resourceFilter(mResourceInstanceIdentifier));
112 QCOMPARE(mailsInOutbox.size(), 0);
113
114 auto mailsInDrafts = Store::read<ApplicationDomain::Mail>(Query().resourceFilter(mStorageResource));
115 QCOMPARE(mailsInDrafts.size(), 1);
116
117 }
85 118
86}; 119};
87 120