summaryrefslogtreecommitdiffstats
path: root/examples/maildirresource/maildirresource.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2016-05-29 15:20:00 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2016-05-29 15:20:00 +0200
commitf211ffd9aaa57fe46a792c3b005981d55dde670f (patch)
tree4a1cb4c8d2bc3072505a0518ea019969508f624f /examples/maildirresource/maildirresource.cpp
parentdabd408dcd372f16c7934597db30346869cd8ad8 (diff)
downloadsink-f211ffd9aaa57fe46a792c3b005981d55dde670f.tar.gz
sink-f211ffd9aaa57fe46a792c3b005981d55dde670f.zip
The maildirresource is back in action
Diffstat (limited to 'examples/maildirresource/maildirresource.cpp')
-rw-r--r--examples/maildirresource/maildirresource.cpp536
1 files changed, 280 insertions, 256 deletions
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp
index 3f6ae54..9503971 100644
--- a/examples/maildirresource/maildirresource.cpp
+++ b/examples/maildirresource/maildirresource.cpp
@@ -36,6 +36,9 @@
36#include "indexupdater.h" 36#include "indexupdater.h"
37#include "libmaildir/maildir.h" 37#include "libmaildir/maildir.h"
38#include "inspection.h" 38#include "inspection.h"
39#include "synchronizer.h"
40#include "sourcewriteback.h"
41#include "adaptorfactoryregistry.h"
39#include <QDate> 42#include <QDate>
40#include <QUuid> 43#include <QUuid>
41#include <QDir> 44#include <QDir>
@@ -82,13 +85,13 @@ public:
82 db.findLatest(folderIdentifier, [&](const QByteArray &, const QByteArray &value) { 85 db.findLatest(folderIdentifier, [&](const QByteArray &, const QByteArray &value) {
83 Sink::EntityBuffer buffer(value); 86 Sink::EntityBuffer buffer(value);
84 const Sink::Entity &entity = buffer.entity(); 87 const Sink::Entity &entity = buffer.entity();
85 const auto adaptor = mFolderAdaptorFactory->createAdaptor(entity); 88 const auto adaptor = Sink::AdaptorFactoryRegistry::instance().getFactory<Sink::ApplicationDomain::Folder>(PLUGIN_NAME)->createAdaptor(entity);
86 auto parentFolder = adaptor->getProperty("parent").toString(); 89 auto parentFolder = adaptor->getProperty("parent").toString();
87 if (mMaildirPath.endsWith(adaptor->getProperty("name").toString())) { 90 if (mMaildirPath.endsWith(adaptor->getProperty("name").toString())) {
88 folderPath = mMaildirPath; 91 folderPath = mMaildirPath;
89 } else { 92 } else {
90 auto folderName = adaptor->getProperty("name").toString(); 93 auto folderName = adaptor->getProperty("name").toString();
91 //TODO handle non toplevel folders 94 //FIXME handle non toplevel folders
92 folderPath = mMaildirPath + "/" + folderName; 95 folderPath = mMaildirPath + "/" + folderName;
93 } 96 }
94 }); 97 });
@@ -140,19 +143,42 @@ public:
140 { 143 {
141 //TODO deal with moves 144 //TODO deal with moves
142 const auto mimeMessage = newEntity.getProperty("mimeMessage"); 145 const auto mimeMessage = newEntity.getProperty("mimeMessage");
143 if (mimeMessage.isValid()) { 146 if (mimeMessage.isValid() && mimeMessage.toString() != oldEntity.getProperty("mimeMessage").toString()) {
147 //Remove the olde mime message if there is a new one
148 const auto filePath = getFilePathFromMimeMessagePath(oldEntity.getProperty("mimeMessage").toString());
149 QFile::remove(filePath);
150
144 newEntity.setProperty("mimeMessage", moveMessage(mimeMessage.toString(), newEntity.getProperty("folder").toByteArray(), transaction)); 151 newEntity.setProperty("mimeMessage", moveMessage(mimeMessage.toString(), newEntity.getProperty("folder").toByteArray(), transaction));
152 Trace() << "Modified message: " << filePath << oldEntity.getProperty("mimeMessage").toString();
153 }
154
155 auto mimeMessagePath = newEntity.getProperty("mimeMessage").toString();
156 const auto maildirPath = getPath(newEntity.getProperty("folder").toByteArray(), transaction);
157 KPIM::Maildir maildir(maildirPath, false);
158 QString identifier = KPIM::Maildir::getKeyFromFile(mimeMessagePath);
159
160 //get flags from
161 KPIM::Maildir::Flags flags;
162 if (!newEntity.getProperty("unread").toBool()) {
163 flags |= KPIM::Maildir::Seen;
164 }
165 if (newEntity.getProperty("important").toBool()) {
166 flags |= KPIM::Maildir::Flagged;
145 } 167 }
168
169 const auto newRemoteId = maildir.changeEntryFlags(identifier, flags);
170
146 updatedIndexedProperties(newEntity); 171 updatedIndexedProperties(newEntity);
147 } 172 }
148 173
149 void deletedEntity(const QByteArray &uid, qint64 revision, const Sink::ApplicationDomain::BufferAdaptor &oldEntity, Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE 174 void deletedEntity(const QByteArray &uid, qint64 revision, const Sink::ApplicationDomain::BufferAdaptor &oldEntity, Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE
150 { 175 {
176 const auto filePath = getFilePathFromMimeMessagePath(oldEntity.getProperty("mimeMessage").toString());
177 QFile::remove(filePath);
151 } 178 }
152 QByteArray mDraftsFolder; 179 QByteArray mDraftsFolder;
153 QByteArray mResourceInstanceIdentifier; 180 QByteArray mResourceInstanceIdentifier;
154 QString mMaildirPath; 181 QString mMaildirPath;
155 QSharedPointer<MaildirFolderAdaptorFactory> mFolderAdaptorFactory;
156}; 182};
157 183
158class FolderPreprocessor : public Sink::Preprocessor 184class FolderPreprocessor : public Sink::Preprocessor
@@ -179,282 +205,271 @@ public:
179 QString mMaildirPath; 205 QString mMaildirPath;
180}; 206};
181 207
182MaildirResource::MaildirResource(const QByteArray &instanceIdentifier, const QSharedPointer<Sink::Pipeline> &pipeline)
183 : Sink::GenericResource(instanceIdentifier, pipeline),
184 mMailAdaptorFactory(QSharedPointer<MaildirMailAdaptorFactory>::create()),
185 mFolderAdaptorFactory(QSharedPointer<MaildirFolderAdaptorFactory>::create())
186{
187 auto config = ResourceConfig::getConfiguration(instanceIdentifier);
188 mMaildirPath = QDir::cleanPath(QDir::fromNativeSeparators(config.value("path").toString()));
189 //Chop a trailing slash if necessary
190 if (mMaildirPath.endsWith("/")) {
191 mMaildirPath.chop(1);
192 }
193
194 auto folderUpdater = new FolderUpdater(QByteArray());
195 addType(ENTITY_TYPE_MAIL, mMailAdaptorFactory,
196 QVector<Sink::Preprocessor*>() << folderUpdater << new DefaultIndexUpdater<Sink::ApplicationDomain::Mail>);
197 auto folderPreprocessor = new FolderPreprocessor;
198 addType(ENTITY_TYPE_FOLDER, mFolderAdaptorFactory,
199 QVector<Sink::Preprocessor*>() << folderPreprocessor << new DefaultIndexUpdater<Sink::ApplicationDomain::Folder>);
200 208
201 KPIM::Maildir dir(mMaildirPath, true); 209class MaildirSynchronizer : public Sink::Synchronizer {
202 mDraftsFolder = dir.addSubFolder("drafts"); 210public:
203 Trace() << "Started maildir resource for maildir: " << mMaildirPath; 211 MaildirSynchronizer(const QByteArray &resourceType, const QByteArray &resourceInstanceIdentifier)
204 auto mainStore = QSharedPointer<Sink::Storage>::create(Sink::storageLocation(), mResourceInstanceIdentifier, Sink::Storage::ReadOnly); 212 : Sink::Synchronizer(resourceType, resourceInstanceIdentifier)
205 auto syncStore = QSharedPointer<Sink::Storage>::create(Sink::storageLocation(), mResourceInstanceIdentifier + ".synchronization", Sink::Storage::ReadWrite); 213 {
206 auto transaction = mainStore->createTransaction(Sink::Storage::ReadOnly);
207 auto synchronizationTransaction = syncStore->createTransaction(Sink::Storage::ReadWrite);
208
209 auto remoteId = createFolder(mDraftsFolder, "folder", transaction, synchronizationTransaction);
210 auto draftsFolderLocalId = resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId, synchronizationTransaction);
211 synchronizationTransaction.commit();
212
213 folderUpdater->mDraftsFolder = draftsFolderLocalId;
214 folderUpdater->mResourceInstanceIdentifier = mResourceInstanceIdentifier;
215 folderUpdater->mFolderAdaptorFactory = mFolderAdaptorFactory;
216 folderUpdater->mMaildirPath = mMaildirPath;
217 folderPreprocessor->mMaildirPath = mMaildirPath;
218}
219
220static QStringList listRecursive( const QString &root, const KPIM::Maildir &dir )
221{
222 QStringList list;
223 foreach (const QString &sub, dir.subFolderList()) {
224 const KPIM::Maildir md = dir.subFolder(sub);
225 if (!md.isValid()) {
226 continue;
227 }
228 QString path = root + "/" + sub;
229 list << path;
230 list += listRecursive(path, md );
231 }
232 return list;
233}
234 214
235QByteArray MaildirResource::createFolder(const QString &folderPath, const QByteArray &icon, Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction)
236{
237 auto remoteId = folderPath.toUtf8();
238 auto bufferType = ENTITY_TYPE_FOLDER;
239 KPIM::Maildir md(folderPath, folderPath == mMaildirPath);
240 Sink::ApplicationDomain::Folder folder;
241 folder.setProperty("name", md.name());
242 folder.setProperty("icon", icon);
243
244 if (!md.isRoot()) {
245 folder.setProperty("parent", resolveRemoteId(ENTITY_TYPE_FOLDER, md.parent().path().toUtf8(), synchronizationTransaction));
246 } 215 }
247 createOrModify(transaction, synchronizationTransaction, *mFolderAdaptorFactory, bufferType, remoteId, folder);
248 return remoteId;
249}
250 216
251QStringList MaildirResource::listAvailableFolders() 217 static QStringList listRecursive( const QString &root, const KPIM::Maildir &dir )
252{ 218 {
253 KPIM::Maildir dir(mMaildirPath, true); 219 QStringList list;
254 if (!dir.isValid()) { 220 foreach (const QString &sub, dir.subFolderList()) {
255 return QStringList(); 221 const KPIM::Maildir md = dir.subFolder(sub);
222 if (!md.isValid()) {
223 continue;
224 }
225 QString path = root + "/" + sub;
226 list << path;
227 list += listRecursive(path, md );
228 }
229 return list;
256 } 230 }
257 QStringList folderList;
258 folderList << mMaildirPath;
259 folderList += listRecursive(mMaildirPath, dir);
260 return folderList;
261}
262 231
263void MaildirResource::synchronizeFolders(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction) 232 QByteArray createFolder(const QString &folderPath, const QByteArray &icon)
264{ 233 {
265 const QByteArray bufferType = ENTITY_TYPE_FOLDER; 234 auto remoteId = folderPath.toUtf8();
266 QStringList folderList = listAvailableFolders(); 235 auto bufferType = ENTITY_TYPE_FOLDER;
267 Trace() << "Found folders " << folderList; 236 KPIM::Maildir md(folderPath, folderPath == mMaildirPath);
268 237 Sink::ApplicationDomain::Folder folder;
269 scanForRemovals(transaction, synchronizationTransaction, bufferType, 238 folder.setProperty("name", md.name());
270 [&bufferType, &transaction](const std::function<void(const QByteArray &)> &callback) { 239 folder.setProperty("icon", icon);
271 //TODO Instead of iterating over all entries in the database, which can also pick up the same item multiple times, 240
272 //we should rather iterate over an index that contains every uid exactly once. The remoteId index would be such an index, 241 if (!md.isRoot()) {
273 //but we currently fail to iterate over all entries in an index it seems. 242 folder.setProperty("parent", syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, md.parent().path().toUtf8()));
274 // auto remoteIds = synchronizationTransaction.openDatabase("rid.mapping." + bufferType, std::function<void(const Sink::Storage::Error &)>(), true);
275 auto mainDatabase = Sink::Storage::mainDatabase(transaction, bufferType);
276 mainDatabase.scan("", [&](const QByteArray &key, const QByteArray &) {
277 callback(key);
278 return true;
279 });
280 },
281 [&folderList](const QByteArray &remoteId) -> bool {
282 return folderList.contains(remoteId);
283 } 243 }
284 ); 244 createOrModify(bufferType, remoteId, folder);
285 245 return remoteId;
286 for (const auto folderPath : folderList) {
287 createFolder(folderPath, "folder", transaction, synchronizationTransaction);
288 } 246 }
289}
290 247
291void MaildirResource::synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path) 248 QStringList listAvailableFolders()
292{ 249 {
293 Trace() << "Synchronizing mails" << path; 250 KPIM::Maildir dir(mMaildirPath, true);
294 auto time = QSharedPointer<QTime>::create(); 251 if (!dir.isValid()) {
295 time->start(); 252 return QStringList();
296 const QByteArray bufferType = ENTITY_TYPE_MAIL; 253 }
297 254 QStringList folderList;
298 KPIM::Maildir maildir(path, true); 255 folderList << mMaildirPath;
299 if (!maildir.isValid()) { 256 folderList += listRecursive(mMaildirPath, dir);
300 Warning() << "Failed to sync folder " << maildir.lastError(); 257 return folderList;
301 return;
302 } 258 }
303 259
304 Trace() << "Importing new mail."; 260 void synchronizeFolders()
305 maildir.importNewMails(); 261 {
262 const QByteArray bufferType = ENTITY_TYPE_FOLDER;
263 QStringList folderList = listAvailableFolders();
264 Trace() << "Found folders " << folderList;
265
266 scanForRemovals(bufferType,
267 [this, &bufferType](const std::function<void(const QByteArray &)> &callback) {
268 //TODO Instead of iterating over all entries in the database, which can also pick up the same item multiple times,
269 //we should rather iterate over an index that contains every uid exactly once. The remoteId index would be such an index,
270 //but we currently fail to iterate over all entries in an index it seems.
271 // auto remoteIds = synchronizationTransaction.openDatabase("rid.mapping." + bufferType, std::function<void(const Sink::Storage::Error &)>(), true);
272 auto mainDatabase = Sink::Storage::mainDatabase(transaction(), bufferType);
273 mainDatabase.scan("", [&](const QByteArray &key, const QByteArray &) {
274 callback(key);
275 return true;
276 });
277 },
278 [&folderList](const QByteArray &remoteId) -> bool {
279 return folderList.contains(remoteId);
280 }
281 );
306 282
307 auto listingPath = maildir.pathToCurrent(); 283 for (const auto folderPath : folderList) {
308 auto entryIterator = QSharedPointer<QDirIterator>::create(listingPath, QDir::Files); 284 createFolder(folderPath, "folder");
309 Trace() << "Looking into " << listingPath; 285 }
286 }
310 287
311 const auto folderLocalId = resolveRemoteId(ENTITY_TYPE_FOLDER, path.toUtf8(), synchronizationTransaction); 288 void synchronizeMails(const QString &path)
289 {
290 Trace() << "Synchronizing mails" << path;
291 auto time = QSharedPointer<QTime>::create();
292 time->start();
293 const QByteArray bufferType = ENTITY_TYPE_MAIL;
294
295 KPIM::Maildir maildir(path, true);
296 if (!maildir.isValid()) {
297 Warning() << "Failed to sync folder " << maildir.lastError();
298 return;
299 }
312 300
313 auto property = "folder"; 301 Trace() << "Importing new mail.";
314 scanForRemovals(transaction, synchronizationTransaction, bufferType, 302 maildir.importNewMails();
315 [&](const std::function<void(const QByteArray &)> &callback) { 303
316 Index index(bufferType + ".index." + property, transaction); 304 auto listingPath = maildir.pathToCurrent();
317 index.lookup(folderLocalId, [&](const QByteArray &sinkId) { 305 auto entryIterator = QSharedPointer<QDirIterator>::create(listingPath, QDir::Files);
318 callback(sinkId); 306 Trace() << "Looking into " << listingPath;
307
308 const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, path.toUtf8());
309
310 auto property = "folder";
311 scanForRemovals(bufferType,
312 [&](const std::function<void(const QByteArray &)> &callback) {
313 Index index(bufferType + ".index." + property, transaction());
314 index.lookup(folderLocalId, [&](const QByteArray &sinkId) {
315 callback(sinkId);
316 },
317 [&](const Index::Error &error) {
318 Warning() << "Error in index: " << error.message << property;
319 });
319 }, 320 },
320 [&](const Index::Error &error) { 321 [](const QByteArray &remoteId) -> bool {
321 Warning() << "Error in index: " << error.message << property; 322 return QFile(remoteId).exists();
322 }); 323 }
323 }, 324 );
324 [](const QByteArray &remoteId) -> bool {
325 return QFile(remoteId).exists();
326 }
327 );
328 325
329 mSynchronizerQueue.startTransaction(); 326 int count = 0;
330 int count = 0; 327 while (entryIterator->hasNext()) {
331 while (entryIterator->hasNext()) { 328 count++;
332 count++; 329 const QString filePath = QDir::fromNativeSeparators(entryIterator->next());
333 const QString filePath = QDir::fromNativeSeparators(entryIterator->next()); 330 const QString fileName = entryIterator->fileName();
334 const QString fileName = entryIterator->fileName(); 331 const auto remoteId = filePath.toUtf8();
335 const auto remoteId = filePath.toUtf8();
336 332
337 const auto flags = maildir.readEntryFlags(fileName); 333 const auto flags = maildir.readEntryFlags(fileName);
338 const auto maildirKey = maildir.getKeyFromFile(fileName); 334 const auto maildirKey = maildir.getKeyFromFile(fileName);
339 335
340 Trace() << "Found a mail " << filePath << " : " << fileName; 336 Trace() << "Found a mail " << filePath << " : " << fileName;
341 337
342 Sink::ApplicationDomain::Mail mail; 338 Sink::ApplicationDomain::Mail mail;
343 mail.setProperty("folder", folderLocalId); 339 mail.setProperty("folder", folderLocalId);
344 //We only store the directory path + key, so we facade can add the changing bits (flags) 340 //We only store the directory path + key, so we facade can add the changing bits (flags)
345 mail.setProperty("mimeMessage", KPIM::Maildir::getDirectoryFromFile(filePath) + maildirKey); 341 mail.setProperty("mimeMessage", KPIM::Maildir::getDirectoryFromFile(filePath) + maildirKey);
346 mail.setProperty("unread", !flags.testFlag(KPIM::Maildir::Seen)); 342 mail.setProperty("unread", !flags.testFlag(KPIM::Maildir::Seen));
347 mail.setProperty("important", flags.testFlag(KPIM::Maildir::Flagged)); 343 mail.setProperty("important", flags.testFlag(KPIM::Maildir::Flagged));
348 344
349 createOrModify(transaction, synchronizationTransaction, *mMailAdaptorFactory, bufferType, remoteId, mail); 345 createOrModify(bufferType, remoteId, mail);
346 }
347 commitSync();
348 const auto elapsed = time->elapsed();
349 Log() << "Synchronized " << count << " mails in " << listingPath << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
350 } 350 }
351 mSynchronizerQueue.commit();
352 const auto elapsed = time->elapsed();
353 Log() << "Synchronized " << count << " mails in " << listingPath << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
354 351
355} 352 KAsync::Job<void> synchronizeWithSource()
353 {
354 Log() << " Synchronizing";
355 return KAsync::start<void>([this]() {
356 {
357 synchronizeFolders();
358 //The next sync needs the folders available
359 commit();
360 commitSync();
361 }
362 for (const auto &folder : listAvailableFolders()) {
363 synchronizeMails(folder);
364 //Don't let the transaction grow too much
365 commit();
366 commitSync();
367 }
368 Log() << "Done Synchronizing";
369 });
370 }
356 371
357KAsync::Job<void> MaildirResource::synchronizeWithSource(Sink::Storage &mainStore, Sink::Storage &synchronizationStore) 372public:
358{ 373 QString mMaildirPath;
359 Log() << " Synchronizing"; 374};
360 return KAsync::start<void>([this, &mainStore, &synchronizationStore]() {
361 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
362 {
363 auto synchronizationTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite);
364 synchronizeFolders(transaction, synchronizationTransaction);
365 //The next sync needs the folders available
366 synchronizationTransaction.commit();
367 }
368 for (const auto &folder : listAvailableFolders()) {
369 auto synchronizationTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite);
370 synchronizeMails(transaction, synchronizationTransaction, folder);
371 //Don't let the transaction grow too much
372 synchronizationTransaction.commit();
373 }
374 Log() << "Done Synchronizing";
375 });
376}
377 375
378KAsync::Job<QByteArray> MaildirResource::replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId) 376class MaildirWriteback : public Sink::SourceWriteBack
379{ 377{
380 if (operation == Sink::Operation_Creation) { 378public:
381 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); 379 MaildirWriteback(const QByteArray &resourceType, const QByteArray &resourceInstanceIdentifier) : Sink::SourceWriteBack(resourceType, resourceInstanceIdentifier)
382 Trace() << "Mail created: " << remoteId; 380 {
383 return KAsync::start<QByteArray>([=]() -> QByteArray {
384 return remoteId.toUtf8();
385 });
386 } else if (operation == Sink::Operation_Removal) {
387 Trace() << "Removing a mail: " << oldRemoteId;
388 QFile::remove(oldRemoteId);
389 return KAsync::null<QByteArray>();
390 } else if (operation == Sink::Operation_Modification) {
391 Trace() << "Modifying a mail: " << oldRemoteId;
392 381
393 const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); 382 }
394 const auto maildirPath = KPIM::Maildir::getDirectoryFromFile(filePath);
395 KPIM::Maildir maildir(maildirPath, false);
396 383
397 const auto messagePathParts = filePath.split("/"); 384 KAsync::Job<QByteArray> replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId)
398 if (messagePathParts.isEmpty()) { 385 {
399 Warning() << "No message path available: " << oldRemoteId; 386 if (operation == Sink::Operation_Creation) {
400 return KAsync::error<QByteArray>(1, "No message path available."); 387 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
401 } 388 Trace() << "Mail created: " << remoteId;
402 const auto newIdentifier = messagePathParts.last(); 389 return KAsync::start<QByteArray>([=]() -> QByteArray {
403 QString identifier; 390 return remoteId.toUtf8();
404 if (newIdentifier != KPIM::Maildir::getKeyFromFile(oldRemoteId)) { 391 });
405 //Remove the old mime message if it changed 392 } else if (operation == Sink::Operation_Removal) {
406 Trace() << "Removing old mime message: " << oldRemoteId; 393 Trace() << "Removing a mail: " << oldRemoteId;
407 QFile(oldRemoteId).remove(); 394 // QFile::remove(oldRemoteId);
408 identifier = newIdentifier; 395 return KAsync::null<QByteArray>();
409 } else { 396 } else if (operation == Sink::Operation_Modification) {
410 //The identifier needs to contain the flags for changeEntryFlags to work 397 Trace() << "Modifying a mail: " << oldRemoteId;
411 Q_ASSERT(!oldRemoteId.split('/').isEmpty()); 398 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
412 identifier = oldRemoteId.split('/').last(); 399 return KAsync::start<QByteArray>([=]() -> QByteArray {
400 return remoteId.toUtf8();
401 });
413 } 402 }
403 return KAsync::null<QByteArray>();
404 }
414 405
415 //get flags from 406 KAsync::Job<QByteArray> replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId)
416 KPIM::Maildir::Flags flags; 407 {
417 if (!mail.getUnread()) { 408 if (operation == Sink::Operation_Creation) {
418 flags |= KPIM::Maildir::Seen; 409 auto folderName = folder.getName();
419 } 410 //FIXME handle non toplevel folders
420 if (mail.getImportant()) { 411 auto path = mMaildirPath + "/" + folderName;
421 flags |= KPIM::Maildir::Flagged; 412 Trace() << "Creating a new folder: " << path;
413 KPIM::Maildir maildir(path, false);
414 maildir.create();
415 return KAsync::start<QByteArray>([=]() -> QByteArray {
416 return path.toUtf8();
417 });
418 } else if (operation == Sink::Operation_Removal) {
419 const auto path = oldRemoteId;
420 Trace() << "Removing a folder: " << path;
421 KPIM::Maildir maildir(path, false);
422 maildir.remove();
423 return KAsync::null<QByteArray>();
424 } else if (operation == Sink::Operation_Modification) {
425 Warning() << "Folder modifications are not implemented";
426 return KAsync::start<QByteArray>([=]() -> QByteArray {
427 return oldRemoteId;
428 });
422 } 429 }
423 430 return KAsync::null<QByteArray>();
424 const auto newRemoteId = maildir.changeEntryFlags(identifier, flags);
425 Warning() << "New remote id: " << QString(maildirPath + "/cur/" + newRemoteId);
426 return KAsync::start<QByteArray>([=]() -> QByteArray {
427 return QString(maildirPath + "/cur/" + newRemoteId).toUtf8();
428 });
429 } 431 }
430 return KAsync::null<QByteArray>();
431}
432 432
433KAsync::Job<QByteArray> MaildirResource::replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId) 433public:
434 QString mMaildirPath;
435};
436
437
438MaildirResource::MaildirResource(const QByteArray &instanceIdentifier, const QSharedPointer<Sink::Pipeline> &pipeline)
439 : Sink::GenericResource(PLUGIN_NAME, instanceIdentifier, pipeline)
434{ 440{
435 if (operation == Sink::Operation_Creation) { 441 auto config = ResourceConfig::getConfiguration(instanceIdentifier);
436 auto folderName = folder.getName(); 442 mMaildirPath = QDir::cleanPath(QDir::fromNativeSeparators(config.value("path").toString()));
437 //FIXME handle non toplevel folders 443 //Chop a trailing slash if necessary
438 auto path = mMaildirPath + "/" + folderName; 444 if (mMaildirPath.endsWith("/")) {
439 Trace() << "Creating a new folder: " << path; 445 mMaildirPath.chop(1);
440 KPIM::Maildir maildir(path, false);
441 maildir.create();
442 return KAsync::start<QByteArray>([=]() -> QByteArray {
443 return path.toUtf8();
444 });
445 } else if (operation == Sink::Operation_Removal) {
446 const auto path = oldRemoteId;
447 Trace() << "Removing a folder: " << path;
448 KPIM::Maildir maildir(path, false);
449 maildir.remove();
450 return KAsync::null<QByteArray>();
451 } else if (operation == Sink::Operation_Modification) {
452 Warning() << "Folder modifications are not implemented";
453 return KAsync::start<QByteArray>([=]() -> QByteArray {
454 return oldRemoteId;
455 });
456 } 446 }
457 return KAsync::null<QByteArray>(); 447
448 auto synchronizer = QSharedPointer<MaildirSynchronizer>::create(PLUGIN_NAME, instanceIdentifier);
449 synchronizer->mMaildirPath = mMaildirPath;
450 setupSynchronizer(synchronizer);
451 auto changereplay = QSharedPointer<MaildirWriteback>::create(PLUGIN_NAME, instanceIdentifier);
452 changereplay->mMaildirPath = mMaildirPath;
453 setupChangereplay(changereplay);
454
455 auto folderUpdater = new FolderUpdater(QByteArray());
456 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << folderUpdater << new DefaultIndexUpdater<Sink::ApplicationDomain::Mail>);
457 auto folderPreprocessor = new FolderPreprocessor;
458 setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << folderPreprocessor << new DefaultIndexUpdater<Sink::ApplicationDomain::Folder>);
459
460 KPIM::Maildir dir(mMaildirPath, true);
461 mDraftsFolder = dir.addSubFolder("drafts");
462 Trace() << "Started maildir resource for maildir: " << mMaildirPath;
463
464 auto remoteId = synchronizer->createFolder(mDraftsFolder, "folder");
465 auto draftsFolderLocalId = synchronizer->syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, remoteId);
466 synchronizer->commit();
467 synchronizer->commitSync();
468
469 folderUpdater->mDraftsFolder = draftsFolderLocalId;
470 folderUpdater->mResourceInstanceIdentifier = mResourceInstanceIdentifier;
471 folderUpdater->mMaildirPath = mMaildirPath;
472 folderPreprocessor->mMaildirPath = mMaildirPath;
458} 473}
459 474
460void MaildirResource::removeFromDisk(const QByteArray &instanceIdentifier) 475void MaildirResource::removeFromDisk(const QByteArray &instanceIdentifier)
@@ -471,14 +486,13 @@ KAsync::Job<void> MaildirResource::inspect(int inspectionType, const QByteArray
471 auto mainStore = QSharedPointer<Sink::Storage>::create(Sink::storageLocation(), mResourceInstanceIdentifier, Sink::Storage::ReadOnly); 486 auto mainStore = QSharedPointer<Sink::Storage>::create(Sink::storageLocation(), mResourceInstanceIdentifier, Sink::Storage::ReadOnly);
472 auto transaction = mainStore->createTransaction(Sink::Storage::ReadOnly); 487 auto transaction = mainStore->createTransaction(Sink::Storage::ReadOnly);
473 488
489 auto entityStore = QSharedPointer<EntityStore>::create(mResourceType, mResourceInstanceIdentifier, transaction);
490 auto syncStore = QSharedPointer<RemoteIdMap>::create(synchronizationTransaction);
491
474 Trace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue; 492 Trace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue;
475 493
476 if (domainType == ENTITY_TYPE_MAIL) { 494 if (domainType == ENTITY_TYPE_MAIL) {
477 auto mainDatabase = Sink::Storage::mainDatabase(transaction, ENTITY_TYPE_MAIL); 495 auto mail = entityStore->read<Sink::ApplicationDomain::Mail>(entityId);
478 auto bufferAdaptor = getLatest(mainDatabase, entityId, *mMailAdaptorFactory);
479 Q_ASSERT(bufferAdaptor);
480
481 const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, entityId, 0, bufferAdaptor);
482 const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); 496 const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
483 497
484 if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) { 498 if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) {
@@ -510,13 +524,11 @@ KAsync::Job<void> MaildirResource::inspect(int inspectionType, const QByteArray
510 } 524 }
511 } 525 }
512 if (domainType == ENTITY_TYPE_FOLDER) { 526 if (domainType == ENTITY_TYPE_FOLDER) {
513 const auto remoteId = resolveLocalId(ENTITY_TYPE_FOLDER, entityId, synchronizationTransaction); 527 const auto remoteId = syncStore->resolveLocalId(ENTITY_TYPE_FOLDER, entityId);
514 auto mainDatabase = Sink::Storage::mainDatabase(transaction, ENTITY_TYPE_FOLDER); 528 auto folder = entityStore->read<Sink::ApplicationDomain::Folder>(entityId);
515 auto bufferAdaptor = getLatest(mainDatabase, entityId, *mMailAdaptorFactory);
516 Q_ASSERT(bufferAdaptor);
517 529
518 const Sink::ApplicationDomain::Folder folder(mResourceInstanceIdentifier, entityId, 0, bufferAdaptor);
519 if (inspectionType == Sink::ResourceControl::Inspection::CacheIntegrityInspectionType) { 530 if (inspectionType == Sink::ResourceControl::Inspection::CacheIntegrityInspectionType) {
531 Trace() << "Inspecting cache integrity" << remoteId;
520 if (!QDir(remoteId).exists()) { 532 if (!QDir(remoteId).exists()) {
521 return KAsync::error<void>(1, "The directory is not existing: " + remoteId); 533 return KAsync::error<void>(1, "The directory is not existing: " + remoteId);
522 } 534 }
@@ -533,21 +545,27 @@ KAsync::Job<void> MaildirResource::inspect(int inspectionType, const QByteArray
533 QDir dir(remoteId + "/cur"); 545 QDir dir(remoteId + "/cur");
534 const QFileInfoList list = dir.entryInfoList(QDir::Files); 546 const QFileInfoList list = dir.entryInfoList(QDir::Files);
535 if (list.size() != expectedCount) { 547 if (list.size() != expectedCount) {
548 for (const auto &fileInfo : list) {
549 Warning() << "Found in cache: " << fileInfo.fileName();
550 }
536 return KAsync::error<void>(1, QString("Wrong number of files; found %1 instead of %2.").arg(list.size()).arg(expectedCount)); 551 return KAsync::error<void>(1, QString("Wrong number of files; found %1 instead of %2.").arg(list.size()).arg(expectedCount));
537 } 552 }
538 if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) { 553 if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) {
539 if (!remoteId.endsWith(folder.getName().toUtf8())) { 554 if (!remoteId.endsWith(folder.getName().toUtf8())) {
540 return KAsync::error<void>(1, "Wrong folder name: " + remoteId); 555 return KAsync::error<void>(1, "Wrong folder name: " + remoteId);
541 } 556 }
557 //TODO we shouldn't use the remoteId here to figure out the path, it could be gone/changed already
542 if (QDir(remoteId).exists() != expectedValue.toBool()) { 558 if (QDir(remoteId).exists() != expectedValue.toBool()) {
543 return KAsync::error<void>(1, "Wrong folder existence: " + remoteId); 559 return KAsync::error<void>(1, "Wrong folder existence: " + remoteId);
544 } 560 }
545 } 561 }
546 } 562 }
563
547 } 564 }
548 return KAsync::null<void>(); 565 return KAsync::null<void>();
549} 566}
550 567
568
551MaildirResourceFactory::MaildirResourceFactory(QObject *parent) 569MaildirResourceFactory::MaildirResourceFactory(QObject *parent)
552 : Sink::ResourceFactory(parent) 570 : Sink::ResourceFactory(parent)
553{ 571{
@@ -565,3 +583,9 @@ void MaildirResourceFactory::registerFacades(Sink::FacadeFactory &factory)
565 factory.registerFacade<Sink::ApplicationDomain::Folder, MaildirResourceFolderFacade>(PLUGIN_NAME); 583 factory.registerFacade<Sink::ApplicationDomain::Folder, MaildirResourceFolderFacade>(PLUGIN_NAME);
566} 584}
567 585
586void MaildirResourceFactory::registerAdaptorFactories(Sink::AdaptorFactoryRegistry &registry)
587{
588 registry.registerFactory<Sink::ApplicationDomain::Mail, MaildirMailAdaptorFactory>(PLUGIN_NAME);
589 registry.registerFactory<Sink::ApplicationDomain::Folder, MaildirFolderAdaptorFactory>(PLUGIN_NAME);
590}
591