diff --git a/src/common/davcollectionfetchjob.cpp b/src/common/davcollectionfetchjob.cpp index 2f0b505..dc2c44d 100644 --- a/src/common/davcollectionfetchjob.cpp +++ b/src/common/davcollectionfetchjob.cpp @@ -1,109 +1,107 @@ /* Copyright (c) 2010 Tobias Koenig Copyright (c) 2018 Rémi Nicole 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 "davcollectionfetchjob.h" #include "daverror.h" #include "davjob.h" #include "davmanager.h" #include "davprotocolbase.h" #include "utils.h" using namespace KDAV2; DavCollectionFetchJob::DavCollectionFetchJob(const DavCollection &collection, QObject *parent) : DavJobBase(parent), mCollection(collection) { } void DavCollectionFetchJob::start() { const DavProtocolBase *protocol = DavManager::self()->davProtocol(mCollection.url().protocol()); Q_ASSERT(protocol); XMLQueryBuilder::Ptr builder(protocol->collectionsQuery()); auto job = DavManager::self()->createPropFindJob( mCollection.url().url(), builder->buildQuery(), /* depth = */ 0); connect(job, &DavJob::result, this, &DavCollectionFetchJob::davJobFinished); } DavCollection DavCollectionFetchJob::collection() const { return mCollection; } void DavCollectionFetchJob::davJobFinished(KJob *job) { auto *storedJob = qobject_cast(job); const int responseCode = storedJob->responseCode(); if (storedJob->error()) { setLatestResponseCode(responseCode); setError(ERR_PROBLEM_WITH_REQUEST); setJobErrorText(storedJob->errorText()); setJobError(storedJob->error()); setErrorTextFromDavError(); } else { /* * Extract data from a document like the following: * * * * /path/to/collection/ * * * collection name * * * * * ctag * * HTTP/1.1 200 OK * * * */ const QDomDocument document = storedJob->response(); - //qWarning() << document.toString(); - const QDomElement documentElement = document.documentElement(); QDomElement responseElement = Utils::firstChildElementNS( documentElement, QStringLiteral("DAV:"), QStringLiteral("response")); // Validate that we got a valid PROPFIND response if (documentElement.localName().compare(QStringLiteral("multistatus"), Qt::CaseInsensitive) != 0) { setError(ERR_COLLECTIONFETCH); setErrorTextFromDavError(); emitResult(); return; } if (!Utils::extractCollection(responseElement, mCollection.url(), mCollection)) { setError(ERR_COLLECTIONFETCH); setErrorTextFromDavError(); emitResult(); return; } } emitResult(); } diff --git a/src/common/davcollectionsfetchjob.cpp b/src/common/davcollectionsfetchjob.cpp index e483021..1bb3aa0 100644 --- a/src/common/davcollectionsfetchjob.cpp +++ b/src/common/davcollectionsfetchjob.cpp @@ -1,266 +1,322 @@ /* Copyright (c) 2010 Tobias Koenig 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 "davcollectionsfetchjob.h" #include "davmanager.h" #include "davprincipalhomesetsfetchjob.h" +#include "davcollectionfetchjob.h" #include "davprotocolbase.h" #include "utils.h" #include "daverror.h" #include "davjob.h" #include "libkdav2_debug.h" #include #include using namespace KDAV2; DavCollectionsFetchJob::DavCollectionsFetchJob(const DavUrl &url, QObject *parent) : DavJobBase(parent), mUrl(url), mSubJobCount(0) { } void DavCollectionsFetchJob::start() { if (DavManager::self()->davProtocol(mUrl.protocol())->supportsPrincipals()) { DavPrincipalHomeSetsFetchJob *job = new DavPrincipalHomeSetsFetchJob(mUrl); connect(job, &DavPrincipalHomeSetsFetchJob::result, this, &DavCollectionsFetchJob::principalFetchFinished); job->start(); } else { doCollectionsFetch(mUrl.url()); } } DavCollection::List DavCollectionsFetchJob::collections() const { return mCollections; } DavUrl DavCollectionsFetchJob::davUrl() const { return mUrl; } void DavCollectionsFetchJob::doCollectionsFetch(const QUrl &url) { ++mSubJobCount; const QDomDocument collectionQuery = DavManager::self()->davProtocol(mUrl.protocol())->collectionsQuery()->buildQuery(); auto job = DavManager::self()->createPropFindJob(url, collectionQuery); connect(job, &DavJob::result, this, &DavCollectionsFetchJob::collectionsFetchFinished); } void DavCollectionsFetchJob::principalFetchFinished(KJob *job) { const DavPrincipalHomeSetsFetchJob *davJob = qobject_cast(job); if (davJob->error()) { if (davJob->latestResponseCode()) { // If we have a HTTP response code then this may mean that // the URL was not a principal URL. Retry as if it were a calendar URL. qCDebug(KDAV2_LOG) << "Principal fetch failed, retrying: " << job->errorText(); doCollectionsFetch(mUrl.url()); } else { // Just give up here. setDavError(davJob->davError()); setErrorTextFromDavError(); emitResult(); } return; } const QStringList homeSets = davJob->homeSets(); qCDebug(KDAV2_LOG) << "Found " << homeSets.size() << " homesets"; qCDebug(KDAV2_LOG) << homeSets; if (homeSets.isEmpty()) { // Same as above, retry as if it were a calendar URL. doCollectionsFetch(mUrl.url()); return; } foreach (const QString &homeSet, homeSets) { QUrl url = mUrl.url(); if (homeSet.startsWith(QLatin1Char('/'))) { // homeSet is only a path, use request url to complete url.setPath(homeSet, QUrl::TolerantMode); } else { // homeSet is a complete url QUrl tmpUrl(homeSet); tmpUrl.setUserName(url.userName()); tmpUrl.setPassword(url.password()); url = tmpUrl; } doCollectionsFetch(url); } } void DavCollectionsFetchJob::collectionsFetchFinished(KJob *job) { auto davJob = qobject_cast(job); const int responseCode = davJob->responseCode(); if (davJob->error()) { if (davJob->url() != mUrl.url()) { // Retry as if the initial URL was a calendar URL. // We can end up here when retrieving a homeset on // which a PROPFIND resulted in an error doCollectionsFetch(mUrl.url()); --mSubJobCount; return; } setLatestResponseCode(responseCode); setError(ERR_PROBLEM_WITH_REQUEST); setJobErrorText(davJob->errorText()); setJobError(davJob->error()); setErrorTextFromDavError(); } else { // For use in the collectionDiscovered() signal QUrl _jobUrl = mUrl.url(); _jobUrl.setUserInfo(QString()); const QString jobUrl = _jobUrl.toDisplayString(); + qCDebug(KDAV2_LOG).noquote() << davJob->response().toString(); + // Validate that we got a valid PROPFIND response QDomElement rootElement = davJob->response().documentElement(); if (rootElement.localName().compare(QStringLiteral("multistatus"), Qt::CaseInsensitive) != 0) { setError(ERR_COLLECTIONFETCH); setErrorTextFromDavError(); subjobFinished(); return; } QByteArray resp(davJob->response().toByteArray()); QBuffer buffer(&resp); buffer.open(QIODevice::ReadOnly); QXmlQuery xquery; if (!xquery.setFocus(&buffer)) { setError(ERR_COLLECTIONFETCH_XQUERY_SETFOCUS); setErrorTextFromDavError(); subjobFinished(); return; } xquery.setQuery(DavManager::self()->davProtocol(mUrl.protocol())->collectionsXQuery()); if (!xquery.isValid()) { setError(ERR_COLLECTIONFETCH_XQUERY_INVALID); setErrorTextFromDavError(); subjobFinished(); return; } QString responsesStr; xquery.evaluateTo(&responsesStr); responsesStr.prepend(QStringLiteral("")); responsesStr.append(QStringLiteral("")); QDomDocument document; if (!document.setContent(responsesStr, true)) { setError(ERR_COLLECTIONFETCH); setErrorTextFromDavError(); subjobFinished(); return; } if (!error()) { /* * Extract information from a document like the following: * * * * /caldav.php/test1.user/home/ * * * * * * * * * * * * * * * Test1 User * * * * * * 12345 * * HTTP/1.1 200 OK * * * */ const QDomElement responsesElement = document.documentElement(); QDomElement responseElement = Utils::firstChildElementNS( responsesElement, QStringLiteral("DAV:"), QStringLiteral("response")); while (!responseElement.isNull()) { DavCollection collection; if (!Utils::extractCollection(responseElement, mUrl, collection)) { continue; } QUrl url = collection.url().url(); // don't add this resource if it has already been detected bool alreadySeen = false; foreach (const DavCollection &seen, mCollections) { if (seen.url().toDisplayString() == url.toDisplayString()) { alreadySeen = true; } } if (alreadySeen) { responseElement = Utils::nextSiblingElementNS( responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); continue; } + bool protocolSupportsCTags = DavManager::self()->davProtocol(mUrl.protocol())->supportsCTags(); + if (protocolSupportsCTags && collection.CTag() == "") { + qCDebug(KDAV2_LOG) << "No CTag found for" + << collection.url().url().toDisplayString() + << "from the home set, trying from the direct URL"; + refreshIndividualCollection(collection); + + responseElement = Utils::nextSiblingElementNS( + responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + mCollections << collection; Q_EMIT collectionDiscovered(mUrl.protocol(), url.toDisplayString(), jobUrl); responseElement = Utils::nextSiblingElementNS( responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); } } } subjobFinished(); } +// This is a workaroud for Google who doesn't support providing the CTag +// directly from the home set. We refresh the collections individually from +// their own URLs, but only if we haven't found a CTag with the home set +// request. +void DavCollectionsFetchJob::refreshIndividualCollection(const DavCollection &collection) +{ + ++mSubJobCount; + auto individualFetchJob = new DavCollectionFetchJob(collection, this); + connect(individualFetchJob, &DavCollectionFetchJob::result, this, &DavCollectionsFetchJob::individualCollectionRefreshed); + individualFetchJob->start(); +} + +void DavCollectionsFetchJob::individualCollectionRefreshed(KJob *job) +{ + const auto *davJob = qobject_cast(job); + + if (davJob->error()) { + if (davJob->latestResponseCode()) { + // If we have a HTTP response code then this may mean that + // the URL was not a principal URL. Retry as if it were a calendar URL. + qCDebug(KDAV2_LOG) << "Individual fetch failed, retrying: " << job->errorText(); + doCollectionsFetch(mUrl.url()); + } else { + setDavError(davJob->davError()); + setErrorTextFromDavError(); + emitResult(); + } + return; + } + + qCDebug(KDAV2_LOG) << "Collection" + << davJob->collection().url().url().toDisplayString() << "refreshed"; + + if (davJob->collection().CTag() == "") { + qWarning() << "Collection with an empty CTag"; + } + + mCollections << davJob->collection(); + subjobFinished(); +} + void DavCollectionsFetchJob::subjobFinished() { if (--mSubJobCount == 0) { emitResult(); } } diff --git a/src/common/davcollectionsfetchjob.h b/src/common/davcollectionsfetchjob.h index d7cc8e0..4dca346 100644 --- a/src/common/davcollectionsfetchjob.h +++ b/src/common/davcollectionsfetchjob.h @@ -1,91 +1,93 @@ /* Copyright (c) 2010 Tobias Koenig 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. */ #ifndef KDAV2_DAVCOLLECTIONSFETCHJOB_H #define KDAV2_DAVCOLLECTIONSFETCHJOB_H #include "kpimkdav2_export.h" #include "davcollection.h" #include "davjobbase.h" #include "davurl.h" #include namespace KDAV2 { /** * @short A job that fetches all DAV collection. * * This job is used to fetch all DAV collection that are available * under a certain DAV url. */ class KPIMKDAV2_EXPORT DavCollectionsFetchJob : public DavJobBase { Q_OBJECT public: /** * Creates a new dav collections fetch job. * * @param url The DAV url of the DAV collection whose sub collections shall be fetched. * @param parent The parent object. */ explicit DavCollectionsFetchJob(const DavUrl &url, QObject *parent = nullptr); /** * Starts the job. */ void start() Q_DECL_OVERRIDE; /** * Returns the list of fetched DAV collections. */ DavCollection::List collections() const; /** * Return the DavUrl used by this job */ DavUrl davUrl() const; Q_SIGNALS: /** * This signal is emitted every time a new collection has been discovered. * * @param collectionUrl The URL of the discovered collection * @param configuredUrl The URL given to the job */ void collectionDiscovered(int protocol, const QString &collectionUrl, const QString &configuredUrl); private Q_SLOTS: void principalFetchFinished(KJob *); void collectionsFetchFinished(KJob *); + void individualCollectionRefreshed(KJob *); private: void doCollectionsFetch(const QUrl &url); + void refreshIndividualCollection(const DavCollection &); void subjobFinished(); DavUrl mUrl; DavCollection::List mCollections; uint mSubJobCount; }; } #endif diff --git a/src/common/davprotocolbase.h b/src/common/davprotocolbase.h index 10305ba..dee0242 100644 --- a/src/common/davprotocolbase.h +++ b/src/common/davprotocolbase.h @@ -1,139 +1,146 @@ /* Copyright (c) 2009 Grégory Oestreicher 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. */ #ifndef KDAV2_DAVPROTOCOLBASE_H #define KDAV2_DAVPROTOCOLBASE_H #include "kpimkdav2_export.h" #include "davcollection.h" #include #include #include #include #include namespace KDAV2 { /** * @short Base class for XML query builders */ class KPIMKDAV2_EXPORT XMLQueryBuilder { public: typedef QSharedPointer Ptr; virtual ~XMLQueryBuilder(); virtual QDomDocument buildQuery() const = 0; virtual QString mimeType() const = 0; void setParameter(const QString &key, const QVariant &value); QVariant parameter(const QString &key) const; private: QMap mParameters; }; /** * @short Base class for various DAV groupware dialects. * * This class provides an interface to query the DAV dialect * specific features and abstract them. * * The functionality is implemented in: * @li CaldavProtocol * @li CarddavProtocol * @li GroupdavProtocol */ class KPIMKDAV2_EXPORT DavProtocolBase { public: /** * Destroys the dav protocol base. */ virtual ~DavProtocolBase(); /** * Returns whether the dav protocol dialect supports principal * queries. If true, it must return the home set it provides * access to with principalHomeSet() and the home set namespace * with principalHomeSetNS(); */ virtual bool supportsPrincipals() const = 0; + /** + * Return whether the dav protocol dialect supports CTags. + * + * If true, it must fetch them in the collectionsQuery(). + */ + virtual bool supportsCTags() const = 0; + /** * Returns whether the dav protocol dialect supports the REPORT * command to query all resources of a collection. * If not, PROPFIND command will be used instead. */ virtual bool useReport() const = 0; /** * Returns whether the dav protocol dialect supports the MULTIGET command. * * If MULTIGET is supported, the content of all dav resources * can be fetched in ResourceBase::retrieveItems() already and * there is no need to call ResourceBase::retrieveItem() for every single * dav resource. * * Protocols that have MULTIGET capabilities must inherit from * DavMultigetProtocol instead of this class. */ virtual bool useMultiget() const = 0; /** * Returns the home set that this protocol supports. */ virtual QString principalHomeSet() const; /** * Returns the namespace of the home set. */ virtual QString principalHomeSetNS() const; /** * Returns the XML document that represents the DAV query to * list all available DAV collections. */ virtual XMLQueryBuilder::Ptr collectionsQuery() const = 0; /** * Returns the XQuery string that filters out the relevant XML elements * from the result returned by the query that is provided by collectionQuery(). */ virtual QString collectionsXQuery() const = 0; /** * Returns a list of XML documents that represent DAV queries to * list all available DAV resources inside a specific DAV collection. */ virtual QVector itemsQueries() const = 0; /** * Returns the possible content types for the collection that * is described by the passed @p propstat element of a PROPFIND result. */ virtual DavCollection::ContentTypes collectionContentTypes(const QDomElement &propstat) const = 0; }; } #endif diff --git a/src/protocols/caldavprotocol.cpp b/src/protocols/caldavprotocol.cpp index d8540d8..d5115a3 100644 --- a/src/protocols/caldavprotocol.cpp +++ b/src/protocols/caldavprotocol.cpp @@ -1,423 +1,428 @@ /* Copyright (c) 2009 Grégory Oestreicher 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 "caldavprotocol.h" #include "common/utils.h" #include #include #include #include #include using namespace KDAV2; class CaldavCollectionQueryBuilder : public XMLQueryBuilder { public: QDomDocument buildQuery() const Q_DECL_OVERRIDE { QDomDocument document; QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); document.appendChild(propfindElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); propfindElement.appendChild(propElement); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); propElement.appendChild(document.createElementNS(QStringLiteral("http://apple.com/ns/ical/"), QStringLiteral("calendar-color"))); propElement.appendChild(document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("supported-calendar-component-set"))); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-privilege-set"))); propElement.appendChild(document.createElementNS(QStringLiteral("http://calendarserver.org/ns/"), QStringLiteral("getctag"))); return document; } QString mimeType() const Q_DECL_OVERRIDE { return QString(); } }; class CaldavListEventQueryBuilder : public XMLQueryBuilder { public: QDomDocument buildQuery() const Q_DECL_OVERRIDE { QString startTime = parameter(QStringLiteral("start")).toString(); QString endTime = parameter(QStringLiteral("end")).toString(); QDomDocument document; QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query")); document.appendChild(queryElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); queryElement.appendChild(propElement); QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")); propElement.appendChild(getetagElement); QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")); propElement.appendChild(getRTypeElement); QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter")); queryElement.appendChild(filterElement); QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name")); nameAttribute.setValue(QStringLiteral("VCALENDAR")); compfilterElement.setAttributeNode(nameAttribute); filterElement.appendChild(compfilterElement); QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); nameAttribute = document.createAttribute(QStringLiteral("name")); nameAttribute.setValue(QStringLiteral("VEVENT")); subcompfilterElement.setAttributeNode(nameAttribute); if (!startTime.isEmpty() || !endTime.isEmpty()) { QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range")); if (!startTime.isEmpty()) { QDomAttr startAttribute = document.createAttribute(QStringLiteral("start")); startAttribute.setValue(startTime); timeRangeElement.setAttributeNode(startAttribute); } if (!endTime.isEmpty()) { QDomAttr endAttribute = document.createAttribute(QStringLiteral("end")); endAttribute.setValue(endTime); timeRangeElement.setAttributeNode(endAttribute); } subcompfilterElement.appendChild(timeRangeElement); } compfilterElement.appendChild(subcompfilterElement); return document; } QString mimeType() const Q_DECL_OVERRIDE { return QStringLiteral("application/x-vnd.akonadi.calendar.event"); } }; class CaldavListTodoQueryBuilder : public XMLQueryBuilder { public: QDomDocument buildQuery() const Q_DECL_OVERRIDE { QString startTime = parameter(QStringLiteral("start")).toString(); QString endTime = parameter(QStringLiteral("end")).toString(); QDomDocument document; QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query")); document.appendChild(queryElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); queryElement.appendChild(propElement); QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")); propElement.appendChild(getetagElement); QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")); propElement.appendChild(getRTypeElement); QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter")); queryElement.appendChild(filterElement); QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name")); nameAttribute.setValue(QStringLiteral("VCALENDAR")); compfilterElement.setAttributeNode(nameAttribute); filterElement.appendChild(compfilterElement); QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); nameAttribute = document.createAttribute(QStringLiteral("name")); nameAttribute.setValue(QStringLiteral("VTODO")); subcompfilterElement.setAttributeNode(nameAttribute); if (!startTime.isEmpty() || !endTime.isEmpty()) { QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range")); if (!startTime.isEmpty()) { QDomAttr startAttribute = document.createAttribute(QStringLiteral("start")); startAttribute.setValue(startTime); timeRangeElement.setAttributeNode(startAttribute); } if (!endTime.isEmpty()) { QDomAttr endAttribute = document.createAttribute(QStringLiteral("end")); endAttribute.setValue(endTime); timeRangeElement.setAttributeNode(endAttribute); } subcompfilterElement.appendChild(timeRangeElement); } compfilterElement.appendChild(subcompfilterElement); return document; } QString mimeType() const Q_DECL_OVERRIDE { return QStringLiteral("application/x-vnd.akonadi.calendar.todo"); } }; class CaldavListJournalQueryBuilder : public XMLQueryBuilder { public: QDomDocument buildQuery() const Q_DECL_OVERRIDE { QString startTime = parameter(QStringLiteral("start")).toString(); QString endTime = parameter(QStringLiteral("end")).toString(); QDomDocument document; QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query")); document.appendChild(queryElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); queryElement.appendChild(propElement); QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")); propElement.appendChild(getetagElement); QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")); propElement.appendChild(getRTypeElement); QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter")); queryElement.appendChild(filterElement); QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name")); nameAttribute.setValue(QStringLiteral("VCALENDAR")); compfilterElement.setAttributeNode(nameAttribute); filterElement.appendChild(compfilterElement); QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); nameAttribute = document.createAttribute(QStringLiteral("name")); nameAttribute.setValue(QStringLiteral("VJOURNAL")); subcompfilterElement.setAttributeNode(nameAttribute); if (!startTime.isEmpty() || !endTime.isEmpty()) { QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range")); if (!startTime.isEmpty()) { QDomAttr startAttribute = document.createAttribute(QStringLiteral("start")); startAttribute.setValue(startTime); timeRangeElement.setAttributeNode(startAttribute); } if (!endTime.isEmpty()) { QDomAttr endAttribute = document.createAttribute(QStringLiteral("end")); endAttribute.setValue(endTime); timeRangeElement.setAttributeNode(endAttribute); } subcompfilterElement.appendChild(timeRangeElement); } compfilterElement.appendChild(subcompfilterElement); return document; } QString mimeType() const Q_DECL_OVERRIDE { return QStringLiteral("application/x-vnd.akonadi.calendar.journal"); } }; class CaldavMultigetQueryBuilder : public XMLQueryBuilder { public: QDomDocument buildQuery() const Q_DECL_OVERRIDE { QDomDocument document; const QStringList urls = parameter(QStringLiteral("urls")).toStringList(); if (urls.isEmpty()) { return document; } QDomElement multigetElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-multiget")); document.appendChild(multigetElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); multigetElement.appendChild(propElement); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"))); propElement.appendChild(document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-data"))); for (const QString &url : urls) { QDomElement hrefElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("href")); const QUrl pathUrl = QUrl::fromUserInput(url); const QDomText textNode = document.createTextNode(pathUrl.toString()); hrefElement.appendChild(textNode); multigetElement.appendChild(hrefElement); } return document; } QString mimeType() const Q_DECL_OVERRIDE { return QString(); } }; CaldavProtocol::CaldavProtocol() { } bool CaldavProtocol::supportsPrincipals() const { return true; } +bool CaldavProtocol::supportsCTags() const +{ + return true; +} + bool CaldavProtocol::useReport() const { return true; } bool CaldavProtocol::useMultiget() const { return true; } QString CaldavProtocol::principalHomeSet() const { return QStringLiteral("calendar-home-set"); } QString CaldavProtocol::principalHomeSetNS() const { return QStringLiteral("urn:ietf:params:xml:ns:caldav"); } XMLQueryBuilder::Ptr CaldavProtocol::collectionsQuery() const { return XMLQueryBuilder::Ptr(new CaldavCollectionQueryBuilder()); } QString CaldavProtocol::collectionsXQuery() const { //const QString query( "//*[local-name()='calendar' and namespace-uri()='urn:ietf:params:xml:ns:caldav']/ancestor::*[local-name()='prop' and namespace-uri()='DAV:']/*[local-name()='supported-calendar-component-set' and namespace-uri()='urn:ietf:params:xml:ns:caldav']/*[local-name()='comp' and namespace-uri()='urn:ietf:params:xml:ns:caldav' and (@name='VTODO' or @name='VEVENT')]/ancestor::*[local-name()='response' and namespace-uri()='DAV:']" ); const QString query(QStringLiteral("//*[local-name()='calendar' and namespace-uri()='urn:ietf:params:xml:ns:caldav']/ancestor::*[local-name()='prop' and namespace-uri()='DAV:']/ancestor::*[local-name()='response' and namespace-uri()='DAV:']")); return query; } QVector CaldavProtocol::itemsQueries() const { QVector ret; ret << XMLQueryBuilder::Ptr(new CaldavListEventQueryBuilder()); ret << XMLQueryBuilder::Ptr(new CaldavListTodoQueryBuilder()); ret << XMLQueryBuilder::Ptr(new CaldavListJournalQueryBuilder()); return ret; } XMLQueryBuilder::Ptr CaldavProtocol::itemsReportQuery(const QStringList &urls) const { XMLQueryBuilder::Ptr ret(new CaldavMultigetQueryBuilder()); ret->setParameter(QStringLiteral("urls"), urls); return ret; } QString CaldavProtocol::responseNamespace() const { return QStringLiteral("urn:ietf:params:xml:ns:caldav"); } QString CaldavProtocol::dataTagName() const { return QStringLiteral("calendar-data"); } DavCollection::ContentTypes CaldavProtocol::collectionContentTypes(const QDomElement &propstatElement) const { /* * Extract the content type information from a propstat like the following * * * * * * * * * * * * * * * Test1 User * * HTTP/1.1 200 OK * */ const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); const QDomElement supportedcomponentElement = Utils::firstChildElementNS(propElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("supported-calendar-component-set")); DavCollection::ContentTypes contentTypes; QDomElement compElement = Utils::firstChildElementNS(supportedcomponentElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); /* * Assign the content-type if the server didn't return anything. * According to RFC4791, §5.2.3: * In the absence of this property, the server MUST accept all * component types, and the client can assume that all component * types are accepted. */ if (compElement.isNull()) { contentTypes |= DavCollection::Calendar; contentTypes |= DavCollection::Events; contentTypes |= DavCollection::Todos; contentTypes |= DavCollection::FreeBusy; contentTypes |= DavCollection::Journal; } while (!compElement.isNull()) { const QString type = compElement.attribute(QStringLiteral("name")).toLower(); if (type == QLatin1String("vcalendar")) { contentTypes |= DavCollection::Calendar; } else if (type == QLatin1String("vevent")) { contentTypes |= DavCollection::Events; } else if (type == QLatin1String("vtodo")) { contentTypes |= DavCollection::Todos; } else if (type == QLatin1String("vfreebusy")) { contentTypes |= DavCollection::FreeBusy; } else if (type == QLatin1String("vjournal")) { contentTypes |= DavCollection::Journal; } compElement = Utils::nextSiblingElementNS(compElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); } return contentTypes; } diff --git a/src/protocols/caldavprotocol.h b/src/protocols/caldavprotocol.h index d727676..4e09516 100644 --- a/src/protocols/caldavprotocol.h +++ b/src/protocols/caldavprotocol.h @@ -1,43 +1,44 @@ /* Copyright (c) 2009 Grégory Oestreicher 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. */ #ifndef KDAV2_CALDAVPROTOCOL_H #define KDAV2_CALDAVPROTOCOL_H #include "common/davmultigetprotocol.h" class CaldavProtocol : public KDAV2::DavMultigetProtocol { public: CaldavProtocol(); bool supportsPrincipals() const Q_DECL_OVERRIDE; + bool supportsCTags() const Q_DECL_OVERRIDE; bool useReport() const Q_DECL_OVERRIDE; bool useMultiget() const Q_DECL_OVERRIDE; QString principalHomeSet() const Q_DECL_OVERRIDE; QString principalHomeSetNS() const Q_DECL_OVERRIDE; KDAV2::XMLQueryBuilder::Ptr collectionsQuery() const Q_DECL_OVERRIDE; QString collectionsXQuery() const Q_DECL_OVERRIDE; QVector itemsQueries() const Q_DECL_OVERRIDE; KDAV2::XMLQueryBuilder::Ptr itemsReportQuery(const QStringList &urls) const Q_DECL_OVERRIDE; QString responseNamespace() const Q_DECL_OVERRIDE; QString dataTagName() const Q_DECL_OVERRIDE; KDAV2::DavCollection::ContentTypes collectionContentTypes(const QDomElement &propstat) const Q_DECL_OVERRIDE; }; #endif diff --git a/src/protocols/carddavprotocol.cpp b/src/protocols/carddavprotocol.cpp index 45e9572..53a32a9 100644 --- a/src/protocols/carddavprotocol.cpp +++ b/src/protocols/carddavprotocol.cpp @@ -1,188 +1,193 @@ /* Copyright (c) 2010 Tobias Koenig 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 "carddavprotocol.h" #include #include #include #include using namespace KDAV2; class CarddavCollectionQueryBuilder : public XMLQueryBuilder { public: QDomDocument buildQuery() const Q_DECL_OVERRIDE { QDomDocument document; QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); document.appendChild(propfindElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); propfindElement.appendChild(propElement); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); propElement.appendChild(document.createElementNS(QStringLiteral("http://calendarserver.org/ns/"), QStringLiteral("getctag"))); return document; } QString mimeType() const Q_DECL_OVERRIDE { return QString(); } }; class CarddavListItemsQueryBuilder : public XMLQueryBuilder { public: QDomDocument buildQuery() const Q_DECL_OVERRIDE { QDomDocument document; QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); document.appendChild(propfindElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); propfindElement.appendChild(propElement); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"))); return document; } QString mimeType() const Q_DECL_OVERRIDE { return QStringLiteral("text/directory"); } }; class CarddavMultigetQueryBuilder : public XMLQueryBuilder { public: QDomDocument buildQuery() const Q_DECL_OVERRIDE { QDomDocument document; const QStringList urls = parameter(QStringLiteral("urls")).toStringList(); if (urls.isEmpty()) { return document; } QDomElement multigetElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:carddav"), QStringLiteral("addressbook-multiget")); document.appendChild(multigetElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); multigetElement.appendChild(propElement); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"))); QDomElement addressDataElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:carddav"), QStringLiteral("address-data")); addressDataElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("allprop"))); propElement.appendChild(addressDataElement); for (const QString &url : urls) { QDomElement hrefElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("href")); const QUrl pathUrl = QUrl::fromUserInput(url); const QDomText textNode = document.createTextNode(pathUrl.toString()); hrefElement.appendChild(textNode); multigetElement.appendChild(hrefElement); } return document; } QString mimeType() const Q_DECL_OVERRIDE { return QString(); } }; CarddavProtocol::CarddavProtocol() { } bool CarddavProtocol::supportsPrincipals() const { return true; } +bool CarddavProtocol::supportsCTags() const +{ + return true; +} + bool CarddavProtocol::useReport() const { return false; } bool CarddavProtocol::useMultiget() const { return true; } QString CarddavProtocol::principalHomeSet() const { return QStringLiteral("addressbook-home-set"); } QString CarddavProtocol::principalHomeSetNS() const { return QStringLiteral("urn:ietf:params:xml:ns:carddav"); } XMLQueryBuilder::Ptr CarddavProtocol::collectionsQuery() const { return XMLQueryBuilder::Ptr(new CarddavCollectionQueryBuilder()); } QString CarddavProtocol::collectionsXQuery() const { const QString query(QStringLiteral("//*[local-name()='addressbook' and namespace-uri()='urn:ietf:params:xml:ns:carddav']/ancestor::*[local-name()='response' and namespace-uri()='DAV:']")); return query; } QVector CarddavProtocol::itemsQueries() const { QVector ret; ret << XMLQueryBuilder::Ptr(new CarddavListItemsQueryBuilder()); return ret; } XMLQueryBuilder::Ptr CarddavProtocol::itemsReportQuery(const QStringList &urls) const { XMLQueryBuilder::Ptr ret(new CarddavMultigetQueryBuilder()); ret->setParameter(QStringLiteral("urls"), urls); return ret; } QString CarddavProtocol::responseNamespace() const { return QStringLiteral("urn:ietf:params:xml:ns:carddav"); } QString CarddavProtocol::dataTagName() const { return QStringLiteral("address-data"); } DavCollection::ContentTypes CarddavProtocol::collectionContentTypes(const QDomElement &) const { return DavCollection::Contacts; } diff --git a/src/protocols/carddavprotocol.h b/src/protocols/carddavprotocol.h index ed59609..91b8feb 100644 --- a/src/protocols/carddavprotocol.h +++ b/src/protocols/carddavprotocol.h @@ -1,43 +1,44 @@ /* Copyright (c) 2010 Tobias Koenig 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. */ #ifndef KDAV2_CARDDAVPROTOCOL_H #define KDAV2_CARDDAVPROTOCOL_H #include "common/davmultigetprotocol.h" class CarddavProtocol : public KDAV2::DavMultigetProtocol { public: CarddavProtocol(); bool supportsPrincipals() const Q_DECL_OVERRIDE; + bool supportsCTags() const Q_DECL_OVERRIDE; bool useReport() const Q_DECL_OVERRIDE; bool useMultiget() const Q_DECL_OVERRIDE; QString principalHomeSet() const Q_DECL_OVERRIDE; QString principalHomeSetNS() const Q_DECL_OVERRIDE; KDAV2::XMLQueryBuilder::Ptr collectionsQuery() const Q_DECL_OVERRIDE; QString collectionsXQuery() const Q_DECL_OVERRIDE; QVector itemsQueries() const Q_DECL_OVERRIDE; KDAV2::XMLQueryBuilder::Ptr itemsReportQuery(const QStringList &urls) const Q_DECL_OVERRIDE; QString responseNamespace() const Q_DECL_OVERRIDE; QString dataTagName() const Q_DECL_OVERRIDE; KDAV2::DavCollection::ContentTypes collectionContentTypes(const QDomElement &propstat) const Q_DECL_OVERRIDE; }; #endif diff --git a/src/protocols/groupdavprotocol.cpp b/src/protocols/groupdavprotocol.cpp index 493b2d4..d1c4cb3 100644 --- a/src/protocols/groupdavprotocol.cpp +++ b/src/protocols/groupdavprotocol.cpp @@ -1,153 +1,158 @@ /* Copyright (c) 2009 Grégory Oestreicher 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 "groupdavprotocol.h" #include "common/utils.h" #include #include using namespace KDAV2; class GroupdavCollectionQueryBuilder : public XMLQueryBuilder { public: QDomDocument buildQuery() const Q_DECL_OVERRIDE { QDomDocument document; QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); document.appendChild(propfindElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); propfindElement.appendChild(propElement); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); return document; } QString mimeType() const Q_DECL_OVERRIDE { return QString(); } }; class GroupdavItemQueryBuilder : public XMLQueryBuilder { public: QDomDocument buildQuery() const Q_DECL_OVERRIDE { QDomDocument document; QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); document.appendChild(propfindElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); propfindElement.appendChild(propElement); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"))); return document; } QString mimeType() const Q_DECL_OVERRIDE { return QString(); } }; GroupdavProtocol::GroupdavProtocol() { } bool GroupdavProtocol::supportsPrincipals() const { return false; } +bool GroupdavProtocol::supportsCTags() const +{ + return false; +} + bool GroupdavProtocol::useReport() const { return false; } bool GroupdavProtocol::useMultiget() const { return false; } XMLQueryBuilder::Ptr GroupdavProtocol::collectionsQuery() const { return XMLQueryBuilder::Ptr(new GroupdavCollectionQueryBuilder()); } QString GroupdavProtocol::collectionsXQuery() const { const QString query(QStringLiteral("//*[(local-name()='vevent-collection' or local-name()='vtodo-collection' or local-name()='vcard-collection') and namespace-uri()='http://groupdav.org/']/ancestor::*[local-name()='response' and namespace-uri()='DAV:']")); return query; } QVector GroupdavProtocol::itemsQueries() const { QVector ret; ret << XMLQueryBuilder::Ptr(new GroupdavItemQueryBuilder()); return ret; } DavCollection::ContentTypes GroupdavProtocol::collectionContentTypes(const QDomElement &propstatElement) const { /* * Extract the content type information from a propstat like the following * * * HTTP/1.1 200 OK * * Tasks * * * * * Sat, 30 Jan 2010 17:52:41 -0100 * * */ const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); const QDomElement resourcetypeElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("resourcetype")); DavCollection::ContentTypes contentTypes; if (!Utils::firstChildElementNS(resourcetypeElement, QStringLiteral("http://groupdav.org/"), QStringLiteral("vevent-collection")).isNull()) { contentTypes |= DavCollection::Events; } if (!Utils::firstChildElementNS(resourcetypeElement, QStringLiteral("http://groupdav.org/"), QStringLiteral("vtodo-collection")).isNull()) { contentTypes |= DavCollection::Todos; } if (!Utils::firstChildElementNS(resourcetypeElement, QStringLiteral("http://groupdav.org/"), QStringLiteral("vcard-collection")).isNull()) { contentTypes |= DavCollection::Contacts; } return contentTypes; } diff --git a/src/protocols/groupdavprotocol.h b/src/protocols/groupdavprotocol.h index c6ddbdd..a3401cf 100644 --- a/src/protocols/groupdavprotocol.h +++ b/src/protocols/groupdavprotocol.h @@ -1,38 +1,39 @@ /* Copyright (c) 2009 Grégory Oestreicher 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. */ #ifndef GROUPDAVPROTOCOL_H #define GROUPDAVPROTOCOL_H #include "common/davprotocolbase.h" class GroupdavProtocol : public KDAV2::DavProtocolBase { public: GroupdavProtocol(); bool supportsPrincipals() const Q_DECL_OVERRIDE; + bool supportsCTags() const Q_DECL_OVERRIDE; bool useReport() const Q_DECL_OVERRIDE; bool useMultiget() const Q_DECL_OVERRIDE; KDAV2::XMLQueryBuilder::Ptr collectionsQuery() const Q_DECL_OVERRIDE; QString collectionsXQuery() const Q_DECL_OVERRIDE; QVector itemsQueries() const Q_DECL_OVERRIDE; KDAV2::DavCollection::ContentTypes collectionContentTypes(const QDomElement &propstat) const Q_DECL_OVERRIDE; }; #endif