File indexing completed on 2024-11-17 04:44:02
0001 /* 0002 Copyright (c) 2010 Grégory Oestreicher <greg@kamago.net> 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 "davprincipalhomesetsfetchjob.h" 0020 0021 #include "davmanager.h" 0022 #include "davprotocolbase.h" 0023 #include "daverror.h" 0024 #include "utils.h" 0025 #include "davjob.h" 0026 0027 using namespace KDAV2; 0028 0029 DavPrincipalHomeSetsFetchJob::DavPrincipalHomeSetsFetchJob(const DavUrl &url, QObject *parent) 0030 : DavJobBase(parent), mUrl(url) 0031 { 0032 } 0033 0034 void DavPrincipalHomeSetsFetchJob::start() 0035 { 0036 fetchHomeSets(false); 0037 } 0038 0039 void DavPrincipalHomeSetsFetchJob::fetchHomeSets(bool homeSetsOnly) 0040 { 0041 QDomDocument document; 0042 0043 QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); 0044 document.appendChild(propfindElement); 0045 0046 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); 0047 propfindElement.appendChild(propElement); 0048 0049 const QString homeSet = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSet(); 0050 const QString homeSetNS = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSetNS(); 0051 propElement.appendChild(document.createElementNS(homeSetNS, homeSet)); 0052 0053 if (!homeSetsOnly) { 0054 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-principal"))); 0055 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-URL"))); 0056 } 0057 0058 DavJob *job = DavManager::self()->createPropFindJob(mUrl.url(), document, QStringLiteral("0")); 0059 connect(job, &DavJob::result, this, &DavPrincipalHomeSetsFetchJob::davJobFinished); 0060 } 0061 0062 QStringList DavPrincipalHomeSetsFetchJob::homeSets() const 0063 { 0064 return mHomeSets; 0065 } 0066 0067 QUrl DavPrincipalHomeSetsFetchJob::url() const 0068 { 0069 return mUrl.url(); 0070 } 0071 void DavPrincipalHomeSetsFetchJob::davJobFinished(KJob *job) 0072 { 0073 auto davJob = static_cast<DavJob*>(job); 0074 if (davJob->error()) { 0075 setErrorFromJob(davJob); 0076 emitResult(); 0077 return; 0078 } 0079 0080 /* 0081 * Extract information from a document like the following (if no homeset is defined) : 0082 * 0083 * <D:multistatus xmlns:D="DAV:"> 0084 * <D:response xmlns:D="DAV:"> 0085 * <D:href xmlns:D="DAV:">/dav/</D:href> 0086 * <D:propstat xmlns:D="DAV:"> 0087 * <D:status xmlns:D="DAV:">HTTP/1.1 200 OK</D:status> 0088 * <D:prop xmlns:D="DAV:"> 0089 * <D:current-user-principal xmlns:D="DAV:"> 0090 * <D:href xmlns:D="DAV:">/principals/users/gdacoin/</D:href> 0091 * </D:current-user-principal> 0092 * </D:prop> 0093 * </D:propstat> 0094 * <D:propstat xmlns:D="DAV:"> 0095 * <D:status xmlns:D="DAV:">HTTP/1.1 404 Not Found</D:status> 0096 * <D:prop xmlns:D="DAV:"> 0097 * <principal-URL xmlns="DAV:"/> 0098 * <calendar-home-set xmlns="urn:ietf:params:xml:ns:caldav"/> 0099 * </D:prop> 0100 * </D:propstat> 0101 * </D:response> 0102 * </D:multistatus> 0103 * 0104 * Or like this (if the homeset is defined): 0105 * 0106 * <?xml version="1.0" encoding="utf-8" ?> 0107 * <multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav"> 0108 * <response> 0109 * <href>/principals/users/greg%40kamago.net/</href> 0110 * <propstat> 0111 * <prop> 0112 * <C:calendar-home-set> 0113 * <href>/greg%40kamago.net/</href> 0114 * </C:calendar-home-set> 0115 * </prop> 0116 * <status>HTTP/1.1 200 OK</status> 0117 * </propstat> 0118 * </response> 0119 * </multistatus> 0120 */ 0121 0122 mUrl.setUrl(davJob->url()); 0123 const QString homeSet = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSet(); 0124 const QString homeSetNS = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSetNS(); 0125 QString nextRoundHref; // The content of the href element that will be used if no homeset was found. 0126 // This is either given by current-user-principal or by principal-URL. 0127 0128 const QDomDocument document = davJob->response(); 0129 const QDomElement multistatusElement = document.documentElement(); 0130 0131 QDomElement responseElement = Utils::firstChildElementNS(multistatusElement, QStringLiteral("DAV:"), QStringLiteral("response")); 0132 while (!responseElement.isNull()) { 0133 0134 const QDomElement propstatElement = [&] { 0135 // check for the valid propstat, without giving up on first error 0136 const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); 0137 for (int i = 0; i < propstats.length(); ++i) { 0138 const QDomElement propstatCandidate = propstats.item(i).toElement(); 0139 const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); 0140 if (statusElement.text().contains(QLatin1String("200"))) { 0141 return propstatCandidate; 0142 } 0143 } 0144 return QDomElement{}; 0145 }(); 0146 0147 if (propstatElement.isNull()) { 0148 responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); 0149 continue; 0150 } 0151 0152 // extract home sets 0153 const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); 0154 const QDomElement homeSetElement = Utils::firstChildElementNS(propElement, homeSetNS, homeSet); 0155 0156 if (!homeSetElement.isNull()) { 0157 QDomElement hrefElement = Utils::firstChildElementNS(homeSetElement, QStringLiteral("DAV:"), QStringLiteral("href")); 0158 0159 while (!hrefElement.isNull()) { 0160 const QString href = hrefElement.text(); 0161 if (!mHomeSets.contains(href)) { 0162 mHomeSets << href; 0163 } 0164 0165 hrefElement = Utils::nextSiblingElementNS(hrefElement, QStringLiteral("DAV:"), QStringLiteral("href")); 0166 } 0167 } else { 0168 // Trying to get the principal url, given either by current-user-principal or principal-URL 0169 QDomElement urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("current-user-principal")); 0170 if (urlHolder.isNull()) { 0171 urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("principal-URL")); 0172 } 0173 0174 if (!urlHolder.isNull()) { 0175 // Getting the href that will be used for the next round 0176 QDomElement hrefElement = Utils::firstChildElementNS(urlHolder, QStringLiteral("DAV:"), QStringLiteral("href")); 0177 if (!hrefElement.isNull()) { 0178 nextRoundHref = hrefElement.text(); 0179 } 0180 } 0181 } 0182 0183 responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); 0184 } 0185 0186 /* 0187 * Now either we got one or more homesets, or we got an href for the next round 0188 * or nothing can be found by this job. 0189 * If we have homesets, we're done here and can notify the caller. 0190 * Else we must ensure that we have an href for the next round. 0191 */ 0192 if (!mHomeSets.isEmpty() || nextRoundHref.isEmpty()) { 0193 emitResult(); 0194 } else { 0195 QUrl nextRoundUrl(mUrl.url()); 0196 0197 if (nextRoundHref.startsWith(QLatin1Char('/'))) { 0198 // nextRoundHref is only a path, use request url to complete 0199 nextRoundUrl.setPath(nextRoundHref, QUrl::TolerantMode); 0200 } else { 0201 // href is a complete url 0202 nextRoundUrl = QUrl::fromUserInput(nextRoundHref); 0203 nextRoundUrl.setUserName(mUrl.url().userName()); 0204 nextRoundUrl.setPassword(mUrl.url().password()); 0205 } 0206 0207 mUrl.setUrl(nextRoundUrl); 0208 // And one more round, fetching only homesets 0209 fetchHomeSets(true); 0210 } 0211 }