File indexing completed on 2024-11-17 04:44:01

0001 /*
0002     Copyright (c) 2018 Christian Mollekopf <mollekopf@kolabsys.com>
0003 
0004     This program is free software; you can redistribute it and/or modify
0005     it under the terms of the GNU General Public License as published by
0006     the Free Software Foundation; either version 2 of the License, or
0007     (at your option) any later version.
0008 
0009     This program is distributed in the hope that it will be useful,
0010     but WITHOUT ANY WARRANTY; without even the implied warranty of
0011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0012     GNU General Public License for more details.
0013 
0014     You should have received a copy of the GNU General Public License
0015     along with this program; if not, write to the Free Software
0016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0017 */
0018 
0019 #include "davdiscoveryjob.h"
0020 
0021 #include "libkdav2_debug.h"
0022 #include "davmanager.h"
0023 #include "davprotocolbase.h"
0024 #include "daverror.h"
0025 #include "utils.h"
0026 #include "davjob.h"
0027 
0028 using namespace KDAV2;
0029 
0030 DavDiscoveryJob::DavDiscoveryJob(const DavUrl &davUrl, const QString &wellKnownSuffix, QObject *parent)
0031     : DavJobBase(parent), mUrl(davUrl)
0032 {
0033     auto url = davUrl.url();
0034     if (!url.toString().contains("/.well-known/")) {
0035         url.setPath(url.path() + "/.well-known/" + wellKnownSuffix);
0036         mUrl.setUrl(url);
0037     }
0038 }
0039 
0040 void DavDiscoveryJob::start()
0041 {
0042     QDomDocument document;
0043 
0044     QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind"));
0045     document.appendChild(propfindElement);
0046 
0047     QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
0048     propfindElement.appendChild(propElement);
0049 
0050     propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-principal")));
0051     propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-URL")));
0052 
0053     DavJob *job = DavManager::self()->createPropFindJob(mUrl.url(), document, QStringLiteral("0"));
0054     connect(job, &DavJob::result, this, &DavDiscoveryJob::davJobFinished);
0055 }
0056 
0057 QUrl DavDiscoveryJob::url() const
0058 {
0059     return mUrl.url();
0060 }
0061 
0062 void DavDiscoveryJob::davJobFinished(KJob *job)
0063 {
0064     DavJob *davJob = static_cast<DavJob*>(job);
0065 
0066     if (davJob->error()) {
0067         //Retry on the root uri on 404, otherwise fail
0068         if (davJob->httpStatusCode() == 404 && davJob->url().path() != QStringLiteral("/")) {
0069             auto url = mUrl.url();
0070             url.setPath("/");
0071             mUrl.setUrl(url);
0072             qCDebug(KDAV2_LOG) << "Retrying on root url" << url;
0073             start();
0074             return;
0075         }
0076         setErrorFromJob(davJob);
0077         emitResult();
0078         return;
0079     }
0080     mUrl.setUrl(davJob->url());
0081 
0082     const QDomDocument document = davJob->response();
0083     const QDomElement multistatusElement = document.documentElement();
0084 
0085 
0086     const QString principalHref = [&] {
0087         QDomElement responseElement = Utils::firstChildElementNS(multistatusElement, QStringLiteral("DAV:"), QStringLiteral("response"));
0088         while (!responseElement.isNull()) {
0089 
0090             const QDomElement propstatElement = [&] {
0091                 // check for the valid propstat, without giving up on first error
0092                 const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat"));
0093                 for (int i = 0; i < propstats.length(); ++i) {
0094                     const QDomElement propstatCandidate = propstats.item(i).toElement();
0095                     const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status"));
0096                     if (statusElement.text().contains(QLatin1String("200"))) {
0097                         return propstatCandidate;
0098                     }
0099                 }
0100                 return QDomElement{};
0101             }();
0102 
0103             if (propstatElement.isNull()) {
0104                 responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
0105                 continue;
0106             }
0107 
0108             // extract home sets
0109             const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop"));
0110 
0111             // Trying to get the principal url, given either by current-user-principal or principal-URL
0112             QDomElement urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("current-user-principal"));
0113             if (urlHolder.isNull()) {
0114                 urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("principal-URL"));
0115             }
0116 
0117             if (!urlHolder.isNull()) {
0118                 // Getting the href that will be used for the next round
0119                 const QDomElement hrefElement = Utils::firstChildElementNS(urlHolder, QStringLiteral("DAV:"), QStringLiteral("href"));
0120                 if (!hrefElement.isNull()) {
0121                     return hrefElement.text();
0122                 }
0123             }
0124 
0125             responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
0126         }
0127         return QString{};
0128     }();
0129 
0130     QUrl principalUrl(mUrl.url());
0131 
0132     if (principalHref.startsWith(QLatin1Char('/'))) {
0133         // principalHref is only a path, use request url to complete
0134         principalUrl.setPath(principalHref, QUrl::TolerantMode);
0135     } else {
0136         // href is a complete url
0137         principalUrl = QUrl::fromUserInput(principalHref);
0138         principalUrl.setUserName(mUrl.url().userName());
0139         principalUrl.setPassword(mUrl.url().password());
0140     }
0141 
0142     mUrl.setUrl(principalUrl);
0143     emitResult();
0144 }