From 57a88e6c1514b85d25b066059defcd62d2ccd8d6 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 9 Mar 2017 16:14:02 +0100 Subject: Addressbook support --- examples/davresource/davresource.cpp | 374 +++++------------------------------ 1 file changed, 44 insertions(+), 330 deletions(-) (limited to 'examples/davresource/davresource.cpp') diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp index df2c4b4..3d23a04 100644 --- a/examples/davresource/davresource.cpp +++ b/examples/davresource/davresource.cpp @@ -21,10 +21,8 @@ #include "facade.h" #include "resourceconfig.h" -#include "index.h" #include "log.h" #include "definitions.h" -#include "inspection.h" #include "synchronizer.h" #include "inspector.h" @@ -40,181 +38,14 @@ #include #include -#include -#include - //This is the resources entity type, and not the domain type #define ENTITY_TYPE_CONTACT "contact" -#define ENTITY_TYPE_ADDRESSBOOK "folder" +#define ENTITY_TYPE_ADDRESSBOOK "addressbook" SINK_DEBUG_AREA("davresource") using namespace Sink; -/*static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) -{ - auto parts = mimeMessagePath.split('/'); - const auto key = parts.takeLast(); - const auto path = parts.join("/") + "/cur/"; - - QDir dir(path); - const QFileInfoList list = dir.entryInfoList(QStringList() << (key+"*"), QDir::Files); - if (list.size() != 1) { - SinkWarning() << "Failed to find message " << mimeMessagePath; - SinkWarning() << "Failed to find message " << path; - return QString(); - } - return list.first().filePath(); -} - -class MaildirMailPropertyExtractor : public MailPropertyExtractor -{ -protected: - virtual QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) const Q_DECL_OVERRIDE - { - return ::getFilePathFromMimeMessagePath(mimeMessagePath); - } -}; - -class MaildirMimeMessageMover : public Sink::Preprocessor -{ -public: - MaildirMimeMessageMover(const QByteArray &resourceInstanceIdentifier, const QString &maildirPath) : mResourceInstanceIdentifier(resourceInstanceIdentifier), mMaildirPath(maildirPath) {} - - QString getPath(const QByteArray &folderIdentifier) - { - if (folderIdentifier.isEmpty()) { - return mMaildirPath; - } - QString folderPath; - const auto folder = entityStore().readLatest(folderIdentifier); - if (mMaildirPath.endsWith(folder.getName())) { - folderPath = mMaildirPath; - } else { - auto folderName = folder.getName(); - //FIXME handle non toplevel folders - folderPath = mMaildirPath + "/" + folderName; - } - return folderPath; - } - - QString moveMessage(const QString &oldPath, const QByteArray &folder) - { - if (oldPath.startsWith(Sink::temporaryFileLocation())) { - const auto path = getPath(folder); - KPIM::Contactdir maildir(path, false); - if (!maildir.isValid(true)) { - SinkWarning() << "Maildir is not existing: " << path; - } - auto identifier = maildir.addEntryFromPath(oldPath); - return path + "/" + identifier; - } else { - //Handle moves - const auto path = getPath(folder); - KPIM::Contactdir maildir(path, false); - if (!maildir.isValid(true)) { - SinkWarning() << "Maildir is not existing: " << path; - } - auto oldIdentifier = KPIM::Contactdir::getKeyFromFile(oldPath); - auto pathParts = oldPath.split('/'); - pathParts.takeLast(); - auto oldDirectory = pathParts.join('/'); - if (oldDirectory == path) { - return oldPath; - } - KPIM::Contactdir oldMaildir(oldDirectory, false); - if (!oldMaildir.isValid(false)) { - SinkWarning() << "Maildir is not existing: " << path; - } - auto identifier = oldMaildir.moveEntryTo(oldIdentifier, maildir); - return path + "/" + identifier; - } - } - - void newEntity(Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE - { - auto mail = newEntity.cast(); - const auto mimeMessage = mail.getMimeMessagePath(); - if (!mimeMessage.isNull()) { - const auto path = moveMessage(mimeMessage, mail.getFolder()); - auto blob = ApplicationDomain::BLOB{path}; - blob.isExternal = false; - mail.setProperty(ApplicationDomain::Contact::MimeMessage::name, QVariant::fromValue(blob)); - } - } - - void modifiedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity, Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE - { - auto newMail = newEntity.cast(); - const ApplicationDomain::Contact oldMail{oldEntity}; - const auto mimeMessage = newMail.getMimeMessagePath(); - const auto newFolder = newMail.getFolder(); - const bool mimeMessageChanged = !mimeMessage.isNull() && mimeMessage != oldMail.getMimeMessagePath(); - const bool folderChanged = !newFolder.isNull() && newFolder != oldMail.getFolder(); - if (mimeMessageChanged || folderChanged) { - SinkTrace() << "Moving mime message: " << mimeMessageChanged << folderChanged; - auto newPath = moveMessage(mimeMessage, newMail.getFolder()); - if (newPath != oldMail.getMimeMessagePath()) { - const auto oldPath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath()); - auto blob = ApplicationDomain::BLOB{newPath}; - blob.isExternal = false; - newMail.setProperty(ApplicationDomain::Contact::MimeMessage::name, QVariant::fromValue(blob)); - //Remove the olde mime message if there is a new one - QFile::remove(oldPath); - } - } - - auto mimeMessagePath = newMail.getMimeMessagePath(); - const auto maildirPath = getPath(newMail.getFolder()); - KPIM::Contactdir maildir(maildirPath, false); - const auto file = getFilePathFromMimeMessagePath(mimeMessagePath); - QString identifier = KPIM::Contactdir::getKeyFromFile(file); - - //get flags from - KPIM::Contactdir::Flags flags; - if (!newMail.getUnread()) { - flags |= KPIM::Contactdir::Seen; - } - if (newMail.getImportant()) { - flags |= KPIM::Contactdir::Flagged; - } - - maildir.changeEntryFlags(identifier, flags); - } - - void deletedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity) Q_DECL_OVERRIDE - { - const ApplicationDomain::Contact oldMail{oldEntity}; - const auto filePath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath()); - QFile::remove(filePath); - } - QByteArray mResourceInstanceIdentifier; - QString mMaildirPath; -}; - -class FolderPreprocessor : public Sink::Preprocessor -{ -public: - FolderPreprocessor(const QString maildirPath) : mMaildirPath(maildirPath) {} - - void newEntity(Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE - { - auto folderName = Sink::ApplicationDomain::Folder{newEntity}.getName(); - const auto path = mMaildirPath + "/" + folderName; - KPIM::Contactdir maildir(path, false); - maildir.create(); - } - - void modifiedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity, Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE - { - } - - void deletedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity) Q_DECL_OVERRIDE - { - } - QString mMaildirPath; -};*/ - static KAsync::Job runJob(KJob *job) { return KAsync::start([job](KAsync::Future &future) { @@ -240,33 +71,33 @@ public: } - QByteArray createAddressbook(const QString &folderName, const QString &folderPath, const QString &parentFolderRid, const QByteArray &icon) + QByteArray createAddressbook(const QString &addressbookName, const QString &addressbookPath, const QString &parentAddressbookRid) { - SinkTrace() << "Creating addressbook: " << folderName << parentFolderRid; - const auto remoteId = folderPath.toUtf8(); + SinkTrace() << "Creating addressbook: " << addressbookName << parentAddressbookRid; + const auto remoteId = addressbookPath.toUtf8(); const auto bufferType = ENTITY_TYPE_ADDRESSBOOK; - Sink::ApplicationDomain::Folder folder; - folder.setName(folderName); - folder.setIcon(icon); + Sink::ApplicationDomain::Addressbook addressbook; + addressbook.setName(addressbookName); QHash mergeCriteria; - if (!parentFolderRid.isEmpty()) { - folder.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, parentFolderRid.toUtf8())); + if (!parentAddressbookRid.isEmpty()) { + addressbook.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, parentAddressbookRid.toUtf8())); } - createOrModify(bufferType, remoteId, folder, mergeCriteria); + createOrModify(bufferType, remoteId, addressbook, mergeCriteria); return remoteId; } - void synchronizeAddressbooks(const KDAV::DavCollection::List &folderList) + void synchronizeAddressbooks(const KDAV::DavCollection::List &addressbookList) { const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK; - SinkTrace() << "Found addressbooks " << folderList.size(); + SinkTrace() << "Found addressbooks " << addressbookList.size(); QVector ridList; - for(const auto &f : folderList) { - const auto &rid = f.url().toDisplayString(); - ridList.append(rid.toUtf8()); - createAddressbook(f.displayName(), rid, "", "addressbook"); + for(const auto &f : addressbookList) { + const auto &rid = getRid(f); + SinkTrace() << "Found addressbook:" << rid; + ridList.append(rid); + createAddressbook(f.displayName(), rid, ""); } scanForRemovals(bufferType, @@ -284,21 +115,32 @@ public: list << Synchronizer::SyncRequest{query}; } else { //We want to synchronize everything - list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName())}; + list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName())}; list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName())}; } return list; } + static QByteArray getRid(const KDAV::DavItem &item) + { + return item.url().toDisplayString().toUtf8(); + } + + static QByteArray getRid(const KDAV::DavCollection &item) + { + return item.url().toDisplayString().toUtf8(); + } + KAsync::Job synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE { - if (query.type() == ApplicationDomain::getTypeName()) { - SinkLogCtx(mLogCtx) << "Synchronizing folders:" << mResourceUrl.url(); + if (query.type() == ApplicationDomain::getTypeName()) { + SinkLogCtx(mLogCtx) << "Synchronizing addressbooks:" << mResourceUrl.url(); auto collectionsFetchJob = new KDAV::DavCollectionsFetchJob(mResourceUrl); auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] (const KAsync::Error &error) { if (error) { - SinkWarningCtx(mLogCtx) << "Failed to synchronize folders." << collectionsFetchJob->errorString(); + SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString(); } else { + SinkWarningCtx(mLogCtx) << "Fetched addressbooks." << collectionsFetchJob->errorString(); synchronizeAddressbooks(collectionsFetchJob ->collections()); } }); @@ -312,7 +154,8 @@ public: return collectionsFetchJob->collections(); }) .serialEach([this, ridList](const KDAV::DavCollection &collection) { - auto collId = collection.url().toDisplayString().toLatin1(); + auto collId = getRid(collection); + const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId); auto ctag = collection.CTag().toLatin1(); if (ctag != syncStore().readValue(collId + "_ctagXX")) { SinkTraceCtx(mLogCtx) << "Syncing " << collId; @@ -323,22 +166,23 @@ public: auto colljob = runJob(davItemsListJob).then([davItemsListJob] { return KAsync::value(davItemsListJob->items()); }) - .serialEach([this, ridList, bufferType, mergeCriteria] (const KDAV::DavItem &item) { - QByteArray rid = item.url().toDisplayString().toUtf8(); + .serialEach([=] (const KDAV::DavItem &item) { + QByteArray rid = getRid(item); if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){ SinkTrace() << "Updating " << rid; auto davItemFetchJob = new KDAV::DavItemFetchJob(item); auto itemjob = runJob(davItemFetchJob) - .then([this, davItemFetchJob, bufferType, mergeCriteria] { + .then([=] { const auto item = davItemFetchJob->item(); - const auto rid = item.url().toDisplayString().toUtf8(); + const auto rid = getRid(item); Sink::ApplicationDomain::Contact contact; contact.setVcard(item.data()); + contact.setAddressbook(addressbookLocalId); createOrModify(bufferType, rid, contact, mergeCriteria); return item; }) .then([this, ridList] (const KDAV::DavItem &item) { - const auto rid = item.url().toDisplayString().toUtf8(); + const auto rid = getRid(item); syncStore().writeValue(rid + "_etag", item.etag().toLatin1()); ridList->append(rid); return rid; @@ -375,43 +219,11 @@ public: KAsync::Job replay(const ApplicationDomain::Contact &contact, Sink::Operation operation, const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE { - /* - if (operation == Sink::Operation_Creation) { - const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); - SinkTrace() << "Contact created: " << remoteId; - return KAsync::value(remoteId.toUtf8()); - } else if (operation == Sink::Operation_Removal) { - SinkTrace() << "Removing a contact " << oldRemoteId; - return KAsync::null(); - } else if (operation == Sink::Operation_Modification) { - SinkTrace() << "Modifying a contact: " << oldRemoteId; - const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); - return KAsync::value(remoteId.toUtf8()); - }*/ return KAsync::null(); } - KAsync::Job replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE + KAsync::Job replay(const ApplicationDomain::Addressbook &addressbook, Sink::Operation operation, const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE { - /* - if (operation == Sink::Operation_Creation) { - auto folderName = folder.getName(); - //FIXME handle non toplevel folders - auto path = mMaildirPath + "/" + folderName; - SinkTrace() << "Creating a new folder: " << path; - KPIM::Contactdir maildir(path, false); - maildir.create(); - return KAsync::value(path.toUtf8()); - } else if (operation == Sink::Operation_Removal) { - const auto path = oldRemoteId; - SinkTrace() << "Removing a folder: " << path; - KPIM::Contactdir maildir(path, false); - maildir.remove(); - return KAsync::null(); - } else if (operation == Sink::Operation_Modification) { - SinkWarning() << "Folder modifications are not implemented"; - return KAsync::value(oldRemoteId); - }*/ return KAsync::null(); } @@ -419,103 +231,6 @@ public: KDAV::DavUrl mResourceUrl; }; -/* -class MaildirInspector : public Sink::Inspector { -public: - MaildirInspector(const Sink::ResourceContext &resourceContext) - : Sink::Inspector(resourceContext) - { - - } -protected: - - KAsync::Job inspect(int inspectionType, const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue) Q_DECL_OVERRIDE { - auto synchronizationStore = QSharedPointer::create(Sink::storageLocation(), mResourceContext.instanceId() + ".synchronization", Sink::Storage::DataStore::ReadOnly); - auto synchronizationTransaction = synchronizationStore->createTransaction(Sink::Storage::DataStore::ReadOnly); - - auto mainStore = QSharedPointer::create(Sink::storageLocation(), mResourceContext.instanceId(), Sink::Storage::DataStore::ReadOnly); - auto transaction = mainStore->createTransaction(Sink::Storage::DataStore::ReadOnly); - - Sink::Storage::EntityStore entityStore(mResourceContext, {"maildirresource"}); - auto syncStore = QSharedPointer::create(synchronizationTransaction); - - SinkTrace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue; - - if (domainType == ENTITY_TYPE_MAIL) { - auto mail = entityStore.readLatest(entityId); - const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); - - if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) { - if (property == "unread") { - const auto flags = KPIM::Contactdir::readEntryFlags(filePath.split('/').last()); - if (expectedValue.toBool() && (flags & KPIM::Contactdir::Seen)) { - return KAsync::error(1, "Expected unread but couldn't find it."); - } - if (!expectedValue.toBool() && !(flags & KPIM::Contactdir::Seen)) { - return KAsync::error(1, "Expected read but couldn't find it."); - } - return KAsync::null(); - } - if (property == "subject") { - KMime::Message *msg = new KMime::Message; - msg->setHead(KMime::CRLFtoLF(KPIM::Contactdir::readEntryHeadersFromFile(filePath))); - msg->parse(); - - if (msg->subject(true)->asUnicodeString() != expectedValue.toString()) { - return KAsync::error(1, "Subject not as expected: " + msg->subject(true)->asUnicodeString()); - } - return KAsync::null(); - } - } - if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) { - if (QFileInfo(filePath).exists() != expectedValue.toBool()) { - return KAsync::error(1, "Wrong file existence: " + filePath); - } - } - } - if (domainType == ENTITY_TYPE_FOLDER) { - const auto remoteId = syncStore->resolveLocalId(ENTITY_TYPE_FOLDER, entityId); - auto folder = entityStore.readLatest(entityId); - - if (inspectionType == Sink::ResourceControl::Inspection::CacheIntegrityInspectionType) { - SinkTrace() << "Inspecting cache integrity" << remoteId; - if (!QDir(remoteId).exists()) { - return KAsync::error(1, "The directory is not existing: " + remoteId); - } - - int expectedCount = 0; - Index index("mail.index.folder", transaction); - index.lookup(entityId, [&](const QByteArray &sinkId) { - expectedCount++; - }, - [&](const Index::Error &error) { - SinkWarning() << "Error in index: " << error.message << property; - }); - - QDir dir(remoteId + "/cur"); - const QFileInfoList list = dir.entryInfoList(QDir::Files); - if (list.size() != expectedCount) { - for (const auto &fileInfo : list) { - SinkWarning() << "Found in cache: " << fileInfo.fileName(); - } - return KAsync::error(1, QString("Wrong number of files; found %1 instead of %2.").arg(list.size()).arg(expectedCount)); - } - } - if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) { - if (!remoteId.endsWith(folder.getName().toUtf8())) { - return KAsync::error(1, "Wrong folder name: " + remoteId); - } - //TODO we shouldn't use the remoteId here to figure out the path, it could be gone/changed already - if (QDir(remoteId).exists() != expectedValue.toBool()) { - return KAsync::error(1, "Wrong folder existence: " + remoteId); - } - } - - } - return KAsync::null(); - } -};*/ - DavResource::DavResource(const Sink::ResourceContext &resourceContext) : Sink::GenericResource(resourceContext) @@ -530,7 +245,6 @@ DavResource::DavResource(const Sink::ResourceContext &resourceContext) auto synchronizer = QSharedPointer::create(resourceContext); synchronizer->mResourceUrl = mResourceUrl; setupSynchronizer(synchronizer); - //setupInspector(QSharedPointer::create(resourceContext)); setupPreprocessors(ENTITY_TYPE_CONTACT, QVector() << new ContactPropertyExtractor); } @@ -539,8 +253,8 @@ DavResource::DavResource(const Sink::ResourceContext &resourceContext) DavResourceFactory::DavResourceFactory(QObject *parent) : Sink::ResourceFactory(parent, {Sink::ApplicationDomain::ResourceCapabilities::Contact::contact, - Sink::ApplicationDomain::ResourceCapabilities::Mail::folder, - "-folder.rename"} + Sink::ApplicationDomain::ResourceCapabilities::Contact::addressbook, + } ) { } @@ -553,13 +267,13 @@ Sink::Resource *DavResourceFactory::createResource(const ResourceContext &contex void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory) { factory.registerFacade(name); - factory.registerFacade(name); + factory.registerFacade(name); } void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry ®istry) { registry.registerFactory(name); - registry.registerFactory(name); + registry.registerFactory(name); } void DavResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier) -- cgit v1.2.3