KDAV

davprincipalhomesetsfetchjob.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Grégory Oestreicher <greg@kamago.net>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "davprincipalhomesetsfetchjob.h"
8#include "davjobbase_p.h"
9
10#include "daverror.h"
11#include "davmanager_p.h"
12#include "davprotocolbase_p.h"
13#include "protocolinfo.h"
14#include "utils_p.h"
15
16#include <KIO/DavJob>
17#include <KIO/Job>
18
19using namespace KDAV;
20
21namespace KDAV
22{
23class DavPrincipalHomeSetsFetchJobPrivate : public DavJobBasePrivate
24{
25public:
26 void davJobFinished(KJob *job);
27 /**
28 * Start the fetch process.
29 *
30 * There may be two rounds necessary if the first request
31 * does not returns the home sets, but only the current-user-principal
32 * or the principal-URL. The bool flag is here to prevent requesting
33 * those last two on each request, as they are only fetched in
34 * the first round.
35 *
36 * @param fetchHomeSetsOnly If set to true the request will not include
37 * the current-user-principal and principal-URL props.
38 */
39 void fetchHomeSets(bool fetchHomeSetsOnly);
40
41 DavUrl mUrl;
42 QStringList mHomeSets;
43};
44}
45
47 : DavJobBase(new DavPrincipalHomeSetsFetchJobPrivate, parent)
48{
50 d->mUrl = url;
51}
52
54{
56 d->fetchHomeSets(false);
57}
58
59void DavPrincipalHomeSetsFetchJobPrivate::fetchHomeSets(bool homeSetsOnly)
60{
61 QDomDocument document;
62
63 QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind"));
64 document.appendChild(propfindElement);
65
66 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
67 propfindElement.appendChild(propElement);
68
69 const QString homeSet = ProtocolInfo::principalHomeSet(mUrl.protocol());
70 const QString homeSetNS = ProtocolInfo::principalHomeSetNS(mUrl.protocol());
71 propElement.appendChild(document.createElementNS(homeSetNS, homeSet));
72
73 if (!homeSetsOnly) {
74 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-principal")));
75 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-URL")));
76 }
77
78 KIO::DavJob *job = DavManager::self()->createPropFindJob(mUrl.url(), document.toString(), QStringLiteral("0"));
79 job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true"));
80 QObject::connect(job, &KIO::DavJob::result, q_ptr, [this](KJob *job) {
81 davJobFinished(job);
82 });
83}
84
90
91void DavPrincipalHomeSetsFetchJobPrivate::davJobFinished(KJob *job)
92{
93 KIO::DavJob *davJob = qobject_cast<KIO::DavJob *>(job);
94 const QString responseCodeStr = davJob->queryMetaData(QStringLiteral("responsecode"));
95 const int responseCode = responseCodeStr.isEmpty() ? 0 : responseCodeStr.toInt();
96
97 // KIO::DavJob does not set error() even if the HTTP status code is a 4xx or a 5xx
98 if (davJob->error() || (responseCode >= 400 && responseCode < 600)) {
99 QString err;
100 if (davJob->error() && davJob->error() != KIO::ERR_WORKER_DEFINED) {
101 err = KIO::buildErrorString(davJob->error(), davJob->errorText());
102 } else {
103 err = davJob->errorText();
104 }
105
106 setLatestResponseCode(responseCode);
107 setError(ERR_PROBLEM_WITH_REQUEST);
108 setJobErrorText(davJob->errorText());
109 setJobError(davJob->error());
110 setErrorTextFromDavError();
111
112 emitResult();
113 return;
114 }
115
116 /*
117 * Extract information from a document like the following (if no homeset is defined) :
118 *
119 * <D:multistatus xmlns:D="DAV:">
120 * <D:response xmlns:D="DAV:">
121 * <D:href xmlns:D="DAV:">/dav/</D:href>
122 * <D:propstat xmlns:D="DAV:">
123 * <D:status xmlns:D="DAV:">HTTP/1.1 200 OK</D:status>
124 * <D:prop xmlns:D="DAV:">
125 * <D:current-user-principal xmlns:D="DAV:">
126 * <D:href xmlns:D="DAV:">/principals/users/gdacoin/</D:href>
127 * </D:current-user-principal>
128 * </D:prop>
129 * </D:propstat>
130 * <D:propstat xmlns:D="DAV:">
131 * <D:status xmlns:D="DAV:">HTTP/1.1 404 Not Found</D:status>
132 * <D:prop xmlns:D="DAV:">
133 * <principal-URL xmlns="DAV:"/>
134 * <calendar-home-set xmlns="urn:ietf:params:xml:ns:caldav"/>
135 * </D:prop>
136 * </D:propstat>
137 * </D:response>
138 * </D:multistatus>
139 *
140 * Or like this (if the homeset is defined):
141 *
142 * <?xml version="1.0" encoding="utf-8" ?>
143 * <multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
144 * <response>
145 * <href>/principals/users/greg%40kamago.net/</href>
146 * <propstat>
147 * <prop>
148 * <C:calendar-home-set>
149 * <href>/greg%40kamago.net/</href>
150 * </C:calendar-home-set>
151 * </prop>
152 * <status>HTTP/1.1 200 OK</status>
153 * </propstat>
154 * </response>
155 * </multistatus>
156 */
157
158 const QString homeSet = ProtocolInfo::principalHomeSet(mUrl.protocol());
159 const QString homeSetNS = ProtocolInfo::principalHomeSetNS(mUrl.protocol());
160 QString nextRoundHref; // The content of the href element that will be used if no homeset was found.
161 // This is either given by current-user-principal or by principal-URL.
162
163 QDomDocument document;
164 document.setContent(davJob->responseData(), QDomDocument::ParseOption::UseNamespaceProcessing);
165 const QDomElement multistatusElement = document.documentElement();
166
167 QDomElement responseElement = Utils::firstChildElementNS(multistatusElement, QStringLiteral("DAV:"), QStringLiteral("response"));
168 while (!responseElement.isNull()) {
169 QDomElement propstatElement;
170
171 // check for the valid propstat, without giving up on first error
172 {
173 const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat"));
174 for (int i = 0; i < propstats.length(); ++i) {
175 const QDomElement propstatCandidate = propstats.item(i).toElement();
176 const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status"));
177 if (statusElement.text().contains(QLatin1String("200"))) {
178 propstatElement = propstatCandidate;
179 }
180 }
181 }
182
183 if (propstatElement.isNull()) {
184 responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
185 continue;
186 }
187
188 // extract home sets
189 const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop"));
190 const QDomElement homeSetElement = Utils::firstChildElementNS(propElement, homeSetNS, homeSet);
191
192 if (!homeSetElement.isNull()) {
193 QDomElement hrefElement = Utils::firstChildElementNS(homeSetElement, QStringLiteral("DAV:"), QStringLiteral("href"));
194
195 while (!hrefElement.isNull()) {
196 const QString href = hrefElement.text();
197 if (!mHomeSets.contains(href)) {
198 mHomeSets << href;
199 }
200
201 hrefElement = Utils::nextSiblingElementNS(hrefElement, QStringLiteral("DAV:"), QStringLiteral("href"));
202 }
203 } else {
204 // Trying to get the principal url, given either by current-user-principal or principal-URL
205 QDomElement urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("current-user-principal"));
206 if (urlHolder.isNull()) {
207 urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("principal-URL"));
208 }
209
210 if (!urlHolder.isNull()) {
211 // Getting the href that will be used for the next round
212 QDomElement hrefElement = Utils::firstChildElementNS(urlHolder, QStringLiteral("DAV:"), QStringLiteral("href"));
213 if (!hrefElement.isNull()) {
214 nextRoundHref = hrefElement.text();
215 }
216 }
217 }
218
219 responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
220 }
221
222 /*
223 * Now either we got one or more homesets, or we got an href for the next round
224 * or nothing can be found by this job.
225 * If we have homesets, we're done here and can notify the caller.
226 * Else we must ensure that we have an href for the next round.
227 */
228 if (!mHomeSets.isEmpty() || nextRoundHref.isEmpty()) {
229 emitResult();
230 } else {
231 QUrl nextRoundUrl(mUrl.url());
232
233 if (nextRoundHref.startsWith(QLatin1Char('/'))) {
234 // nextRoundHref is only a path, use request url to complete
235 nextRoundUrl.setPath(nextRoundHref, QUrl::TolerantMode);
236 } else {
237 // href is a complete url
238 nextRoundUrl = QUrl::fromUserInput(nextRoundHref);
239 nextRoundUrl.setUserName(mUrl.url().userName());
240 nextRoundUrl.setPassword(mUrl.url().password());
241 }
242
243 mUrl.setUrl(nextRoundUrl);
244 // And one more round, fetching only homesets
245 fetchHomeSets(true);
246 }
247}
248
249#include "moc_davprincipalhomesetsfetchjob.cpp"
base class for the jobs used by the resource.
Definition davjobbase.h:27
A job that fetches home sets for a principal.
DavPrincipalHomeSetsFetchJob(const DavUrl &url, QObject *parent=nullptr)
Creates a new DAV principals home sets fetch job.
QStringList homeSets() const
Returns the found home sets.
A helper class to combine URL and protocol of a DAV URL.
Definition davurl.h:27
QUrl url() const
Returns the URL that identifies the DAV object.
Definition davurl.cpp:45
Protocol protocol() const
Returns the DAV protocol dialect that is used to retrieve the DAV object.
Definition davurl.cpp:55
void setUrl(const QUrl &url)
Sets the url that identifies the DAV object.
Definition davurl.cpp:40
QByteArray responseData() const
void addMetaData(const QMap< QString, QString > &values)
QString queryMetaData(const QString &key)
int error() const
void result(KJob *job)
QString errorText() const
KDAV_EXPORT QString principalHomeSetNS(KDAV::Protocol protocol)
Returns the principal home set namespace of protocol.
KDAV_EXPORT QString principalHomeSet(KDAV::Protocol protocol)
Returns the principal home set of protocol.
The KDAV namespace.
KIOCORE_EXPORT QString buildErrorString(int errorCode, const QString &errorText)
ERR_WORKER_DEFINED
QDomElement createElementNS(const QString &nsURI, const QString &qName)
QDomElement documentElement() const const
ParseResult setContent(QAnyStringView text, ParseOptions options)
QString toString(int indent) const const
QDomNodeList elementsByTagNameNS(const QString &nsURI, const QString &localName) const const
QString text() const const
QDomNode appendChild(const QDomNode &newChild)
bool isNull() const const
QDomElement toElement() const const
QDomNode item(int index) const const
int length() const const
bool isEmpty() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
TolerantMode
QUrl fromUserInput(const QString &userInput, const QString &workingDirectory, UserInputResolutionOptions options)
QString password(ComponentFormattingOptions options) const const
QString userName(ComponentFormattingOptions options) const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:34 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.