summaryrefslogtreecommitdiffstats
path: root/examples/davresource/davresource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/davresource/davresource.cpp')
-rw-r--r--examples/davresource/davresource.cpp315
1 files changed, 0 insertions, 315 deletions
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 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "davresource.h"
21
22#include "facade.h"
23#include "resourceconfig.h"
24#include "log.h"
25#include "definitions.h"
26#include "synchronizer.h"
27#include "inspector.h"
28
29#include "facadefactory.h"
30#include "adaptorfactoryregistry.h"
31
32#include "contactpreprocessor.h"
33
34#include <QNetworkReply>
35#include <KDAV2/DavCollection>
36#include <KDAV2/DavCollectionsFetchJob>
37#include <KDAV2/DavItem>
38#include <KDAV2/DavItemsListJob>
39#include <KDAV2/DavItemFetchJob>
40#include <KDAV2/EtagCache>
41#include <KDAV2/DavJobBase>
42
43//This is the resources entity type, and not the domain type
44#define ENTITY_TYPE_CONTACT "contact"
45#define ENTITY_TYPE_ADDRESSBOOK "addressbook"
46
47using namespace Sink;
48
49static int translateDavError(KJob *job)
50{
51 const int responseCode = static_cast<KDAV2::DavJobBase*>(job)->latestResponseCode();
52
53 switch (responseCode) {
54 case QNetworkReply::HostNotFoundError:
55 return ApplicationDomain::NoServerError;
56 //Since we don't login we will just not have the necessary permissions ot view the object
57 case QNetworkReply::OperationCanceledError:
58 return ApplicationDomain::LoginError;
59 }
60 return ApplicationDomain::UnknownError;
61}
62
63static KAsync::Job<void> runJob(KJob *job)
64{
65 return KAsync::start<void>([job](KAsync::Future<void> &future) {
66 QObject::connect(job, &KJob::result, [&future](KJob *job) {
67 SinkTrace() << "Job done: " << job->metaObject()->className();
68 if (job->error()) {
69 SinkWarning() << "Job failed: " << job->errorString() << job->metaObject()->className() << job->error() << static_cast<KDAV2::DavJobBase*>(job)->latestResponseCode();
70 future.setError(translateDavError(job), job->errorString());
71 } else {
72 future.setFinished();
73 }
74 });
75 SinkTrace() << "Starting job: " << job->metaObject()->className();
76 job->start();
77 });
78}
79
80class ContactSynchronizer : public Sink::Synchronizer {
81public:
82 ContactSynchronizer(const Sink::ResourceContext &resourceContext)
83 : Sink::Synchronizer(resourceContext)
84 {
85
86 }
87
88 QByteArray createAddressbook(const QString &addressbookName, const QString &addressbookPath, const QString &parentAddressbookRid)
89 {
90 SinkTrace() << "Creating addressbook: " << addressbookName << parentAddressbookRid;
91 const auto remoteId = addressbookPath.toUtf8();
92 const auto bufferType = ENTITY_TYPE_ADDRESSBOOK;
93 Sink::ApplicationDomain::Addressbook addressbook;
94 addressbook.setName(addressbookName);
95 QHash<QByteArray, Query::Comparator> mergeCriteria;
96
97 if (!parentAddressbookRid.isEmpty()) {
98 addressbook.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, parentAddressbookRid.toUtf8()));
99 }
100 createOrModify(bufferType, remoteId, addressbook, mergeCriteria);
101 return remoteId;
102 }
103
104 void synchronizeAddressbooks(const KDAV2::DavCollection::List &addressbookList)
105 {
106 const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK;
107 SinkTrace() << "Found addressbooks " << addressbookList.size();
108
109 QVector<QByteArray> ridList;
110 for(const auto &f : addressbookList) {
111 const auto &rid = getRid(f);
112 SinkLog() << "Found addressbook:" << rid << f.displayName();
113 ridList.append(rid);
114 createAddressbook(f.displayName(), rid, "");
115 }
116
117 scanForRemovals(bufferType,
118 [&ridList](const QByteArray &remoteId) -> bool {
119 return ridList.contains(remoteId);
120 }
121 );
122 }
123
124 QList<Synchronizer::SyncRequest> getSyncRequests(const Sink::QueryBase &query) Q_DECL_OVERRIDE
125 {
126 QList<Synchronizer::SyncRequest> list;
127 if (!query.type().isEmpty()) {
128 //We want to synchronize something specific
129 list << Synchronizer::SyncRequest{query};
130 } else {
131 //We want to synchronize everything
132 list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>())};
133 list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName<ApplicationDomain::Contact>())};
134 }
135 return list;
136 }
137
138 static QByteArray getRid(const KDAV2::DavItem &item)
139 {
140 return item.url().toDisplayString().toUtf8();
141 }
142
143 static QByteArray getRid(const KDAV2::DavCollection &item)
144 {
145 return item.url().toDisplayString().toUtf8();
146 }
147
148 KAsync::Job<void> synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE
149 {
150 if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) {
151 SinkLogCtx(mLogCtx) << "Synchronizing addressbooks:" << resourceUrl().url();
152 auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(resourceUrl());
153 auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] (const KAsync::Error &error) {
154 if (error) {
155 SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString();
156 } else {
157 synchronizeAddressbooks(collectionsFetchJob->collections());
158 }
159 });
160 return job;
161 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) {
162 SinkLogCtx(mLogCtx) << "Synchronizing contacts.";
163 auto ridList = QSharedPointer<QByteArrayList>::create();
164 auto total = QSharedPointer<int>::create(0);
165 auto progress = QSharedPointer<int>::create(0);
166 auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(resourceUrl());
167 auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] {
168 synchronizeAddressbooks(collectionsFetchJob ->collections());
169 return collectionsFetchJob->collections();
170 })
171 .serialEach([=](const KDAV2::DavCollection &collection) {
172 auto collId = getRid(collection);
173 const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId);
174 auto ctag = collection.CTag().toLatin1();
175 if (ctag != syncStore().readValue(collId + "_ctagXX")) {
176 SinkTraceCtx(mLogCtx) << "Syncing " << collId;
177 auto cache = std::shared_ptr<KDAV2::EtagCache>(new KDAV2::EtagCache());
178 auto davItemsListJob = new KDAV2::DavItemsListJob(collection.url(), cache);
179 QHash<QByteArray, Query::Comparator> mergeCriteria;
180 auto colljob = runJob(davItemsListJob).then([=] {
181 const auto items = davItemsListJob->items();
182 *total = items.size();
183 return KAsync::value(items);
184 })
185 .serialEach([=] (const KDAV2::DavItem &item) {
186 QByteArray rid = getRid(item);
187 if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){
188 SinkTrace() << "Updating " << rid;
189 auto davItemFetchJob = new KDAV2::DavItemFetchJob(item);
190 auto itemjob = runJob(davItemFetchJob)
191 .then([=] {
192 const auto item = davItemFetchJob->item();
193 const auto rid = getRid(item);
194 Sink::ApplicationDomain::Contact contact;
195 contact.setVcard(item.data());
196 contact.setAddressbook(addressbookLocalId);
197 createOrModify(ENTITY_TYPE_CONTACT, rid, contact, mergeCriteria);
198 return item;
199 })
200 .then([=] (const KDAV2::DavItem &item) {
201 const auto rid = getRid(item);
202 syncStore().writeValue(rid + "_etag", item.etag().toLatin1());
203 ridList->append(rid);
204 *progress += 1;
205 reportProgress(*progress, *total, QByteArrayList{} << addressbookLocalId);
206 //commit every 5 contacts (so contacts start appearing in the UI)
207 if ((*progress % 5) == 0) {
208 commit();
209 }
210 return rid;
211 });
212 return itemjob;
213 } else {
214 ridList->append(rid);
215 return KAsync::value(rid);
216 }
217 })
218 .then([=] () {
219 syncStore().writeValue(collId + "_ctag", ctag);
220 });
221 return colljob;
222 } else {
223 SinkTraceCtx(mLogCtx) << "Collection unchanged: " << ctag;
224 // for(const auto &item : addressbook) {
225 // ridList->append(rid);
226 // }
227 return KAsync::null<void>();
228 }
229 })
230 .then<void>([this, ridList] () {
231 scanForRemovals(ENTITY_TYPE_CONTACT,
232 [&ridList](const QByteArray &remoteId) -> bool {
233 return ridList->contains(remoteId);
234 });
235 });
236 return job;
237 } else {
238 return KAsync::null<void>();
239 }
240 }
241
242KAsync::Job<QByteArray> replay(const ApplicationDomain::Contact &contact, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
243 {
244 return KAsync::null<QByteArray>();
245 }
246
247 KAsync::Job<QByteArray> replay(const ApplicationDomain::Addressbook &addressbook, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
248 {
249 return KAsync::null<QByteArray>();
250 }
251
252 KDAV2::DavUrl resourceUrl() const
253 {
254 if (secret().isEmpty()) {
255 return {};
256 }
257 auto resourceUrl = mServer;
258 resourceUrl.setUserName(mUsername);
259 resourceUrl.setPassword(secret());
260 return KDAV2::DavUrl{resourceUrl, KDAV2::CardDav};
261 }
262
263public:
264 QUrl mServer;
265 QString mUsername;
266};
267
268
269DavResource::DavResource(const Sink::ResourceContext &resourceContext)
270 : Sink::GenericResource(resourceContext)
271{
272 auto config = ResourceConfig::getConfiguration(resourceContext.instanceId());
273 auto server = QUrl::fromUserInput(config.value("server").toString());
274 auto username = config.value("username").toString();
275
276 auto synchronizer = QSharedPointer<ContactSynchronizer>::create(resourceContext);
277 synchronizer->mServer = server;
278 synchronizer->mUsername = username;
279 setupSynchronizer(synchronizer);
280
281 setupPreprocessors(ENTITY_TYPE_CONTACT, QVector<Sink::Preprocessor*>() << new ContactPropertyExtractor);
282}
283
284
285DavResourceFactory::DavResourceFactory(QObject *parent)
286 : Sink::ResourceFactory(parent,
287 {Sink::ApplicationDomain::ResourceCapabilities::Contact::contact,
288 Sink::ApplicationDomain::ResourceCapabilities::Contact::addressbook,
289 Sink::ApplicationDomain::ResourceCapabilities::Contact::storage
290 }
291 )
292{
293}
294
295Sink::Resource *DavResourceFactory::createResource(const ResourceContext &context)
296{
297 return new DavResource(context);
298}
299
300void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory)
301{
302 factory.registerFacade<ApplicationDomain::Contact, DefaultFacade<ApplicationDomain::Contact>>(name);
303 factory.registerFacade<ApplicationDomain::Addressbook, DefaultFacade<ApplicationDomain::Addressbook>>(name);
304}
305
306void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry &registry)
307{
308 registry.registerFactory<ApplicationDomain::Contact, DefaultAdaptorFactory<ApplicationDomain::Contact>>(name);
309 registry.registerFactory<ApplicationDomain::Addressbook, DefaultAdaptorFactory<ApplicationDomain::Addressbook>>(name);
310}
311
312void DavResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier)
313{
314 DavResource::removeFromDisk(instanceIdentifier);
315}