Libkdav2

davprincipalhomesetsfetchjob.cpp
1 /*
2  Copyright (c) 2010 GrĂ©gory Oestreicher <[email protected]>
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18 
19 #include "davprincipalhomesetsfetchjob.h"
20 
21 #include "davmanager.h"
22 #include "davprotocolbase.h"
23 #include "daverror.h"
24 #include "utils.h"
25 #include "davjob.h"
26 
27 using namespace KDAV2;
28 
30  : DavJobBase(parent), mUrl(url)
31 {
32 }
33 
35 {
36  fetchHomeSets(false);
37 }
38 
39 void DavPrincipalHomeSetsFetchJob::fetchHomeSets(bool homeSetsOnly)
40 {
41  QDomDocument document;
42 
43  QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind"));
44  document.appendChild(propfindElement);
45 
46  QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
47  propfindElement.appendChild(propElement);
48 
49  const QString homeSet = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSet();
50  const QString homeSetNS = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSetNS();
51  propElement.appendChild(document.createElementNS(homeSetNS, homeSet));
52 
53  if (!homeSetsOnly) {
54  propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-principal")));
55  propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-URL")));
56  }
57 
58  DavJob *job = DavManager::self()->createPropFindJob(mUrl.url(), document, QStringLiteral("0"));
59  connect(job, &DavJob::result, this, &DavPrincipalHomeSetsFetchJob::davJobFinished);
60 }
61 
63 {
64  return mHomeSets;
65 }
66 
67 QUrl DavPrincipalHomeSetsFetchJob::url() const
68 {
69  return mUrl.url();
70 }
71 void DavPrincipalHomeSetsFetchJob::davJobFinished(KJob *job)
72 {
73  auto davJob = static_cast<DavJob*>(job);
74  if (davJob->error()) {
75  setErrorFromJob(davJob);
76  emitResult();
77  return;
78  }
79 
80  /*
81  * Extract information from a document like the following (if no homeset is defined) :
82  *
83  * <D:multistatus xmlns:D="DAV:">
84  * <D:response xmlns:D="DAV:">
85  * <D:href xmlns:D="DAV:">/dav/</D:href>
86  * <D:propstat xmlns:D="DAV:">
87  * <D:status xmlns:D="DAV:">HTTP/1.1 200 OK</D:status>
88  * <D:prop xmlns:D="DAV:">
89  * <D:current-user-principal xmlns:D="DAV:">
90  * <D:href xmlns:D="DAV:">/principals/users/gdacoin/</D:href>
91  * </D:current-user-principal>
92  * </D:prop>
93  * </D:propstat>
94  * <D:propstat xmlns:D="DAV:">
95  * <D:status xmlns:D="DAV:">HTTP/1.1 404 Not Found</D:status>
96  * <D:prop xmlns:D="DAV:">
97  * <principal-URL xmlns="DAV:"/>
98  * <calendar-home-set xmlns="urn:ietf:params:xml:ns:caldav"/>
99  * </D:prop>
100  * </D:propstat>
101  * </D:response>
102  * </D:multistatus>
103  *
104  * Or like this (if the homeset is defined):
105  *
106  * <?xml version="1.0" encoding="utf-8" ?>
107  * <multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
108  * <response>
109  * <href>/principals/users/greg%40kamago.net/</href>
110  * <propstat>
111  * <prop>
112  * <C:calendar-home-set>
113  * <href>/greg%40kamago.net/</href>
114  * </C:calendar-home-set>
115  * </prop>
116  * <status>HTTP/1.1 200 OK</status>
117  * </propstat>
118  * </response>
119  * </multistatus>
120  */
121 
122  mUrl.setUrl(davJob->url());
123  const QString homeSet = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSet();
124  const QString homeSetNS = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSetNS();
125  QString nextRoundHref; // The content of the href element that will be used if no homeset was found.
126  // This is either given by current-user-principal or by principal-URL.
127 
128  const QDomDocument document = davJob->response();
129  const QDomElement multistatusElement = document.documentElement();
130 
131  QDomElement responseElement = Utils::firstChildElementNS(multistatusElement, QStringLiteral("DAV:"), QStringLiteral("response"));
132  while (!responseElement.isNull()) {
133 
134  const QDomElement propstatElement = [&] {
135  // check for the valid propstat, without giving up on first error
136  const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat"));
137  for (int i = 0; i < propstats.length(); ++i) {
138  const QDomElement propstatCandidate = propstats.item(i).toElement();
139  const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status"));
140  if (statusElement.text().contains(QLatin1String("200"))) {
141  return propstatCandidate;
142  }
143  }
144  return QDomElement{};
145  }();
146 
147  if (propstatElement.isNull()) {
148  responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
149  continue;
150  }
151 
152  // extract home sets
153  const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop"));
154  const QDomElement homeSetElement = Utils::firstChildElementNS(propElement, homeSetNS, homeSet);
155 
156  if (!homeSetElement.isNull()) {
157  QDomElement hrefElement = Utils::firstChildElementNS(homeSetElement, QStringLiteral("DAV:"), QStringLiteral("href"));
158 
159  while (!hrefElement.isNull()) {
160  const QString href = hrefElement.text();
161  if (!mHomeSets.contains(href)) {
162  mHomeSets << href;
163  }
164 
165  hrefElement = Utils::nextSiblingElementNS(hrefElement, QStringLiteral("DAV:"), QStringLiteral("href"));
166  }
167  } else {
168  // Trying to get the principal url, given either by current-user-principal or principal-URL
169  QDomElement urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("current-user-principal"));
170  if (urlHolder.isNull()) {
171  urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("principal-URL"));
172  }
173 
174  if (!urlHolder.isNull()) {
175  // Getting the href that will be used for the next round
176  QDomElement hrefElement = Utils::firstChildElementNS(urlHolder, QStringLiteral("DAV:"), QStringLiteral("href"));
177  if (!hrefElement.isNull()) {
178  nextRoundHref = hrefElement.text();
179  }
180  }
181  }
182 
183  responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
184  }
185 
186  /*
187  * Now either we got one or more homesets, or we got an href for the next round
188  * or nothing can be found by this job.
189  * If we have homesets, we're done here and can notify the caller.
190  * Else we must ensure that we have an href for the next round.
191  */
192  if (!mHomeSets.isEmpty() || nextRoundHref.isEmpty()) {
193  emitResult();
194  } else {
195  QUrl nextRoundUrl(mUrl.url());
196 
197  if (nextRoundHref.startsWith(QLatin1Char('/'))) {
198  // nextRoundHref is only a path, use request url to complete
199  nextRoundUrl.setPath(nextRoundHref, QUrl::TolerantMode);
200  } else {
201  // href is a complete url
202  nextRoundUrl = QUrl::fromUserInput(nextRoundHref);
203  nextRoundUrl.setUserName(mUrl.url().userName());
204  nextRoundUrl.setPassword(mUrl.url().password());
205  }
206 
207  mUrl.setUrl(nextRoundUrl);
208  // And one more round, fetching only homesets
209  fetchHomeSets(true);
210  }
211 }
DavJob * createPropFindJob(const QUrl &url, const QDomDocument &document, const QString &depth=QStringLiteral("1"))
Returns a preconfigured DAV PROPFIND job.
Definition: davmanager.cpp:66
QUrl url() const
Returns the url that identifies the DAV object.
Definition: davurl.cpp:41
QDomNode item(int index) const const
QDomNodeList elementsByTagNameNS(const QString &nsURI, const QString &localName) const const
void emitResult()
QString userName(QUrl::ComponentFormattingOptions options) const const
QDomNode appendChild(const QDomNode &newChild)
DavPrincipalHomeSetsFetchJob(const DavUrl &url, QObject *parent=nullptr)
Creates a new dav principals home sets fetch job.
static DavManager * self()
Returns the global instance of the DAV manager.
Definition: davmanager.cpp:52
base class for the jobs used by the resource.
Definition: davjobbase.h:37
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
TolerantMode
QDomElement documentElement() const const
QDomElement createElementNS(const QString &nsURI, const QString &qName)
void setUrl(const QUrl &url)
Sets the url that identifies the DAV object.
Definition: davurl.cpp:36
QUrl fromUserInput(const QString &userInput)
A helper class to combine url and protocol of a DAV url.
Definition: davurl.h:35
QDomElement toElement() const const
void setPath(const QString &path, QUrl::ParsingMode mode)
QString text() const const
const DavProtocolBase * davProtocol(Protocol protocol)
Returns the DAV protocol dialect object for the given DAV protocol.
Definition: davmanager.cpp:140
bool isEmpty() const const
bool isEmpty() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QStringList homeSets() const
Returns the found home sets.
QString password(QUrl::ComponentFormattingOptions options) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QDomElement KPIMKDAV2_EXPORT firstChildElementNS(const QDomElement &parent, const QString &namespaceUri, const QString &tagName)
Returns the first child element of parent that has the given tagName and is part of the namespaceUri...
Definition: utils.cpp:37
bool isNull() const const
Protocol protocol() const
Returns the DAV protocol dialect that is used to retrieve the DAV object.
Definition: davurl.cpp:51
QDomElement KPIMKDAV2_EXPORT nextSiblingElementNS(const QDomElement &element, const QString &namespaceUri, const QString &tagName)
Returns the next sibling element of element that has the given tagName and is part of the namespaceUr...
Definition: utils.cpp:51
void setErrorFromJob(DavJob *, ErrorNumber jobErrorCode=ERR_PROBLEM_WITH_REQUEST)
Set the error of this job from a failed DavJob (executed by this job).
Definition: davjobbase.cpp:99
int length() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Jan 27 2022 23:10:33 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.