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,2010 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 00032 #include <kdebug.h> 00033 #include <kconfiggroup.h> 00034 #include <ksharedconfig.h> 00035 #include <kprotocolinfo.h> 00036 #include <klocalizedstring.h> 00037 00038 #include <QtCore/QUrl> 00039 #include <QtGui/QWidget> 00040 #include <QtCore/QWeakPointer> 00041 #include <QtDBus/QDBusInterface> 00042 #include <QtDBus/QDBusConnection> 00043 #include <QtDBus/QDBusReply> 00044 #include <QtNetwork/QNetworkReply> 00045 #include <QtNetwork/QNetworkRequest> 00046 #include <QtNetwork/QSslCipher> 00047 #include <QtNetwork/QSslCertificate> 00048 #include <QtNetwork/QSslConfiguration> 00049 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 static qint64 sizeFromRequest(const QNetworkRequest& req) 00061 { 00062 const QVariant size = req.header(QNetworkRequest::ContentLengthHeader); 00063 if (!size.isValid()) 00064 return -1; 00065 bool ok = false; 00066 const qlonglong value = size.toLongLong(&ok); 00067 return (ok ? value : -1); 00068 } 00069 00070 namespace KIO { 00071 00072 class AccessManager::AccessManagerPrivate 00073 { 00074 public: 00075 AccessManagerPrivate() 00076 : externalContentAllowed(true), 00077 emitReadyReadOnMetaDataChange(false), 00078 window(0) 00079 {} 00080 00081 void setMetaDataForRequest(QNetworkRequest request, KIO::MetaData& metaData); 00082 00083 bool externalContentAllowed; 00084 bool emitReadyReadOnMetaDataChange; 00085 KIO::MetaData requestMetaData; 00086 KIO::MetaData sessionMetaData; 00087 QWidget* window; 00088 }; 00089 00090 namespace Integration { 00091 00092 class CookieJar::CookieJarPrivate 00093 { 00094 public: 00095 CookieJarPrivate() 00096 : windowId((WId)-1), 00097 isEnabled(true), 00098 isStorageDisabled(false) 00099 {} 00100 00101 WId windowId; 00102 bool isEnabled; 00103 bool isStorageDisabled; 00104 }; 00105 00106 } 00107 00108 } 00109 00110 using namespace KIO; 00111 00112 AccessManager::AccessManager(QObject *parent) 00113 :QNetworkAccessManager(parent), d(new AccessManager::AccessManagerPrivate()) 00114 { 00115 // KDE Cookiejar (KCookieJar) integration... 00116 setCookieJar(new KIO::Integration::CookieJar); 00117 } 00118 00119 AccessManager::~AccessManager() 00120 { 00121 delete d; 00122 } 00123 00124 void AccessManager::setExternalContentAllowed(bool allowed) 00125 { 00126 d->externalContentAllowed = allowed; 00127 } 00128 00129 bool AccessManager::isExternalContentAllowed() const 00130 { 00131 return d->externalContentAllowed; 00132 } 00133 00134 #ifndef KDE_NO_DEPRECATED 00135 void AccessManager::setCookieJarWindowId(WId id) 00136 { 00137 QWidget* window = QWidget::find(id); 00138 if (!window) { 00139 return; 00140 } 00141 00142 KIO::Integration::CookieJar *jar = qobject_cast<KIO::Integration::CookieJar *> (cookieJar()); 00143 if (jar) { 00144 jar->setWindowId(id); 00145 } 00146 00147 d->window = window->isWindow() ? window : window->window(); 00148 } 00149 #endif 00150 00151 void AccessManager::setWindow(QWidget* widget) 00152 { 00153 if (!widget) { 00154 return; 00155 } 00156 00157 d->window = widget->isWindow() ? widget : widget->window(); 00158 00159 if (!d->window) { 00160 return; 00161 } 00162 00163 KIO::Integration::CookieJar *jar = qobject_cast<KIO::Integration::CookieJar *> (cookieJar()); 00164 if (jar) { 00165 jar->setWindowId(d->window->winId()); 00166 } 00167 } 00168 00169 #ifndef KDE_NO_DEPRECATED 00170 WId AccessManager::cookieJarWindowid() const 00171 { 00172 KIO::Integration::CookieJar *jar = qobject_cast<KIO::Integration::CookieJar *> (cookieJar()); 00173 if (jar) 00174 return jar->windowId(); 00175 00176 return 0; 00177 } 00178 #endif 00179 00180 QWidget* AccessManager::window() const 00181 { 00182 return d->window; 00183 } 00184 00185 KIO::MetaData& AccessManager::requestMetaData() 00186 { 00187 return d->requestMetaData; 00188 } 00189 00190 KIO::MetaData& AccessManager::sessionMetaData() 00191 { 00192 return d->sessionMetaData; 00193 } 00194 00195 void AccessManager::putReplyOnHold(QNetworkReply* reply) 00196 { 00197 KDEPrivate::AccessManagerReply* r = qobject_cast<KDEPrivate::AccessManagerReply*>(reply); 00198 if (!r) { 00199 return; 00200 } 00201 00202 r->putOnHold(); 00203 } 00204 00205 void AccessManager::setEmitReadyReadOnMetaDataChange(bool enable) 00206 { 00207 d->emitReadyReadOnMetaDataChange = enable; 00208 } 00209 00210 QNetworkReply *AccessManager::createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData) 00211 { 00212 /* 00213 * WORKAROUND: Since there is no way to do a synchronous KIO request 00214 * without creating a nested event loop and creating such a loop here is 00215 * the surest way to encounter the inherent flaws of creating such loops, 00216 * i.e. crashes (bug# 287778), we let Qt's networking layer handle such 00217 * requests because it can block without the need for nested event loops. 00218 * 00219 * The only consequence of doing this is the HTTP response will never be 00220 * cached since it won't be handled by kio_http. 00221 */ 00222 if (req.attribute(gSynchronousNetworkRequestAttribute).toBool()) { 00223 return QNetworkAccessManager::createRequest(op, req, outgoingData); 00224 } 00225 00226 KIO::SimpleJob *kioJob = 0; 00227 const KUrl reqUrl (req.url()); 00228 00229 if (!d->externalContentAllowed && 00230 !KDEPrivate::AccessManagerReply::isLocalRequest(reqUrl) && 00231 reqUrl.scheme() != QL1S("data")) { 00232 kDebug( 7044 ) << "Blocked: " << reqUrl; 00233 KDEPrivate::AccessManagerReply* reply = new KDEPrivate::AccessManagerReply(op, req, kioJob, d->emitReadyReadOnMetaDataChange, this); 00234 reply->setStatus(i18n("Blocked request."),QNetworkReply::ContentAccessDenied); 00235 return reply; 00236 } 00237 00238 // Check if the internal ignore content disposition header is set. 00239 const bool ignoreContentDisposition = req.hasRawHeader("x-kdewebkit-ignore-disposition"); 00240 00241 // Retrieve the KIO meta data... 00242 KIO::MetaData metaData; 00243 d->setMetaDataForRequest(req, metaData); 00244 00245 switch (op) { 00246 case HeadOperation: { 00247 //kDebug( 7044 ) << "HeadOperation:" << reqUrl; 00248 kioJob = KIO::mimetype(reqUrl, KIO::HideProgressInfo); 00249 break; 00250 } 00251 case GetOperation: { 00252 //kDebug( 7044 ) << "GetOperation:" << reqUrl; 00253 if (!reqUrl.path().isEmpty() || reqUrl.host().isEmpty()) 00254 kioJob = KIO::get(reqUrl, KIO::NoReload, KIO::HideProgressInfo); 00255 else 00256 kioJob = KIO::stat(reqUrl, KIO::HideProgressInfo); 00257 00258 // WORKAROUND: Avoid the brain damaged stuff QtWebKit does when a POST 00259 // operation is redirected! See BR# 268694. 00260 metaData.remove(QL1S("content-type")); // Remove the content-type from a GET/HEAD request! 00261 break; 00262 } 00263 case PutOperation: { 00264 //kDebug( 7044 ) << "PutOperation:" << reqUrl; 00265 if (outgoingData) 00266 kioJob = KIO::storedPut(outgoingData->readAll(), reqUrl, -1, KIO::HideProgressInfo); 00267 else 00268 kioJob = KIO::put(reqUrl, -1, KIO::HideProgressInfo); 00269 break; 00270 } 00271 case PostOperation: { 00272 kioJob = KIO::http_post(reqUrl, outgoingData, sizeFromRequest(req), KIO::HideProgressInfo); 00273 if (!metaData.contains(QL1S("content-type"))) { 00274 const QVariant header = req.header(QNetworkRequest::ContentTypeHeader); 00275 if (header.isValid()) { 00276 metaData.insert(QL1S("content-type"), 00277 (QL1S("Content-Type: ") + header.toString())); 00278 } else { 00279 metaData.insert(QL1S("content-type"), 00280 QL1S("Content-Type: application/x-www-form-urlencoded")); 00281 } 00282 } 00283 break; 00284 } 00285 case DeleteOperation: { 00286 //kDebug(7044) << "DeleteOperation:" << reqUrl; 00287 kioJob = KIO::http_delete(reqUrl, KIO::HideProgressInfo); 00288 break; 00289 } 00290 case CustomOperation: { 00291 const QByteArray& method = req.attribute(QNetworkRequest::CustomVerbAttribute).toByteArray(); 00292 //kDebug(7044) << "CustomOperation:" << reqUrl << "method:" << method << "outgoing data:" << outgoingData; 00293 00294 if (method.isEmpty()) { 00295 KDEPrivate::AccessManagerReply* reply = new KDEPrivate::AccessManagerReply(op, req, kioJob, d->emitReadyReadOnMetaDataChange, this); 00296 reply->setStatus(i18n("Unknown HTTP verb."), QNetworkReply::ProtocolUnknownError); 00297 return reply; 00298 } 00299 00300 if (outgoingData) 00301 kioJob = KIO::http_post(reqUrl, outgoingData, sizeFromRequest(req), KIO::HideProgressInfo); 00302 else 00303 kioJob = KIO::get(reqUrl, KIO::NoReload, KIO::HideProgressInfo); 00304 00305 metaData.insert(QL1S("CustomHTTPMethod"), method); 00306 break; 00307 } 00308 default: { 00309 kWarning(7044) << "Unsupported KIO operation requested! Defering to QNetworkAccessManager..."; 00310 return QNetworkAccessManager::createRequest(op, req, outgoingData); 00311 } 00312 } 00313 00314 // Set the window on the the KIO ui delegate 00315 if (d->window) { 00316 kioJob->ui()->setWindow(d->window); 00317 } 00318 00319 // Disable internal automatic redirection handling 00320 kioJob->setRedirectionHandlingEnabled(false); 00321 00322 // Set the job priority 00323 switch (req.priority()) { 00324 case QNetworkRequest::HighPriority: 00325 KIO::Scheduler::setJobPriority(kioJob, -5); 00326 break; 00327 case QNetworkRequest::LowPriority: 00328 KIO::Scheduler::setJobPriority(kioJob, 5); 00329 break; 00330 default: 00331 break; 00332 } 00333 00334 // Set the meta data for this job... 00335 kioJob->setMetaData(metaData); 00336 00337 // Create the reply... 00338 KDEPrivate::AccessManagerReply *reply = new KDEPrivate::AccessManagerReply(op, req, kioJob, d->emitReadyReadOnMetaDataChange, this); 00339 00340 if (ignoreContentDisposition) { 00341 kDebug(7044) << "Content-Disposition WILL BE IGNORED!"; 00342 reply->setIgnoreContentDisposition(ignoreContentDisposition); 00343 } 00344 00345 return reply; 00346 } 00347 00348 void AccessManager::AccessManagerPrivate::setMetaDataForRequest(QNetworkRequest request, KIO::MetaData& metaData) 00349 { 00350 // Add any meta data specified within request... 00351 QVariant userMetaData = request.attribute (static_cast<QNetworkRequest::Attribute>(MetaData)); 00352 if (userMetaData.isValid() && userMetaData.type() == QVariant::Map) 00353 metaData += userMetaData.toMap(); 00354 00355 metaData.insert(QL1S("PropagateHttpHeader"), QL1S("true")); 00356 00357 if (request.hasRawHeader("User-Agent")) { 00358 metaData.insert(QL1S("UserAgent"), request.rawHeader("User-Agent")); 00359 request.setRawHeader("User-Agent", QByteArray()); 00360 } 00361 00362 if (request.hasRawHeader("Accept")) { 00363 metaData.insert(QL1S("accept"), request.rawHeader("Accept")); 00364 request.setRawHeader("Accept", QByteArray()); 00365 } 00366 00367 if (request.hasRawHeader("Accept-Charset")) { 00368 metaData.insert(QL1S("Charsets"), request.rawHeader("Accept-Charset")); 00369 request.setRawHeader("Accept-Charset", QByteArray()); 00370 } 00371 00372 if (request.hasRawHeader("Accept-Language")) { 00373 metaData.insert(QL1S("Languages"), request.rawHeader("Accept-Language")); 00374 request.setRawHeader("Accept-Language", QByteArray()); 00375 } 00376 00377 if (request.hasRawHeader("Referer")) { 00378 metaData.insert(QL1S("referrer"), request.rawHeader("Referer")); 00379 request.setRawHeader("Referer", QByteArray()); 00380 } 00381 00382 if (request.hasRawHeader("Content-Type")) { 00383 metaData.insert(QL1S("content-type"), request.rawHeader("Content-Type")); 00384 request.setRawHeader("Content-Type", QByteArray()); 00385 } 00386 00387 if (request.attribute(QNetworkRequest::AuthenticationReuseAttribute) == QNetworkRequest::Manual) { 00388 metaData.insert(QL1S("no-preemptive-auth-reuse"), QL1S("true")); 00389 } 00390 00391 request.setRawHeader("Content-Length", QByteArray()); 00392 request.setRawHeader("Connection", QByteArray()); 00393 request.setRawHeader("If-None-Match", QByteArray()); 00394 request.setRawHeader("If-Modified-Since", QByteArray()); 00395 request.setRawHeader("x-kdewebkit-ignore-disposition", QByteArray()); 00396 00397 QStringList customHeaders; 00398 Q_FOREACH(const QByteArray &key, request.rawHeaderList()) { 00399 const QByteArray value = request.rawHeader(key); 00400 if (value.length()) 00401 customHeaders << (key + QL1S(": ") + value); 00402 } 00403 00404 if (!customHeaders.isEmpty()) { 00405 metaData.insert(QL1S("customHTTPHeader"), customHeaders.join("\r\n")); 00406 } 00407 00408 // Append per request meta data, if any... 00409 if (!requestMetaData.isEmpty()) { 00410 metaData += requestMetaData; 00411 // Clear per request meta data... 00412 requestMetaData.clear(); 00413 } 00414 00415 // Append per session meta data, if any... 00416 if (!sessionMetaData.isEmpty()) { 00417 metaData += sessionMetaData; 00418 } 00419 } 00420 00421 00422 using namespace KIO::Integration; 00423 00424 static QSsl::SslProtocol qSslProtocolFromString(const QString& str) 00425 { 00426 if (str.compare(QLatin1String("SSLv3"), Qt::CaseInsensitive) == 0) { 00427 return QSsl::SslV3; 00428 } 00429 00430 if (str.compare(QLatin1String("SSLv2"), Qt::CaseInsensitive) == 0) { 00431 return QSsl::SslV2; 00432 } 00433 00434 if (str.compare(QLatin1String("TLSv1"), Qt::CaseInsensitive) == 0) { 00435 return QSsl::TlsV1; 00436 } 00437 00438 return QSsl::AnyProtocol; 00439 } 00440 00441 bool KIO::Integration::sslConfigFromMetaData(const KIO::MetaData& metadata, QSslConfiguration& sslconfig) 00442 { 00443 bool success = false; 00444 00445 if (metadata.contains(QL1S("ssl_in_use"))) { 00446 const QSsl::SslProtocol sslProto = qSslProtocolFromString(metadata.value(QL1S("ssl_protocol_version"))); 00447 QList<QSslCipher> cipherList; 00448 cipherList << QSslCipher(metadata.value(QL1S("ssl_cipher_name")), sslProto); 00449 sslconfig.setCaCertificates(QSslCertificate::fromData(metadata.value(QL1S("ssl_peer_chain")).toUtf8())); 00450 sslconfig.setCiphers(cipherList); 00451 sslconfig.setProtocol(sslProto); 00452 success = sslconfig.isNull(); 00453 } 00454 00455 return success; 00456 } 00457 00458 CookieJar::CookieJar(QObject* parent) 00459 :QNetworkCookieJar(parent), d(new CookieJar::CookieJarPrivate) 00460 { 00461 reparseConfiguration(); 00462 } 00463 00464 CookieJar::~CookieJar() 00465 { 00466 delete d; 00467 } 00468 00469 WId CookieJar::windowId() const 00470 { 00471 return d->windowId; 00472 } 00473 00474 bool CookieJar::isCookieStorageDisabled() const 00475 { 00476 return d->isStorageDisabled; 00477 } 00478 00479 QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl &url) const 00480 { 00481 QList<QNetworkCookie> cookieList; 00482 00483 if (!d->isEnabled) { 00484 return cookieList; 00485 } 00486 QDBusInterface kcookiejar("org.kde.kded", "/modules/kcookiejar", "org.kde.KCookieServer"); 00487 QDBusReply<QString> reply = kcookiejar.call("findDOMCookies", url.toString(QUrl::RemoveUserInfo), (qlonglong)d->windowId); 00488 00489 if (!reply.isValid()) { 00490 kWarning(7044) << "Unable to communicate with the cookiejar!"; 00491 return cookieList; 00492 } 00493 00494 const QString cookieStr = reply.value(); 00495 const QStringList cookies = cookieStr.split(QL1S("; "), QString::SkipEmptyParts); 00496 Q_FOREACH(const QString& cookie, cookies) { 00497 const int index = cookie.indexOf(QL1C('=')); 00498 const QString name = cookie.left(index); 00499 const QString value = cookie.right((cookie.length() - index - 1)); 00500 cookieList << QNetworkCookie(name.toUtf8(), value.toUtf8()); 00501 //kDebug(7044) << "cookie: name=" << name << ", value=" << value; 00502 } 00503 00504 return cookieList; 00505 } 00506 00507 bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url) 00508 { 00509 if (!d->isEnabled) { 00510 return false; 00511 } 00512 00513 QDBusInterface kcookiejar("org.kde.kded", "/modules/kcookiejar", "org.kde.KCookieServer"); 00514 Q_FOREACH(const QNetworkCookie &cookie, cookieList) { 00515 QByteArray cookieHeader ("Set-Cookie: "); 00516 if (d->isStorageDisabled && !cookie.isSessionCookie()) { 00517 QNetworkCookie sessionCookie(cookie); 00518 sessionCookie.setExpirationDate(QDateTime()); 00519 cookieHeader += sessionCookie.toRawForm(); 00520 } else { 00521 cookieHeader += cookie.toRawForm(); 00522 } 00523 kcookiejar.call("addCookies", url.toString(QUrl::RemoveUserInfo), cookieHeader, (qlonglong)d->windowId); 00524 //kDebug(7044) << "[" << d->windowId << "]" << cookieHeader << " from " << url; 00525 } 00526 00527 return !kcookiejar.lastError().isValid(); 00528 } 00529 00530 void CookieJar::setDisableCookieStorage(bool disable) 00531 { 00532 d->isStorageDisabled = disable; 00533 } 00534 00535 void CookieJar::setWindowId(WId id) 00536 { 00537 d->windowId = id; 00538 } 00539 00540 void CookieJar::reparseConfiguration() 00541 { 00542 KConfigGroup cfg = KSharedConfig::openConfig("kcookiejarrc", KConfig::NoGlobals)->group("Cookie Policy"); 00543 d->isEnabled = cfg.readEntry("Cookies", true); 00544 } 00545 00546 #include "accessmanager.moc" 00547
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:55:18 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:55:18 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.