summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSandro Knauß <sknauss@kde.org>2017-01-16 18:40:10 +0100
committerSandro Knauß <sknauss@kde.org>2017-01-30 10:59:30 +0100
commita8075ac935cec172972b7bea375db2c70eb4bec8 (patch)
treefae9ec8f8bd5fe225bb80e0801a63c3aa8594c5b
parent2486b417dbfb4daf0741f7b870e3d276fac87729 (diff)
downloadsink-a8075ac935cec172972b7bea375db2c70eb4bec8.tar.gz
sink-a8075ac935cec172972b7bea375db2c70eb4bec8.zip
starting with davresource
-rw-r--r--CMakeLists.txt1
-rw-r--r--examples/CMakeLists.txt1
-rw-r--r--examples/davresource/CMakeLists.txt17
-rw-r--r--examples/davresource/davresource.cpp572
-rw-r--r--examples/davresource/davresource.h68
-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
9 files changed, 812 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d049054..48a06f7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,6 +22,7 @@ include(KDEInstallDirs)
22 22
23find_package(Qt5 COMPONENTS REQUIRED Core Network) 23find_package(Qt5 COMPONENTS REQUIRED Core Network)
24find_package(KF5 COMPONENTS REQUIRED Async Mime) 24find_package(KF5 COMPONENTS REQUIRED Async Mime)
25find_package(KPimKDAV REQUIRED)
25find_package(FlatBuffers REQUIRED) 26find_package(FlatBuffers REQUIRED)
26 27
27find_program(MEMORYCHECK_COMMAND valgrind) 28find_program(MEMORYCHECK_COMMAND valgrind)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 9d87b1c..abc7441 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -8,3 +8,4 @@ if (BUILD_MAILDIR)
8 add_subdirectory(imapresource) 8 add_subdirectory(imapresource)
9endif() 9endif()
10add_subdirectory(mailtransportresource) 10add_subdirectory(mailtransportresource)
11add_subdirectory(davresource)
diff --git a/examples/davresource/CMakeLists.txt b/examples/davresource/CMakeLists.txt
new file mode 100644
index 0000000..d60db22
--- /dev/null
+++ b/examples/davresource/CMakeLists.txt
@@ -0,0 +1,17 @@
1project(sink_resource_dav)
2
3add_definitions(-DQT_PLUGIN)
4include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
5
6find_package(KF5 COMPONENTS REQUIRED Mime)
7
8add_library(${PROJECT_NAME} SHARED facade.cpp davresource.cpp domainadaptor.cpp)
9qt5_use_modules(${PROJECT_NAME} Core Network)
10target_link_libraries(${PROJECT_NAME} sink KPim::KDAV)
11
12install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH})
13
14#add_definitions(-DTESTDATAPATH="${CMAKE_CURRENT_SOURCE_DIR}/tests/data")
15
16#add_subdirectory(libmaildir)
17#add_subdirectory(tests)
diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp
new file mode 100644
index 0000000..b7843b7
--- /dev/null
+++ b/examples/davresource/davresource.cpp
@@ -0,0 +1,572 @@
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 "davresource.h"
21
22#include "facade.h"
23#include "resourceconfig.h"
24#include "index.h"
25#include "log.h"
26#include "definitions.h"
27#include "inspection.h"
28#include "synchronizer.h"
29#include "inspector.h"
30
31#include "facadefactory.h"
32#include "adaptorfactoryregistry.h"
33
34#include <KDAV/DavCollection>
35#include <KDAV/DavCollectionsFetchJob>
36#include <KDAV/DavItemsListJob>
37#include <KDAV/DavItemFetchJob>
38#include <KDAV/EtagCache>
39
40#include <QDir>
41#include <QDirIterator>
42
43//This is the resources entity type, and not the domain type
44#define ENTITY_TYPE_CONTACT "contact"
45#define ENTITY_TYPE_ADDRESSBOOK "folder"
46
47SINK_DEBUG_AREA("davresource")
48
49using namespace Sink;
50
51/*static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath)
52{
53 auto parts = mimeMessagePath.split('/');
54 const auto key = parts.takeLast();
55 const auto path = parts.join("/") + "/cur/";
56
57 QDir dir(path);
58 const QFileInfoList list = dir.entryInfoList(QStringList() << (key+"*"), QDir::Files);
59 if (list.size() != 1) {
60 SinkWarning() << "Failed to find message " << mimeMessagePath;
61 SinkWarning() << "Failed to find message " << path;
62 return QString();
63 }
64 return list.first().filePath();
65}
66
67class MaildirMailPropertyExtractor : public MailPropertyExtractor
68{
69protected:
70 virtual QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) const Q_DECL_OVERRIDE
71 {
72 return ::getFilePathFromMimeMessagePath(mimeMessagePath);
73 }
74};
75
76class MaildirMimeMessageMover : public Sink::Preprocessor
77{
78public:
79 MaildirMimeMessageMover(const QByteArray &resourceInstanceIdentifier, const QString &maildirPath) : mResourceInstanceIdentifier(resourceInstanceIdentifier), mMaildirPath(maildirPath) {}
80
81 QString getPath(const QByteArray &folderIdentifier)
82 {
83 if (folderIdentifier.isEmpty()) {
84 return mMaildirPath;
85 }
86 QString folderPath;
87 const auto folder = entityStore().readLatest<ApplicationDomain::Folder>(folderIdentifier);
88 if (mMaildirPath.endsWith(folder.getName())) {
89 folderPath = mMaildirPath;
90 } else {
91 auto folderName = folder.getName();
92 //FIXME handle non toplevel folders
93 folderPath = mMaildirPath + "/" + folderName;
94 }
95 return folderPath;
96 }
97
98 QString moveMessage(const QString &oldPath, const QByteArray &folder)
99 {
100 if (oldPath.startsWith(Sink::temporaryFileLocation())) {
101 const auto path = getPath(folder);
102 KPIM::Contactdir maildir(path, false);
103 if (!maildir.isValid(true)) {
104 SinkWarning() << "Maildir is not existing: " << path;
105 }
106 auto identifier = maildir.addEntryFromPath(oldPath);
107 return path + "/" + identifier;
108 } else {
109 //Handle moves
110 const auto path = getPath(folder);
111 KPIM::Contactdir maildir(path, false);
112 if (!maildir.isValid(true)) {
113 SinkWarning() << "Maildir is not existing: " << path;
114 }
115 auto oldIdentifier = KPIM::Contactdir::getKeyFromFile(oldPath);
116 auto pathParts = oldPath.split('/');
117 pathParts.takeLast();
118 auto oldDirectory = pathParts.join('/');
119 if (oldDirectory == path) {
120 return oldPath;
121 }
122 KPIM::Contactdir oldMaildir(oldDirectory, false);
123 if (!oldMaildir.isValid(false)) {
124 SinkWarning() << "Maildir is not existing: " << path;
125 }
126 auto identifier = oldMaildir.moveEntryTo(oldIdentifier, maildir);
127 return path + "/" + identifier;
128 }
129 }
130
131 void newEntity(Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
132 {
133 auto mail = newEntity.cast<ApplicationDomain::Contact>();
134 const auto mimeMessage = mail.getMimeMessagePath();
135 if (!mimeMessage.isNull()) {
136 const auto path = moveMessage(mimeMessage, mail.getFolder());
137 auto blob = ApplicationDomain::BLOB{path};
138 blob.isExternal = false;
139 mail.setProperty(ApplicationDomain::Contact::MimeMessage::name, QVariant::fromValue(blob));
140 }
141 }
142
143 void modifiedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity, Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
144 {
145 auto newMail = newEntity.cast<ApplicationDomain::Contact>();
146 const ApplicationDomain::Contact oldMail{oldEntity};
147 const auto mimeMessage = newMail.getMimeMessagePath();
148 const auto newFolder = newMail.getFolder();
149 const bool mimeMessageChanged = !mimeMessage.isNull() && mimeMessage != oldMail.getMimeMessagePath();
150 const bool folderChanged = !newFolder.isNull() && newFolder != oldMail.getFolder();
151 if (mimeMessageChanged || folderChanged) {
152 SinkTrace() << "Moving mime message: " << mimeMessageChanged << folderChanged;
153 auto newPath = moveMessage(mimeMessage, newMail.getFolder());
154 if (newPath != oldMail.getMimeMessagePath()) {
155 const auto oldPath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath());
156 auto blob = ApplicationDomain::BLOB{newPath};
157 blob.isExternal = false;
158 newMail.setProperty(ApplicationDomain::Contact::MimeMessage::name, QVariant::fromValue(blob));
159 //Remove the olde mime message if there is a new one
160 QFile::remove(oldPath);
161 }
162 }
163
164 auto mimeMessagePath = newMail.getMimeMessagePath();
165 const auto maildirPath = getPath(newMail.getFolder());
166 KPIM::Contactdir maildir(maildirPath, false);
167 const auto file = getFilePathFromMimeMessagePath(mimeMessagePath);
168 QString identifier = KPIM::Contactdir::getKeyFromFile(file);
169
170 //get flags from
171 KPIM::Contactdir::Flags flags;
172 if (!newMail.getUnread()) {
173 flags |= KPIM::Contactdir::Seen;
174 }
175 if (newMail.getImportant()) {
176 flags |= KPIM::Contactdir::Flagged;
177 }
178
179 maildir.changeEntryFlags(identifier, flags);
180 }
181
182 void deletedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity) Q_DECL_OVERRIDE
183 {
184 const ApplicationDomain::Contact oldMail{oldEntity};
185 const auto filePath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath());
186 QFile::remove(filePath);
187 }
188 QByteArray mResourceInstanceIdentifier;
189 QString mMaildirPath;
190};
191
192class FolderPreprocessor : public Sink::Preprocessor
193{
194public:
195 FolderPreprocessor(const QString maildirPath) : mMaildirPath(maildirPath) {}
196
197 void newEntity(Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
198 {
199 auto folderName = Sink::ApplicationDomain::Folder{newEntity}.getName();
200 const auto path = mMaildirPath + "/" + folderName;
201 KPIM::Contactdir maildir(path, false);
202 maildir.create();
203 }
204
205 void modifiedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity, Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
206 {
207 }
208
209 void deletedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity) Q_DECL_OVERRIDE
210 {
211 }
212 QString mMaildirPath;
213};*/
214
215static KAsync::Job<void> runJob(KJob *job)
216{
217 return KAsync::start<void>([job](KAsync::Future<void> &future) {
218 QObject::connect(job, &KJob::result, [&future](KJob *job) {
219 SinkTrace() << "Job done: " << job->metaObject()->className();
220 if (job->error()) {
221 SinkWarning() << "Job failed: " << job->errorString();
222 future.setError(job->error(), job->errorString());
223 } else {
224 future.setFinished();
225 }
226 });
227 SinkTrace() << "Starting job: " << job->metaObject()->className();
228 job->start();
229 });
230}
231
232class ContactSynchronizer : public Sink::Synchronizer {
233public:
234 ContactSynchronizer(const Sink::ResourceContext &resourceContext)
235 : Sink::Synchronizer(resourceContext)
236 {
237
238 }
239
240 QByteArray createAddressbook(const QString &folderName, const QString &folderPath, const QString &parentFolderRid, const QByteArray &icon)
241 {
242 SinkTrace() << "Creating addressbook: " << folderName << parentFolderRid;
243 const auto remoteId = folderPath.toUtf8();
244 const auto bufferType = ENTITY_TYPE_ADDRESSBOOK;
245 Sink::ApplicationDomain::Folder folder;
246 folder.setName(folderName);
247 folder.setIcon(icon);
248 QHash<QByteArray, Query::Comparator> mergeCriteria;
249
250 if (!parentFolderRid.isEmpty()) {
251 folder.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, parentFolderRid.toUtf8()));
252 }
253 createOrModify(bufferType, remoteId, folder, mergeCriteria);
254 return remoteId;
255 }
256
257 void synchronizeAddressbooks(const KDAV::DavCollection::List &folderList)
258 {
259 const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK;
260 SinkTrace() << "Found addressbooks " << folderList.size();
261
262 QVector<QByteArray> ridList;
263 for(const auto &f : folderList) {
264 const auto &rid = f.url().toDisplayString();
265 ridList.append(rid.toUtf8());
266 createAddressbook(f.displayName(), rid, "", "addressbook");
267 }
268
269 scanForRemovals(bufferType,
270 [&ridList](const QByteArray &remoteId) -> bool {
271 return ridList.contains(remoteId);
272 }
273 );
274 }
275
276 QList<Synchronizer::SyncRequest> getSyncRequests(const Sink::QueryBase &query) Q_DECL_OVERRIDE
277 {
278 QList<Synchronizer::SyncRequest> list;
279 if (!query.type().isEmpty()) {
280 //We want to synchronize something specific
281 list << Synchronizer::SyncRequest{query};
282 } else {
283 //We want to synchronize everything
284 list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName<ApplicationDomain::Folder>())};
285 list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName<ApplicationDomain::Contact>())};
286 }
287 return list;
288 }
289
290 KAsync::Job<void> synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE
291 {
292 auto job = KAsync::null<void>();
293
294 if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Folder>()) {
295 auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl);
296 job = runJob(collectionsFetchJob).syncThen<void>([this, collectionsFetchJob] {
297 synchronizeAddressbooks(collectionsFetchJob ->collections());
298 });
299 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) {
300 // for one Collection/Addressbook
301 /*
302 auto cache = std::shared:ptr<KDAV::EtagCache>(new KDAV::EtagCache());
303 foreach(const auto &item, collection) { // item is a Sink item
304 cache->setEtag(item.remoteID(), item.etag());
305 }
306 auto job = KDAV::DavItemsListJob(davCollection.url(), cache);
307 job->exec();
308 changedItems = job->changedItems();
309 foreach(const auto &item, changedItems) { // item is a DavItem
310 addOrModifyItem(item);
311 }
312 removedItems = job->deletedItems();
313 foreach(const auto &item, removedItems) { // item is a DavItem
314 deleteSinkItem(item);
315 }
316 */
317
318 auto cache = std::shared_ptr<KDAV::EtagCache>(new KDAV::EtagCache());
319 QVector<KDAV::DavUrl> folders;
320 if (query.hasFilter<ApplicationDomain::Mail::Folder>()) {
321 auto folderFilter = query.getFilter<ApplicationDomain::Mail::Folder>();
322 auto localIds = resolveFilter(folderFilter);
323 auto folderRemoteIds = syncStore().resolveLocalIds(ApplicationDomain::getTypeName<ApplicationDomain::Folder>(), localIds);
324 for (const auto &r : folderRemoteIds) {
325 auto url = QUrl::fromUserInput(r);
326 url.setUserInfo(mResourceUrl.url().userInfo());
327 folders << KDAV::DavUrl(url, mResourceUrl.protocol());
328 }
329 } else {
330 //return KAsync::null<void>();
331 auto url = QUrl::fromUserInput("https://apps.kolabnow.com/addressbooks/test1%40kolab.org/9290e784-c876-412f-8385-be292d64b2c6/");
332 url.setUserInfo(mResourceUrl.url().userInfo());
333 folders << KDAV::DavUrl(url, mResourceUrl.protocol());
334 }
335 const auto folder = folders.first();
336 SinkTrace() << "Syncing " << folder.toDisplayString();
337 auto davItemsListJob = new KDAV::DavItemsListJob(folder, cache);
338 job = runJob(davItemsListJob).syncThen<void>([this, davItemsListJob, folder] {
339 const QByteArray bufferType = ENTITY_TYPE_CONTACT;
340 QHash<QByteArray, Query::Comparator> mergeCriteria;
341 QStringList ridList;
342 for(const auto &item : davItemsListJob->items()) {
343 auto davItemFetchJob = new KDAV::DavItemFetchJob(item);
344 auto job = runJob(davItemFetchJob).syncThen<void>([this, davItemFetchJob,bufferType, mergeCriteria] {
345 const auto item = davItemFetchJob->item();
346 const QByteArray rid = item.url().toDisplayString().toUtf8();
347 Sink::ApplicationDomain::Contact contact;
348 /*contact.setUid("");
349 contact.setFn("fn");
350 contact.setEmails(QByteArrayList());*/
351 contact.setVcard(item.data());
352 createOrModify(bufferType, rid, contact, mergeCriteria);
353 });
354 ridList << item.url().toDisplayString();
355 }
356
357 scanForRemovals(bufferType,
358 [&ridList](const QByteArray &remoteId) -> bool {
359 return ridList.contains(remoteId);
360 });
361 });
362 }
363 return job;
364}
365
366KAsync::Job<QByteArray> replay(const ApplicationDomain::Contact &contact, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
367 {
368 /*
369 if (operation == Sink::Operation_Creation) {
370 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
371 SinkTrace() << "Contact created: " << remoteId;
372 return KAsync::value(remoteId.toUtf8());
373 } else if (operation == Sink::Operation_Removal) {
374 SinkTrace() << "Removing a contact " << oldRemoteId;
375 return KAsync::null<QByteArray>();
376 } else if (operation == Sink::Operation_Modification) {
377 SinkTrace() << "Modifying a contact: " << oldRemoteId;
378 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
379 return KAsync::value(remoteId.toUtf8());
380 }*/
381 return KAsync::null<QByteArray>();
382 }
383
384 KAsync::Job<QByteArray> replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
385 {
386 /*
387 if (operation == Sink::Operation_Creation) {
388 auto folderName = folder.getName();
389 //FIXME handle non toplevel folders
390 auto path = mMaildirPath + "/" + folderName;
391 SinkTrace() << "Creating a new folder: " << path;
392 KPIM::Contactdir maildir(path, false);
393 maildir.create();
394 return KAsync::value(path.toUtf8());
395 } else if (operation == Sink::Operation_Removal) {
396 const auto path = oldRemoteId;
397 SinkTrace() << "Removing a folder: " << path;
398 KPIM::Contactdir maildir(path, false);
399 maildir.remove();
400 return KAsync::null<QByteArray>();
401 } else if (operation == Sink::Operation_Modification) {
402 SinkWarning() << "Folder modifications are not implemented";
403 return KAsync::value(oldRemoteId);
404 }*/
405 return KAsync::null<QByteArray>();
406 }
407
408public:
409 KDAV::DavUrl mResourceUrl;
410};
411
412/*
413class MaildirInspector : public Sink::Inspector {
414public:
415 MaildirInspector(const Sink::ResourceContext &resourceContext)
416 : Sink::Inspector(resourceContext)
417 {
418
419 }
420protected:
421
422 KAsync::Job<void> inspect(int inspectionType, const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue) Q_DECL_OVERRIDE {
423 auto synchronizationStore = QSharedPointer<Sink::Storage::DataStore>::create(Sink::storageLocation(), mResourceContext.instanceId() + ".synchronization", Sink::Storage::DataStore::ReadOnly);
424 auto synchronizationTransaction = synchronizationStore->createTransaction(Sink::Storage::DataStore::ReadOnly);
425
426 auto mainStore = QSharedPointer<Sink::Storage::DataStore>::create(Sink::storageLocation(), mResourceContext.instanceId(), Sink::Storage::DataStore::ReadOnly);
427 auto transaction = mainStore->createTransaction(Sink::Storage::DataStore::ReadOnly);
428
429 Sink::Storage::EntityStore entityStore(mResourceContext, {"maildirresource"});
430 auto syncStore = QSharedPointer<SynchronizerStore>::create(synchronizationTransaction);
431
432 SinkTrace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue;
433
434 if (domainType == ENTITY_TYPE_MAIL) {
435 auto mail = entityStore.readLatest<Sink::ApplicationDomain::Contact>(entityId);
436 const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
437
438 if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) {
439 if (property == "unread") {
440 const auto flags = KPIM::Contactdir::readEntryFlags(filePath.split('/').last());
441 if (expectedValue.toBool() && (flags & KPIM::Contactdir::Seen)) {
442 return KAsync::error<void>(1, "Expected unread but couldn't find it.");
443 }
444 if (!expectedValue.toBool() && !(flags & KPIM::Contactdir::Seen)) {
445 return KAsync::error<void>(1, "Expected read but couldn't find it.");
446 }
447 return KAsync::null<void>();
448 }
449 if (property == "subject") {
450 KMime::Message *msg = new KMime::Message;
451 msg->setHead(KMime::CRLFtoLF(KPIM::Contactdir::readEntryHeadersFromFile(filePath)));
452 msg->parse();
453
454 if (msg->subject(true)->asUnicodeString() != expectedValue.toString()) {
455 return KAsync::error<void>(1, "Subject not as expected: " + msg->subject(true)->asUnicodeString());
456 }
457 return KAsync::null<void>();
458 }
459 }
460 if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) {
461 if (QFileInfo(filePath).exists() != expectedValue.toBool()) {
462 return KAsync::error<void>(1, "Wrong file existence: " + filePath);
463 }
464 }
465 }
466 if (domainType == ENTITY_TYPE_FOLDER) {
467 const auto remoteId = syncStore->resolveLocalId(ENTITY_TYPE_FOLDER, entityId);
468 auto folder = entityStore.readLatest<Sink::ApplicationDomain::Folder>(entityId);
469
470 if (inspectionType == Sink::ResourceControl::Inspection::CacheIntegrityInspectionType) {
471 SinkTrace() << "Inspecting cache integrity" << remoteId;
472 if (!QDir(remoteId).exists()) {
473 return KAsync::error<void>(1, "The directory is not existing: " + remoteId);
474 }
475
476 int expectedCount = 0;
477 Index index("mail.index.folder", transaction);
478 index.lookup(entityId, [&](const QByteArray &sinkId) {
479 expectedCount++;
480 },
481 [&](const Index::Error &error) {
482 SinkWarning() << "Error in index: " << error.message << property;
483 });
484
485 QDir dir(remoteId + "/cur");
486 const QFileInfoList list = dir.entryInfoList(QDir::Files);
487 if (list.size() != expectedCount) {
488 for (const auto &fileInfo : list) {
489 SinkWarning() << "Found in cache: " << fileInfo.fileName();
490 }
491 return KAsync::error<void>(1, QString("Wrong number of files; found %1 instead of %2.").arg(list.size()).arg(expectedCount));
492 }
493 }
494 if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) {
495 if (!remoteId.endsWith(folder.getName().toUtf8())) {
496 return KAsync::error<void>(1, "Wrong folder name: " + remoteId);
497 }
498 //TODO we shouldn't use the remoteId here to figure out the path, it could be gone/changed already
499 if (QDir(remoteId).exists() != expectedValue.toBool()) {
500 return KAsync::error<void>(1, "Wrong folder existence: " + remoteId);
501 }
502 }
503
504 }
505 return KAsync::null<void>();
506 }
507};*/
508
509
510DavResource::DavResource(const Sink::ResourceContext &resourceContext)
511 : Sink::GenericResource(resourceContext)
512{
513 auto config = ResourceConfig::getConfiguration(resourceContext.instanceId());
514 auto resourceUrl = QUrl::fromUserInput(config.value("resourceUrl").toString());
515 resourceUrl.setUserName(config.value("username").toString());
516 resourceUrl.setPassword(config.value("password").toString());
517
518 mResourceUrl = KDAV::DavUrl(resourceUrl, KDAV::CardDav);
519
520 auto synchronizer = QSharedPointer<ContactSynchronizer>::create(resourceContext);
521 synchronizer->mResourceUrl = mResourceUrl;
522 setupSynchronizer(synchronizer);
523 //setupInspector(QSharedPointer<MaildirInspector>::create(resourceContext));
524
525 /*
526 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new SpecialPurposeProcessor(resourceContext.resourceType, resourceContext.instanceId()) << new MaildirMimeMessageMover(resourceContext.instanceId(), mMaildirPath) << new MaildirMailPropertyExtractor);
527 setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << new FolderPreprocessor(mMaildirPath));
528
529 KPIM::Contactdir dir(mMaildirPath, true);
530 SinkTrace() << "Started maildir resource for maildir: " << mMaildirPath;
531 {
532 auto draftsFolder = dir.addSubFolder("Drafts");
533 auto remoteId = synchronizer->createFolder(draftsFolder, "folder", QByteArrayList() << "drafts");
534 auto draftsFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId);
535 }
536 {
537 auto trashFolder = dir.addSubFolder("Trash");
538 auto remoteId = synchronizer->createFolder(trashFolder, "folder", QByteArrayList() << "trash");
539 auto trashFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId);
540 }
541 synchronizer->commit();*/
542}
543
544
545DavResourceFactory::DavResourceFactory(QObject *parent)
546 : Sink::ResourceFactory(parent,
547 {"-folder.rename"}
548 )
549{
550}
551
552Sink::Resource *DavResourceFactory::createResource(const ResourceContext &context)
553{
554 return new DavResource(context);
555}
556
557void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory)
558{
559 factory.registerFacade<Sink::ApplicationDomain::Contact, DavResourceContactFacade>(name);
560 factory.registerFacade<Sink::ApplicationDomain::Folder, DavResourceFolderFacade>(name);
561}
562
563void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry &registry)
564{
565 registry.registerFactory<Sink::ApplicationDomain::Contact, ContactAdaptorFactory>(name);
566 registry.registerFactory<Sink::ApplicationDomain::Folder, AddressbookAdaptorFactory>(name);
567}
568
569void DavResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier)
570{
571 DavResource::removeFromDisk(instanceIdentifier);
572}
diff --git a/examples/davresource/davresource.h b/examples/davresource/davresource.h
new file mode 100644
index 0000000..3b228c2
--- /dev/null
+++ b/examples/davresource/davresource.h
@@ -0,0 +1,68 @@
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#pragma once
21
22#include "common/genericresource.h"
23
24#include <KDAV/DavUrl>
25#include <Async/Async>
26
27#include <flatbuffers/flatbuffers.h>
28
29class ContactAdaptorFactory;
30class AddressbookAdaptorFactory;
31
32/**
33 * A DAV resource.
34 *
35 * Implementation details:
36 * The remoteid's have the following formats:
37 * files: full file path
38 * directories: full directory path
39 *
40 * The resource moves all messages from new to cur during sync and thus expectes all messages that are in the store to always reside in cur.
41 * The tmp directory is never directly used
42 */
43class DavResource : public Sink::GenericResource
44{
45public:
46 DavResource(const Sink::ResourceContext &resourceContext);
47
48private:
49 QStringList listAvailableFolders();
50
51 KDAV::DavUrl mResourceUrl;
52};
53
54class DavResourceFactory : public Sink::ResourceFactory
55{
56 Q_OBJECT
57 Q_PLUGIN_METADATA(IID "sink.davresource")
58 Q_INTERFACES(Sink::ResourceFactory)
59
60public:
61 DavResourceFactory(QObject *parent = 0);
62
63 Sink::Resource *createResource(const Sink::ResourceContext &context) Q_DECL_OVERRIDE;
64 void registerFacades(const QByteArray &resourceName, Sink::FacadeFactory &factory) Q_DECL_OVERRIDE;
65 void registerAdaptorFactories(const QByteArray &resourceName, Sink::AdaptorFactoryRegistry &registry) Q_DECL_OVERRIDE;
66 void removeDataFromDisk(const QByteArray &instanceIdentifier) Q_DECL_OVERRIDE;
67};
68
diff --git a/examples/davresource/domainadaptor.cpp b/examples/davresource/domainadaptor.cpp
new file mode 100644
index 0000000..861a10e
--- /dev/null
+++ b/examples/davresource/domainadaptor.cpp
@@ -0,0 +1,35 @@
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
new file mode 100644
index 0000000..7e3c723
--- /dev/null
+++ b/examples/davresource/domainadaptor.h
@@ -0,0 +1,38 @@
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
new file mode 100644
index 0000000..b56815a
--- /dev/null
+++ b/examples/davresource/facade.cpp
@@ -0,0 +1,44 @@
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
new file mode 100644
index 0000000..02bd5dc
--- /dev/null
+++ b/examples/davresource/facade.h
@@ -0,0 +1,36 @@
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};