diff --git a/src/common/davcollectioncreatejob.cpp b/src/common/davcollectioncreatejob.cpp index 6bfc3e1..405ad8e 100644 --- a/src/common/davcollectioncreatejob.cpp +++ b/src/common/davcollectioncreatejob.cpp @@ -1,247 +1,242 @@ /* Copyright (c) 2010 Grégory Oestreicher 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 "davcollectioncreatejob.h" #include "davcollectionfetchjob.h" #include "davcollectionmodifyjob.h" #include "daverror.h" #include "davjob.h" #include "davmanager.h" #include #include using namespace KDAV2; DavCollectionCreateJob::DavCollectionCreateJob(const DavCollection &collection, QObject *parent) : DavJobBase(parent), mCollection(collection), mRedirectCount(0) { } void DavCollectionCreateJob::start() { auto protocol = mCollection.url().protocol(); switch (protocol) { case CalDav: // This is a calendar, use the MKCALENDAR request createCalendar(); break; case CardDav: // This is an addressbook, use the extended MKCOL request createAddressbook(); break; default: { // This is a normal collection auto job = DavManager::self()->createMkColJob(collectionUrl()); connect(job, &DavJob::result, this, &DavCollectionCreateJob::collectionCreated); } } } DavCollection DavCollectionCreateJob::collection() const { return mCollection; } QUrl DavCollectionCreateJob::collectionUrl() const { return mCollection.url().url(); } void DavCollectionCreateJob::collectionCreated(KJob *job) { auto storedJob = static_cast(job); if (storedJob->error()) { - setLatestResponseCode(storedJob->responseCode()); - setError(ERR_COLLECTIONCREATE); - setJobErrorText(storedJob->errorText()); - setJobError(storedJob->error()); - setErrorTextFromDavError(); - + setErrorFromJob(storedJob, ERR_COLLECTIONCREATE); emitResult(); return; } DavCollectionModifyJob *modifyJob = new DavCollectionModifyJob(DavUrl(storedJob->url(), mCollection.url().protocol()), this); modifyJob->setProperty(QStringLiteral("displayname"), mCollection.displayName()); connect(modifyJob, &DavCollectionFetchJob::result, this, &DavCollectionCreateJob::collectionModified); modifyJob->start(); } void DavCollectionCreateJob::collectionModified(KJob *job) { if (job->error()) { setError(ERR_PROBLEM_WITH_REQUEST); setErrorTextFromDavError(); emitResult(); return; } DavCollectionFetchJob *fetchJob = new DavCollectionFetchJob(mCollection, this); connect(fetchJob, &DavCollectionFetchJob::result, this, &DavCollectionCreateJob::collectionRefreshed); fetchJob->start(); } void DavCollectionCreateJob::collectionRefreshed(KJob *job) { if (job->error()) { setError(ERR_PROBLEM_WITH_REQUEST); setErrorTextFromDavError(); emitResult(); return; } DavCollectionFetchJob *fetchJob = qobject_cast(job); mCollection = fetchJob->collection(); emitResult(); } void DavCollectionCreateJob::createCalendar() { // clang-format off /* Create a query like this: * * * * * Test Calendar * #24b0a3ff * * * * * * * * */ // clang-format on QDomDocument document; auto mkcalElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("mkcalendar")); document.appendChild(mkcalElement); auto setElement = mkcalElement.appendChild( document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("set"))); auto propElement = setElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"))); if (!mCollection.displayName().isEmpty()) { auto displayNameElement = propElement.appendChild( document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); displayNameElement.appendChild(document.createTextNode(mCollection.displayName())); } if (mCollection.color().isValid()) { auto colorElement = propElement.appendChild(document.createElementNS( QStringLiteral("http://apple.com/ns/ical/"), QStringLiteral("calendar-color"))); colorElement.appendChild(document.createTextNode(mCollection.color().name() + "FF")); } auto compSetElement = propElement.appendChild(document.createElementNS( QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("supported-calendar-component-set"))); auto supportedComp = mCollection.contentTypes(); if (supportedComp.testFlag(DavCollection::Events)) { auto compElement = document.createElementNS( QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); compElement.setAttribute(QStringLiteral("name"), QStringLiteral("VEVENT")); compSetElement.appendChild(compElement); } if (supportedComp.testFlag(DavCollection::Todos)) { auto compElement = document.createElementNS( QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); compElement.setAttribute(QStringLiteral("name"), QStringLiteral("VTODO")); compSetElement.appendChild(compElement); } if (supportedComp.testFlag(DavCollection::FreeBusy)) { auto compElement = document.createElementNS( QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); compElement.setAttribute(QStringLiteral("name"), QStringLiteral("VFREEBUSY")); compSetElement.appendChild(compElement); } if (supportedComp.testFlag(DavCollection::Journal)) { auto compElement = document.createElementNS( QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); compElement.setAttribute(QStringLiteral("name"), QStringLiteral("VJOURNAL")); compSetElement.appendChild(compElement); } auto job = DavManager::self()->createMkCalendarJob(collectionUrl(), document); // Skip the modification connect(job, &DavJob::result, this, &DavCollectionCreateJob::collectionModified); } void DavCollectionCreateJob::createAddressbook() { // clang-format off /* Create a query like this: * * * * * * * * * Lisa's Contacts * * * */ // clang-format on QDomDocument document; auto mkcolElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("mkcol")); document.appendChild(mkcolElement); auto setElement = mkcolElement.appendChild( document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("set"))); auto propElement = setElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"))); auto resourceTypeElement = propElement.appendChild( document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); resourceTypeElement.appendChild( document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("collection"))); resourceTypeElement.appendChild(document.createElementNS( QStringLiteral("urn:ietf:params:xml:ns:carddav"), QStringLiteral("addressbook"))); if (!mCollection.displayName().isEmpty()) { auto displayNameElement = propElement.appendChild( document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); displayNameElement.appendChild(document.createTextNode(mCollection.displayName())); } auto job = DavManager::self()->createMkColJob(collectionUrl(), document); // Skip the modification connect(job, &DavJob::result, this, &DavCollectionCreateJob::collectionModified); } diff --git a/src/common/davcollectiondeletejob.cpp b/src/common/davcollectiondeletejob.cpp index 5ea123e..85f1ed5 100644 --- a/src/common/davcollectiondeletejob.cpp +++ b/src/common/davcollectiondeletejob.cpp @@ -1,55 +1,49 @@ /* Copyright (c) 2010 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 "davcollectiondeletejob.h" #include "daverror.h" #include "davjob.h" #include "davmanager.h" using namespace KDAV2; DavCollectionDeleteJob::DavCollectionDeleteJob(const DavUrl &url, QObject *parent) : DavJobBase(parent), mUrl(url) { } void DavCollectionDeleteJob::start() { DavJob *job = DavManager::self()->createDeleteJob(mUrl.url()); connect(job, &DavJob::result, this, &DavCollectionDeleteJob::davJobFinished); } void DavCollectionDeleteJob::davJobFinished(KJob *job) { auto *deleteJob = qobject_cast(job); //TODO Ignore deleteJob->error() != KIO::ERR_NO_CONTENT if (deleteJob->error()) { - const int responseCode = deleteJob->responseCode(); - - setLatestResponseCode(responseCode); - setError(ERR_COLLECTIONDELETE); - setJobErrorText(deleteJob->errorText()); - setJobError(deleteJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(deleteJob, ERR_COLLECTIONDELETE); } emitResult(); } diff --git a/src/common/davcollectionfetchjob.cpp b/src/common/davcollectionfetchjob.cpp index f2002b3..d7d0682 100644 --- a/src/common/davcollectionfetchjob.cpp +++ b/src/common/davcollectionfetchjob.cpp @@ -1,109 +1,103 @@ /* 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 #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 = */ QStringLiteral("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(); - + auto storedJob = static_cast(job); if (storedJob->error()) { - setLatestResponseCode(responseCode); - setError(ERR_PROBLEM_WITH_REQUEST); - setJobErrorText(storedJob->errorText()); - setJobError(storedJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(storedJob); } 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(); 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/davcollectionmodifyjob.cpp b/src/common/davcollectionmodifyjob.cpp index ae4a8d2..e12e133 100644 --- a/src/common/davcollectionmodifyjob.cpp +++ b/src/common/davcollectionmodifyjob.cpp @@ -1,152 +1,145 @@ /* Copyright (c) 2010 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 "davcollectionmodifyjob.h" #include "davmanager.h" #include "daverror.h" #include "utils.h" #include "davjob.h" using namespace KDAV2; DavCollectionModifyJob::DavCollectionModifyJob(const DavUrl &url, QObject *parent) : DavJobBase(parent), mUrl(url) { } void DavCollectionModifyJob::setProperty(const QString &prop, const QString &value, const QString &ns) { QDomElement propElement; if (ns.isEmpty()) { propElement = mQuery.createElement(prop); } else { propElement = mQuery.createElementNS(ns, prop); } const QDomText textElement = mQuery.createTextNode(value); propElement.appendChild(textElement); mSetProperties << propElement; } void DavCollectionModifyJob::removeProperty(const QString &prop, const QString &ns) { QDomElement propElement; if (ns.isEmpty()) { propElement = mQuery.createElement(prop); } else { propElement = mQuery.createElementNS(ns, prop); } mRemoveProperties << propElement; } void DavCollectionModifyJob::start() { if (mSetProperties.isEmpty() && mRemoveProperties.isEmpty()) { setError(ERR_COLLECTIONMODIFY_NO_PROPERITES); setErrorTextFromDavError(); emitResult(); return; } QDomDocument mQuery; QDomElement propertyUpdateElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propertyupdate")); mQuery.appendChild(propertyUpdateElement); if (!mSetProperties.isEmpty()) { QDomElement setElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("set")); propertyUpdateElement.appendChild(setElement); QDomElement propElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); setElement.appendChild(propElement); foreach (const QDomElement &element, mSetProperties) { propElement.appendChild(element); } } if (!mRemoveProperties.isEmpty()) { QDomElement removeElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("remove")); propertyUpdateElement.appendChild(removeElement); QDomElement propElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); removeElement.appendChild(propElement); foreach (const QDomElement &element, mSetProperties) { propElement.appendChild(element); } } auto job = DavManager::self()->createPropPatchJob(mUrl.url(), mQuery); connect(job, &DavJob::result, this, &DavCollectionModifyJob::davJobFinished); } void DavCollectionModifyJob::davJobFinished(KJob *job) { - DavJob *davJob = qobject_cast(job); - const int responseCode = davJob->responseCode(); - + auto davJob = static_cast(job); if (davJob->error()) { - setLatestResponseCode(responseCode); - setError(ERR_COLLECTIONMODIFY); - setJobErrorText(davJob->errorText()); - setJobError(davJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(davJob, ERR_COLLECTIONMODIFY); emitResult(); return; } const QDomDocument response = davJob->response(); QDomElement responseElement = Utils::firstChildElementNS(response.documentElement(), QStringLiteral("DAV:"), QStringLiteral("response")); bool hasError = false; QString errorText; // parse all propstats answers to get the eventual errors const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); for (int i = 0; i < propstats.length(); ++i) { const QDomElement propstatElement = propstats.item(i).toElement(); const QDomElement statusElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("status")); const QString statusText = statusElement.text(); if (statusText.contains(QStringLiteral("200"))) { continue; } else { // Generic error hasError = true; break; } } if (hasError) { setError(ERR_COLLECTIONMODIFY_RESPONSE); // Trying to get more information about the error const QDomElement responseDescriptionElement = Utils::firstChildElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("responsedescription")); if (!responseDescriptionElement.isNull()) { setJobErrorText(responseDescriptionElement.text()); } - setErrorTextFromDavError(); } emitResult(); } diff --git a/src/common/davcollectionsfetchjob.cpp b/src/common/davcollectionsfetchjob.cpp index fe998d1..c4c1623 100644 --- a/src/common/davcollectionsfetchjob.cpp +++ b/src/common/davcollectionsfetchjob.cpp @@ -1,321 +1,315 @@ /* 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 (davJob->latestHttpStatusCode()) { // 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\n " << homeSets; //Update the url in case of redirects mUrl.setUrl(davJob->url()); 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 = static_cast(job); 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(davJob->responseCode()); - setError(ERR_PROBLEM_WITH_REQUEST); - setJobErrorText(davJob->errorText()); - setJobError(davJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(davJob); } else { // For use in the collectionDiscovered() signal QUrl _jobUrl = mUrl.url(); _jobUrl.setUserInfo(QString()); const QString jobUrl = _jobUrl.toDisplayString(); // 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 (davJob->latestHttpStatusCode()) { // 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/davdiscoveryjob.cpp b/src/common/davdiscoveryjob.cpp index 5ab17b8..552b1f8 100644 --- a/src/common/davdiscoveryjob.cpp +++ b/src/common/davdiscoveryjob.cpp @@ -1,147 +1,142 @@ /* Copyright (c) 2018 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 "davdiscoveryjob.h" #include "davmanager.h" #include "davprotocolbase.h" #include "daverror.h" #include "utils.h" #include "davjob.h" using namespace KDAV2; DavDiscoveryJob::DavDiscoveryJob(const DavUrl &davUrl, const QString &wellKnownSuffix, QObject *parent) : DavJobBase(parent), mUrl(davUrl) { auto url = davUrl.url(); if (!url.toString().contains("/.well-known/")) { url.setPath(url.path() + "/.well-known/" + wellKnownSuffix); mUrl.setUrl(url); } } void DavDiscoveryJob::start() { 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("current-user-principal"))); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-URL"))); DavJob *job = DavManager::self()->createPropFindJob(mUrl.url(), document, QStringLiteral("0")); connect(job, &DavJob::result, this, &DavDiscoveryJob::davJobFinished); } QUrl DavDiscoveryJob::url() const { return mUrl.url(); } void DavDiscoveryJob::davJobFinished(KJob *job) { - DavJob *davJob = qobject_cast(job); + DavJob *davJob = static_cast(job); if (davJob->error()) { //Retry on the root uri on 404, otherwise fail - if (davJob->responseCode() == 404 && davJob->url().path() != QStringLiteral("/")) { + if (davJob->httpStatusCode() == 404 && davJob->url().path() != QStringLiteral("/")) { auto url = mUrl.url(); url.setPath("/"); mUrl.setUrl(url); start(); return; } - setLatestResponseCode(davJob->responseCode()); - setError(ERR_PROBLEM_WITH_REQUEST); - setJobErrorText(davJob->errorText()); - setJobError(davJob->error()); - setErrorTextFromDavError(); - + setErrorFromJob(davJob); emitResult(); return; } mUrl.setUrl(davJob->url()); const QDomDocument document = davJob->response(); const QDomElement multistatusElement = document.documentElement(); const QString principalHref = [&] { QDomElement responseElement = Utils::firstChildElementNS(multistatusElement, QStringLiteral("DAV:"), QStringLiteral("response")); while (!responseElement.isNull()) { const QDomElement propstatElement = [&] { // check for the valid propstat, without giving up on first error const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); for (int i = 0; i < propstats.length(); ++i) { const QDomElement propstatCandidate = propstats.item(i).toElement(); const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); if (statusElement.text().contains(QLatin1String("200"))) { return propstatCandidate; } } return QDomElement{}; }(); if (propstatElement.isNull()) { responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); continue; } // extract home sets const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); // Trying to get the principal url, given either by current-user-principal or principal-URL QDomElement urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("current-user-principal")); if (urlHolder.isNull()) { urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("principal-URL")); } if (!urlHolder.isNull()) { // Getting the href that will be used for the next round const QDomElement hrefElement = Utils::firstChildElementNS(urlHolder, QStringLiteral("DAV:"), QStringLiteral("href")); if (!hrefElement.isNull()) { return hrefElement.text(); } } responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); } return QString{}; }(); QUrl principalUrl(mUrl.url()); if (principalHref.startsWith(QLatin1Char('/'))) { // principalHref is only a path, use request url to complete principalUrl.setPath(principalHref, QUrl::TolerantMode); } else { // href is a complete url principalUrl = QUrl::fromUserInput(principalHref); principalUrl.setUserName(mUrl.url().userName()); principalUrl.setPassword(mUrl.url().password()); } mUrl.setUrl(principalUrl); emitResult(); } diff --git a/src/common/daverror.cpp b/src/common/daverror.cpp index 7ab635b..fe09cd2 100644 --- a/src/common/daverror.cpp +++ b/src/common/daverror.cpp @@ -1,162 +1,127 @@ /* Copyright (c) 2016 Sandro Knauß 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 "daverror.h" using namespace KDAV2; Error::Error() : mErrorNumber(NO_ERR) + , mHttpStatusCode(0) , mResponseCode(0) , mJobErrorCode(0) { } -Error::Error(ErrorNumber errNo, int responseCode, const QString &errorText, int jobErrorCode) +Error::Error(ErrorNumber errNo, int httpStatusCode, int responseCode ,const QString &errorText, int jobErrorCode) : mErrorNumber(errNo) + , mHttpStatusCode(httpStatusCode) , mResponseCode(responseCode) , mErrorText(errorText) , mJobErrorCode(jobErrorCode) { } ErrorNumber Error::errorNumber() const { return mErrorNumber; } - QString Error::internalErrorText() const { return mErrorText; } - int Error::jobErrorCode() const { return mJobErrorCode; } - -int Error::responseCode() const +int Error::httpStatusCode() const { - return mResponseCode; + return mHttpStatusCode; } -QString KDAV2::Error::translatedJobError() const +int Error::responseCode() const { - QString err; - err = mErrorText; - return err; + return mResponseCode; } QString Error::errorText() const { - QString result; - - QString err = translatedJobError(); - switch (mErrorNumber) { - case ERR_PROBLEM_WITH_REQUEST: { - // User-side error - if (mResponseCode == 401) { - err = QStringLiteral("Invalid username/password"); - } else if (mResponseCode == 403) { - err = QStringLiteral("Access forbidden"); - } else if (mResponseCode == 404) { - err = QStringLiteral("Resource not found"); - } else { - err = QStringLiteral("HTTP error"); - } - result = QStringLiteral("There was a problem with the request.\n" - "%1 (%2).").arg(err).arg(mResponseCode); - break; - } - case ERR_NO_MULTIGET: { - result = QStringLiteral("Protocol for the collection does not support MULTIGET"); - break; - } - case ERR_SERVER_UNRECOVERABLE: { - result = QStringLiteral("The server encountered an error that prevented it from completing your request: %1 (%2)").arg(err).arg(mResponseCode); - break; - } - case ERR_COLLECTIONDELETE: { - result = QStringLiteral("There was a problem with the request. The collection has not been deleted from the server.\n" - "%1 (%2).").arg(err).arg(mResponseCode); - break; - } - case ERR_COLLECTIONFETCH: { - result = QStringLiteral("Invalid responses from backend"); - break; - } - case ERR_COLLECTIONFETCH_XQUERY_SETFOCUS: { - result = QStringLiteral("Error setting focus for XQuery"); - break; - } - case ERR_COLLECTIONFETCH_XQUERY_INVALID: { - result = QStringLiteral("Invalid XQuery submitted by DAV implementation"); - break; - } - case ERR_COLLECTIONMODIFY: { - result = QStringLiteral("There was a problem with the request. The collection has not been modified on the server.\n" - "%1 (%2).").arg(err).arg(mResponseCode); - break; - } - case ERR_COLLECTIONMODIFY_NO_PROPERITES: { - result = QStringLiteral("No properties to change or remove"); - break; - } - case ERR_COLLECTIONMODIFY_RESPONSE: { - result = QStringLiteral("There was an error when modifying the properties"); - if (!mErrorText.isEmpty()) { - result.append(QStringLiteral("\nThe server returned more information:\n%1").arg(mErrorText)); - } - break; - } - case ERR_COLLECTIONCREATE: { - result = QStringLiteral("There was an error when creating the collection"); - break; - } - case ERR_ITEMCREATE: { - result = QStringLiteral("There was a problem with the request. The item has not been created on the server.\n" - "%1 (%2).").arg(err).arg(mResponseCode); - break; - } - case ERR_ITEMDELETE: { - result = QStringLiteral("There was a problem with the request. The item has not been deleted from the server.\n" - "%1 (%2).").arg(err).arg(mResponseCode); - break; - } - case ERR_ITEMMODIFY: { - result = QStringLiteral("There was a problem with the request. The item was not modified on the server.\n" - "%1 (%2).").arg(err).arg(mResponseCode); - break; - } - case ERR_ITEMLIST: { - result = QStringLiteral("There was a problem with the request."); - break; - }; - case ERR_ITEMLIST_NOMIMETYPE: { - result = QStringLiteral("There was a problem with the request. The requested mimetypes are not supported."); - break; - } - case NO_ERR: - break; + case ERR_PROBLEM_WITH_REQUEST: { + // User-side error + QString err; + if (mResponseCode == 401) { + err = QStringLiteral("Invalid username/password"); + } else if (mResponseCode == 403) { + err = QStringLiteral("Access forbidden"); + } else if (mResponseCode == 404) { + err = QStringLiteral("Resource not found"); + } else { + err = QStringLiteral("HTTP error"); + } + return QStringLiteral("There was a problem with the request.\n" + "%1 (%2).").arg(err).arg(mResponseCode); + } + case ERR_NO_MULTIGET: + return QStringLiteral("Protocol for the collection does not support MULTIGET"); + case ERR_SERVER_UNRECOVERABLE: + return QStringLiteral("The server encountered an error that prevented it from completing your request: %1 (%2)").arg(internalErrorText()).arg(mResponseCode); + case ERR_COLLECTIONDELETE: + return QStringLiteral("There was a problem with the request. The collection has not been deleted from the server.\n" + "%1 (%2).").arg(internalErrorText()).arg(mResponseCode); + case ERR_COLLECTIONFETCH: + return QStringLiteral("Invalid responses from backend"); + case ERR_COLLECTIONFETCH_XQUERY_SETFOCUS: + return QStringLiteral("Error setting focus for XQuery"); + case ERR_COLLECTIONFETCH_XQUERY_INVALID: + return QStringLiteral("Invalid XQuery submitted by DAV implementation"); + case ERR_COLLECTIONMODIFY: + return QStringLiteral("There was a problem with the request. The collection has not been modified on the server.\n" + "%1 (%2).").arg(internalErrorText()).arg(mResponseCode); + case ERR_COLLECTIONMODIFY_NO_PROPERITES: + return QStringLiteral("No properties to change or remove"); + case ERR_COLLECTIONMODIFY_RESPONSE: { + auto result = QStringLiteral("There was an error when modifying the properties"); + if (!mErrorText.isEmpty()) { + result.append(QStringLiteral("\nThe server returned more information:\n%1").arg(mErrorText)); + } + return result; + } + case ERR_COLLECTIONCREATE: + return QStringLiteral("There was an error when creating the collection"); + case ERR_ITEMCREATE: + return QStringLiteral("There was a problem with the request. The item has not been created on the server.\n" + "%1 (%2).").arg(internalErrorText()).arg(mResponseCode); + case ERR_ITEMDELETE: + return QStringLiteral("There was a problem with the request. The item has not been deleted from the server.\n" + "%1 (%2).").arg(internalErrorText()).arg(mResponseCode); + case ERR_ITEMMODIFY: + return QStringLiteral("There was a problem with the request. The item was not modified on the server.\n" + "%1 (%2).").arg(internalErrorText()).arg(mResponseCode); + case ERR_ITEMLIST: + return QStringLiteral("There was a problem with the request."); + case ERR_ITEMLIST_NOMIMETYPE: + return QStringLiteral("There was a problem with the request. The requested mimetypes are not supported."); + case NO_ERR: + break; } - return result; + return internalErrorText(); } diff --git a/src/common/daverror.h b/src/common/daverror.h index f77fad4..fa4cc49 100644 --- a/src/common/daverror.h +++ b/src/common/daverror.h @@ -1,72 +1,73 @@ /* Copyright (c) 2016 Sandro Knauß 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_DAVERROR_H #define KDAV2_DAVERROR_H #include "kpimkdav2_export.h" #include #include namespace KDAV2 { enum ErrorNumber { NO_ERR = 0, ERR_PROBLEM_WITH_REQUEST = KJob::UserDefinedError + 200, ERR_NO_MULTIGET, ERR_SERVER_UNRECOVERABLE, ERR_COLLECTIONDELETE = ERR_PROBLEM_WITH_REQUEST + 10, ERR_COLLECTIONFETCH = ERR_PROBLEM_WITH_REQUEST + 20, ERR_COLLECTIONFETCH_XQUERY_SETFOCUS, ERR_COLLECTIONFETCH_XQUERY_INVALID, ERR_COLLECTIONMODIFY = ERR_PROBLEM_WITH_REQUEST + 30, ERR_COLLECTIONMODIFY_NO_PROPERITES, ERR_COLLECTIONMODIFY_RESPONSE, ERR_COLLECTIONCREATE = ERR_PROBLEM_WITH_REQUEST + 40, ERR_ITEMCREATE = ERR_PROBLEM_WITH_REQUEST + 100, ERR_ITEMDELETE = ERR_PROBLEM_WITH_REQUEST + 110, ERR_ITEMMODIFY = ERR_PROBLEM_WITH_REQUEST + 120, ERR_ITEMLIST = ERR_PROBLEM_WITH_REQUEST + 130, ERR_ITEMLIST_NOMIMETYPE }; class KPIMKDAV2_EXPORT Error { public: explicit Error(); - explicit Error(ErrorNumber errNo, int responseCode, const QString &errorText, int jobErrorCode); + explicit Error(ErrorNumber errNo, int httpStatusCode, int responseCode, const QString &errorText, int jobErrorCode); ErrorNumber errorNumber() const; + int httpStatusCode() const; int responseCode() const; QString internalErrorText() const; int jobErrorCode() const; - QString translatedJobError() const; QString errorText() const; private: ErrorNumber mErrorNumber; + int mHttpStatusCode; int mResponseCode; QString mErrorText; int mJobErrorCode; }; } #endif diff --git a/src/common/davitemcreatejob.cpp b/src/common/davitemcreatejob.cpp index 4347e5e..5f49706 100644 --- a/src/common/davitemcreatejob.cpp +++ b/src/common/davitemcreatejob.cpp @@ -1,80 +1,76 @@ /* 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 "davitemcreatejob.h" #include "davitemfetchjob.h" #include "davmanager.h" #include "daverror.h" #include "davjob.h" #include "libkdav2_debug.h" using namespace KDAV2; DavItemCreateJob::DavItemCreateJob(const DavItem &item, QObject *parent) : DavJobBase(parent), mItem(item) { } void DavItemCreateJob::start() { auto job = DavManager::self()->createCreateJob(mItem.data(), itemUrl(), mItem.contentType().toLatin1()); connect(job, &DavJob::result, this, &DavItemCreateJob::davJobFinished); } DavItem DavItemCreateJob::item() const { return mItem; } QUrl DavItemCreateJob::itemUrl() const { return mItem.url().url(); } void DavItemCreateJob::davJobFinished(KJob *job) { auto storedJob = static_cast(job); if (storedJob->error()) { - setLatestResponseCode(storedJob->responseCode()); - setError(ERR_ITEMCREATE); - setJobErrorText(storedJob->errorText()); - setJobError(storedJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(storedJob, ERR_ITEMCREATE); emitResult(); return; } mItem.setUrl(DavUrl(storedJob->url(), mItem.url().protocol())); DavItemFetchJob *fetchJob = new DavItemFetchJob(mItem); connect(fetchJob, &DavItemFetchJob::result, this, &DavItemCreateJob::itemRefreshed); fetchJob->start(); } void DavItemCreateJob::itemRefreshed(KJob *job) { if (!job->error()) { DavItemFetchJob *fetchJob = qobject_cast(job); mItem.setEtag(fetchJob->item().etag()); } emitResult(); } diff --git a/src/common/davitemdeletejob.cpp b/src/common/davitemdeletejob.cpp index 439c49e..d332f69 100644 --- a/src/common/davitemdeletejob.cpp +++ b/src/common/davitemdeletejob.cpp @@ -1,86 +1,81 @@ /* 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 "davitemdeletejob.h" #include "davitemfetchjob.h" #include "davmanager.h" #include "daverror.h" #include "davjob.h" using namespace KDAV2; DavItemDeleteJob::DavItemDeleteJob(const DavItem &item, QObject *parent) : DavJobBase(parent), mItem(item), mFreshResponseCode(-1) { } void DavItemDeleteJob::start() { DavJob *job = DavManager::self()->createDeleteJob(mItem.url().url()); connect(job, &DavJob::result, this, &DavItemDeleteJob::davJobFinished); } DavItem DavItemDeleteJob::freshItem() const { return mFreshItem; } int DavItemDeleteJob::freshResponseCode() const { return mFreshResponseCode; } void DavItemDeleteJob::davJobFinished(KJob *job) { - auto *deleteJob = qobject_cast(job); + auto deleteJob = static_cast(job); if (deleteJob->error()) { - const int responseCode = deleteJob->responseCode(); - + const int responseCode = deleteJob->httpStatusCode(); if (responseCode != 404 && responseCode != 410) { - setLatestResponseCode(responseCode); - setError(ERR_ITEMDELETE); - setJobErrorText(deleteJob->errorText()); - setJobError(deleteJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(deleteJob, ERR_ITEMDELETE); } if (hasConflict()) { DavItemFetchJob *fetchJob = new DavItemFetchJob(mItem); connect(fetchJob, &DavItemFetchJob::result, this, &DavItemDeleteJob::conflictingItemFetched); fetchJob->start(); return; } } emitResult(); } void DavItemDeleteJob::conflictingItemFetched(KJob *job) { DavItemFetchJob *fetchJob = qobject_cast(job); - mFreshResponseCode = fetchJob->latestResponseCode(); + mFreshResponseCode = fetchJob->latestHttpStatusCode(); if (!job->error()) { mFreshItem = fetchJob->item(); } emitResult(); } diff --git a/src/common/davitemdeletejob.h b/src/common/davitemdeletejob.h index 2a8fa3b..e25addc 100644 --- a/src/common/davitemdeletejob.h +++ b/src/common/davitemdeletejob.h @@ -1,74 +1,74 @@ /* 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_DAVITEMDELETEJOB_H #define KDAV2_DAVITEMDELETEJOB_H #include "kpimkdav2_export.h" #include "davitem.h" #include "davjobbase.h" #include "davurl.h" namespace KDAV2 { /** * @short A job to delete a DAV item on the DAV server. */ class KPIMKDAV2_EXPORT DavItemDeleteJob : public DavJobBase { Q_OBJECT public: /** * Creates a new dav item delete job. * * @param item The item that shall be deleted. * @param parent The parent object. */ DavItemDeleteJob(const DavItem &item, QObject *parent = nullptr); /** * Starts the job. */ void start() Q_DECL_OVERRIDE; /** * Returns the item that triggered the conflict, if any. */ DavItem freshItem() const; /** - * Returns the response code we got when fetching the fresh item. + * Returns the http response code we got when fetching the fresh item. */ int freshResponseCode() const; private Q_SLOTS: void davJobFinished(KJob *); void conflictingItemFetched(KJob *); private: DavItem mItem; DavItem mFreshItem; int mFreshResponseCode; }; } #endif diff --git a/src/common/davitemfetchjob.cpp b/src/common/davitemfetchjob.cpp index 3142c05..f143c92 100644 --- a/src/common/davitemfetchjob.cpp +++ b/src/common/davitemfetchjob.cpp @@ -1,63 +1,56 @@ /* 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 "davitemfetchjob.h" #include "davmanager.h" #include "daverror.h" #include "davjob.h" using namespace KDAV2; DavItemFetchJob::DavItemFetchJob(const DavItem &item, QObject *parent) : DavJobBase(parent), mItem(item) { } void DavItemFetchJob::start() { auto job = DavManager::self()->createGetJob(mItem.url().url()); connect(job, &DavJob::result, this, &DavItemFetchJob::davJobFinished); } DavItem DavItemFetchJob::item() const { return mItem; } void DavItemFetchJob::davJobFinished(KJob *job) { - auto *storedJob = qobject_cast(job); - const int responseCode = storedJob->responseCode(); - setLatestResponseCode(responseCode); - + auto *storedJob = static_cast(job); if (storedJob->error()) { - setLatestResponseCode(responseCode); - setError(ERR_PROBLEM_WITH_REQUEST); - setJobErrorText(storedJob->errorText()); - setJobError(storedJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(storedJob); } else { mItem.setData(storedJob->data()); mItem.setContentType(storedJob->getContentTypeHeader()); mItem.setEtag(storedJob->getETagHeader()); } emitResult(); } diff --git a/src/common/davitemmodifyjob.cpp b/src/common/davitemmodifyjob.cpp index d7875d5..c5c9a72 100644 --- a/src/common/davitemmodifyjob.cpp +++ b/src/common/davitemmodifyjob.cpp @@ -1,124 +1,118 @@ /* 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 "davitemmodifyjob.h" #include "davitemfetchjob.h" #include "davmanager.h" #include "daverror.h" #include "davjob.h" using namespace KDAV2; DavItemModifyJob::DavItemModifyJob(const DavItem &item, QObject *parent) : DavJobBase(parent), mItem(item), mFreshResponseCode(0) { } void DavItemModifyJob::start() { auto job = DavManager::self()->createModifyJob(mItem.data(), itemUrl(), mItem.contentType().toUtf8(), mItem.etag().toUtf8()); connect(job, &DavJob::result, this, &DavItemModifyJob::davJobFinished); } DavItem DavItemModifyJob::item() const { return mItem; } DavItem DavItemModifyJob::freshItem() const { return mFreshItem; } int DavItemModifyJob::freshResponseCode() const { return mFreshResponseCode; } QUrl DavItemModifyJob::itemUrl() const { return mItem.url().url(); } void DavItemModifyJob::davJobFinished(KJob *job) { - auto *storedJob = qobject_cast(job); + auto storedJob = static_cast(job); if (storedJob->error()) { - const int responseCode = storedJob->responseCode();; - - setLatestResponseCode(responseCode); - setError(ERR_ITEMMODIFY); - setJobErrorText(storedJob->errorText()); - setJobError(storedJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(storedJob, ERR_ITEMMODIFY); if (hasConflict()) { DavItemFetchJob *fetchJob = new DavItemFetchJob(mItem); connect(fetchJob, &DavItemFetchJob::result, this, &DavItemModifyJob::conflictingItemFetched); fetchJob->start(); } else { emitResult(); } return; } const auto location = storedJob->getLocationHeader(); QUrl url; if (location.isEmpty()) { url = storedJob->url(); } else if (location.startsWith(QLatin1Char('/'))) { url = storedJob->url(); url.setPath(location, QUrl::TolerantMode); } else { url = QUrl::fromUserInput(location); } url.setUserInfo(itemUrl().userInfo()); mItem.setUrl(DavUrl(url, mItem.url().protocol())); DavItemFetchJob *fetchJob = new DavItemFetchJob(mItem); connect(fetchJob, &DavItemFetchJob::result, this, &DavItemModifyJob::itemRefreshed); fetchJob->start(); } void DavItemModifyJob::itemRefreshed(KJob *job) { if (!job->error()) { DavItemFetchJob *fetchJob = qobject_cast(job); mItem.setEtag(fetchJob->item().etag()); } else { mItem.setEtag(QString()); } emitResult(); } void DavItemModifyJob::conflictingItemFetched(KJob *job) { DavItemFetchJob *fetchJob = qobject_cast(job); - mFreshResponseCode = fetchJob->latestResponseCode(); + mFreshResponseCode = fetchJob->latestHttpStatusCode(); if (!job->error()) { mFreshItem = fetchJob->item(); } emitResult(); } diff --git a/src/common/davitemmodifyjob.h b/src/common/davitemmodifyjob.h index a0c77a0..6220c69 100644 --- a/src/common/davitemmodifyjob.h +++ b/src/common/davitemmodifyjob.h @@ -1,82 +1,82 @@ /* 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_DAVITEMMODIFYJOB_H #define KDAV2_DAVITEMMODIFYJOB_H #include "kpimkdav2_export.h" #include "davitem.h" #include "davjobbase.h" #include "davurl.h" namespace KDAV2 { /** * @short A job that modifies a DAV item on the DAV server. */ class KPIMKDAV2_EXPORT DavItemModifyJob : public DavJobBase { Q_OBJECT public: /** * Creates a new dav item modify job. * * @param item The item that shall be modified. * @param parent The parent object. */ DavItemModifyJob(const DavItem &item, QObject *parent = nullptr); /** * Starts the job. */ void start() Q_DECL_OVERRIDE; /** * Returns the modified item including the updated etag information. */ DavItem item() const; QUrl itemUrl() const; /** * Returns the item that triggered the conflict, if any. */ DavItem freshItem() const; /** - * Returns the response code we got when fetching the fresh item. + * Returns the http response code we got when fetching the fresh item. */ int freshResponseCode() const; private Q_SLOTS: void davJobFinished(KJob *); void itemRefreshed(KJob *); void conflictingItemFetched(KJob *); private: DavItem mItem; DavItem mFreshItem; int mFreshResponseCode; }; } #endif diff --git a/src/common/davitemsfetchjob.cpp b/src/common/davitemsfetchjob.cpp index 634b5b8..7184602 100644 --- a/src/common/davitemsfetchjob.cpp +++ b/src/common/davitemsfetchjob.cpp @@ -1,160 +1,153 @@ /* Copyright (c) 2010 Grégory Oestreicher Based on DavItemsListJob which is 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 "davitemsfetchjob.h" #include "davmanager.h" #include "davmultigetprotocol.h" #include "utils.h" #include "daverror.h" #include "davjob.h" using namespace KDAV2; DavItemsFetchJob::DavItemsFetchJob(const DavUrl &collectionUrl, const QStringList &urls, QObject *parent) : DavJobBase(parent), mCollectionUrl(collectionUrl), mUrls(urls) { } void DavItemsFetchJob::start() { const DavMultigetProtocol *protocol = dynamic_cast(DavManager::self()->davProtocol(mCollectionUrl.protocol())); if (!protocol) { setError(ERR_NO_MULTIGET); setErrorTextFromDavError(); emitResult(); return; } if (mUrls.isEmpty()) { setError(ERR_PROBLEM_WITH_REQUEST); setErrorText("DavItemsFetchJob without urls."); emitResult(); return; } const QDomDocument report = protocol->itemsReportQuery(mUrls)->buildQuery(); DavJob *job = DavManager::self()->createReportJob(mCollectionUrl.url(), report, QStringLiteral("0")); connect(job, &DavJob::result, this, &DavItemsFetchJob::davJobFinished); } DavItem::List DavItemsFetchJob::items() const { DavItem::List values; values.reserve(mItems.size()); Q_FOREACH (const auto &value, mItems) { values << value; } return values; } DavItem DavItemsFetchJob::item(const QString &url) const { return mItems.value(url); } void DavItemsFetchJob::davJobFinished(KJob *job) { - DavJob *davJob = qobject_cast(job); - const int responseCode = davJob->responseCode(); - + auto davJob = static_cast(job); if (davJob->error()) { - setLatestResponseCode(responseCode); - setError(ERR_PROBLEM_WITH_REQUEST); - setJobErrorText(davJob->errorText()); - setJobError(davJob->error()); - setErrorTextFromDavError(); - + setErrorFromJob(davJob); emitResult(); return; } const DavMultigetProtocol *protocol = static_cast(DavManager::self()->davProtocol(mCollectionUrl.protocol())); const QDomDocument document = davJob->response(); const QDomElement documentElement = document.documentElement(); QDomElement responseElement = Utils::firstChildElementNS(documentElement, QStringLiteral("DAV:"), QStringLiteral("response")); while (!responseElement.isNull()) { QDomElement propstatElement = Utils::firstChildElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("propstat")); if (propstatElement.isNull()) { responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); continue; } // Check for errors const QDomElement statusElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("status")); if (!statusElement.text().contains(QLatin1String("200"))) { responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); continue; } const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); DavItem item; // extract path const QDomElement hrefElement = Utils::firstChildElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("href")); const QString href = hrefElement.text(); QUrl url = davJob->url(); if (href.startsWith(QLatin1Char('/'))) { // href is only a path, use request url to complete url.setPath(href, QUrl::TolerantMode); } else { // href is a complete url url = QUrl::fromUserInput(href); } auto _url = url; _url.setUserInfo(mCollectionUrl.url().userInfo()); item.setUrl(DavUrl(_url, mCollectionUrl.protocol())); // extract etag const QDomElement getetagElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("getetag")); item.setEtag(getetagElement.text()); // extract content const QDomElement dataElement = Utils::firstChildElementNS(propElement, protocol->responseNamespace(), protocol->dataTagName()); if (dataElement.isNull()) { responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); continue; } const QByteArray data = dataElement.firstChild().toText().data().toUtf8(); if (data.isEmpty()) { responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); continue; } item.setData(data); mItems.insert(item.url().toDisplayString(), item); responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); } emitResult(); } diff --git a/src/common/davitemslistjob.cpp b/src/common/davitemslistjob.cpp index d08ef7d..6008e80 100644 --- a/src/common/davitemslistjob.cpp +++ b/src/common/davitemslistjob.cpp @@ -1,239 +1,233 @@ /* 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 "davitemslistjob.h" #include "daverror.h" #include "davmanager.h" #include "davprotocolbase.h" #include "davurl.h" #include "utils.h" #include "davjob.h" #include #include using namespace KDAV2; class DavItemsListJobPrivate { public: DavItemsListJobPrivate(const DavUrl &url); DavUrl mUrl; QStringList mMimeTypes; QString mRangeStart; QString mRangeEnd; DavItem::List mItems; QSet mSeenUrls; // to prevent events duplication with some servers uint mSubJobCount; }; DavItemsListJobPrivate::DavItemsListJobPrivate(const DavUrl &url) : mUrl(url) , mSubJobCount(0) { } DavItemsListJob::DavItemsListJob(const DavUrl &url, QObject *parent) : DavJobBase(parent) , d(std::unique_ptr(new DavItemsListJobPrivate(url))) { } DavItemsListJob::~DavItemsListJob() { } void DavItemsListJob::setContentMimeTypes(const QStringList &types) { d->mMimeTypes = types; } void DavItemsListJob::setTimeRange(const QString &start, const QString &end) { d->mRangeStart = start; d->mRangeEnd = end; } void DavItemsListJob::start() { const DavProtocolBase *protocol = DavManager::self()->davProtocol(d->mUrl.protocol()); Q_ASSERT(protocol); QVectorIterator it(protocol->itemsQueries()); while (it.hasNext()) { XMLQueryBuilder::Ptr builder = it.next(); if (!d->mRangeStart.isEmpty()) { builder->setParameter(QStringLiteral("start"), d->mRangeStart); } if (!d->mRangeEnd.isEmpty()) { builder->setParameter(QStringLiteral("end"), d->mRangeEnd); } const QDomDocument props = builder->buildQuery(); const QString mimeType = builder->mimeType(); if (d->mMimeTypes.isEmpty() || d->mMimeTypes.contains(mimeType)) { ++d->mSubJobCount; if (protocol->useReport()) { DavJob *job = DavManager::self()->createReportJob(d->mUrl.url(), props); job->setProperty("davType", QStringLiteral("report")); job->setProperty("itemsMimeType", mimeType); connect(job, &DavJob::result, this, &DavItemsListJob::davJobFinished); } else { DavJob *job = DavManager::self()->createPropFindJob(d->mUrl.url(), props); job->setProperty("davType", QStringLiteral("propFind")); job->setProperty("itemsMimeType", mimeType); connect(job, &DavJob::result, this, &DavItemsListJob::davJobFinished); } } } if (d->mSubJobCount == 0) { setError(ERR_ITEMLIST_NOMIMETYPE); setErrorTextFromDavError(); emitResult(); } } DavItem::List DavItemsListJob::items() const { return d->mItems; } void DavItemsListJob::davJobFinished(KJob *job) { - DavJob *davJob = qobject_cast(job); - const int responseCode = davJob->responseCode(); - + auto davJob = static_cast(job); if (davJob->error()) { - setLatestResponseCode(responseCode); - setError(ERR_PROBLEM_WITH_REQUEST); - setJobErrorText(davJob->errorText()); - setJobError(davJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(davJob); } else { /* * Extract data from a document like the following: * * * * /caldav.php/test1.user/home/KOrganizer-166749289.780.ics * * * "b4bbea0278f4f63854c4167a7656024a" * * HTTP/1.1 200 OK * * * * /caldav.php/test1.user/home/KOrganizer-399416366.464.ics * * * "52eb129018398a7da4f435b2bc4c6cd5" * * HTTP/1.1 200 OK * * * */ const QString itemsMimeType = job->property("itemsMimeType").toString(); const QDomDocument document = davJob->response(); const QDomElement documentElement = document.documentElement(); QDomElement responseElement = Utils::firstChildElementNS(documentElement, QStringLiteral("DAV:"), QStringLiteral("response")); while (!responseElement.isNull()) { QDomElement propstatElement; // check for the valid propstat, without giving up on first error { const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); for (int i = 0; i < propstats.length(); ++i) { const QDomElement propstatCandidate = propstats.item(i).toElement(); const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); if (statusElement.text().contains(QStringLiteral("200"))) { propstatElement = propstatCandidate; } } } if (propstatElement.isNull()) { responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); continue; } const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); // check whether it is a dav collection ... const QDomElement resourcetypeElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("resourcetype")); if (!responseElement.isNull()) { const QDomElement collectionElement = Utils::firstChildElementNS(resourcetypeElement, QStringLiteral("DAV:"), QStringLiteral("collection")); if (!collectionElement.isNull()) { responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); continue; } } // ... if not it is an item DavItem item; item.setContentType(itemsMimeType); // extract path const QDomElement hrefElement = Utils::firstChildElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("href")); const QString href = hrefElement.text(); QUrl url = davJob->url(); url.setUserInfo(QString()); if (href.startsWith(QLatin1Char('/'))) { // href is only a path, use request url to complete url.setPath(href, QUrl::TolerantMode); } else { // href is a complete url url = QUrl::fromUserInput(href); } QString itemUrl = url.toDisplayString(); if (d->mSeenUrls.contains(itemUrl)) { responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); continue; } d->mSeenUrls << itemUrl; auto _url = url; _url.setUserInfo(d->mUrl.url().userInfo()); item.setUrl(DavUrl(_url, d->mUrl.protocol())); // extract etag const QDomElement getetagElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("getetag")); item.setEtag(getetagElement.text()); d->mItems << item; responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); } } if (--d->mSubJobCount == 0) { emitResult(); } } diff --git a/src/common/davjob.cpp b/src/common/davjob.cpp index 811920a..4d7e4fc 100644 --- a/src/common/davjob.cpp +++ b/src/common/davjob.cpp @@ -1,150 +1,157 @@ /* Copyright (c) 2014 Gregory 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 "davjob.h" #include "davmanager.h" #include "libkdav2_debug.h" #include using namespace KDAV2; class DavJobPrivate { public: QByteArray data; QDomDocument doc; QUrl url; QString location; QString etag; QString contentType; - int responseCode = 0; + QNetworkReply::NetworkError responseCode = QNetworkReply::NoError; + int httpStatusCode = 0; }; DavJob::DavJob(QNetworkReply *reply, QUrl url, QObject *parent) : KJob(parent), d(new DavJobPrivate) { d->url = url; connectToReply(reply); } DavJob::~DavJob() { } void DavJob::connectToReply(QNetworkReply *reply) { QObject::connect(reply, &QNetworkReply::readyRead, this, [=] () { d->data.append(reply->readAll()); }); QObject::connect(reply, static_cast(&QNetworkReply::error), this, [=] (QNetworkReply::NetworkError error) { qCWarning(KDAV2_LOG) << "Error " << error << reply->errorString() << "HTTP Status code " << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); qCWarning(KDAV2_LOG) << "Error " << reply->readAll(); }); QObject::connect(reply, &QNetworkReply::metaDataChanged, this, [=] () { qCDebug(KDAV2_LOG) << "Metadata changed: " << reply->rawHeaderPairs(); d->location = reply->rawHeader("Location"); d->etag = reply->rawHeader("ETag"); //"text/x-vcard; charset=utf-8" -> "text/x-vcard" d->contentType = reply->rawHeader("Content-Type").split(';').first(); }); QObject::connect(reply, &QNetworkReply::finished, this, [=] () { //This is a workaround for QNetworkAccessManager::setRedirectPolicy(QNetworkRequest::UserVerifiedRedirectPolicy), //which does not seem to work with multiple redirects. const auto possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if(!possibleRedirectUrl.isEmpty()) { qCDebug(KDAV2_LOG) << "Redirecting to " << possibleRedirectUrl; auto request = reply->request(); request.setUrl(possibleRedirectUrl); reply->disconnect(this); //Set in QWebdav const auto requestData = reply->property("requestData").toByteArray(); d->data.clear(); auto redirectReply = [&] { if (reply->property("isPut").toBool()) { return DavManager::networkAccessManager()->put(request, requestData); } return DavManager::networkAccessManager()->sendCustomRequest(request, request.attribute(QNetworkRequest::CustomVerbAttribute).toByteArray(), requestData); }(); redirectReply->setProperty("requestData", requestData); connectToReply(redirectReply); return; } //Could have changed due to redirects d->url = reply->url(); d->doc.setContent(d->data, true); if (KDAV2_LOG().isDebugEnabled()) { QTextStream stream(stdout, QIODevice::WriteOnly); d->doc.save(stream, 2); } d->responseCode = reply->error(); + d->httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (d->responseCode) { setError(KJob::UserDefinedError); setErrorText(reply->errorString()); } emitResult(); }); } void DavJob::start() { } QDomDocument DavJob::response() const { return d->doc; } QByteArray DavJob::data() const { return d->data; } QUrl DavJob::url() const { return d->url; } QString DavJob::getLocationHeader() const { return d->location; } QString DavJob::getETagHeader() const { return d->etag; } QString DavJob::getContentTypeHeader() const { return d->contentType; } -int DavJob::responseCode() const +QNetworkReply::NetworkError DavJob::responseCode() const { return d->responseCode; } + +int DavJob::httpStatusCode() const +{ + return d->httpStatusCode; +} diff --git a/src/common/davjob.h b/src/common/davjob.h index 2201821..4d7ee29 100644 --- a/src/common/davjob.h +++ b/src/common/davjob.h @@ -1,60 +1,61 @@ /* Copyright (c) 2014 Gregory 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_DAVJOB_H #define KDAV2_DAVJOB_H #include #include "kpimkdav2_export.h" #include #include #include #include class DavJobPrivate; namespace KDAV2 { class KPIMKDAV2_EXPORT DavJob : public KJob { Q_OBJECT public: explicit DavJob(QNetworkReply *reply, QUrl url, QObject *parent = nullptr); ~DavJob(); virtual void start() Q_DECL_OVERRIDE; QDomDocument response() const; QByteArray data() const; QUrl url() const; - int responseCode() const; + QNetworkReply::NetworkError responseCode() const; + int httpStatusCode() const; QString getLocationHeader() const; QString getETagHeader() const; QString getContentTypeHeader() const; private: void connectToReply(QNetworkReply *reply); std::unique_ptr d; }; } #endif diff --git a/src/common/davjobbase.cpp b/src/common/davjobbase.cpp index 107fc9e..597e392 100644 --- a/src/common/davjobbase.cpp +++ b/src/common/davjobbase.cpp @@ -1,119 +1,126 @@ /* Copyright (c) 2014 Gregory 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 "davjobbase.h" -#include "daverror.h" +#include "davjob.h" using namespace KDAV2; -class DavJobBasePrivate { -public: - DavJobBasePrivate(); - - int mLatestResponseCode; - int mJobErrorCode; +struct DavJobBasePrivate { + int mLatestHttpStatusCode{0}; + int mLatestResponseCode{0}; + int mJobErrorCode{0}; QString mInternalErrorText; }; -DavJobBasePrivate::DavJobBasePrivate() - : mLatestResponseCode(0) - , mJobErrorCode(0) -{ -} - DavJobBase::DavJobBase(QObject *parent) : KJob(parent) , d(std::unique_ptr(new DavJobBasePrivate())) { } DavJobBase::~DavJobBase() { } +unsigned int DavJobBase::latestHttpStatusCode() const +{ + return d->mLatestHttpStatusCode; +} + unsigned int DavJobBase::latestResponseCode() const { return d->mLatestResponseCode; } bool DavJobBase::canRetryLater() const { - switch (latestResponseCode()) { + switch (latestHttpStatusCode()) { case 0: // Likely a timeout or a connection failure. if (error()) { return true; } break; case 401: // Authentication required case 402: // Payment required case 407: // Proxy authentication required case 408: // Request timeout case 423: // Locked case 429: // Too many requests case 501: case 502: case 503: case 504: case 507: // Insufficient storage case 511: // Network authentication required return true; default: break; } return false; } bool DavJobBase::hasConflict() const { - return latestResponseCode() == 412; + return latestHttpStatusCode() == 412; } -void DavJobBase::setLatestResponseCode(unsigned int code) +void DavJobBase::setLatestHttpStatusCode(unsigned int code) { - d->mLatestResponseCode = code; + d->mLatestHttpStatusCode = code; } Error DavJobBase::davError() const { - return Error((KDAV2::ErrorNumber)error(), d->mLatestResponseCode, d->mInternalErrorText, d->mJobErrorCode); + return Error((KDAV2::ErrorNumber)error(), d->mLatestHttpStatusCode, d->mLatestResponseCode, d->mInternalErrorText, d->mJobErrorCode); } void DavJobBase::setJobErrorText(const QString &errorText) { d->mInternalErrorText = errorText; } void DavJobBase::setJobError(unsigned int jobErrorCode) { d->mJobErrorCode = jobErrorCode; } void DavJobBase::setErrorTextFromDavError() { setErrorText(davError().errorText()); } void DavJobBase::setDavError(const Error &error) { setError(error.errorNumber()); - setLatestResponseCode(error.responseCode()); + setLatestHttpStatusCode(error.responseCode()); setJobErrorText(error.internalErrorText()); setJobError(error.jobErrorCode()); + setErrorText(error.errorText()); +} + +void DavJobBase::setErrorFromJob(DavJob *job, unsigned int jobErrorCode) +{ + setLatestHttpStatusCode(job->httpStatusCode()); + d->mLatestResponseCode = job->responseCode(); + setError(jobErrorCode); + setJobErrorText(job->errorText()); + setJobError(job->error()); } diff --git a/src/common/davjobbase.h b/src/common/davjobbase.h index 0576ca1..049cfd6 100644 --- a/src/common/davjobbase.h +++ b/src/common/davjobbase.h @@ -1,105 +1,118 @@ /* Copyright (c) 2014 Gregory 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_DAVJOBBASE_H #define KDAV2_DAVJOBBASE_H #include - #include "kpimkdav2_export.h" +#include +#include "daverror.h" -#include - -class DavJobBasePrivate; +struct DavJobBasePrivate; namespace KDAV2 { class Error; +class DavJob; /** * @short base class for the jobs used by the resource. */ class KPIMKDAV2_EXPORT DavJobBase : public KJob { Q_OBJECT public: explicit DavJobBase(QObject *parent = nullptr); ~DavJobBase(); /** - * Get the latest response code. + * Get the latest http status code. * * If no response code has been set then 0 will be returned, but will * be meaningless unless error() is non-zero. In that case this means * that the latest error was not at the HTTP level. */ + unsigned int latestHttpStatusCode() const; + + /** + * Get the response code. + * + * This is a QNetworkReply::NetworkError + * + * If no response code has been set then 0 will be returned, but will + * be meaningless unless error() is non-zero. In that case this means + * that the latest error was not at the HTTP level. + * + */ unsigned int latestResponseCode() const; /** * Check if the job can be retried later. * * This will return true for transient errors, i.e. if the response code * is either zero and error() is set or if the HTTP response code hints * at a temporary error. * * The HTTP response codes considered retryable are: * - 401 * - 402 * - 407 * - 408 * - 423 * - 429 * - 501 to 504, inclusive * - 507 * - 511 */ bool canRetryLater() const; /** * Check if the job failed because of a conflict */ bool hasConflict() const; /** * Returns a instance of the KDAV2:Error to be able to translate the error */ Error davError() const; protected: /** * Sets the latest response code received. * * Only really useful in case of error, though success codes can * also be set. * * @param code The code to set, should be a valid HTTP response code or zero. */ - void setLatestResponseCode(unsigned int code); + void setLatestHttpStatusCode(unsigned int code); void setJobErrorText(const QString &errorText); void setJobError(unsigned int jobErrorCode); void setErrorTextFromDavError(); void setDavError(const Error &error); + void setErrorFromJob(DavJob*, unsigned int jobErrorCode = ERR_PROBLEM_WITH_REQUEST); private: std::unique_ptr d; }; } #endif diff --git a/src/common/davprincipalhomesetsfetchjob.cpp b/src/common/davprincipalhomesetsfetchjob.cpp index d962695..af5851c 100644 --- a/src/common/davprincipalhomesetsfetchjob.cpp +++ b/src/common/davprincipalhomesetsfetchjob.cpp @@ -1,217 +1,211 @@ /* Copyright (c) 2010 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 "davprincipalhomesetsfetchjob.h" #include "davmanager.h" #include "davprotocolbase.h" #include "daverror.h" #include "utils.h" #include "davjob.h" using namespace KDAV2; DavPrincipalHomeSetsFetchJob::DavPrincipalHomeSetsFetchJob(const DavUrl &url, QObject *parent) : DavJobBase(parent), mUrl(url) { } void DavPrincipalHomeSetsFetchJob::start() { fetchHomeSets(false); } void DavPrincipalHomeSetsFetchJob::fetchHomeSets(bool homeSetsOnly) { QDomDocument document; QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); document.appendChild(propfindElement); QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); propfindElement.appendChild(propElement); const QString homeSet = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSet(); const QString homeSetNS = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSetNS(); propElement.appendChild(document.createElementNS(homeSetNS, homeSet)); if (!homeSetsOnly) { propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-principal"))); propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-URL"))); } DavJob *job = DavManager::self()->createPropFindJob(mUrl.url(), document, QStringLiteral("0")); connect(job, &DavJob::result, this, &DavPrincipalHomeSetsFetchJob::davJobFinished); } QStringList DavPrincipalHomeSetsFetchJob::homeSets() const { return mHomeSets; } QUrl DavPrincipalHomeSetsFetchJob::url() const { return mUrl.url(); } void DavPrincipalHomeSetsFetchJob::davJobFinished(KJob *job) { - DavJob *davJob = qobject_cast(job); - + auto davJob = static_cast(job); if (davJob->error()) { - setLatestResponseCode(davJob->responseCode()); - setError(ERR_PROBLEM_WITH_REQUEST); - setJobErrorText(davJob->errorText()); - setJobError(davJob->error()); - setErrorTextFromDavError(); - + setErrorFromJob(davJob); emitResult(); return; } /* * Extract information from a document like the following (if no homeset is defined) : * * * * /dav/ * * HTTP/1.1 200 OK * * * /principals/users/gdacoin/ * * * * * HTTP/1.1 404 Not Found * * * * * * * * * Or like this (if the homeset is defined): * * * * * /principals/users/greg%40kamago.net/ * * * * /greg%40kamago.net/ * * * HTTP/1.1 200 OK * * * */ mUrl.setUrl(davJob->url()); const QString homeSet = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSet(); const QString homeSetNS = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSetNS(); QString nextRoundHref; // The content of the href element that will be used if no homeset was found. // This is either given by current-user-principal or by principal-URL. const QDomDocument document = davJob->response(); const QDomElement multistatusElement = document.documentElement(); QDomElement responseElement = Utils::firstChildElementNS(multistatusElement, QStringLiteral("DAV:"), QStringLiteral("response")); while (!responseElement.isNull()) { const QDomElement propstatElement = [&] { // check for the valid propstat, without giving up on first error const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); for (int i = 0; i < propstats.length(); ++i) { const QDomElement propstatCandidate = propstats.item(i).toElement(); const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); if (statusElement.text().contains(QLatin1String("200"))) { return propstatCandidate; } } return QDomElement{}; }(); if (propstatElement.isNull()) { responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); continue; } // extract home sets const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); const QDomElement homeSetElement = Utils::firstChildElementNS(propElement, homeSetNS, homeSet); if (!homeSetElement.isNull()) { QDomElement hrefElement = Utils::firstChildElementNS(homeSetElement, QStringLiteral("DAV:"), QStringLiteral("href")); while (!hrefElement.isNull()) { const QString href = hrefElement.text(); if (!mHomeSets.contains(href)) { mHomeSets << href; } hrefElement = Utils::nextSiblingElementNS(hrefElement, QStringLiteral("DAV:"), QStringLiteral("href")); } } else { // Trying to get the principal url, given either by current-user-principal or principal-URL QDomElement urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("current-user-principal")); if (urlHolder.isNull()) { urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("principal-URL")); } if (!urlHolder.isNull()) { // Getting the href that will be used for the next round QDomElement hrefElement = Utils::firstChildElementNS(urlHolder, QStringLiteral("DAV:"), QStringLiteral("href")); if (!hrefElement.isNull()) { nextRoundHref = hrefElement.text(); } } } responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); } /* * Now either we got one or more homesets, or we got an href for the next round * or nothing can be found by this job. * If we have homesets, we're done here and can notify the caller. * Else we must ensure that we have an href for the next round. */ if (!mHomeSets.isEmpty() || nextRoundHref.isEmpty()) { emitResult(); } else { QUrl nextRoundUrl(mUrl.url()); if (nextRoundHref.startsWith(QLatin1Char('/'))) { // nextRoundHref is only a path, use request url to complete nextRoundUrl.setPath(nextRoundHref, QUrl::TolerantMode); } else { // href is a complete url nextRoundUrl = QUrl::fromUserInput(nextRoundHref); nextRoundUrl.setUserName(mUrl.url().userName()); nextRoundUrl.setPassword(mUrl.url().password()); } mUrl.setUrl(nextRoundUrl); // And one more round, fetching only homesets fetchHomeSets(true); } } diff --git a/src/common/davprincipalsearchjob.cpp b/src/common/davprincipalsearchjob.cpp index a5128b7..24a0ed6 100644 --- a/src/common/davprincipalsearchjob.cpp +++ b/src/common/davprincipalsearchjob.cpp @@ -1,366 +1,351 @@ /* Copyright (c) 2011 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 "davprincipalsearchjob.h" #include "davmanager.h" #include "utils.h" #include "daverror.h" #include "davjob.h" #include using namespace KDAV2; DavPrincipalSearchJob::DavPrincipalSearchJob(const DavUrl &url, DavPrincipalSearchJob::FilterType type, const QString &filter, QObject *parent) : DavJobBase(parent), mUrl(url), mType(type), mFilter(filter), mPrincipalPropertySearchSubJobCount(0), mPrincipalPropertySearchSubJobSuccessful(false) { } void DavPrincipalSearchJob::fetchProperty(const QString &name, const QString &ns) { QString propNamespace = ns; if (propNamespace.isEmpty()) { propNamespace = QStringLiteral("DAV:"); } mFetchProperties << QPair(propNamespace, name); } DavUrl DavPrincipalSearchJob::davUrl() const { return mUrl; } void DavPrincipalSearchJob::start() { /* * The first step is to try to locate the URL that contains the principals. * This is done with a PROPFIND request and a XML like this: * * * * * * */ QDomDocument query; QDomElement propfind = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); query.appendChild(propfind); QDomElement prop = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); propfind.appendChild(prop); QDomElement principalCollectionSet = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-collection-set")); prop.appendChild(principalCollectionSet); DavJob *job = DavManager::self()->createPropFindJob(mUrl.url(), query); connect(job, &DavJob::result, this, &DavPrincipalSearchJob::principalCollectionSetSearchFinished); job->start(); } void DavPrincipalSearchJob::principalCollectionSetSearchFinished(KJob *job) { DavJob *davJob = qobject_cast(job); - const int responseCode = davJob->responseCode(); - if (davJob->error()) { - setLatestResponseCode(responseCode); - setError(ERR_PROBLEM_WITH_REQUEST); - setJobErrorText(davJob->errorText()); - setJobError(davJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(davJob); emitResult(); return; } if (job->error()) { setError(job->error()); setErrorText(job->errorText()); emitResult(); return; } /* * Extract information from a document like the following: * * * * * http://www.example.com/papers/ * * * * http://www.example.com/acl/users/ * http://www.example.com/acl/groups/ * * * HTTP/1.1 200 OK * * * */ QDomDocument document = davJob->response(); QDomElement documentElement = document.documentElement(); QDomElement responseElement = Utils::firstChildElementNS(documentElement, QStringLiteral("DAV:"), QStringLiteral("response")); if (responseElement.isNull()) { emitResult(); return; } // check for the valid propstat, without giving up on first error QDomElement propstatElement; { const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); for (int i = 0; i < propstats.length(); ++i) { const QDomElement propstatCandidate = propstats.item(i).toElement(); const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); if (statusElement.text().contains(QStringLiteral("200"))) { propstatElement = propstatCandidate; } } } if (propstatElement.isNull()) { emitResult(); return; } QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); if (propElement.isNull()) { emitResult(); return; } QDomElement principalCollectionSetElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("principal-collection-set")); if (principalCollectionSetElement.isNull()) { emitResult(); return; } QDomNodeList hrefNodes = principalCollectionSetElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("href")); for (int i = 0; i < hrefNodes.size(); ++i) { QDomElement hrefElement = hrefNodes.at(i).toElement(); QString href = hrefElement.text(); QUrl url = mUrl.url(); if (href.startsWith(QLatin1Char('/'))) { // href is only a path, use request url to complete url.setPath(href, QUrl::TolerantMode); } else { // href is a complete url QUrl tmpUrl(href); tmpUrl.setUserName(url.userName()); tmpUrl.setPassword(url.password()); url = tmpUrl; } QDomDocument principalPropertySearchQuery; buildReportQuery(principalPropertySearchQuery); DavJob *reportJob = DavManager::self()->createReportJob(url, principalPropertySearchQuery); connect(reportJob, &DavJob::result, this, &DavPrincipalSearchJob::principalPropertySearchFinished); ++mPrincipalPropertySearchSubJobCount; reportJob->start(); } } void DavPrincipalSearchJob::principalPropertySearchFinished(KJob *job) { --mPrincipalPropertySearchSubJobCount; if (job->error() && !mPrincipalPropertySearchSubJobSuccessful) { setError(job->error()); setErrorText(job->errorText()); if (mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } - DavJob *davJob = qobject_cast(job); + DavJob *davJob = static_cast(job); - const int responseCode = davJob->responseCode(); + const int responseCode = davJob->httpStatusCode(); if (responseCode > 499 && responseCode < 600 && !mPrincipalPropertySearchSubJobSuccessful) { // Server-side error, unrecoverable - setLatestResponseCode(responseCode); - setError(ERR_SERVER_UNRECOVERABLE); - setJobErrorText(davJob->errorText()); - setJobError(davJob->error()); - setErrorTextFromDavError(); + setErrorFromJob(davJob, ERR_SERVER_UNRECOVERABLE); if (mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } else if (responseCode > 399 && responseCode < 500 && !mPrincipalPropertySearchSubJobSuccessful) { - setLatestResponseCode(responseCode); - setError(ERR_PROBLEM_WITH_REQUEST); - setJobErrorText(davJob->errorText()); - setJobError(davJob->error()); - setErrorTextFromDavError(); - + setErrorFromJob(davJob); if (mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } if (!mPrincipalPropertySearchSubJobSuccessful) { setError(0); // nope, everything went fine mPrincipalPropertySearchSubJobSuccessful = true; } /* * Extract infos from a document like the following: * * * * http://www.example.com/users/jdoe * * * John Doe * * HTTP/1.1 200 OK * * */ const QDomDocument document = davJob->response(); const QDomElement documentElement = document.documentElement(); QDomElement responseElement = Utils::firstChildElementNS(documentElement, QStringLiteral("DAV:"), QStringLiteral("response")); if (responseElement.isNull()) { if (mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } // check for the valid propstat, without giving up on first error QDomElement propstatElement; { const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); const int propStatsEnd(propstats.length()); for (int i = 0; i < propStatsEnd; ++i) { const QDomElement propstatCandidate = propstats.item(i).toElement(); const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); if (statusElement.text().contains(QStringLiteral("200"))) { propstatElement = propstatCandidate; } } } if (propstatElement.isNull()) { if (mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); if (propElement.isNull()) { if (mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } // All requested properties are now under propElement, so let's find them typedef QPair PropertyPair; foreach (const PropertyPair &fetchProperty, mFetchProperties) { QDomNodeList fetchNodes = propElement.elementsByTagNameNS(fetchProperty.first, fetchProperty.second); for (int i = 0; i < fetchNodes.size(); ++i) { QDomElement fetchElement = fetchNodes.at(i).toElement(); Result result; result.propertyNamespace = fetchProperty.first; result.property = fetchProperty.second; result.value = fetchElement.text(); mResults << result; } } if (mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } } QList< DavPrincipalSearchJob::Result > DavPrincipalSearchJob::results() const { return mResults; } void DavPrincipalSearchJob::buildReportQuery(QDomDocument &query) { /* * Build a document like the following, where XXX will * be replaced by the properties the user want to fetch: * * * * * * * * FILTER * * * XXX * * */ QDomElement principalPropertySearch = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-property-search")); query.appendChild(principalPropertySearch); QDomElement propertySearch = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("property-search")); principalPropertySearch.appendChild(propertySearch); QDomElement prop = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); propertySearch.appendChild(prop); if (mType == DisplayName) { QDomElement displayName = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname")); prop.appendChild(displayName); } else if (mType == EmailAddress) { QDomElement calendarUserAddressSet = query.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-user-address-set")); prop.appendChild(calendarUserAddressSet); //QDomElement hrefElement = query.createElementNS( "DAV:", "href" ); //prop.appendChild( hrefElement ); } QDomElement match = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("match")); propertySearch.appendChild(match); QDomText propFilter = query.createTextNode(mFilter); match.appendChild(propFilter); prop = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); principalPropertySearch.appendChild(prop); typedef QPair PropertyPair; foreach (const PropertyPair &fetchProperty, mFetchProperties) { QDomElement elem = query.createElementNS(fetchProperty.first, fetchProperty.second); prop.appendChild(elem); } }