summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/maildirresource/libmaildir/maildir.cpp2
-rw-r--r--examples/maildirresource/maildirresource.cpp113
-rw-r--r--tests/maildirresourcetest.cpp58
3 files changed, 129 insertions, 44 deletions
diff --git a/examples/maildirresource/libmaildir/maildir.cpp b/examples/maildirresource/libmaildir/maildir.cpp
index 1018b4a..e06219a 100644
--- a/examples/maildirresource/libmaildir/maildir.cpp
+++ b/examples/maildirresource/libmaildir/maildir.cpp
@@ -749,7 +749,7 @@ bool Maildir::removeEntry(const QString& key)
749 } 749 }
750 // KeyCache *keyCache = KeyCache::self(); 750 // KeyCache *keyCache = KeyCache::self();
751 // keyCache->removeKey(d->path, key); 751 // keyCache->removeKey(d->path, key);
752 qWarning() << "Real key"; 752 qWarning() << "Real key" << realKey;
753 QFile file(realKey); 753 QFile file(realKey);
754 if (!file.remove()) { 754 if (!file.remove()) {
755 qWarning() << file.errorString() << file.error(); 755 qWarning() << file.errorString() << file.error();
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp
index 72610a1..3b0c427 100644
--- a/examples/maildirresource/maildirresource.cpp
+++ b/examples/maildirresource/maildirresource.cpp
@@ -65,12 +65,54 @@ public:
65 Sink::EntityBuffer buffer(value); 65 Sink::EntityBuffer buffer(value);
66 const Sink::Entity &entity = buffer.entity(); 66 const Sink::Entity &entity = buffer.entity();
67 const auto adaptor = mFolderAdaptorFactory->createAdaptor(entity); 67 const auto adaptor = mFolderAdaptorFactory->createAdaptor(entity);
68 auto folderName = adaptor->getProperty("name").toString(); 68 auto parentFolder = adaptor->getProperty("parent").toString();
69 //TODO handle non toplevel folders 69 if (mMaildirPath.endsWith(adaptor->getProperty("name").toString())) {
70 folderPath = mMaildirPath + "/" + folderName; 70 folderPath = mMaildirPath;
71 } else {
72 auto folderName = adaptor->getProperty("name").toString();
73 //TODO handle non toplevel folders
74 folderPath = mMaildirPath + "/" + folderName;
75 }
71 }); 76 });
72 return folderPath; 77 return folderPath;
73 } 78 }
79
80 QString moveMessage(const QString &oldPath, const QByteArray &folder, Sink::Storage::Transaction &transaction)
81 {
82 if (oldPath.startsWith(Sink::temporaryFileLocation())) {
83 const auto path = getPath(folder, transaction);
84 KPIM::Maildir maildir(path, false);
85 if (!maildir.isValid(true)) {
86 qWarning() << "Maildir is not existing: " << path;
87 }
88 auto identifier = maildir.addEntryFromPath(oldPath);
89 return path + "/" + identifier;
90 }
91 return oldPath;
92 }
93 void updatedIndexedProperties(Sink::ApplicationDomain::BufferAdaptor &newEntity)
94 {
95 const auto mimeMessagePath = newEntity.getProperty("mimeMessage").toString();
96 auto parts = mimeMessagePath.split('/');
97 const auto key = parts.takeLast();
98 const auto path = parts.join("/") + "/cur/";
99
100 QDir dir(path);
101 const QFileInfoList list = dir.entryInfoList(QStringList() << (key+"*"), QDir::Files);
102 if (list.size() != 1) {
103 Warning() << "Failed to find message " << path << key << list.size();
104 return;
105 }
106
107 KMime::Message *msg = new KMime::Message;
108 msg->setHead(KMime::CRLFtoLF(KPIM::Maildir::readEntryHeadersFromFile(list.first().filePath())));
109 msg->parse();
110
111 newEntity.setProperty("subject", msg->subject(true)->asUnicodeString());
112 newEntity.setProperty("sender", msg->from(true)->asUnicodeString());
113 newEntity.setProperty("senderName", msg->from(true)->asUnicodeString());
114 newEntity.setProperty("date", msg->date(true)->dateTime());
115 }
74 116
75 void newEntity(const QByteArray &uid, qint64 revision, Sink::ApplicationDomain::BufferAdaptor &newEntity, Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE 117 void newEntity(const QByteArray &uid, qint64 revision, Sink::ApplicationDomain::BufferAdaptor &newEntity, Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE
76 { 118 {
@@ -79,46 +121,20 @@ public:
79 } 121 }
80 const auto mimeMessage = newEntity.getProperty("mimeMessage"); 122 const auto mimeMessage = newEntity.getProperty("mimeMessage");
81 if (mimeMessage.isValid()) { 123 if (mimeMessage.isValid()) {
82 const auto oldPath = mimeMessage.toString(); 124 newEntity.setProperty("mimeMessage", moveMessage(mimeMessage.toString(), newEntity.getProperty("folder").toByteArray(), transaction));
83 if (oldPath.startsWith(Sink::temporaryFileLocation())) {
84 auto folder = newEntity.getProperty("folder").toByteArray();
85 const auto path = getPath(folder, transaction);
86 KPIM::Maildir maildir(path, false);
87 if (!maildir.isValid(true)) {
88 qWarning() << "Maildir is not existing: " << path;
89 }
90 auto identifier = maildir.addEntryFromPath(oldPath);
91 newEntity.setProperty("mimeMessage", path + "/" + identifier);
92 }
93 }
94
95 {
96 const auto mimeMessagePath = newEntity.getProperty("mimeMessage").toString();
97 auto parts = mimeMessagePath.split('/');
98 const auto key = parts.takeLast();
99 const auto path = parts.join("/") + "/cur/";
100
101 QDir dir(path);
102 const QFileInfoList list = dir.entryInfoList(QStringList() << (key+"*"), QDir::Files);
103 if (list.size() != 1) {
104 Warning() << "Failed to find message " << path << key << list.size();
105 return;
106 }
107
108 KMime::Message *msg = new KMime::Message;
109 msg->setHead(KMime::CRLFtoLF(KPIM::Maildir::readEntryHeadersFromFile(list.first().filePath())));
110 msg->parse();
111
112 newEntity.setProperty("subject", msg->subject(true)->asUnicodeString());
113 newEntity.setProperty("sender", msg->from(true)->asUnicodeString());
114 newEntity.setProperty("senderName", msg->from(true)->asUnicodeString());
115 newEntity.setProperty("date", msg->date(true)->dateTime());
116 } 125 }
126 updatedIndexedProperties(newEntity);
117 } 127 }
118 128
119 void modifiedEntity(const QByteArray &uid, qint64 revision, const Sink::ApplicationDomain::BufferAdaptor &oldEntity, Sink::ApplicationDomain::BufferAdaptor &newEntity, 129 void modifiedEntity(const QByteArray &uid, qint64 revision, const Sink::ApplicationDomain::BufferAdaptor &oldEntity, Sink::ApplicationDomain::BufferAdaptor &newEntity,
120 Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE 130 Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE
121 { 131 {
132 //TODO deal with moves
133 const auto mimeMessage = newEntity.getProperty("mimeMessage");
134 if (mimeMessage.isValid()) {
135 newEntity.setProperty("mimeMessage", moveMessage(mimeMessage.toString(), newEntity.getProperty("folder").toByteArray(), transaction));
136 }
137 updatedIndexedProperties(newEntity);
122 } 138 }
123 139
124 void deletedEntity(const QByteArray &uid, qint64 revision, const Sink::ApplicationDomain::BufferAdaptor &oldEntity, Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE 140 void deletedEntity(const QByteArray &uid, qint64 revision, const Sink::ApplicationDomain::BufferAdaptor &oldEntity, Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE
@@ -419,22 +435,33 @@ KAsync::Job<void> MaildirResource::replay(Sink::Storage &synchronizationStore, c
419 const auto uid = Sink::Storage::uidFromKey(key); 435 const auto uid = Sink::Storage::uidFromKey(key);
420 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, uid, synchronizationTransaction); 436 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, uid, synchronizationTransaction);
421 Trace() << "Modifying a mail: " << remoteId; 437 Trace() << "Modifying a mail: " << remoteId;
422 auto parts = remoteId.split('/');
423 const auto filename = parts.takeLast(); //filename
424 parts.removeLast(); //cur/new folder
425 auto maildirPath = parts.join('/');
426 438
439 const auto maildirPath = KPIM::Maildir::getDirectoryFromFile(remoteId);
427 KPIM::Maildir maildir(maildirPath, false); 440 KPIM::Maildir maildir(maildirPath, false);
428 441
429 const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, mMailAdaptorFactory->createAdaptor(entity)); 442 const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, mMailAdaptorFactory->createAdaptor(entity));
443 auto newIdentifier = mail.getMimeMessagePath().split("/").last();
444 QString identifier;
445 if (newIdentifier != KPIM::Maildir::getKeyFromFile(remoteId)) {
446 //Remove the old mime message if it changed
447 Trace() << "Removing old mime message: " << remoteId;
448 QFile(remoteId).remove();
449 identifier = newIdentifier;
450 } else {
451 //The identifier needs to contain the flags for changeEntryFlags to work
452 identifier = remoteId.split('/').last();
453 }
430 454
431 //get flags from 455 //get flags from
432 KPIM::Maildir::Flags flags; 456 KPIM::Maildir::Flags flags;
433 if (!mail.getProperty("unread").toBool()) { 457 if (!mail.getUnread()) {
434 flags |= KPIM::Maildir::Seen; 458 flags |= KPIM::Maildir::Seen;
435 } 459 }
460 if (mail.getImportant()) {
461 flags |= KPIM::Maildir::Flagged;
462 }
436 463
437 auto newRemoteId = maildir.changeEntryFlags(filename, flags); 464 const auto newRemoteId = maildir.changeEntryFlags(identifier, flags);
438 updateRemoteId(ENTITY_TYPE_MAIL, uid, QString(maildirPath + "/cur/" + newRemoteId).toUtf8(), synchronizationTransaction); 465 updateRemoteId(ENTITY_TYPE_MAIL, uid, QString(maildirPath + "/cur/" + newRemoteId).toUtf8(), synchronizationTransaction);
439 } else { 466 } else {
440 Warning() << "Unkown operation" << operation; 467 Warning() << "Unkown operation" << operation;
diff --git a/tests/maildirresourcetest.cpp b/tests/maildirresourcetest.cpp
index 6d78242..968abde 100644
--- a/tests/maildirresourcetest.cpp
+++ b/tests/maildirresourcetest.cpp
@@ -466,6 +466,64 @@ private slots:
466 QVERIFY(!result2.errorCode()); 466 QVERIFY(!result2.errorCode());
467 } 467 }
468 468
469 void testEditMail()
470 {
471 using namespace Sink;
472 using namespace Sink::ApplicationDomain;
473
474 auto query = Query::ResourceFilter("org.kde.maildir.instance1");
475 Store::synchronize(query).exec().waitForFinished();
476 ResourceControl::flushMessageQueue(query.resources).exec().waitForFinished();
477
478 Folder f;
479
480 auto result = Store::fetchOne<Folder>(
481 Query::ResourceFilter("org.kde.maildir.instance1") + Query::PropertyFilter("name", "maildir1") + Query::RequestedProperties(QByteArrayList() << "name"))
482 .then<void, KAsync::Job<void>, Folder>([query, &f](const Folder &folder) {
483 f = folder;
484 return Store::fetchAll<Mail>(Query::ResourceFilter("org.kde.maildir.instance1") + Query::PropertyFilter("folder", folder) +
485 Query::RequestedProperties(QByteArrayList() << "folder"
486 << "subject" << "mimeMessage"))
487 .then<void, KAsync::Job<void>, QList<Mail::Ptr>>([query](const QList<Mail::Ptr> &mails) {
488 ASYNCCOMPARE(mails.size(), 1);
489 auto mail = mails.first();
490 auto message = KMime::Message::Ptr::create();
491 message->subject(true)->fromUnicodeString("Test1", "utf8");
492 message->assemble();
493 mail->setMimeMessage(message->encodedContent());
494 return Store::modify(*mail);
495 });
496 })
497 .exec();
498 result.waitForFinished();
499 QVERIFY(!result.errorCode());
500
501 ResourceControl::flushMessageQueue(query.resources).exec().waitForFinished();
502
503 // Verify that we can still query for all relevant information
504 auto result2 = Store::fetchAll<Mail>(
505 Query::ResourceFilter("org.kde.maildir.instance1") + Query::PropertyFilter("folder", f) + Query::RequestedProperties(QByteArrayList() << "folder"
506 << "subject"
507 << "mimeMessage"
508 << "unread"))
509 .then<void, KAsync::Job<void>, QList<Mail::Ptr>>([](const QList<Mail::Ptr> &mails) {
510 ASYNCCOMPARE(mails.size(), 1);
511 auto mail = mails.first();
512 ASYNCCOMPARE(mail->getProperty("subject").toString(), QString("Test1"));
513 ASYNCVERIFY(QFileInfo(mail->getMimeMessagePath()).exists());
514 return KAsync::null<void>();
515 })
516 .exec();
517 result2.waitForFinished();
518 QVERIFY(!result2.errorCode());
519
520 //Ensure we didn't leave a stale message behind
521 auto targetPath = tempDir.path() + "/maildir1/cur";
522 QDir dir(targetPath);
523 dir.setFilter(QDir::Files);
524 QTRY_COMPARE(dir.count(), static_cast<unsigned int>(1));
525 }
526
469 void testCreateDraft() 527 void testCreateDraft()
470 { 528 {
471 Sink::Query query; 529 Sink::Query query;