Libkdav2

davdiscoveryjob.cpp
1 /*
2  Copyright (c) 2018 Christian Mollekopf <[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 "davdiscoveryjob.h"
20 
21 #include "libkdav2_debug.h"
22 #include "davmanager.h"
23 #include "davprotocolbase.h"
24 #include "daverror.h"
25 #include "utils.h"
26 #include "davjob.h"
27 
28 using namespace KDAV2;
29 
30 DavDiscoveryJob::DavDiscoveryJob(const DavUrl &davUrl, const QString &wellKnownSuffix, QObject *parent)
31  : DavJobBase(parent), mUrl(davUrl)
32 {
33  auto url = davUrl.url();
34  if (!url.toString().contains("/.well-known/")) {
35  url.setPath(url.path() + "/.well-known/" + wellKnownSuffix);
36  mUrl.setUrl(url);
37  }
38 }
39 
41 {
42  QDomDocument document;
43 
44  QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind"));
45  document.appendChild(propfindElement);
46 
47  QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
48  propfindElement.appendChild(propElement);
49 
50  propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-principal")));
51  propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-URL")));
52 
53  DavJob *job = DavManager::self()->createPropFindJob(mUrl.url(), document, QStringLiteral("0"));
54  connect(job, &DavJob::result, this, &DavDiscoveryJob::davJobFinished);
55 }
56 
58 {
59  return mUrl.url();
60 }
61 
62 void DavDiscoveryJob::davJobFinished(KJob *job)
63 {
64  DavJob *davJob = static_cast<DavJob*>(job);
65 
66  if (davJob->error()) {
67  //Retry on the root uri on 404, otherwise fail
68  if (davJob->httpStatusCode() == 404 && davJob->url().path() != QStringLiteral("/")) {
69  auto url = mUrl.url();
70  url.setPath("/");
71  mUrl.setUrl(url);
72  qCDebug(KDAV2_LOG) << "Retrying on root url" << url;
73  start();
74  return;
75  }
76  setErrorFromJob(davJob);
77  emitResult();
78  return;
79  }
80  mUrl.setUrl(davJob->url());
81 
82  const QDomDocument document = davJob->response();
83  const QDomElement multistatusElement = document.documentElement();
84 
85 
86  const QString principalHref = [&] {
87  QDomElement responseElement = Utils::firstChildElementNS(multistatusElement, QStringLiteral("DAV:"), QStringLiteral("response"));
88  while (!responseElement.isNull()) {
89 
90  const QDomElement propstatElement = [&] {
91  // check for the valid propstat, without giving up on first error
92  const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat"));
93  for (int i = 0; i < propstats.length(); ++i) {
94  const QDomElement propstatCandidate = propstats.item(i).toElement();
95  const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status"));
96  if (statusElement.text().contains(QLatin1String("200"))) {
97  return propstatCandidate;
98  }
99  }
100  return QDomElement{};
101  }();
102 
103  if (propstatElement.isNull()) {
104  responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
105  continue;
106  }
107 
108  // extract home sets
109  const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop"));
110 
111  // Trying to get the principal url, given either by current-user-principal or principal-URL
112  QDomElement urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("current-user-principal"));
113  if (urlHolder.isNull()) {
114  urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("principal-URL"));
115  }
116 
117  if (!urlHolder.isNull()) {
118  // Getting the href that will be used for the next round
119  const QDomElement hrefElement = Utils::firstChildElementNS(urlHolder, QStringLiteral("DAV:"), QStringLiteral("href"));
120  if (!hrefElement.isNull()) {
121  return hrefElement.text();
122  }
123  }
124 
125  responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
126  }
127  return QString{};
128  }();
129 
130  QUrl principalUrl(mUrl.url());
131 
132  if (principalHref.startsWith(QLatin1Char('/'))) {
133  // principalHref is only a path, use request url to complete
134  principalUrl.setPath(principalHref, QUrl::TolerantMode);
135  } else {
136  // href is a complete url
137  principalUrl = QUrl::fromUserInput(principalHref);
138  principalUrl.setUserName(mUrl.url().userName());
139  principalUrl.setPassword(mUrl.url().password());
140  }
141 
142  mUrl.setUrl(principalUrl);
143  emitResult();
144 }
QString text() const const
QDomElement toElement() const const
void start() override
Starts the job.
static DavManager * self()
Returns the global instance of the DAV manager.
Definition: davmanager.cpp:52
QDomElement createElementNS(const QString &nsURI, const QString &qName)
QUrl url() const
Returns the found principal url.
A helper class to combine url and protocol of a DAV url.
Definition: davurl.h:35
bool isNull() const const
QString userName(QUrl::ComponentFormattingOptions options) const const
TolerantMode
QDomNodeList elementsByTagNameNS(const QString &nsURI, const QString &localName) const const
base class for the jobs used by the resource.
Definition: davjobbase.h:37
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
DavDiscoveryJob(const DavUrl &url, const QString &wellKnownSuffix, QObject *parent=nullptr)
Discover the url of the server.
QString toString(QUrl::FormattingOptions options) const const
QDomNode item(int index) const const
QDomElement documentElement() const const
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
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString path(QUrl::ComponentFormattingOptions options) const const
QDomNode appendChild(const QDomNode &newChild)
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
void setPath(const QString &path, QUrl::ParsingMode mode)
void setUrl(const QUrl &url)
Sets the url that identifies the DAV object.
Definition: davurl.cpp:36
QString password(QUrl::ComponentFormattingOptions options) const const
void emitResult()
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 contains(QChar ch, Qt::CaseSensitivity cs) const const
QUrl url() const
Returns the url that identifies the DAV object.
Definition: davurl.cpp:41
int length() const const
DavJob * createPropFindJob(const QUrl &url, const QDomDocument &document, const QString &depth=QStringLiteral("1"))
Returns a preconfigured DAV PROPFIND job.
Definition: davmanager.cpp:66
QUrl fromUserInput(const QString &userInput)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Wed Aug 10 2022 04:09:53 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.