KIO
accessmanager.cpp
Go to the documentation of this file.
00001 /* 00002 * This file is part of the KDE project. 00003 * 00004 * Copyright (C) 2009 - 2012 Dawit Alemayehu <adawit @ kde.org> 00005 * Copyright (C) 2008 - 2009 Urs Wolfer <uwolfer @ kde.org> 00006 * Copyright (C) 2007 Trolltech ASA 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Library General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Library General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Library General Public License 00019 * along with this library; see the file COPYING.LIB. If not, write to 00020 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 * Boston, MA 02110-1301, USA. 00022 * 00023 */ 00024 00025 #include "accessmanager.h" 00026 00027 #include "accessmanagerreply_p.h" 00028 #include "job.h" 00029 #include "scheduler.h" 00030 #include "jobuidelegate.h" 00031 #include "netaccess.h" 00032 00033 #include <kdebug.h> 00034 #include <kconfiggroup.h> 00035 #include <ksharedconfig.h> 00036 #include <kprotocolinfo.h> 00037 #include <klocalizedstring.h> 00038 00039 #include <QtCore/QUrl> 00040 #include <QtCore/QPointer> 00041 #include <QtGui/QWidget> 00042 #include <QtDBus/QDBusInterface> 00043 #include <QtDBus/QDBusConnection> 00044 #include <QtDBus/QDBusReply> 00045 #include <QtNetwork/QNetworkReply> 00046 #include <QtNetwork/QNetworkRequest> 00047 #include <QtNetwork/QSslCipher> 00048 #include <QtNetwork/QSslCertificate> 00049 #include <QtNetwork/QSslConfiguration> 00050 00051 #define QL1S(x) QLatin1String(x) 00052 #define QL1C(x) QLatin1Char(x) 00053 00054 #if QT_VERSION >= 0x040800 00055 static QNetworkRequest::Attribute gSynchronousNetworkRequestAttribute = QNetworkRequest::SynchronousRequestAttribute; 00056 #else // QtWebkit hack to use the internal attribute 00057 static QNetworkRequest::Attribute gSynchronousNetworkRequestAttribute = static_cast<QNetworkRequest::Attribute>(QNetworkRequest::HttpPipeliningWasUsedAttribute + 7); 00058 #endif 00059 00060 00061 00062 00063 static qint64 sizeFromRequest(const QNetworkRequest& req) 00064 { 00065 const QVariant size = req.header(QNetworkRequest::ContentLengthHeader); 00066 if (!size.isValid()) 00067 return -1; 00068 bool ok = false; 00069 const qlonglong value = size.toLongLong(&ok); 00070 return (ok ? value : -1); 00071 } 00072 00073 namespace KIO { 00074 00075 class AccessManager::AccessManagerPrivate 00076 { 00077 public: 00078 AccessManagerPrivate() 00079 : externalContentAllowed(true), 00080 emitReadyReadOnMetaDataChange(false), 00081 window(0) 00082 {} 00083 00084 void setMetaDataForRequest(QNetworkRequest request, KIO::MetaData& metaData); 00085 00086 bool externalContentAllowed; 00087 bool emitReadyReadOnMetaDataChange; 00088 KIO::MetaData requestMetaData; 00089 KIO::MetaData sessionMetaData; 00090 QPointer<QWidget> window; 00091 }; 00092 00093 namespace Integration { 00094 00095 class CookieJar::CookieJarPrivate 00096 { 00097 public: 00098 CookieJarPrivate() 00099 : windowId((WId)-1), 00100 isEnabled(true), 00101 isStorageDisabled(false) 00102 {} 00103 00104 WId windowId; 00105 bool isEnabled; 00106 bool isStorageDisabled; 00107 }; 00108 00109 } 00110 00111 } 00112 00113 using namespace KIO; 00114 00115 AccessManager::AccessManager(QObject *parent) 00116 :QNetworkAccessManager(parent), d(new AccessManager::AccessManagerPrivate()) 00117 { 00118 // KDE Cookiejar (KCookieJar) integration... 00119 setCookieJar(new KIO::Integration::CookieJar); 00120 } 00121 00122 AccessManager::~AccessManager() 00123 { 00124 delete d; 00125 } 00126 00127 void AccessManager::setExternalContentAllowed(bool allowed) 00128 { 00129 d->externalContentAllowed = allowed; 00130 } 00131 00132 bool AccessManager::isExternalContentAllowed() const 00133 { 00134 return d->externalContentAllowed; 00135 } 00136 00137 #ifndef KDE_NO_DEPRECATED 00138 void AccessManager::setCookieJarWindowId(WId id) 00139 { 00140 QWidget* window = QWidget::find(id); 00141 if (!window) { 00142 return; 00143 } 00144 00145 KIO::Integration::CookieJar *jar = qobject_cast<KIO::Integration::CookieJar *> (cookieJar()); 00146 if (jar) { 00147 jar->setWindowId(id); 00148 } 00149 00150 d->window = window->isWindow() ? window : window->window(); 00151 } 00152 #endif 00153 00154 void AccessManager::setWindow(QWidget* widget) 00155 { 00156 if (!widget) { 00157 return; 00158 } 00159 00160 d->window = widget->isWindow() ? widget : widget->window(); 00161 00162 if (!d->window) { 00163 return; 00164 } 00165 00166 KIO::Integration::CookieJar *jar = qobject_cast<KIO::Integration::CookieJar *> (cookieJar()); 00167 if (jar) { 00168 jar->setWindowId(d->window->winId()); 00169 } 00170 } 00171 00172 #ifndef KDE_NO_DEPRECATED 00173 WId AccessManager::cookieJarWindowid() const 00174 { 00175 KIO::Integration::CookieJar *jar = qobject_cast<KIO::Integration::CookieJar *> (cookieJar()); 00176 if (jar) 00177 return jar->windowId(); 00178 00179 return 0; 00180 } 00181 #endif 00182 00183 QWidget* AccessManager::window() const 00184 { 00185 return d->window; 00186 } 00187 00188 KIO::MetaData& AccessManager::requestMetaData() 00189 { 00190 return d->requestMetaData; 00191 } 00192 00193 KIO::MetaData& AccessManager::sessionMetaData() 00194 { 00195 return d->sessionMetaData; 00196 } 00197 00198 void AccessManager::putReplyOnHold(QNetworkReply* reply) 00199 { 00200 KDEPrivate::AccessManagerReply* r = qobject_cast<KDEPrivate::AccessManagerReply*>(reply); 00201 if (!r) { 00202 return; 00203 } 00204 00205 r->putOnHold(); 00206 } 00207 00208 void AccessManager::setEmitReadyReadOnMetaDataChange(bool enable) 00209 { 00210 d->emitReadyReadOnMetaDataChange = enable; 00211 } 00212 00213 QNetworkReply *AccessManager::createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData) 00214 { 00215 const KUrl reqUrl (req.url()); 00216 00217 if (!d->externalContentAllowed && 00218 !KDEPrivate::AccessManagerReply::isLocalRequest(reqUrl) && 00219 reqUrl.scheme() != QL1S("data")) { 00220 kDebug( 7044 ) << "Blocked: " << reqUrl; 00221 return new KDEPrivate::AccessManagerReply(op, req, QNetworkReply::ContentAccessDenied, i18n("Blocked request."), this); 00222 } 00223 00224 // Check if the internal ignore content disposition header is set. 00225 const bool ignoreContentDisposition = req.hasRawHeader("x-kdewebkit-ignore-disposition"); 00226 00227 // Retrieve the KIO meta data... 00228 KIO::MetaData metaData; 00229 d->setMetaDataForRequest(req, metaData); 00230 00231 KIO::SimpleJob *kioJob = 0; 00232 00233 switch (op) { 00234 case HeadOperation: { 00235 //kDebug( 7044 ) << "HeadOperation:" << reqUrl; 00236 kioJob = KIO::mimetype(reqUrl, KIO::HideProgressInfo); 00237 break; 00238 } 00239 case GetOperation: { 00240 //kDebug( 7044 ) << "GetOperation:" << reqUrl; 00241 if (!reqUrl.path().isEmpty() || reqUrl.host().isEmpty()) 00242 kioJob = KIO::get(reqUrl, KIO::NoReload, KIO::HideProgressInfo); 00243 else 00244 kioJob = KIO::stat(reqUrl, KIO::HideProgressInfo); 00245 00246 // WORKAROUND: Avoid the brain damaged stuff QtWebKit does when a POST 00247 // operation is redirected! See BR# 268694. 00248 metaData.remove(QL1S("content-type")); // Remove the content-type from a GET/HEAD request! 00249 break; 00250 } 00251 case PutOperation: { 00252 //kDebug( 7044 ) << "PutOperation:" << reqUrl; 00253 if (outgoingData) 00254 kioJob = KIO::storedPut(outgoingData->readAll(), reqUrl, -1, KIO::HideProgressInfo); 00255 else 00256 kioJob = KIO::put(reqUrl, -1, KIO::HideProgressInfo); 00257 break; 00258 } 00259 case PostOperation: { 00260 kioJob = KIO::http_post(reqUrl, outgoingData, sizeFromRequest(req), KIO::HideProgressInfo); 00261 if (!metaData.contains(QL1S("content-type"))) { 00262 const QVariant header = req.header(QNetworkRequest::ContentTypeHeader); 00263 if (header.isValid()) { 00264 metaData.insert(QL1S("content-type"), 00265 (QL1S("Content-Type: ") + header.toString())); 00266 } else { 00267 metaData.insert(QL1S("content-type"), 00268 QL1S("Content-Type: application/x-www-form-urlencoded")); 00269 } 00270 } 00271 break; 00272 } 00273 case DeleteOperation: { 00274 //kDebug(7044) << "DeleteOperation:" << reqUrl; 00275 kioJob = KIO::http_delete(reqUrl, KIO::HideProgressInfo); 00276 break; 00277 } 00278 case CustomOperation: { 00279 const QByteArray& method = req.attribute(QNetworkRequest::CustomVerbAttribute).toByteArray(); 00280 //kDebug(7044) << "CustomOperation:" << reqUrl << "method:" << method << "outgoing data:" << outgoingData; 00281 00282 if (method.isEmpty()) { 00283 return new KDEPrivate::AccessManagerReply(op, req, QNetworkReply::ProtocolUnknownError, i18n("Unknown HTTP verb."), this); 00284 } 00285 00286 if (outgoingData) 00287 kioJob = KIO::http_post(reqUrl, outgoingData, sizeFromRequest(req), KIO::HideProgressInfo); 00288 else 00289 kioJob = KIO::get(reqUrl, KIO::NoReload, KIO::HideProgressInfo); 00290 00291 metaData.insert(QL1S("CustomHTTPMethod"), method); 00292 break; 00293 } 00294 default: { 00295 kWarning(7044) << "Unsupported KIO operation requested! Defering to QNetworkAccessManager..."; 00296 return QNetworkAccessManager::createRequest(op, req, outgoingData); 00297 } 00298 } 00299 00300 // Set the job priority 00301 switch (req.priority()) { 00302 case QNetworkRequest::HighPriority: 00303 KIO::Scheduler::setJobPriority(kioJob, -5); 00304 break; 00305 case QNetworkRequest::LowPriority: 00306 KIO::Scheduler::setJobPriority(kioJob, 5); 00307 break; 00308 default: 00309 break; 00310 } 00311 00312 KDEPrivate::AccessManagerReply *reply; 00313 00314 /* 00315 NOTE: Here we attempt to handle synchronous XHR requests. Unfortunately, 00316 due to the fact that QNAM is both synchronous and multi-thread while KIO 00317 is completely the opposite (asynchronous and not thread safe), the code 00318 below might cause crashes like the one reported in bug# 287778 (nested 00319 event loops are inherently dangerous). 00320 00321 Unfortunately, all attempts to address the crash has so far failed due to 00322 the many regressions they caused, e.g. bug# 231932 and 297954. Hence, until 00323 a solution is found, we have to live with the side effects of creating 00324 nested event loops. 00325 */ 00326 if (req.attribute(gSynchronousNetworkRequestAttribute).toBool()) { 00327 KUrl finalURL; 00328 QByteArray data; 00329 00330 if (KIO::NetAccess::synchronousRun(kioJob, d->window, &data, &finalURL, &metaData)) { 00331 reply = new KDEPrivate::AccessManagerReply(op, req, data, finalURL, metaData, this); 00332 kDebug(7044) << "Synchronous XHR:" << reply << reqUrl; 00333 } else { 00334 kWarning(7044) << "Failed to create a synchronous XHR for" << reqUrl; 00335 reply = new KDEPrivate::AccessManagerReply(op, req, QNetworkReply::UnknownNetworkError, kioJob->errorText(), this); 00336 } 00337 } else { 00338 // Set the window on the the KIO ui delegate 00339 if (d->window) { 00340 kioJob->ui()->setWindow(d->window); 00341 } 00342 00343 // Disable internal automatic redirection handling 00344 kioJob->setRedirectionHandlingEnabled(false); 00345 00346 // Set the job priority 00347 switch (req.priority()) { 00348 case QNetworkRequest::HighPriority: 00349 KIO::Scheduler::setJobPriority(kioJob, -5); 00350 break; 00351 case QNetworkRequest::LowPriority: 00352 KIO::Scheduler::setJobPriority(kioJob, 5); 00353 break; 00354 default: 00355 break; 00356 } 00357 00358 // Set the meta data for this job... 00359 kioJob->setMetaData(metaData); 00360 00361 // Create the reply... 00362 reply = new KDEPrivate::AccessManagerReply(op, req, kioJob, d->emitReadyReadOnMetaDataChange, this); 00363 //kDebug(7044) << reply << reqUrl; 00364 } 00365 00366 if (ignoreContentDisposition && reply) { 00367 //kDebug(7044) << "Content-Disposition WILL BE IGNORED!"; 00368 reply->setIgnoreContentDisposition(ignoreContentDisposition); 00369 } 00370 00371 return reply; 00372 } 00373 00374 void AccessManager::AccessManagerPrivate::setMetaDataForRequest(QNetworkRequest request, KIO::MetaData& metaData) 00375 { 00376 // Add any meta data specified within request... 00377 QVariant userMetaData = request.attribute (static_cast<QNetworkRequest::Attribute>(MetaData)); 00378 if (userMetaData.isValid() && userMetaData.type() == QVariant::Map) 00379 metaData += userMetaData.toMap(); 00380 00381 metaData.insert(QL1S("PropagateHttpHeader"), QL1S("true")); 00382 00383 if (request.hasRawHeader("User-Agent")) { 00384 metaData.insert(QL1S("UserAgent"), request.rawHeader("User-Agent")); 00385 request.setRawHeader("User-Agent", QByteArray()); 00386 } 00387 00388 if (request.hasRawHeader("Accept")) { 00389 metaData.insert(QL1S("accept"), request.rawHeader("Accept")); 00390 request.setRawHeader("Accept", QByteArray()); 00391 } 00392 00393 if (request.hasRawHeader("Accept-Charset")) { 00394 metaData.insert(QL1S("Charsets"), request.rawHeader("Accept-Charset")); 00395 request.setRawHeader("Accept-Charset", QByteArray()); 00396 } 00397 00398 if (request.hasRawHeader("Accept-Language")) { 00399 metaData.insert(QL1S("Languages"), request.rawHeader("Accept-Language")); 00400 request.setRawHeader("Accept-Language", QByteArray()); 00401 } 00402 00403 if (request.hasRawHeader("Referer")) { 00404 metaData.insert(QL1S("referrer"), request.rawHeader("Referer")); 00405 request.setRawHeader("Referer", QByteArray()); 00406 } 00407 00408 if (request.hasRawHeader("Content-Type")) { 00409 metaData.insert(QL1S("content-type"), request.rawHeader("Content-Type")); 00410 request.setRawHeader("Content-Type", QByteArray()); 00411 } 00412 00413 if (request.attribute(QNetworkRequest::AuthenticationReuseAttribute) == QNetworkRequest::Manual) { 00414 metaData.insert(QL1S("no-preemptive-auth-reuse"), QL1S("true")); 00415 } 00416 00417 request.setRawHeader("Content-Length", QByteArray()); 00418 request.setRawHeader("Connection", QByteArray()); 00419 request.setRawHeader("If-None-Match", QByteArray()); 00420 request.setRawHeader("If-Modified-Since", QByteArray()); 00421 request.setRawHeader("x-kdewebkit-ignore-disposition", QByteArray()); 00422 00423 QStringList customHeaders; 00424 Q_FOREACH(const QByteArray &key, request.rawHeaderList()) { 00425 const QByteArray value = request.rawHeader(key); 00426 if (value.length()) 00427 customHeaders << (key + QL1S(": ") + value); 00428 } 00429 00430 if (!customHeaders.isEmpty()) { 00431 metaData.insert(QL1S("customHTTPHeader"), customHeaders.join("\r\n")); 00432 } 00433 00434 // Append per request meta data, if any... 00435 if (!requestMetaData.isEmpty()) { 00436 metaData += requestMetaData; 00437 // Clear per request meta data... 00438 requestMetaData.clear(); 00439 } 00440 00441 // Append per session meta data, if any... 00442 if (!sessionMetaData.isEmpty()) { 00443 metaData += sessionMetaData; 00444 } 00445 } 00446 00447 00448 using namespace KIO::Integration; 00449 00450 static QSsl::SslProtocol qSslProtocolFromString(const QString& str) 00451 { 00452 if (str.compare(QLatin1String("SSLv3"), Qt::CaseInsensitive) == 0) { 00453 return QSsl::SslV3; 00454 } 00455 00456 if (str.compare(QLatin1String("SSLv2"), Qt::CaseInsensitive) == 0) { 00457 return QSsl::SslV2; 00458 } 00459 00460 if (str.compare(QLatin1String("TLSv1"), Qt::CaseInsensitive) == 0) { 00461 return QSsl::TlsV1; 00462 } 00463 00464 return QSsl::AnyProtocol; 00465 } 00466 00467 bool KIO::Integration::sslConfigFromMetaData(const KIO::MetaData& metadata, QSslConfiguration& sslconfig) 00468 { 00469 bool success = false; 00470 00471 if (metadata.contains(QL1S("ssl_in_use"))) { 00472 const QSsl::SslProtocol sslProto = qSslProtocolFromString(metadata.value(QL1S("ssl_protocol_version"))); 00473 QList<QSslCipher> cipherList; 00474 cipherList << QSslCipher(metadata.value(QL1S("ssl_cipher_name")), sslProto); 00475 sslconfig.setCaCertificates(QSslCertificate::fromData(metadata.value(QL1S("ssl_peer_chain")).toUtf8())); 00476 sslconfig.setCiphers(cipherList); 00477 sslconfig.setProtocol(sslProto); 00478 success = sslconfig.isNull(); 00479 } 00480 00481 return success; 00482 } 00483 00484 CookieJar::CookieJar(QObject* parent) 00485 :QNetworkCookieJar(parent), d(new CookieJar::CookieJarPrivate) 00486 { 00487 reparseConfiguration(); 00488 } 00489 00490 CookieJar::~CookieJar() 00491 { 00492 delete d; 00493 } 00494 00495 WId CookieJar::windowId() const 00496 { 00497 return d->windowId; 00498 } 00499 00500 bool CookieJar::isCookieStorageDisabled() const 00501 { 00502 return d->isStorageDisabled; 00503 } 00504 00505 QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl &url) const 00506 { 00507 QList<QNetworkCookie> cookieList; 00508 00509 if (!d->isEnabled) { 00510 return cookieList; 00511 } 00512 QDBusInterface kcookiejar("org.kde.kded", "/modules/kcookiejar", "org.kde.KCookieServer"); 00513 QDBusReply<QString> reply = kcookiejar.call("findDOMCookies", url.toString(QUrl::RemoveUserInfo), (qlonglong)d->windowId); 00514 00515 if (!reply.isValid()) { 00516 kWarning(7044) << "Unable to communicate with the cookiejar!"; 00517 return cookieList; 00518 } 00519 00520 const QString cookieStr = reply.value(); 00521 const QStringList cookies = cookieStr.split(QL1S("; "), QString::SkipEmptyParts); 00522 Q_FOREACH(const QString& cookie, cookies) { 00523 const int index = cookie.indexOf(QL1C('=')); 00524 const QString name = cookie.left(index); 00525 const QString value = cookie.right((cookie.length() - index - 1)); 00526 cookieList << QNetworkCookie(name.toUtf8(), value.toUtf8()); 00527 //kDebug(7044) << "cookie: name=" << name << ", value=" << value; 00528 } 00529 00530 return cookieList; 00531 } 00532 00533 bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url) 00534 { 00535 if (!d->isEnabled) { 00536 return false; 00537 } 00538 00539 QDBusInterface kcookiejar("org.kde.kded", "/modules/kcookiejar", "org.kde.KCookieServer"); 00540 Q_FOREACH(const QNetworkCookie &cookie, cookieList) { 00541 QByteArray cookieHeader ("Set-Cookie: "); 00542 if (d->isStorageDisabled && !cookie.isSessionCookie()) { 00543 QNetworkCookie sessionCookie(cookie); 00544 sessionCookie.setExpirationDate(QDateTime()); 00545 cookieHeader += sessionCookie.toRawForm(); 00546 } else { 00547 cookieHeader += cookie.toRawForm(); 00548 } 00549 kcookiejar.call("addCookies", url.toString(QUrl::RemoveUserInfo), cookieHeader, (qlonglong)d->windowId); 00550 //kDebug(7044) << "[" << d->windowId << "]" << cookieHeader << " from " << url; 00551 } 00552 00553 return !kcookiejar.lastError().isValid(); 00554 } 00555 00556 void CookieJar::setDisableCookieStorage(bool disable) 00557 { 00558 d->isStorageDisabled = disable; 00559 } 00560 00561 void CookieJar::setWindowId(WId id) 00562 { 00563 d->windowId = id; 00564 } 00565 00566 void CookieJar::reparseConfiguration() 00567 { 00568 KConfigGroup cfg = KSharedConfig::openConfig("kcookiejarrc", KConfig::NoGlobals)->group("Cookie Policy"); 00569 d->isEnabled = cfg.readEntry("Cookies", true); 00570 } 00571 00572 #include "accessmanager.moc" 00573
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:34:57 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:34:57 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.