From 761328989492db9bd603c2d7f1134d20e485d2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Nicole?= Date: Tue, 27 Mar 2018 18:26:11 +0200 Subject: Add CalDAV support Summary: Notes: - Add a `webdavcommon` folder for WebDAV generic resource code - Move `davresource` to `carddaveresource` and make it use the WebDAV code - For now it tests the CalDAV resource directly on KolabNow (to be changed) - Only synchronization, not adding / changing / removing WebDAV collections or items (to be implemented) - Only events are currently supported (todo, freebusy, etc. are to be implemented but should be straightforward) Fixes T8224 Reviewers: cmollekopf Tags: #sink Maniphest Tasks: T8224 Differential Revision: https://phabricator.kde.org/D11741 --- examples/davresource/davresource.cpp | 315 ----------------------------------- 1 file changed, 315 deletions(-) delete mode 100644 examples/davresource/davresource.cpp (limited to 'examples/davresource/davresource.cpp') diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp deleted file mode 100644 index fde7055..0000000 --- a/examples/davresource/davresource.cpp +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (C) 2015 Christian Mollekopf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "davresource.h" - -#include "facade.h" -#include "resourceconfig.h" -#include "log.h" -#include "definitions.h" -#include "synchronizer.h" -#include "inspector.h" - -#include "facadefactory.h" -#include "adaptorfactoryregistry.h" - -#include "contactpreprocessor.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -//This is the resources entity type, and not the domain type -#define ENTITY_TYPE_CONTACT "contact" -#define ENTITY_TYPE_ADDRESSBOOK "addressbook" - -using namespace Sink; - -static int translateDavError(KJob *job) -{ - const int responseCode = static_cast(job)->latestResponseCode(); - - switch (responseCode) { - case QNetworkReply::HostNotFoundError: - return ApplicationDomain::NoServerError; - //Since we don't login we will just not have the necessary permissions ot view the object - case QNetworkReply::OperationCanceledError: - return ApplicationDomain::LoginError; - } - return ApplicationDomain::UnknownError; -} - -static KAsync::Job runJob(KJob *job) -{ - return KAsync::start([job](KAsync::Future &future) { - QObject::connect(job, &KJob::result, [&future](KJob *job) { - SinkTrace() << "Job done: " << job->metaObject()->className(); - if (job->error()) { - SinkWarning() << "Job failed: " << job->errorString() << job->metaObject()->className() << job->error() << static_cast(job)->latestResponseCode(); - future.setError(translateDavError(job), job->errorString()); - } else { - future.setFinished(); - } - }); - SinkTrace() << "Starting job: " << job->metaObject()->className(); - job->start(); - }); -} - -class ContactSynchronizer : public Sink::Synchronizer { -public: - ContactSynchronizer(const Sink::ResourceContext &resourceContext) - : Sink::Synchronizer(resourceContext) - { - - } - - QByteArray createAddressbook(const QString &addressbookName, const QString &addressbookPath, const QString &parentAddressbookRid) - { - SinkTrace() << "Creating addressbook: " << addressbookName << parentAddressbookRid; - const auto remoteId = addressbookPath.toUtf8(); - const auto bufferType = ENTITY_TYPE_ADDRESSBOOK; - Sink::ApplicationDomain::Addressbook addressbook; - addressbook.setName(addressbookName); - QHash mergeCriteria; - - if (!parentAddressbookRid.isEmpty()) { - addressbook.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, parentAddressbookRid.toUtf8())); - } - createOrModify(bufferType, remoteId, addressbook, mergeCriteria); - return remoteId; - } - - void synchronizeAddressbooks(const KDAV2::DavCollection::List &addressbookList) - { - const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK; - SinkTrace() << "Found addressbooks " << addressbookList.size(); - - QVector ridList; - for(const auto &f : addressbookList) { - const auto &rid = getRid(f); - SinkLog() << "Found addressbook:" << rid << f.displayName(); - ridList.append(rid); - createAddressbook(f.displayName(), rid, ""); - } - - scanForRemovals(bufferType, - [&ridList](const QByteArray &remoteId) -> bool { - return ridList.contains(remoteId); - } - ); - } - - QList getSyncRequests(const Sink::QueryBase &query) Q_DECL_OVERRIDE - { - QList list; - if (!query.type().isEmpty()) { - //We want to synchronize something specific - list << Synchronizer::SyncRequest{query}; - } else { - //We want to synchronize everything - list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName())}; - list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName())}; - } - return list; - } - - static QByteArray getRid(const KDAV2::DavItem &item) - { - return item.url().toDisplayString().toUtf8(); - } - - static QByteArray getRid(const KDAV2::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 addressbooks:" << resourceUrl().url(); - auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(resourceUrl()); - auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] (const KAsync::Error &error) { - if (error) { - SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString(); - } else { - synchronizeAddressbooks(collectionsFetchJob->collections()); - } - }); - return job; - } else if (query.type() == ApplicationDomain::getTypeName()) { - SinkLogCtx(mLogCtx) << "Synchronizing contacts."; - auto ridList = QSharedPointer::create(); - auto total = QSharedPointer::create(0); - auto progress = QSharedPointer::create(0); - auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(resourceUrl()); - auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] { - synchronizeAddressbooks(collectionsFetchJob ->collections()); - return collectionsFetchJob->collections(); - }) - .serialEach([=](const KDAV2::DavCollection &collection) { - 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; - auto cache = std::shared_ptr(new KDAV2::EtagCache()); - auto davItemsListJob = new KDAV2::DavItemsListJob(collection.url(), cache); - QHash mergeCriteria; - auto colljob = runJob(davItemsListJob).then([=] { - const auto items = davItemsListJob->items(); - *total = items.size(); - return KAsync::value(items); - }) - .serialEach([=] (const KDAV2::DavItem &item) { - QByteArray rid = getRid(item); - if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){ - SinkTrace() << "Updating " << rid; - auto davItemFetchJob = new KDAV2::DavItemFetchJob(item); - auto itemjob = runJob(davItemFetchJob) - .then([=] { - const auto item = davItemFetchJob->item(); - const auto rid = getRid(item); - Sink::ApplicationDomain::Contact contact; - contact.setVcard(item.data()); - contact.setAddressbook(addressbookLocalId); - createOrModify(ENTITY_TYPE_CONTACT, rid, contact, mergeCriteria); - return item; - }) - .then([=] (const KDAV2::DavItem &item) { - const auto rid = getRid(item); - syncStore().writeValue(rid + "_etag", item.etag().toLatin1()); - ridList->append(rid); - *progress += 1; - reportProgress(*progress, *total, QByteArrayList{} << addressbookLocalId); - //commit every 5 contacts (so contacts start appearing in the UI) - if ((*progress % 5) == 0) { - commit(); - } - return rid; - }); - return itemjob; - } else { - ridList->append(rid); - return KAsync::value(rid); - } - }) - .then([=] () { - syncStore().writeValue(collId + "_ctag", ctag); - }); - return colljob; - } else { - SinkTraceCtx(mLogCtx) << "Collection unchanged: " << ctag; - // for(const auto &item : addressbook) { - // ridList->append(rid); - // } - return KAsync::null(); - } - }) - .then([this, ridList] () { - scanForRemovals(ENTITY_TYPE_CONTACT, - [&ridList](const QByteArray &remoteId) -> bool { - return ridList->contains(remoteId); - }); - }); - return job; - } else { - return KAsync::null(); - } - } - -KAsync::Job replay(const ApplicationDomain::Contact &contact, Sink::Operation operation, const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE - { - return KAsync::null(); - } - - KAsync::Job replay(const ApplicationDomain::Addressbook &addressbook, Sink::Operation operation, const QByteArray &oldRemoteId, const QList &changedProperties) Q_DECL_OVERRIDE - { - return KAsync::null(); - } - - KDAV2::DavUrl resourceUrl() const - { - if (secret().isEmpty()) { - return {}; - } - auto resourceUrl = mServer; - resourceUrl.setUserName(mUsername); - resourceUrl.setPassword(secret()); - return KDAV2::DavUrl{resourceUrl, KDAV2::CardDav}; - } - -public: - QUrl mServer; - QString mUsername; -}; - - -DavResource::DavResource(const Sink::ResourceContext &resourceContext) - : Sink::GenericResource(resourceContext) -{ - auto config = ResourceConfig::getConfiguration(resourceContext.instanceId()); - auto server = QUrl::fromUserInput(config.value("server").toString()); - auto username = config.value("username").toString(); - - auto synchronizer = QSharedPointer::create(resourceContext); - synchronizer->mServer = server; - synchronizer->mUsername = username; - setupSynchronizer(synchronizer); - - setupPreprocessors(ENTITY_TYPE_CONTACT, QVector() << new ContactPropertyExtractor); -} - - -DavResourceFactory::DavResourceFactory(QObject *parent) - : Sink::ResourceFactory(parent, - {Sink::ApplicationDomain::ResourceCapabilities::Contact::contact, - Sink::ApplicationDomain::ResourceCapabilities::Contact::addressbook, - Sink::ApplicationDomain::ResourceCapabilities::Contact::storage - } - ) -{ -} - -Sink::Resource *DavResourceFactory::createResource(const ResourceContext &context) -{ - return new DavResource(context); -} - -void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory) -{ - factory.registerFacade>(name); - factory.registerFacade>(name); -} - -void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry ®istry) -{ - registry.registerFactory>(name); - registry.registerFactory>(name); -} - -void DavResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier) -{ - DavResource::removeFromDisk(instanceIdentifier); -} -- cgit v1.2.3