From 638e75d6f3d00fb473fd45e325fcfb34c6340c65 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Fri, 10 Jun 2016 15:49:48 +0200 Subject: Create the drafts folder if necessary and merge it with the source version --- examples/imapresource/imapresource.cpp | 128 +++++++++++++++++++++++++--- examples/imapresource/tests/resetmailbox.sh | 1 + 2 files changed, 119 insertions(+), 10 deletions(-) (limited to 'examples/imapresource') diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index fee479a..ac75432 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp @@ -39,12 +39,14 @@ #include "sourcewriteback.h" #include "entitystore.h" #include "remoteidmap.h" +#include "query.h" #include #include #include #include #include "imapserverproxy.h" +#include "entityreader.h" //This is the resources entity type, and not the domain type #define ENTITY_TYPE_MAIL "mail" @@ -56,6 +58,77 @@ using namespace Imap; using namespace Sink; +static QHash specialPurposeFolders() +{ + QHash hash; + //FIXME localize + hash.insert("drafts", "Drafts"); + return hash; +} + +static QHash specialPurposeNames() +{ + QHash hash; + for (const auto &value : specialPurposeFolders().values()) { + hash.insert(value.toLower(), specialPurposeFolders().key(value)); + } + return hash; +} + +//specialpurpose, name +static QHash sSpecialPurposeFolders = specialPurposeFolders(); +//Lowercase-name, specialpurpose +static QHash sSpecialPurposeNames = specialPurposeNames(); + +class DraftsProcessor : public Sink::Preprocessor +{ +public: + DraftsProcessor() {} + + QByteArray ensureDraftsFolder(Sink::Storage::Transaction &transaction) + { + if (mDraftsFolder.isEmpty()) { + //Try to find an existing drafts folder + Sink::EntityReader reader(mResourceInstanceIdentifier, mResourceType, transaction); + reader.query(Sink::Query().filter(Query::Comparator("drafts", Query::Comparator::Contains)), + [this](const ApplicationDomain::Folder &f) -> bool{ + mDraftsFolder = f.identifier(); + return false; + }); + if (mDraftsFolder.isEmpty()) { + Trace() << "Failed to find a drafts folder, creating a new one"; + auto folder = ApplicationDomain::Folder::create(mResourceInstanceIdentifier); + folder.setSpecialPurpose(QByteArrayList() << "drafts"); + folder.setName(sSpecialPurposeFolders.value("drafts")); + folder.setIcon("folder"); + //This processes the pipeline synchronously + createEntity(folder); + mDraftsFolder = folder.identifier(); + } + } + return mDraftsFolder; + } + + void newEntity(const QByteArray &uid, qint64 revision, Sink::ApplicationDomain::BufferAdaptor &newEntity, Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE + { + if (newEntity.getProperty("draft").toBool()) { + newEntity.setProperty("folder", ensureDraftsFolder(transaction)); + } + } + + void modifiedEntity(const QByteArray &uid, qint64 revision, const Sink::ApplicationDomain::BufferAdaptor &oldEntity, Sink::ApplicationDomain::BufferAdaptor &newEntity, + Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE + { + if (newEntity.getProperty("draft").toBool()) { + newEntity.setProperty("folder", ensureDraftsFolder(transaction)); + } + } + + QByteArray mDraftsFolder; + QByteArray mResourceInstanceIdentifier; + QByteArray mResourceType; +}; + class MailPropertyExtractor : public Sink::Preprocessor { public: @@ -97,10 +170,6 @@ public: updatedIndexedProperties(newEntity); } - void deletedEntity(const QByteArray &uid, qint64 revision, const Sink::ApplicationDomain::BufferAdaptor &oldEntity, Sink::Storage::Transaction &transaction) Q_DECL_OVERRIDE - { - } - }; static qint64 uidFromMailRid(const QByteArray &remoteId) @@ -142,13 +211,19 @@ public: const auto remoteId = folderPath.toUtf8(); const auto bufferType = ENTITY_TYPE_FOLDER; Sink::ApplicationDomain::Folder folder; - folder.setProperty("name", folderName); - folder.setProperty("icon", icon); + folder.setProperty(ApplicationDomain::Folder::Name::name, folderName); + folder.setProperty(ApplicationDomain::Folder::Icon::name, icon); + QHash mergeCriteria; + if (sSpecialPurposeNames.contains(folderName.toLower())) { + auto type = sSpecialPurposeNames.value(folderName.toLower()); + folder.setProperty(ApplicationDomain::Folder::SpecialPurpose::name, QVariant::fromValue(QByteArrayList() << type)); + mergeCriteria.insert(ApplicationDomain::Folder::SpecialPurpose::name, Query::Comparator(type, Query::Comparator::Contains)); + } if (!parentFolderRid.isEmpty()) { folder.setProperty("parent", syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, parentFolderRid.toUtf8())); } - createOrModify(bufferType, remoteId, folder); + createOrModify(bufferType, remoteId, folder, mergeCriteria); return remoteId; } @@ -434,14 +509,47 @@ public: } Trace() << "Creating a new folder: " << parentFolder << folder.getName(); auto rid = QSharedPointer::create(); - return login.then(imap->createSubfolder(parentFolder, folder.getName())) + auto createFolder = login.then(imap->createSubfolder(parentFolder, folder.getName())) .then([imap, rid](const QString &createdFolder) { Trace() << "Finished creating a new folder: " << createdFolder; *rid = createdFolder.toUtf8(); - }) + }); + if (folder.getSpecialPurpose().isEmpty()) { + return createFolder + .then([rid](){ + return *rid; + }); + } else { //We try to merge special purpose folders first + auto specialPurposeFolders = QSharedPointer>::create(); + auto mergeJob = imap->login(mUser, mPassword) + .then(imap->fetchFolders([=](const QVector &folders) { + for (const auto &f : folders) { + if (sSpecialPurposeNames.contains(f.pathParts.last().toLower())) { + specialPurposeFolders->insert(sSpecialPurposeNames.value(f.pathParts.last().toLower()), f.path); + }; + } + })) + .then>([specialPurposeFolders, folder, imap, parentFolder, rid]() -> KAsync::Job { + for (const auto &purpose : folder.getSpecialPurpose()) { + if (specialPurposeFolders->contains(purpose)) { + auto f = specialPurposeFolders->value(purpose); + Trace() << "Merging specialpurpose folder with: " << f << " with purpose: " << purpose; + *rid = f.toUtf8(); + return KAsync::null(); + } + Trace() << "No match found for merging, creating a new folder"; + return imap->createSubfolder(parentFolder, folder.getName()) + .then([imap, rid](const QString &createdFolder) { + Trace() << "Finished creating a new folder: " << createdFolder; + *rid = createdFolder.toUtf8(); + }); + + }) .then([rid](){ return *rid; }); + return mergeJob; + } } else if (operation == Sink::Operation_Removal) { Trace() << "Removing a folder: " << oldRemoteId; return login.then(imap->remove(oldRemoteId)) @@ -495,7 +603,7 @@ ImapResource::ImapResource(const QByteArray &instanceIdentifier, const QSharedPo changereplay->mPassword = mPassword; setupChangereplay(changereplay); - setupPreprocessors(ENTITY_TYPE_MAIL, QVector() << new MailPropertyExtractor << new DefaultIndexUpdater); + setupPreprocessors(ENTITY_TYPE_MAIL, QVector() << new DraftsProcessor << new MailPropertyExtractor << new DefaultIndexUpdater); setupPreprocessors(ENTITY_TYPE_FOLDER, QVector() << new DefaultIndexUpdater); } diff --git a/examples/imapresource/tests/resetmailbox.sh b/examples/imapresource/tests/resetmailbox.sh index 5e52d6f..8834b51 100644 --- a/examples/imapresource/tests/resetmailbox.sh +++ b/examples/imapresource/tests/resetmailbox.sh @@ -3,6 +3,7 @@ sudo echo "sam user.doe.* cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost sudo echo "dm user.doe.*" | cyradm --auth PLAIN -u cyrus -w admin localhost sudo echo "cm user.doe.test" | cyradm --auth PLAIN -u cyrus -w admin localhost +sudo echo "cm user.doe.Drafts" | cyradm --auth PLAIN -u cyrus -w admin localhost sudo echo "sam user.doe cyrus c" | cyradm --auth PLAIN -u cyrus -w admin localhost sudo cp /work/source/Sink/examples/imapresource/tests/data/1365777830.R28.localhost.localdomain\:2\,S /var/spool/imap/d/user/doe/test/1. sudo chown cyrus:mail /var/spool/imap/d/user/doe/test/1. -- cgit v1.2.3