File indexing completed on 2025-02-16 04:49:13
0001 /**************************************************************************** 0002 ** QWebDAV Library (qwebdavlib) - LGPL v2.1 0003 ** 0004 ** HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV) 0005 ** from June 2007 0006 ** http://tools.ietf.org/html/rfc4918 0007 ** 0008 ** Web Distributed Authoring and Versioning (WebDAV) SEARCH 0009 ** from November 2008 0010 ** http://tools.ietf.org/html/rfc5323 0011 ** 0012 ** Missing: 0013 ** - LOCK support 0014 ** - process WebDAV SEARCH responses 0015 ** 0016 ** Copyright (C) 2012 Martin Haller <martin.haller@rebnil.com> 0017 ** for QWebDAV library (qwebdavlib) version 1.0 0018 ** https://github.com/mhaller/qwebdavlib 0019 ** 0020 ** Copyright (C) 2012 Timo Zimmermann <meedav@timozimmermann.de> 0021 ** for portions from QWebdav plugin for MeeDav (LGPL v2.1) 0022 ** http://projects.developer.nokia.com/meedav/ 0023 ** 0024 ** Copyright (C) 2009-2010 Corentin Chary <corentin.chary@gmail.com> 0025 ** for portions from QWebdav - WebDAV lib for Qt4 (LGPL v2.1) 0026 ** http://xf.iksaif.net/dev/qwebdav.html 0027 ** 0028 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). 0029 ** for naturalCompare() (LGPL v2.1) 0030 ** http://qt.gitorious.org/qt/qt/blobs/4.7/src/gui/dialogs/qfilesystemmodel.cpp 0031 ** 0032 ** This library is free software; you can redistribute it and/or 0033 ** modify it under the terms of the GNU Library General Public 0034 ** License as published by the Free Software Foundation; either 0035 ** version 2 of the License, or (at your option) any later version. 0036 ** 0037 ** This library is distributed in the hope that it will be useful, 0038 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 0039 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0040 ** Library General Public License for more details. 0041 ** 0042 ** You should have received a copy of the GNU Library General Public License 0043 ** along with this library; see the file COPYING.LIB. If not, write to 0044 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0045 ** Boston, MA 02110-1301, USA. 0046 ** 0047 ** http://www.gnu.org/licenses/lgpl-2.1-standalone.html 0048 ** 0049 ****************************************************************************/ 0050 0051 #include "qwebdav.h" 0052 0053 #include <QDebug> 0054 #include <QTextStream> 0055 0056 Q_LOGGING_CATEGORY(KDAV2_LOG, "org.kde.pim.kdav2.webdav") 0057 0058 QWebdav::QWebdav (QObject *parent) : QNetworkAccessManager(parent) 0059 ,m_rootPath() 0060 ,m_username() 0061 ,m_password() 0062 ,m_baseUrl() 0063 ,m_currentConnectionType(QWebdav::HTTP) 0064 ,m_authenticator_lastReply(nullptr) 0065 0066 { 0067 qRegisterMetaType<QNetworkReply*>("QNetworkReply*"); 0068 0069 connect(this, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(provideAuthenication(QNetworkReply*,QAuthenticator*))); 0070 connect(this, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>))); 0071 } 0072 0073 QWebdav::~QWebdav() 0074 { 0075 } 0076 0077 QString QWebdav::hostname() const 0078 { 0079 return m_baseUrl.host(); 0080 } 0081 0082 int QWebdav::port() const 0083 { 0084 return m_baseUrl.port(); 0085 } 0086 0087 QString QWebdav::rootPath() const 0088 { 0089 return m_rootPath; 0090 } 0091 0092 QString QWebdav::username() const 0093 { 0094 return m_username; 0095 } 0096 0097 QString QWebdav::password() const 0098 { 0099 return m_password; 0100 } 0101 0102 QWebdav::QWebdavConnectionType QWebdav::connectionType() const 0103 { 0104 return m_currentConnectionType; 0105 } 0106 0107 bool QWebdav::isSSL() const 0108 { 0109 return (m_currentConnectionType==QWebdav::HTTPS); 0110 } 0111 0112 void QWebdav::setConnectionSettings(const QWebdavConnectionType connectionType, 0113 const QString& hostname, 0114 const QString& rootPath, 0115 const QString& username, 0116 const QString& password, 0117 int port, 0118 bool ignoreSslErrors) 0119 { 0120 m_rootPath = rootPath; 0121 0122 if ((m_rootPath.size()>0) && (m_rootPath.endsWith("/"))) 0123 m_rootPath.chop(1); 0124 0125 QString uriScheme; 0126 switch (connectionType) 0127 { 0128 case QWebdav::HTTP: 0129 uriScheme = "http"; 0130 break; 0131 case QWebdav::HTTPS: 0132 uriScheme = "https"; 0133 break; 0134 } 0135 0136 m_currentConnectionType = connectionType; 0137 0138 m_baseUrl.setScheme(uriScheme); 0139 m_baseUrl.setHost(hostname); 0140 m_baseUrl.setPath(rootPath); 0141 0142 if (port != 0) { 0143 0144 // use user-defined port number 0145 if ( ! ( ( (port == 80) && (m_currentConnectionType==QWebdav::HTTP) ) || 0146 ( (port == 443) && (m_currentConnectionType==QWebdav::HTTPS) ) ) ) 0147 m_baseUrl.setPort(port); 0148 } 0149 0150 m_ignoreSslErrors = ignoreSslErrors; 0151 0152 m_username = username; 0153 m_password = password; 0154 } 0155 0156 void QWebdav::provideAuthenication(QNetworkReply *reply, QAuthenticator *authenticator) 0157 { 0158 qCDebug(KDAV2_LOG) << "QWebdav::authenticationRequired() option == " << authenticator->options(); 0159 0160 if (reply == m_authenticator_lastReply) { 0161 //Avoid endless retries. This will fail with AuthenticationRequiredError 0162 return; 0163 } 0164 m_authenticator_lastReply = reply; 0165 0166 authenticator->setUser(m_username); 0167 authenticator->setPassword(m_password); 0168 } 0169 0170 void QWebdav::sslErrors(QNetworkReply *reply, const QList<QSslError> &) 0171 { 0172 qCDebug(KDAV2_LOG) << "QWebdav::sslErrors() reply->url == " << reply->url().toString(QUrl::RemoveUserInfo); 0173 0174 if (m_ignoreSslErrors) { 0175 // user accepted this SSL certifcate already ==> ignore SSL errors 0176 reply->ignoreSslErrors(); 0177 } 0178 } 0179 0180 QString QWebdav::absolutePath(const QString &relPath) 0181 { 0182 return QString(m_rootPath + relPath); 0183 0184 } 0185 0186 QNetworkReply* QWebdav::createDAVRequest(const QString& method, QNetworkRequest& req, const QByteArray& outgoingData) 0187 { 0188 if(!outgoingData.isEmpty()) { 0189 req.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData.size()); 0190 req.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml; charset=utf-8"); 0191 } 0192 0193 qCDebug(KDAV2_LOG) << " QWebdav::createDAVRequest\n" 0194 << " " << method << " " << req.url().toString(); 0195 for (const auto &rawHeaderItem : req.rawHeaderList()) { 0196 qCDebug(KDAV2_LOG) << " " << rawHeaderItem << ": " << req.rawHeader(rawHeaderItem); 0197 } 0198 0199 if (KDAV2_LOG().isDebugEnabled()) { 0200 QTextStream stream(stdout, QIODevice::WriteOnly); 0201 stream << outgoingData; 0202 } 0203 0204 auto reply = sendCustomRequest(req, method.toLatin1(), outgoingData); 0205 //For redirects 0206 reply->setProperty("requestData", outgoingData); 0207 return reply; 0208 } 0209 0210 QNetworkReply* QWebdav::list(const QString& path, int depth) 0211 { 0212 QWebdav::PropNames query; 0213 QStringList props; 0214 0215 // Small set of properties 0216 // href in response contains also the name 0217 // e.g. /container/front.html 0218 props << "getlastmodified"; // http://www.webdav.org/specs/rfc4918.html#PROPERTY_getlastmodified 0219 // e.g. Mon, 12 Jan 1998 09:25:56 GMT 0220 props << "getcontentlength"; // http://www.webdav.org/specs/rfc4918.html#PROPERTY_getcontentlength 0221 // e.g. "4525" 0222 props << "resourcetype"; // http://www.webdav.org/specs/rfc4918.html#PROPERTY_resourcetype 0223 // e.g. "collection" for a directory 0224 0225 // Following properties are available as well. 0226 //props << "creationdate"; // http://www.webdav.org/specs/rfc4918.html#PROPERTY_creationdate 0227 // e.g. "1997-12-01T18:27:21-08:00" 0228 //props << "displayname"; // http://www.webdav.org/specs/rfc4918.html#PROPERTY_displayname 0229 // e.g. "Example HTML resource" 0230 //props << "getcontentlanguage"; // http://www.webdav.org/specs/rfc4918.html#PROPERTY_getcontentlanguage 0231 // e.g. "en-US" 0232 //props << "getcontenttype"; // http://www.webdav.org/specs/rfc4918.html#PROPERTY_getcontenttype 0233 // e.g "text/html" 0234 //props << "getetag"; // http://www.webdav.org/specs/rfc4918.html#PROPERTY_getetag 0235 // e.g. "zzyzx" 0236 0237 // Additionally, there are also properties for locking 0238 0239 query["DAV:"] = props; 0240 0241 return propfind(path, query, depth); 0242 } 0243 0244 QNetworkReply* QWebdav::search(const QString& path, const QString& q ) 0245 { 0246 QByteArray query = "<?xml version=\"1.0\"?>\r\n"; 0247 query.append( "<D:searchrequest xmlns:D=\"DAV:\">\r\n" ); 0248 query.append( q.toUtf8() ); 0249 query.append( "</D:searchrequest>\r\n" ); 0250 0251 QNetworkRequest req; 0252 0253 QUrl reqUrl(m_baseUrl); 0254 reqUrl.setPath(absolutePath(path)); 0255 0256 req.setUrl(reqUrl); 0257 0258 return this->createDAVRequest("SEARCH", req, query); 0259 } 0260 0261 QNetworkReply* QWebdav::get(const QString& path, const QMap<QByteArray, QByteArray> &headers) 0262 { 0263 QNetworkRequest req; 0264 0265 QUrl reqUrl(m_baseUrl); 0266 reqUrl.setPath(absolutePath(path)); 0267 0268 for (auto it = headers.constBegin(); it != headers.constEnd(); it++) { 0269 req.setRawHeader(it.key(), it.value()); 0270 } 0271 0272 qCDebug(KDAV2_LOG) << "QWebdav::get() url = " << req.url().toString(QUrl::RemoveUserInfo); 0273 0274 req.setUrl(reqUrl); 0275 0276 return QNetworkAccessManager::get(req); 0277 } 0278 0279 QNetworkReply* QWebdav::put(const QString& path, const QByteArray& data, const QMap<QByteArray, QByteArray> &headers) 0280 { 0281 QNetworkRequest req; 0282 0283 QUrl reqUrl(m_baseUrl); 0284 reqUrl.setPath(absolutePath(path)); 0285 0286 req.setUrl(reqUrl); 0287 for (auto it = headers.constBegin(); it != headers.constEnd(); it++) { 0288 req.setRawHeader(it.key(), it.value()); 0289 } 0290 0291 qCDebug(KDAV2_LOG) << "QWebdav::put() url = " << req.url().toString(QUrl::RemoveUserInfo); 0292 0293 auto reply = QNetworkAccessManager::put(req, data); 0294 reply->setProperty("requestData", data); 0295 reply->setProperty("isPut", true); 0296 return reply; 0297 } 0298 0299 0300 QNetworkReply* QWebdav::propfind(const QString& path, const QWebdav::PropNames& props, int depth) 0301 { 0302 QByteArray query; 0303 0304 query = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"; 0305 query += "<D:propfind xmlns:D=\"DAV:\" >"; 0306 query += "<D:prop>"; 0307 foreach (QString ns, props.keys()) 0308 { 0309 foreach (const QString key, props[ns]) 0310 if (ns == "DAV:") 0311 query += "<D:" + key + "/>"; 0312 else 0313 query += "<" + key + " xmlns=\"" + ns + "\"/>"; 0314 } 0315 query += "</D:prop>"; 0316 query += "</D:propfind>"; 0317 return propfind(path, query, depth); 0318 } 0319 0320 0321 QNetworkReply* QWebdav::propfind(const QString& path, const QByteArray& query, int depth) 0322 { 0323 QNetworkRequest req; 0324 0325 QUrl reqUrl(m_baseUrl); 0326 reqUrl.setPath(absolutePath(path)); 0327 0328 req.setUrl(reqUrl); 0329 req.setRawHeader("Depth", depth == 2 ? QString("infinity").toUtf8() : QString::number(depth).toUtf8()); 0330 0331 return createDAVRequest("PROPFIND", req, query); 0332 } 0333 0334 QNetworkReply* QWebdav::report(const QString& path, const QByteArray& query, int depth) 0335 { 0336 QNetworkRequest req; 0337 0338 QUrl reqUrl(m_baseUrl); 0339 reqUrl.setPath(absolutePath(path)); 0340 0341 req.setUrl(reqUrl); 0342 req.setRawHeader("Depth", depth == 2 ? QString("infinity").toUtf8() : QString::number(depth).toUtf8()); 0343 0344 return createDAVRequest("REPORT", req, query); 0345 } 0346 0347 QNetworkReply* QWebdav::proppatch(const QString& path, const QWebdav::PropValues& props) 0348 { 0349 QByteArray query; 0350 0351 query = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"; 0352 query += "<D:proppatch xmlns:D=\"DAV:\" >"; 0353 query += "<D:prop>"; 0354 foreach (QString ns, props.keys()) 0355 { 0356 QMap < QString , QVariant >::const_iterator i; 0357 0358 for (i = props[ns].constBegin(); i != props[ns].constEnd(); ++i) { 0359 if (ns == "DAV:") { 0360 query += "<D:" + i.key() + ">"; 0361 query += i.value().toString(); 0362 query += "</D:" + i.key() + ">" ; 0363 } else { 0364 query += "<" + i.key() + " xmlns=\"" + ns + "\">"; 0365 query += i.value().toString(); 0366 query += "</" + i.key() + " xmlns=\"" + ns + "\"/>"; 0367 } 0368 } 0369 } 0370 query += "</D:prop>"; 0371 query += "</D:propfind>"; 0372 0373 return proppatch(path, query); 0374 } 0375 0376 QNetworkReply* QWebdav::proppatch(const QString& path, const QByteArray& query) 0377 { 0378 QNetworkRequest req; 0379 0380 QUrl reqUrl(m_baseUrl); 0381 reqUrl.setPath(absolutePath(path)); 0382 0383 req.setUrl(reqUrl); 0384 0385 return createDAVRequest("PROPPATCH", req, query); 0386 } 0387 0388 QNetworkReply* QWebdav::mkdir (const QString& path) 0389 { 0390 QNetworkRequest req; 0391 0392 QUrl reqUrl(m_baseUrl); 0393 reqUrl.setPath(absolutePath(path)); 0394 0395 req.setUrl(reqUrl); 0396 0397 return createDAVRequest("MKCOL", req); 0398 } 0399 0400 QNetworkReply* QWebdav::mkdir (const QString& path, const QByteArray& query) 0401 { 0402 QNetworkRequest req; 0403 0404 QUrl reqUrl(m_baseUrl); 0405 reqUrl.setPath(absolutePath(path)); 0406 0407 req.setUrl(reqUrl); 0408 0409 return createDAVRequest("MKCOL", req, query); 0410 } 0411 0412 QNetworkReply* QWebdav::mkcalendar (const QString& path, const QByteArray& query) 0413 { 0414 QNetworkRequest req; 0415 0416 QUrl reqUrl(m_baseUrl); 0417 reqUrl.setPath(absolutePath(path)); 0418 0419 req.setUrl(reqUrl); 0420 0421 return createDAVRequest("MKCALENDAR", req, query); 0422 } 0423 0424 QNetworkReply* QWebdav::copy(const QString& pathFrom, const QString& pathTo, bool overwrite) 0425 { 0426 QNetworkRequest req; 0427 0428 QUrl reqUrl(m_baseUrl); 0429 reqUrl.setPath(absolutePath(pathFrom)); 0430 0431 req.setUrl(reqUrl); 0432 0433 // RFC4918 Section 10.3 requires an absolute URI for destination raw header 0434 // http://tools.ietf.org/html/rfc4918#section-10.3 0435 // RFC3986 Section 4.3 specifies the term absolute URI 0436 // http://tools.ietf.org/html/rfc3986#section-4.3 0437 QUrl dstUrl(m_baseUrl); 0438 //dstUrl.setUserInfo(""); 0439 dstUrl.setPath(absolutePath(pathTo)); 0440 req.setRawHeader("Destination", dstUrl.toString().toUtf8()); 0441 0442 req.setRawHeader("Depth", "infinity"); 0443 req.setRawHeader("Overwrite", overwrite ? "T" : "F"); 0444 0445 return createDAVRequest("COPY", req); 0446 } 0447 0448 QNetworkReply* QWebdav::move(const QString& pathFrom, const QString& pathTo, bool overwrite) 0449 { 0450 QNetworkRequest req; 0451 0452 QUrl reqUrl(m_baseUrl); 0453 reqUrl.setPath(absolutePath(pathFrom)); 0454 0455 req.setUrl(reqUrl); 0456 0457 // RFC4918 Section 10.3 requires an absolute URI for destination raw header 0458 // http://tools.ietf.org/html/rfc4918#section-10.3 0459 // RFC3986 Section 4.3 specifies the term absolute URI 0460 // http://tools.ietf.org/html/rfc3986#section-4.3 0461 QUrl dstUrl(m_baseUrl); 0462 //dstUrl.setUserInfo(""); 0463 dstUrl.setPath(absolutePath(pathTo)); 0464 req.setRawHeader("Destination", dstUrl.toString().toUtf8()); 0465 0466 req.setRawHeader("Depth", "infinity"); 0467 req.setRawHeader("Overwrite", overwrite ? "T" : "F"); 0468 0469 return createDAVRequest("MOVE", req); 0470 } 0471 0472 QNetworkReply* QWebdav::remove(const QString& path) 0473 { 0474 QNetworkRequest req; 0475 0476 QUrl reqUrl(m_baseUrl); 0477 reqUrl.setPath(absolutePath(path)); 0478 0479 req.setUrl(reqUrl); 0480 0481 return createDAVRequest("DELETE", req); 0482 }