00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "http.h"
00028
00029 #include <config.h>
00030 #include <config-gssapi.h>
00031
00032 #include <fcntl.h>
00033 #include <utime.h>
00034 #include <stdlib.h>
00035 #include <stdio.h>
00036 #include <sys/stat.h>
00037 #include <sys/time.h>
00038 #include <unistd.h>
00039
00040 #include <QtXml/qdom.h>
00041 #include <QtCore/QFile>
00042 #include <QtCore/QRegExp>
00043 #include <QtCore/QDate>
00044 #include <QtDBus/QtDBus>
00045 #include <QtNetwork/QAuthenticator>
00046 #include <QtNetwork/QNetworkProxy>
00047 #include <QtNetwork/QTcpSocket>
00048 #include <QtNetwork/QHostInfo>
00049
00050 #include <kurl.h>
00051 #include <kdebug.h>
00052 #include <klocale.h>
00053 #include <kconfig.h>
00054 #include <kconfiggroup.h>
00055 #include <kservice.h>
00056 #include <kdatetime.h>
00057 #include <kcodecs.h>
00058 #include <kcomponentdata.h>
00059 #include <krandom.h>
00060 #include <kmimetype.h>
00061 #include <ktoolinvocation.h>
00062 #include <kstandarddirs.h>
00063 #include <kremoteencoding.h>
00064
00065 #include <kio/ioslave_defaults.h>
00066 #include <kio/http_slave_defaults.h>
00067
00068 #include <httpfilter.h>
00069
00070 #ifdef HAVE_LIBGSSAPI
00071 #ifdef GSSAPI_MIT
00072 #include <gssapi/gssapi.h>
00073 #else
00074 #include <gssapi.h>
00075 #endif
00076
00077
00078 #if defined(GSS_RFC_COMPLIANT_OIDS) && (GSS_RFC_COMPLIANT_OIDS == 0)
00079 #include <gssapi/gssapi_generic.h>
00080 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
00081 #endif
00082
00083 #endif
00084
00085 #include <misc/kntlm/kntlm.h>
00086 #include <kapplication.h>
00087 #include <kaboutdata.h>
00088 #include <kcmdlineargs.h>
00089 #include <kde_file.h>
00090
00091
00092 #include "parsinghelpers.cpp"
00093
00094 #include "httpauthentication.cpp"
00095
00096 using namespace KIO;
00097
00098 extern "C" int KDE_EXPORT kdemain( int argc, char **argv )
00099 {
00100 QCoreApplication app( argc, argv );
00101 KComponentData componentData( "kio_http", "kdelibs4" );
00102 (void) KGlobal::locale();
00103
00104 if (argc != 4)
00105 {
00106 fprintf(stderr, "Usage: kio_http protocol domain-socket1 domain-socket2\n");
00107 exit(-1);
00108 }
00109
00110 HTTPProtocol slave(argv[1], argv[2], argv[3]);
00111 slave.dispatchLoop();
00112 return 0;
00113 }
00114
00115
00116
00117 static bool isCrossDomainRequest( const QString& fqdn, const QString& originURL )
00118 {
00119
00120 if (originURL == "true")
00121 return true;
00122
00123 KUrl url ( originURL );
00124
00125
00126 QString a = url.host();
00127
00128 QString b = fqdn;
00129
00130 if (a == b)
00131 return false;
00132
00133 QStringList la = a.split('.', QString::SkipEmptyParts);
00134 QStringList lb = b.split('.', QString::SkipEmptyParts);
00135
00136 if (qMin(la.count(), lb.count()) < 2) {
00137 return true;
00138 }
00139
00140 while(la.count() > 2)
00141 la.pop_front();
00142 while(lb.count() > 2)
00143 lb.pop_front();
00144
00145 return la != lb;
00146 }
00147
00148
00149
00150
00151 static QString sanitizeCustomHTTPHeader(const QString& _header)
00152 {
00153 QString sanitizedHeaders;
00154 const QStringList headers = _header.split(QRegExp("[\r\n]"));
00155
00156 for(QStringList::ConstIterator it = headers.begin(); it != headers.end(); ++it)
00157 {
00158 QString header = (*it).toLower();
00159
00160
00161 if (!header.contains(':') || header.startsWith("host") ||
00162 header.startsWith("proxy-authorization") ||
00163 header.startsWith("via"))
00164 continue;
00165
00166 sanitizedHeaders += (*it);
00167 sanitizedHeaders += "\r\n";
00168 }
00169 sanitizedHeaders.chop(2);
00170
00171 return sanitizedHeaders;
00172 }
00173
00174 static bool isEncryptedHttpVariety(const QString &p)
00175 {
00176 return p == "https" || p == "webdavs";
00177 }
00178
00179 static bool isValidProxy(const KUrl &u)
00180 {
00181 return u.isValid() && u.hasHost();
00182 }
00183
00184 static bool isHttpProxy(const KUrl &u)
00185 {
00186 return isValidProxy(u) && u.protocol() == "http";
00187 }
00188
00189 static QString methodString(HTTP_METHOD m)
00190 {
00191 switch(m) {
00192 case HTTP_GET:
00193 return"GET ";
00194 case HTTP_PUT:
00195 return "PUT ";
00196 case HTTP_POST:
00197 return "POST ";
00198 case HTTP_HEAD:
00199 return "HEAD ";
00200 case HTTP_DELETE:
00201 return "DELETE ";
00202 case HTTP_OPTIONS:
00203 return "OPTIONS ";
00204 case DAV_PROPFIND:
00205 return "PROPFIND ";
00206 case DAV_PROPPATCH:
00207 return "PROPPATCH ";
00208 case DAV_MKCOL:
00209 return "MKCOL ";
00210 case DAV_COPY:
00211 return "COPY ";
00212 case DAV_MOVE:
00213 return "MOVE ";
00214 case DAV_LOCK:
00215 return "LOCK ";
00216 case DAV_UNLOCK:
00217 return "UNLOCK ";
00218 case DAV_SEARCH:
00219 return "SEARCH ";
00220 case DAV_SUBSCRIBE:
00221 return "SUBSCRIBE ";
00222 case DAV_UNSUBSCRIBE:
00223 return "UNSUBSCRIBE ";
00224 case DAV_POLL:
00225 return "POLL ";
00226 default:
00227 Q_ASSERT(false);
00228 return QString();
00229 }
00230 }
00231
00232
00233
00234
00235
00236
00237 #define NO_SIZE ((KIO::filesize_t) -1)
00238
00239 #ifdef HAVE_STRTOLL
00240 #define STRTOLL strtoll
00241 #else
00242 #define STRTOLL strtol
00243 #endif
00244
00245
00246
00247
00248
00249 HTTPProtocol::HTTPProtocol( const QByteArray &protocol, const QByteArray &pool,
00250 const QByteArray &app )
00251 : TCPSlaveBase(protocol, pool, app, isEncryptedHttpVariety(protocol))
00252 , m_defaultPort(0)
00253 , m_iSize(NO_SIZE)
00254 , m_isBusy(false)
00255 , m_isFirstRequest(false)
00256 , m_maxCacheAge(DEFAULT_MAX_CACHE_AGE)
00257 , m_maxCacheSize(DEFAULT_MAX_CACHE_SIZE/2)
00258 , m_protocol(protocol)
00259 , m_wwwAuth(0)
00260 , m_proxyAuth(0)
00261 , m_socketProxyAuth(0)
00262 , m_isError(false)
00263 , m_remoteRespTimeout(DEFAULT_RESPONSE_TIMEOUT)
00264 {
00265 reparseConfiguration();
00266 setBlocking(true);
00267 connect(socket(), SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),
00268 this, SLOT(proxyAuthenticationForSocket(const QNetworkProxy &, QAuthenticator *)));
00269 }
00270
00271 HTTPProtocol::~HTTPProtocol()
00272 {
00273 httpClose(false);
00274 }
00275
00276 void HTTPProtocol::reparseConfiguration()
00277 {
00278 kDebug(7113);
00279
00280 delete m_proxyAuth;
00281 delete m_wwwAuth;
00282 m_proxyAuth = 0;
00283 m_wwwAuth = 0;
00284 m_request.proxyUrl.clear();
00285
00286 if (isEncryptedHttpVariety(m_protocol))
00287 m_defaultPort = DEFAULT_HTTPS_PORT;
00288 else
00289 m_defaultPort = DEFAULT_HTTP_PORT;
00290 }
00291
00292 void HTTPProtocol::resetConnectionSettings()
00293 {
00294 m_isEOF = false;
00295 m_isError = false;
00296 }
00297
00298 void HTTPProtocol::resetResponseParsing()
00299 {
00300 m_isRedirection = false;
00301 m_isChunked = false;
00302 m_iSize = NO_SIZE;
00303 clearUnreadBuffer();
00304
00305 m_responseHeaders.clear();
00306 m_contentEncodings.clear();
00307 m_transferEncodings.clear();
00308 m_contentMD5.clear();
00309 m_mimeType.clear();
00310
00311 setMetaData("request-id", m_request.id);
00312 }
00313
00314 void HTTPProtocol::resetSessionSettings()
00315 {
00316
00317
00318 KUrl proxy ( config()->readEntry("UseProxy") );
00319 QNetworkProxy::ProxyType proxyType = QNetworkProxy::NoProxy;
00320
00321 #if 0
00322 if ( m_proxyAuth.realm.isEmpty() || !proxy.isValid() ||
00323 m_request.proxyUrl.host() != proxy.host() ||
00324 m_request.proxyUrl.port() != proxy.port() ||
00325 (!proxy.user().isEmpty() && proxy.user() != m_request.proxyUrl.user()) ||
00326 (!proxy.pass().isEmpty() && proxy.pass() != m_request.proxyUrl.pass()) )
00327 {
00328 m_request.proxyUrl = proxy;
00329
00330 kDebug(7113) << "Using proxy:" << m_request.useProxy()
00331 << "URL: " << m_request.proxyUrl.url()
00332 << "Realm: " << m_proxyAuth.realm;
00333 }
00334 #endif
00335 m_request.proxyUrl = proxy;
00336 kDebug(7113) << "Using proxy:" << isValidProxy(m_request.proxyUrl)
00337 << "URL: " << m_request.proxyUrl.url();
00338
00339
00340 if (isValidProxy(m_request.proxyUrl)) {
00341 if (m_request.proxyUrl.protocol() == "socks") {
00342
00343 proxyType = QNetworkProxy::Socks5Proxy;
00344 } else if (isAutoSsl()) {
00345
00346
00347 proxyType = QNetworkProxy::HttpProxy;
00348 }
00349 m_request.proxyUrl = proxy;
00350 } else {
00351 m_request.proxyUrl = KUrl();
00352 }
00353
00354 QNetworkProxy appProxy(proxyType, m_request.proxyUrl.host(), m_request.proxyUrl.port(),
00355 m_request.proxyUrl.user(), m_request.proxyUrl.pass());
00356 QNetworkProxy::setApplicationProxy(appProxy);
00357
00358 if (isHttpProxy(m_request.proxyUrl) && !isAutoSsl()) {
00359 m_request.isKeepAlive = config()->readEntry("PersistentProxyConnection", false);
00360 kDebug(7113) << "Enable Persistent Proxy Connection: "
00361 << m_request.isKeepAlive;
00362 }
00363
00364 m_request.useCookieJar = config()->readEntry("Cookies", false);
00365 m_request.cacheTag.useCache = config()->readEntry("UseCache", true);
00366 m_request.preferErrorPage = config()->readEntry("errorPage", true);
00367 m_request.doNotAuthenticate = config()->readEntry("no-auth", false);
00368 m_strCacheDir = config()->readPathEntry("CacheDir", QString());
00369 m_maxCacheAge = config()->readEntry("MaxCacheAge", DEFAULT_MAX_CACHE_AGE);
00370 m_request.windowId = config()->readEntry("window-id");
00371
00372 kDebug(7113) << "Window Id =" << m_request.windowId;
00373 kDebug(7113) << "ssl_was_in_use ="
00374 << metaData ("ssl_was_in_use");
00375
00376 m_request.referrer.clear();
00377
00378
00379 if ( config()->readEntry("SendReferrer", true) &&
00380 (isEncryptedHttpVariety(m_protocol) || metaData ("ssl_was_in_use") != "TRUE" ) )
00381 {
00382 KUrl refUrl(metaData("referrer"));
00383 if (refUrl.isValid()) {
00384
00385 QString protocol = refUrl.protocol();
00386 if (protocol.startsWith("webdav")) {
00387 protocol.replace(0, 6, "http");
00388 refUrl.setProtocol(protocol);
00389 }
00390
00391 if (protocol.startsWith("http")) {
00392 m_request.referrer = refUrl.toEncoded(QUrl::RemoveUserInfo | QUrl::RemoveFragment);
00393 }
00394 }
00395 }
00396
00397 if (config()->readEntry("SendLanguageSettings", true)) {
00398 m_request.charsets = config()->readEntry( "Charsets", "iso-8859-1" );
00399 if (!m_request.charsets.isEmpty()) {
00400 m_request.charsets += DEFAULT_PARTIAL_CHARSET_HEADER;
00401 }
00402 m_request.languages = config()->readEntry( "Languages", DEFAULT_LANGUAGE_HEADER );
00403 } else {
00404 m_request.charsets.clear();
00405 m_request.languages.clear();
00406 }
00407
00408
00409 QString resumeOffset = metaData("resume");
00410 if (!resumeOffset.isEmpty()) {
00411 m_request.offset = resumeOffset.toULongLong();
00412 } else {
00413 m_request.offset = 0;
00414 }
00415
00416 QString resumeEndOffset = metaData("resume_until");
00417 if (!resumeEndOffset.isEmpty()) {
00418 m_request.endoffset = resumeEndOffset.toULongLong();
00419 } else {
00420 m_request.endoffset = 0;
00421 }
00422
00423 m_request.disablePassDialog = config()->readEntry("DisablePassDlg", false);
00424 m_request.allowTransferCompression = config()->readEntry("AllowCompressedPage", true);
00425 m_request.id = metaData("request-id");
00426
00427
00428 if (config()->readEntry("SendUserAgent", true)) {
00429 m_request.userAgent = metaData("UserAgent");
00430 } else {
00431 m_request.userAgent.clear();
00432 }
00433
00434
00435
00436
00437 if (m_request.cacheTag.useCache) {
00438 cleanCache();
00439 }
00440
00441 m_request.responseCode = 0;
00442 m_request.prevResponseCode = 0;
00443
00444 delete m_wwwAuth;
00445 m_wwwAuth = 0;
00446
00447
00448 m_remoteRespTimeout = responseTimeout();
00449
00450
00451 setMetaData("referrer", m_request.referrer);
00452
00453
00454
00455
00456 m_request.isKeepAlive = true;
00457 m_request.keepAliveTimeout = 0;
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467 m_isFirstRequest = false;
00468 }
00469
00470 void HTTPProtocol::setHost( const QString& host, quint16 port,
00471 const QString& user, const QString& pass )
00472 {
00473
00474 if ( m_request.url.host() != host )
00475 m_davHostOk = m_davHostUnsupported = false;
00476
00477 m_request.url.setHost(host);
00478
00479
00480 if (host.indexOf(':') == -1) {
00481 m_request.encoded_hostname = QUrl::toAce(host);
00482 } else {
00483 int pos = host.indexOf('%');
00484 if (pos == -1)
00485 m_request.encoded_hostname = '[' + host + ']';
00486 else
00487
00488 m_request.encoded_hostname = '[' + host.left(pos) + ']';
00489 }
00490 m_request.url.setPort((port <= 0) ? m_defaultPort : port);
00491 m_request.url.setUser(user);
00492 m_request.url.setPass(pass);
00493
00494
00495
00496 kDebug(7113) << "Hostname is now:" << m_request.url.host()
00497 << "(" << m_request.encoded_hostname << ")";
00498 }
00499
00500 bool HTTPProtocol::maybeSetRequestUrl(const KUrl &u)
00501 {
00502 kDebug (7113) << u.url();
00503
00504 m_request.url = u;
00505 m_request.url.setPort((u.port() <= 0) ? m_defaultPort : u.port());
00506
00507 if (u.host().isEmpty()) {
00508 error( KIO::ERR_UNKNOWN_HOST, i18n("No host specified."));
00509 return false;
00510 }
00511
00512 if (u.path().isEmpty()) {
00513 KUrl newUrl(u);
00514 newUrl.setPath("/");
00515 redirection(newUrl);
00516 finished();
00517 return false;
00518 }
00519
00520 if (m_protocol != u.protocol().toLatin1()) {
00521 short unsigned int oldDefaultPort = m_defaultPort;
00522 m_protocol = u.protocol().toLatin1();
00523 reparseConfiguration();
00524 if (m_defaultPort != oldDefaultPort && m_request.url.port() == oldDefaultPort) {
00525 m_request.url.setPort(m_defaultPort);
00526 }
00527 }
00528
00529 return true;
00530 }
00531
00532 void HTTPProtocol::proceedUntilResponseContent( bool dataInternal )
00533 {
00534 kDebug (7113);
00535 if (!(proceedUntilResponseHeader() && readBody(dataInternal))) {
00536 return;
00537 }
00538
00539 httpClose(m_request.isKeepAlive);
00540
00541
00542
00543 if (!dataInternal) {
00544 if ((m_request.responseCode == 204) &&
00545 ((m_request.method == HTTP_GET) || (m_request.method == HTTP_POST))) {
00546 error(ERR_NO_CONTENT, "");
00547 } else {
00548 finished();
00549 }
00550 }
00551 }
00552
00553 bool HTTPProtocol::proceedUntilResponseHeader()
00554 {
00555 kDebug (7113);
00556
00557
00558
00559
00560
00561
00562
00563 while (true) {
00564 if (!sendQuery()) {
00565 return false;
00566 }
00567 if (readResponseHeader()) {
00568
00569
00570
00571
00572
00573 if (!m_request.cacheTag.readFromCache) {
00574 m_server.initFrom(m_request);
00575 }
00576 break;
00577 } else if (m_isError) {
00578
00579 return false;
00580 }
00581
00582 if (!m_request.isKeepAlive) {
00583 httpCloseConnection();
00584 }
00585
00586
00587 Q_ASSERT_X(!m_request.cacheTag.readFromCache, "proceedUntilResponseHeader()",
00588 "retrying a request even though the result is cached?!");
00589 if (!m_request.cacheTag.readFromCache) {
00590 m_server.initFrom(m_request);
00591 }
00592 }
00593
00594
00595
00596 kDebug(7113) << "Previous Response:" << m_request.prevResponseCode;
00597 kDebug(7113) << "Current Response:" << m_request.responseCode;
00598
00599 setMetaData("responsecode", QString::number(m_request.responseCode));
00600 setMetaData("content-type", m_mimeType);
00601
00602
00603 m_POSTbuf.clear();
00604
00605 return true;
00606 }
00607
00608 void HTTPProtocol::stat(const KUrl& url)
00609 {
00610 kDebug(7113) << url.url();
00611
00612 if (!maybeSetRequestUrl(url))
00613 return;
00614 resetSessionSettings();
00615
00616 if ( m_protocol != "webdav" && m_protocol != "webdavs" )
00617 {
00618 QString statSide = metaData(QString::fromLatin1("statSide"));
00619 if ( statSide != "source" )
00620 {
00621
00622 error( ERR_DOES_NOT_EXIST, url.prettyUrl() );
00623 return;
00624 }
00625
00626
00627 UDSEntry entry;
00628 entry.insert( KIO::UDSEntry::UDS_NAME, url.fileName() );
00629 entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG );
00630 entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH );
00631
00632 statEntry( entry );
00633 finished();
00634 return;
00635 }
00636
00637 davStatList( url );
00638 }
00639
00640 void HTTPProtocol::listDir( const KUrl& url )
00641 {
00642 kDebug(7113) << url.url();
00643
00644 if (!maybeSetRequestUrl(url))
00645 return;
00646 resetSessionSettings();
00647
00648 davStatList( url, false );
00649 }
00650
00651 void HTTPProtocol::davSetRequest( const QByteArray& requestXML )
00652 {
00653
00654 m_POSTbuf = requestXML;
00655 }
00656
00657 void HTTPProtocol::davStatList( const KUrl& url, bool stat )
00658 {
00659 UDSEntry entry;
00660
00661
00662 if ( !davHostOk() )
00663 return;
00664
00665
00666 QString query = metaData("davSearchQuery");
00667 if ( !query.isEmpty() )
00668 {
00669 QByteArray request = "<?xml version=\"1.0\"?>\r\n";
00670 request.append( "<D:searchrequest xmlns:D=\"DAV:\">\r\n" );
00671 request.append( query.toUtf8() );
00672 request.append( "</D:searchrequest>\r\n" );
00673
00674 davSetRequest( request );
00675 } else {
00676
00677 QByteArray request;
00678 request = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
00679 "<D:propfind xmlns:D=\"DAV:\">";
00680
00681
00682 if ( hasMetaData( "davRequestResponse" ) )
00683 request += metaData( "davRequestResponse" ).toUtf8();
00684 else {
00685
00686 request += "<D:prop>"
00687 "<D:creationdate/>"
00688 "<D:getcontentlength/>"
00689 "<D:displayname/>"
00690 "<D:source/>"
00691 "<D:getcontentlanguage/>"
00692 "<D:getcontenttype/>"
00693 "<D:executable/>"
00694 "<D:getlastmodified/>"
00695 "<D:getetag/>"
00696 "<D:supportedlock/>"
00697 "<D:lockdiscovery/>"
00698 "<D:resourcetype/>"
00699 "</D:prop>";
00700 }
00701 request += "</D:propfind>";
00702
00703 davSetRequest( request );
00704 }
00705
00706
00707 m_request.method = query.isEmpty() ? DAV_PROPFIND : DAV_SEARCH;
00708 m_request.url.setQuery(QString());
00709 m_request.cacheTag.policy = CC_Reload;
00710 m_request.davData.depth = stat ? 0 : 1;
00711 if (!stat)
00712 m_request.url.adjustPath(KUrl::AddTrailingSlash);
00713
00714 proceedUntilResponseContent( true );
00715
00716
00717 if (m_isRedirection) {
00718 finished();
00719 return;
00720 }
00721
00722 QDomDocument multiResponse;
00723 multiResponse.setContent( m_webDavDataBuf, true );
00724
00725 bool hasResponse = false;
00726
00727 for ( QDomNode n = multiResponse.documentElement().firstChild();
00728 !n.isNull(); n = n.nextSibling())
00729 {
00730 QDomElement thisResponse = n.toElement();
00731 if (thisResponse.isNull())
00732 continue;
00733
00734 hasResponse = true;
00735
00736 QDomElement href = thisResponse.namedItem( "href" ).toElement();
00737 if ( !href.isNull() )
00738 {
00739 entry.clear();
00740
00741 QString urlStr = QUrl::fromPercentEncoding(href.text().toUtf8());
00742 #if 0 // qt4/kde4 say: it's all utf8...
00743 int encoding = remoteEncoding()->encodingMib();
00744 if ((encoding == 106) && (!KStringHandler::isUtf8(KUrl::decode_string(urlStr, 4).toLatin1())))
00745 encoding = 4;
00746
00747 KUrl thisURL ( urlStr, encoding );
00748 #else
00749 KUrl thisURL( urlStr );
00750 #endif
00751
00752 if ( thisURL.isValid() ) {
00753
00754 if ( !stat && thisURL.path(KUrl::AddTrailingSlash).length() == url.path(KUrl::AddTrailingSlash).length() )
00755 continue;
00756
00757 entry.insert( KIO::UDSEntry::UDS_NAME, thisURL.fileName() );
00758 } else {
00759
00760 entry.insert( KIO::UDSEntry::UDS_NAME, href.text() );
00761 }
00762
00763 QDomNodeList propstats = thisResponse.elementsByTagName( "propstat" );
00764
00765 davParsePropstats( propstats, entry );
00766
00767 if ( stat )
00768 {
00769
00770 statEntry( entry );
00771 finished();
00772 return;
00773 }
00774 else
00775 {
00776 listEntry( entry, false );
00777 }
00778 }
00779 else
00780 {
00781 kDebug(7113) << "Error: no URL contained in response to PROPFIND on"
00782 << url.prettyUrl();
00783 }
00784 }
00785
00786 if ( stat || !hasResponse )
00787 {
00788 error( ERR_DOES_NOT_EXIST, url.prettyUrl() );
00789 }
00790 else
00791 {
00792 listEntry( entry, true );
00793 finished();
00794 }
00795 }
00796
00797 void HTTPProtocol::davGeneric( const KUrl& url, KIO::HTTP_METHOD method )
00798 {
00799 kDebug(7113) << url.url();
00800
00801 if (!maybeSetRequestUrl(url))
00802 return;
00803 resetSessionSettings();
00804
00805
00806 if ( !davHostOk() )
00807 return;
00808
00809
00810 m_request.method = method;
00811 m_request.url.setQuery(QString());
00812 m_request.cacheTag.policy = CC_Reload;
00813
00814 proceedUntilResponseContent( false );
00815 }
00816
00817 int HTTPProtocol::codeFromResponse( const QString& response )
00818 {
00819 int firstSpace = response.indexOf( ' ' );
00820 int secondSpace = response.indexOf( ' ', firstSpace + 1 );
00821 return response.mid( firstSpace + 1, secondSpace - firstSpace - 1 ).toInt();
00822 }
00823
00824 void HTTPProtocol::davParsePropstats( const QDomNodeList& propstats, UDSEntry& entry )
00825 {
00826 QString mimeType;
00827 bool foundExecutable = false;
00828 bool isDirectory = false;
00829 uint lockCount = 0;
00830 uint supportedLockCount = 0;
00831
00832 for ( int i = 0; i < propstats.count(); i++)
00833 {
00834 QDomElement propstat = propstats.item(i).toElement();
00835
00836 QDomElement status = propstat.namedItem( "status" ).toElement();
00837 if ( status.isNull() )
00838 {
00839
00840 kDebug(7113) << "Error, no status code in this propstat";
00841 return;
00842 }
00843
00844 int code = codeFromResponse( status.text() );
00845
00846 if ( code != 200 )
00847 {
00848 kDebug(7113) << "Warning: status code" << code << "(this may mean that some properties are unavailable";
00849 continue;
00850 }
00851
00852 QDomElement prop = propstat.namedItem( "prop" ).toElement();
00853 if ( prop.isNull() )
00854 {
00855 kDebug(7113) << "Error: no prop segment in this propstat.";
00856 return;
00857 }
00858
00859 if ( hasMetaData( "davRequestResponse" ) )
00860 {
00861 QDomDocument doc;
00862 doc.appendChild(prop);
00863 entry.insert( KIO::UDSEntry::UDS_XML_PROPERTIES, doc.toString() );
00864 }
00865
00866 for ( QDomNode n = prop.firstChild(); !n.isNull(); n = n.nextSibling() )
00867 {
00868 QDomElement property = n.toElement();
00869 if (property.isNull())
00870 continue;
00871
00872 if ( property.namespaceURI() != "DAV:" )
00873 {
00874
00875 continue;
00876 }
00877
00878 if ( property.tagName() == "creationdate" )
00879 {
00880
00881 entry.insert( KIO::UDSEntry::UDS_CREATION_TIME, parseDateTime( property.text(), property.attribute("dt") ) );
00882 }
00883 else if ( property.tagName() == "getcontentlength" )
00884 {
00885
00886 entry.insert( KIO::UDSEntry::UDS_SIZE, property.text().toULong() );
00887 }
00888 else if ( property.tagName() == "displayname" )
00889 {
00890
00891 setMetaData( "davDisplayName", property.text() );
00892 }
00893 else if ( property.tagName() == "source" )
00894 {
00895
00896 QDomElement source = property.namedItem( "link" ).toElement()
00897 .namedItem( "dst" ).toElement();
00898 if ( !source.isNull() )
00899 setMetaData( "davSource", source.text() );
00900 }
00901 else if ( property.tagName() == "getcontentlanguage" )
00902 {
00903
00904 setMetaData( "davContentLanguage", property.text() );
00905 }
00906 else if ( property.tagName() == "getcontenttype" )
00907 {
00908
00909
00910
00911 if ( property.text() == "httpd/unix-directory" )
00912 {
00913 isDirectory = true;
00914 }
00915 else
00916 {
00917 mimeType = property.text();
00918 }
00919 }
00920 else if ( property.tagName() == "executable" )
00921 {
00922
00923 if ( property.text() == "T" )
00924 foundExecutable = true;
00925
00926 }
00927 else if ( property.tagName() == "getlastmodified" )
00928 {
00929
00930 entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, parseDateTime( property.text(), property.attribute("dt") ) );
00931 }
00932 else if ( property.tagName() == "getetag" )
00933 {
00934
00935 setMetaData( "davEntityTag", property.text() );
00936 }
00937 else if ( property.tagName() == "supportedlock" )
00938 {
00939
00940 for ( QDomNode n2 = property.firstChild(); !n2.isNull(); n2 = n2.nextSibling() )
00941 {
00942 QDomElement lockEntry = n2.toElement();
00943 if ( lockEntry.tagName() == "lockentry" )
00944 {
00945 QDomElement lockScope = lockEntry.namedItem( "lockscope" ).toElement();
00946 QDomElement lockType = lockEntry.namedItem( "locktype" ).toElement();
00947 if ( !lockScope.isNull() && !lockType.isNull() )
00948 {
00949
00950 supportedLockCount++;
00951 QString scope = lockScope.firstChild().toElement().tagName();
00952 QString type = lockType.firstChild().toElement().tagName();
00953
00954 setMetaData( QString("davSupportedLockScope%1").arg(supportedLockCount), scope );
00955 setMetaData( QString("davSupportedLockType%1").arg(supportedLockCount), type );
00956 }
00957 }
00958 }
00959 }
00960 else if ( property.tagName() == "lockdiscovery" )
00961 {
00962
00963 davParseActiveLocks( property.elementsByTagName( "activelock" ), lockCount );
00964 }
00965 else if ( property.tagName() == "resourcetype" )
00966 {
00967
00968 if ( !property.namedItem( "collection" ).toElement().isNull() )
00969 {
00970
00971 isDirectory = true;
00972 }
00973 }
00974 else
00975 {
00976 kDebug(7113) << "Found unknown webdav property: " << property.tagName();
00977 }
00978 }
00979 }
00980
00981 setMetaData( "davLockCount", QString("%1").arg(lockCount) );
00982 setMetaData( "davSupportedLockCount", QString("%1").arg(supportedLockCount) );
00983
00984 entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, isDirectory ? S_IFDIR : S_IFREG );
00985
00986 if ( foundExecutable || isDirectory )
00987 {
00988
00989 entry.insert( KIO::UDSEntry::UDS_ACCESS, 0700 );
00990 }
00991 else
00992 {
00993 entry.insert( KIO::UDSEntry::UDS_ACCESS, 0600 );
00994 }
00995
00996 if ( !isDirectory && !mimeType.isEmpty() )
00997 {
00998 entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, mimeType );
00999 }
01000 }
01001
01002 void HTTPProtocol::davParseActiveLocks( const QDomNodeList& activeLocks,
01003 uint& lockCount )
01004 {
01005 for ( int i = 0; i < activeLocks.count(); i++ )
01006 {
01007 QDomElement activeLock = activeLocks.item(i).toElement();
01008
01009 lockCount++;
01010
01011 QDomElement lockScope = activeLock.namedItem( "lockscope" ).toElement();
01012 QDomElement lockType = activeLock.namedItem( "locktype" ).toElement();
01013 QDomElement lockDepth = activeLock.namedItem( "depth" ).toElement();
01014
01015 QDomElement lockOwner = activeLock.namedItem( "owner" ).toElement();
01016 QDomElement lockTimeout = activeLock.namedItem( "timeout" ).toElement();
01017 QDomElement lockToken = activeLock.namedItem( "locktoken" ).toElement();
01018
01019 if ( !lockScope.isNull() && !lockType.isNull() && !lockDepth.isNull() )
01020 {
01021
01022 lockCount++;
01023 QString scope = lockScope.firstChild().toElement().tagName();
01024 QString type = lockType.firstChild().toElement().tagName();
01025 QString depth = lockDepth.text();
01026
01027 setMetaData( QString("davLockScope%1").arg( lockCount ), scope );
01028 setMetaData( QString("davLockType%1").arg( lockCount ), type );
01029 setMetaData( QString("davLockDepth%1").arg( lockCount ), depth );
01030
01031 if ( !lockOwner.isNull() )
01032 setMetaData( QString("davLockOwner%1").arg( lockCount ), lockOwner.text() );
01033
01034 if ( !lockTimeout.isNull() )
01035 setMetaData( QString("davLockTimeout%1").arg( lockCount ), lockTimeout.text() );
01036
01037 if ( !lockToken.isNull() )
01038 {
01039 QDomElement tokenVal = lockScope.namedItem( "href" ).toElement();
01040 if ( !tokenVal.isNull() )
01041 setMetaData( QString("davLockToken%1").arg( lockCount ), tokenVal.text() );
01042 }
01043 }
01044 }
01045 }
01046
01047 long HTTPProtocol::parseDateTime( const QString& input, const QString& type )
01048 {
01049 if ( type == "dateTime.tz" )
01050 {
01051 return KDateTime::fromString( input, KDateTime::ISODate ).toTime_t();
01052 }
01053 else if ( type == "dateTime.rfc1123" )
01054 {
01055 return KDateTime::fromString( input, KDateTime::RFCDate ).toTime_t();
01056 }
01057
01058
01059 time_t time = KDateTime::fromString( input, KDateTime::RFCDate ).toTime_t();
01060 if ( time != 0 )
01061 return time;
01062
01063 return KDateTime::fromString( input, KDateTime::ISODate ).toTime_t();
01064 }
01065
01066 QString HTTPProtocol::davProcessLocks()
01067 {
01068 if ( hasMetaData( "davLockCount" ) )
01069 {
01070 QString response("If:");
01071 int numLocks;
01072 numLocks = metaData( "davLockCount" ).toInt();
01073 bool bracketsOpen = false;
01074 for ( int i = 0; i < numLocks; i++ )
01075 {
01076 if ( hasMetaData( QString("davLockToken%1").arg(i) ) )
01077 {
01078 if ( hasMetaData( QString("davLockURL%1").arg(i) ) )
01079 {
01080 if ( bracketsOpen )
01081 {
01082 response += ')';
01083 bracketsOpen = false;
01084 }
01085 response += " <" + metaData( QString("davLockURL%1").arg(i) ) + '>';
01086 }
01087
01088 if ( !bracketsOpen )
01089 {
01090 response += " (";
01091 bracketsOpen = true;
01092 }
01093 else
01094 {
01095 response += ' ';
01096 }
01097
01098 if ( hasMetaData( QString("davLockNot%1").arg(i) ) )
01099 response += "Not ";
01100
01101 response += '<' + metaData( QString("davLockToken%1").arg(i) ) + '>';
01102 }
01103 }
01104
01105 if ( bracketsOpen )
01106 response += ')';
01107
01108 response += "\r\n";
01109 return response;
01110 }
01111
01112 return QString();
01113 }
01114
01115 bool HTTPProtocol::davHostOk()
01116 {
01117
01118 return true;
01119
01120
01121 if ( m_davHostOk )
01122 {
01123 kDebug(7113) << "true";
01124 return true;
01125 }
01126 else if ( m_davHostUnsupported )
01127 {
01128 kDebug(7113) << " false";
01129 davError( -2 );
01130 return false;
01131 }
01132
01133 m_request.method = HTTP_OPTIONS;
01134
01135
01136 m_request.url.setPath("*");
01137 m_request.url.setQuery(QString());
01138 m_request.cacheTag.policy = CC_Reload;
01139
01140
01141 m_davCapabilities.clear();
01142
01143 proceedUntilResponseHeader();
01144
01145 if (m_davCapabilities.count())
01146 {
01147 for (int i = 0; i < m_davCapabilities.count(); i++)
01148 {
01149 bool ok;
01150 uint verNo = m_davCapabilities[i].toUInt(&ok);
01151 if (ok && verNo > 0 && verNo < 3)
01152 {
01153 m_davHostOk = true;
01154 kDebug(7113) << "Server supports DAV version" << verNo;
01155 }
01156 }
01157
01158 if ( m_davHostOk )
01159 return true;
01160 }
01161
01162 m_davHostUnsupported = true;
01163 davError( -2 );
01164 return false;
01165 }
01166
01167
01168
01169 void HTTPProtocol::davFinished()
01170 {
01171
01172 httpClose(m_request.isKeepAlive);
01173 finished();
01174 }
01175
01176 void HTTPProtocol::mkdir( const KUrl& url, int )
01177 {
01178 kDebug(7113) << url.url();
01179
01180 if (!maybeSetRequestUrl(url))
01181 return;
01182 resetSessionSettings();
01183
01184 m_request.method = DAV_MKCOL;
01185 m_request.url.setQuery(QString());
01186 m_request.cacheTag.policy = CC_Reload;
01187
01188 proceedUntilResponseHeader();
01189
01190 if ( m_request.responseCode == 201 )
01191 davFinished();
01192 else
01193 davError();
01194 }
01195
01196 void HTTPProtocol::get( const KUrl& url )
01197 {
01198 kDebug(7113) << url.url();
01199
01200 if (!maybeSetRequestUrl(url))
01201 return;
01202 resetSessionSettings();
01203
01204 m_request.method = HTTP_GET;
01205
01206 QString tmp(metaData("cache"));
01207 if (!tmp.isEmpty())
01208 m_request.cacheTag.policy = parseCacheControl(tmp);
01209 else
01210 m_request.cacheTag.policy = DEFAULT_CACHE_CONTROL;
01211
01212 proceedUntilResponseContent();
01213 }
01214
01215 void HTTPProtocol::put( const KUrl &url, int, KIO::JobFlags flags )
01216 {
01217 kDebug(7113) << url.url();
01218
01219 if (!maybeSetRequestUrl(url))
01220 return;
01221 resetSessionSettings();
01222
01223
01224 if (!(flags & KIO::Overwrite) && m_protocol.startsWith("webdav")) {
01225
01226 if ( !davHostOk() )
01227 return;
01228
01229 QByteArray request = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
01230 "<D:propfind xmlns:D=\"DAV:\"><D:prop>"
01231 "<D:creationdate/>"
01232 "<D:getcontentlength/>"
01233 "<D:displayname/>"
01234 "<D:resourcetype/>"
01235 "</D:prop></D:propfind>";
01236
01237 davSetRequest( request );
01238
01239
01240 m_request.method = DAV_PROPFIND;
01241 m_request.url.setQuery(QString());
01242 m_request.cacheTag.policy = CC_Reload;
01243 m_request.davData.depth = 0;
01244
01245 proceedUntilResponseContent(true);
01246
01247 if (m_request.responseCode == 207) {
01248 error(ERR_FILE_ALREADY_EXIST, QString());
01249 return;
01250 }
01251
01252 m_isError = false;
01253 }
01254
01255 m_request.method = HTTP_PUT;
01256 m_request.url.setQuery(QString());
01257 m_request.cacheTag.policy = CC_Reload;
01258
01259 proceedUntilResponseHeader();
01260
01261 kDebug(7113) << "error = " << m_isError;
01262 if (m_isError)
01263 return;
01264
01265 kDebug(7113) << "responseCode = " << m_request.responseCode;
01266
01267 httpClose(false);
01268
01269 if ( (m_request.responseCode >= 200) && (m_request.responseCode < 300) )
01270 finished();
01271 else
01272 httpError();
01273 }
01274
01275 void HTTPProtocol::copy( const KUrl& src, const KUrl& dest, int, KIO::JobFlags flags )
01276 {
01277 kDebug(7113) << src.url() << "->" << dest.url();
01278
01279 if (!maybeSetRequestUrl(dest) || !maybeSetRequestUrl(src))
01280 return;
01281 resetSessionSettings();
01282
01283
01284 KUrl newDest = dest;
01285 if (newDest.protocol() == "webdavs")
01286 newDest.setProtocol("https");
01287 else
01288 newDest.setProtocol("http");
01289
01290 m_request.method = DAV_COPY;
01291 m_request.davData.desturl = newDest.url();
01292 m_request.davData.overwrite = (flags & KIO::Overwrite);
01293 m_request.url.setQuery(QString());
01294 m_request.cacheTag.policy = CC_Reload;
01295
01296 proceedUntilResponseHeader();
01297
01298
01299 if ( m_request.responseCode == 201 || m_request.responseCode == 204 )
01300 davFinished();
01301 else
01302 davError();
01303 }
01304
01305 void HTTPProtocol::rename( const KUrl& src, const KUrl& dest, KIO::JobFlags flags )
01306 {
01307 kDebug(7113) << src.url() << "->" << dest.url();
01308
01309 if (!maybeSetRequestUrl(dest) || !maybeSetRequestUrl(src))
01310 return;
01311 resetSessionSettings();
01312
01313
01314 KUrl newDest = dest;
01315 if (newDest.protocol() == "webdavs")
01316 newDest.setProtocol("https");
01317 else
01318 newDest.setProtocol("http");
01319
01320 m_request.method = DAV_MOVE;
01321 m_request.davData.desturl = newDest.url();
01322 m_request.davData.overwrite = (flags & KIO::Overwrite);
01323 m_request.url.setQuery(QString());
01324 m_request.cacheTag.policy = CC_Reload;
01325
01326 proceedUntilResponseHeader();
01327
01328 if ( m_request.responseCode == 201 )
01329 davFinished();
01330 else
01331 davError();
01332 }
01333
01334 void HTTPProtocol::del( const KUrl& url, bool )
01335 {
01336 kDebug(7113) << url.url();
01337
01338 if (!maybeSetRequestUrl(url))
01339 return;
01340 resetSessionSettings();
01341
01342 m_request.method = HTTP_DELETE;
01343 m_request.url.setQuery(QString());;
01344 m_request.cacheTag.policy = CC_Reload;
01345
01346 proceedUntilResponseHeader();
01347
01348
01349
01350 if ( m_protocol.startsWith( "webdav" ) ) {
01351 if ( m_request.responseCode == 200 || m_request.responseCode == 204 )
01352 davFinished();
01353 else
01354 davError();
01355 } else {
01356 if ( m_request.responseCode == 200 || m_request.responseCode == 204 )
01357 finished();
01358 else
01359 error( ERR_SLAVE_DEFINED, i18n( "The resource cannot be deleted." ) );
01360 }
01361 }
01362
01363 void HTTPProtocol::post( const KUrl& url )
01364 {
01365 kDebug(7113) << url.url();
01366
01367 if (!maybeSetRequestUrl(url))
01368 return;
01369 resetSessionSettings();
01370
01371 m_request.method = HTTP_POST;
01372 m_request.cacheTag.policy= CC_Reload;
01373
01374 proceedUntilResponseContent();
01375 }
01376
01377 void HTTPProtocol::davLock( const KUrl& url, const QString& scope,
01378 const QString& type, const QString& owner )
01379 {
01380 kDebug(7113) << url.url();
01381
01382 if (!maybeSetRequestUrl(url))
01383 return;
01384 resetSessionSettings();
01385
01386 m_request.method = DAV_LOCK;
01387 m_request.url.setQuery(QString());
01388 m_request.cacheTag.policy= CC_Reload;
01389
01390
01391 QDomDocument lockReq;
01392
01393 QDomElement lockInfo = lockReq.createElementNS( "DAV:", "lockinfo" );
01394 lockReq.appendChild( lockInfo );
01395
01396 QDomElement lockScope = lockReq.createElement( "lockscope" );
01397 lockInfo.appendChild( lockScope );
01398
01399 lockScope.appendChild( lockReq.createElement( scope ) );
01400
01401 QDomElement lockType = lockReq.createElement( "locktype" );
01402 lockInfo.appendChild( lockType );
01403
01404 lockType.appendChild( lockReq.createElement( type ) );
01405
01406 if ( !owner.isNull() ) {
01407 QDomElement ownerElement = lockReq.createElement( "owner" );
01408 lockReq.appendChild( ownerElement );
01409
01410 QDomElement ownerHref = lockReq.createElement( "href" );
01411 ownerElement.appendChild( ownerHref );
01412
01413 ownerHref.appendChild( lockReq.createTextNode( owner ) );
01414 }
01415
01416
01417 m_POSTbuf = lockReq.toByteArray();
01418
01419 proceedUntilResponseContent( true );
01420
01421 if ( m_request.responseCode == 200 ) {
01422
01423 QDomDocument multiResponse;
01424 multiResponse.setContent( m_webDavDataBuf, true );
01425
01426 QDomElement prop = multiResponse.documentElement().namedItem( "prop" ).toElement();
01427
01428 QDomElement lockdiscovery = prop.namedItem( "lockdiscovery" ).toElement();
01429
01430 uint lockCount = 0;
01431 davParseActiveLocks( lockdiscovery.elementsByTagName( "activelock" ), lockCount );
01432
01433 setMetaData( "davLockCount", QString("%1").arg( lockCount ) );
01434
01435 finished();
01436
01437 } else
01438 davError();
01439 }
01440
01441 void HTTPProtocol::davUnlock( const KUrl& url )
01442 {
01443 kDebug(7113) << url.url();
01444
01445 if (!maybeSetRequestUrl(url))
01446 return;
01447 resetSessionSettings();
01448
01449 m_request.method = DAV_UNLOCK;
01450 m_request.url.setQuery(QString());
01451 m_request.cacheTag.policy= CC_Reload;
01452
01453 proceedUntilResponseContent( true );
01454
01455 if ( m_request.responseCode == 200 )
01456 finished();
01457 else
01458 davError();
01459 }
01460
01461 QString HTTPProtocol::davError( int code , const QString &_url )
01462 {
01463 bool callError = false;
01464 if ( code == -1 ) {
01465 code = m_request.responseCode;
01466 callError = true;
01467 }
01468 if ( code == -2 ) {
01469 callError = true;
01470 }
01471
01472 QString url = _url;
01473 if ( !url.isNull() )
01474 url = m_request.url.url();
01475
01476 QString action, errorString;
01477 KIO::Error kError;
01478
01479
01480 QString ow = i18n( "Otherwise, the request would have succeeded." );
01481
01482 switch ( m_request.method ) {
01483 case DAV_PROPFIND:
01484 action = i18nc( "request type", "retrieve property values" );
01485 break;
01486 case DAV_PROPPATCH:
01487 action = i18nc( "request type", "set property values" );
01488 break;
01489 case DAV_MKCOL:
01490 action = i18nc( "request type", "create the requested folder" );
01491 break;
01492 case DAV_COPY:
01493 action = i18nc( "request type", "copy the specified file or folder" );
01494 break;
01495 case DAV_MOVE:
01496 action = i18nc( "request type", "move the specified file or folder" );
01497 break;
01498 case DAV_SEARCH:
01499 action = i18nc( "request type", "search in the specified folder" );
01500 break;
01501 case DAV_LOCK:
01502 action = i18nc( "request type", "lock the specified file or folder" );
01503 break;
01504 case DAV_UNLOCK:
01505 action = i18nc( "request type", "unlock the specified file or folder" );
01506 break;
01507 case HTTP_DELETE:
01508 action = i18nc( "request type", "delete the specified file or folder" );
01509 break;
01510 case HTTP_OPTIONS:
01511 action = i18nc( "request type", "query the server's capabilities" );
01512 break;
01513 case HTTP_GET:
01514 action = i18nc( "request type", "retrieve the contents of the specified file or folder" );
01515 break;
01516 case HTTP_PUT:
01517 case HTTP_POST:
01518 case HTTP_HEAD:
01519 default:
01520
01521 Q_ASSERT(0);
01522 }
01523
01524
01525 kError = ERR_INTERNAL;
01526 errorString = i18nc("%1: code, %2: request type", "An unexpected error (%1) occurred "
01527 "while attempting to %2.", code, action);
01528
01529 switch ( code )
01530 {
01531 case -2:
01532
01533 kError = ERR_UNSUPPORTED_PROTOCOL;
01534 errorString = i18n("The server does not support the WebDAV protocol.");
01535 break;
01536 case 207:
01537
01538 {
01539
01540
01541
01542
01543
01544 if ( !readBody( true ) && m_isError )
01545 return QString();
01546
01547 QStringList errors;
01548 QDomDocument multiResponse;
01549
01550 multiResponse.setContent( m_webDavDataBuf, true );
01551
01552 QDomElement multistatus = multiResponse.documentElement().namedItem( "multistatus" ).toElement();
01553
01554 QDomNodeList responses = multistatus.elementsByTagName( "response" );
01555
01556 for (int i = 0; i < responses.count(); i++)
01557 {
01558 int errCode;
01559 QString errUrl;
01560
01561 QDomElement response = responses.item(i).toElement();
01562 QDomElement code = response.namedItem( "status" ).toElement();
01563
01564 if ( !code.isNull() )
01565 {
01566 errCode = codeFromResponse( code.text() );
01567 QDomElement href = response.namedItem( "href" ).toElement();
01568 if ( !href.isNull() )
01569 errUrl = href.text();
01570 errors << davError( errCode, errUrl );
01571 }
01572 }
01573
01574
01575 errorString = i18nc( "%1: request type, %2: url",
01576 "An error occurred while attempting to %1, %2. A "
01577 "summary of the reasons is below.", action, url );
01578
01579 errorString += "<ul>";
01580
01581 for ( QStringList::const_iterator it = errors.constBegin(); it != errors.constEnd(); ++it )
01582 errorString += "<li>" + *it + "</li>";
01583
01584 errorString += "</ul>";
01585 }
01586 case 403:
01587 case 500:
01588
01589 kError = ERR_ACCESS_DENIED;
01590 errorString = i18nc( "%1: request type", "Access was denied while attempting to %1.", action );
01591 break;
01592 case 405:
01593
01594 if ( m_request.method == DAV_MKCOL )
01595 {
01596 kError = ERR_DIR_ALREADY_EXIST;
01597 errorString = i18n("The specified folder already exists.");
01598 }
01599 break;
01600 case 409:
01601
01602 kError = ERR_ACCESS_DENIED;
01603 errorString = i18n("A resource cannot be created at the destination "
01604 "until one or more intermediate collections (folders) "
01605 "have been created.");
01606 break;
01607 case 412:
01608
01609 if ( m_request.method == DAV_COPY || m_request.method == DAV_MOVE )
01610 {
01611 kError = ERR_ACCESS_DENIED;
01612 errorString = i18n("The server was unable to maintain the liveness of "
01613 "the properties listed in the propertybehavior XML "
01614 "element or you attempted to overwrite a file while "
01615 "requesting that files are not overwritten. %1",
01616 ow );
01617
01618 }
01619 else if ( m_request.method == DAV_LOCK )
01620 {
01621 kError = ERR_ACCESS_DENIED;
01622 errorString = i18n("The requested lock could not be granted. %1", ow );
01623 }
01624 break;
01625 case 415:
01626
01627 kError = ERR_ACCESS_DENIED;
01628 errorString = i18n("The server does not support the request type of the body.");
01629 break;
01630 case 423:
01631
01632 kError = ERR_ACCESS_DENIED;
01633 errorString = i18nc( "%1: request type", "Unable to %1 because the resource is locked.", action );
01634 break;
01635 case 425:
01636
01637 errorString = i18n("This action was prevented by another error.");
01638 break;
01639 case 502:
01640
01641 if ( m_request.method == DAV_COPY || m_request.method == DAV_MOVE )
01642 {
01643 kError = ERR_WRITE_ACCESS_DENIED;
01644 errorString = i18nc( "%1: request type", "Unable to %1 because the destination server refuses "
01645 "to accept the file or folder.", action );
01646 }
01647 break;
01648 case 507:
01649
01650 kError = ERR_DISK_FULL;
01651 errorString = i18n("The destination resource does not have sufficient space "
01652 "to record the state of the resource after the execution "
01653 "of this method.");
01654 break;
01655 }
01656
01657
01658
01659
01660 if ( callError )
01661 error( ERR_SLAVE_DEFINED, errorString );
01662
01663 return errorString;
01664 }
01665
01666 void HTTPProtocol::httpError()
01667 {
01668 QString action, errorString;
01669 KIO::Error kError;
01670
01671 switch ( m_request.method ) {
01672 case HTTP_PUT:
01673 action = i18nc("request type", "upload %1", m_request.url.prettyUrl());
01674 break;
01675 default:
01676
01677
01678 Q_ASSERT(0);
01679 }
01680
01681
01682 kError = ERR_INTERNAL;
01683 errorString = i18nc("%1: response code, %2: request type",
01684 "An unexpected error (%1) occurred while attempting to %2.",
01685 m_request.responseCode, action);
01686
01687 switch ( m_request.responseCode )
01688 {
01689 case 403:
01690 case 405:
01691 case 500:
01692
01693
01694 kError = ERR_ACCESS_DENIED;
01695 errorString = i18nc( "%1: request type", "Access was denied while attempting to %1.", action );
01696 break;
01697 case 409:
01698
01699 kError = ERR_ACCESS_DENIED;
01700 errorString = i18n("A resource cannot be created at the destination "
01701 "until one or more intermediate collections (folders) "
01702 "have been created.");
01703 break;
01704 case 423:
01705
01706 kError = ERR_ACCESS_DENIED;
01707 errorString = i18nc( "%1: request type", "Unable to %1 because the resource is locked.", action );
01708 break;
01709 case 502:
01710
01711 kError = ERR_WRITE_ACCESS_DENIED;
01712 errorString = i18nc( "%1: request type", "Unable to %1 because the destination server refuses "
01713 "to accept the file or folder.", action );
01714 break;
01715 case 507:
01716
01717 kError = ERR_DISK_FULL;
01718 errorString = i18n("The destination resource does not have sufficient space "
01719 "to record the state of the resource after the execution "
01720 "of this method.");
01721 break;
01722 }
01723
01724
01725
01726
01727 error( ERR_SLAVE_DEFINED, errorString );
01728 }
01729
01730 bool HTTPProtocol::isOffline(const KUrl &url)
01731 {
01732 const int NetWorkStatusUnknown = 1;
01733 const int NetWorkStatusOnline = 8;
01734
01735 QDBusReply<int> reply =
01736 QDBusInterface( "org.kde.kded", "/modules/networkstatus", "org.kde.NetworkStatusModule" ).
01737 call( "status", url.url() );
01738
01739 if ( reply.isValid() )
01740 {
01741 int result = reply;
01742 kDebug(7113) << "networkstatus status = " << result;
01743 return (result != NetWorkStatusUnknown) && (result != NetWorkStatusOnline);
01744 }
01745 kDebug(7113) << "networkstatus <unreachable>";
01746 return false;
01747 }
01748
01749 void HTTPProtocol::multiGet(const QByteArray &data)
01750 {
01751 QDataStream stream(data);
01752 quint32 n;
01753 stream >> n;
01754
01755 kDebug(7113) << n;
01756
01757 HTTPRequest saveRequest;
01758 if (m_isBusy)
01759 saveRequest = m_request;
01760
01761 resetSessionSettings();
01762
01763 for (unsigned i = 0; i < n; i++) {
01764 KUrl url;
01765 stream >> url >> mIncomingMetaData;
01766
01767 if (!maybeSetRequestUrl(url))
01768 continue;
01769
01770
01771
01772
01773 kDebug(7113) << url.url();
01774
01775 m_request.method = HTTP_GET;
01776 m_request.isKeepAlive = true;
01777
01778 QString tmp = metaData("cache");
01779 if (!tmp.isEmpty())
01780 m_request.cacheTag.policy= parseCacheControl(tmp);
01781 else
01782 m_request.cacheTag.policy= DEFAULT_CACHE_CONTROL;
01783
01784 m_requestQueue.append(m_request);
01785 }
01786
01787 if (m_isBusy)
01788 m_request = saveRequest;
01789 #if 0
01790 if (!m_isBusy) {
01791 m_isBusy = true;
01792 QMutableListIterator<HTTPRequest> it(m_requestQueue);
01793 while (it.hasNext()) {
01794 m_request = it.next();
01795 it.remove();
01796 proceedUntilResponseContent();
01797 }
01798 m_isBusy = false;
01799 }
01800 #endif
01801 if (!m_isBusy) {
01802 m_isBusy = true;
01803 QMutableListIterator<HTTPRequest> it(m_requestQueue);
01804
01805 while (it.hasNext()) {
01806 m_request = it.next();
01807 sendQuery();
01808
01809 it.setValue(m_request);
01810 kDebug(7113) << "check one: isKeepAlive =" << m_request.isKeepAlive;
01811 if (!m_request.cacheTag.readFromCache) {
01812 m_server.initFrom(m_request);
01813 }
01814 }
01815
01816
01817
01818 int requestId = 0;
01819 foreach (const HTTPRequest &r, m_requestQueue) {
01820 m_request = r;
01821 kDebug(7113) << "check two: isKeepAlive =" << m_request.isKeepAlive;
01822 setMetaData("request-id", QString::number(requestId++));
01823 sendAndKeepMetaData();
01824 if (!(readResponseHeader() && readBody())) {
01825 return;
01826 }
01827
01828
01829 kDebug(7113) << "check three: isKeepAlive =" << m_request.isKeepAlive;
01830 httpClose(m_request.isKeepAlive);
01831 }
01832
01833 finished();
01834 m_requestQueue.clear();
01835 m_isBusy = false;
01836 }
01837 }
01838
01839 ssize_t HTTPProtocol::write (const void *_buf, size_t nbytes)
01840 {
01841 size_t sent = 0;
01842 const char* buf = static_cast<const char*>(_buf);
01843 while (sent < nbytes)
01844 {
01845 int n = TCPSlaveBase::write(buf + sent, nbytes - sent);
01846
01847 if (n < 0) {
01848
01849 return -1;
01850 }
01851
01852 sent += n;
01853 }
01854
01855 return sent;
01856 }
01857
01858 void HTTPProtocol::clearUnreadBuffer()
01859 {
01860 m_unreadBuf.clear();
01861 }
01862
01863 void HTTPProtocol::unread(char *buf, size_t size)
01864 {
01865
01866 const int newSize = m_unreadBuf.size() + size;
01867 m_unreadBuf.resize(newSize);
01868 for (int i = 0; i < size; i++) {
01869 m_unreadBuf.data()[newSize - i - 1] = buf[i];
01870 }
01871 if (size) {
01872
01873 m_isEOF = false;
01874 }
01875 }
01876
01877 size_t HTTPProtocol::readBuffered(char *buf, size_t size)
01878 {
01879 size_t bytesRead = 0;
01880 if (!m_unreadBuf.isEmpty()) {
01881 const int bufSize = m_unreadBuf.size();
01882 bytesRead = qMin((int)size, bufSize);
01883
01884 for (int i = 0; i < bytesRead; i++) {
01885 buf[i] = m_unreadBuf.constData()[bufSize - i - 1];
01886 }
01887 m_unreadBuf.truncate(bufSize - bytesRead);
01888 }
01889 if (bytesRead < size) {
01890 int rawRead = TCPSlaveBase::read(buf + bytesRead, size - bytesRead);
01891 if (rawRead < 1) {
01892 m_isEOF = true;
01893 return bytesRead;
01894 }
01895 bytesRead += rawRead;
01896 }
01897 return bytesRead;
01898 }
01899
01900
01901
01902
01903
01904 bool HTTPProtocol::readDelimitedText(char *buf, int *idx, int end, int numNewlines)
01905 {
01906 Q_ASSERT(numNewlines >=1 && numNewlines <= 2);
01907 char mybuf[64];
01908 int pos = *idx;
01909 while (pos < end && !m_isEOF) {
01910 int step = qMin((int)sizeof(mybuf), end - pos);
01911 if (m_isChunked) {
01912
01913
01914
01915 step = 1;
01916 }
01917 size_t bufferFill = readBuffered(mybuf, step);
01918
01919 for (int i = 0; i < bufferFill ; i++, pos++) {
01920 char c = mybuf[i];
01921 buf[pos] = c;
01922
01923
01924 if (c == '\n' && pos > (2 * numNewlines - 2) && buf[pos - 1] == '\r' &&
01925 ((numNewlines == 1) || (buf[pos - 3] == '\r' && buf [pos - 2] == '\n'))) {
01926 i++;
01927 unread(&mybuf[i], bufferFill - i);
01928 *idx = pos + 1;
01929 return true;
01930 }
01931 }
01932 }
01933 *idx = pos;
01934 return false;
01935 }
01936
01937
01938 bool HTTPProtocol::httpShouldCloseConnection()
01939 {
01940 kDebug(7113) << "Keep Alive:" << m_request.isKeepAlive << "First:" << m_isFirstRequest;
01941
01942 if (m_isFirstRequest || !isConnected()) {
01943 return false;
01944 }
01945
01946 if (m_request.method != HTTP_GET && m_request.method != HTTP_POST) {
01947 return true;
01948 }
01949
01950 if (m_request.proxyUrl != m_server.proxyUrl) {
01951 return true;
01952 }
01953
01954
01955
01956
01957 if (isValidProxy(m_request.proxyUrl)) {
01958 if (m_request.proxyUrl != m_server.proxyUrl ||
01959 m_request.proxyUrl.user() != m_server.proxyUrl.user() ||
01960 m_request.proxyUrl.pass() != m_server.proxyUrl.pass()) {
01961 return true;
01962 }
01963 } else {
01964 if (m_request.url.host() != m_server.url.host() ||
01965 m_request.url.port() != m_server.url.port() ||
01966 m_request.url.user() != m_server.url.user() ||
01967 m_request.url.pass() != m_server.url.pass()) {
01968 return true;
01969 }
01970 }
01971 return false;
01972 }
01973
01974 bool HTTPProtocol::httpOpenConnection()
01975 {
01976 kDebug(7113);
01977 m_server.clear();
01978
01979
01980
01981 disconnect(socket(), SIGNAL(connected()),
01982 this, SLOT(saveProxyAuthenticationForSocket()));
01983
01984 clearUnreadBuffer();
01985
01986 bool connectOk = false;
01987 if (isHttpProxy(m_request.proxyUrl) && !isAutoSsl()) {
01988 connectOk = connectToHost(m_request.proxyUrl.protocol(), m_request.proxyUrl.host(), m_request.proxyUrl.port());
01989 } else {
01990 connectOk = connectToHost(m_protocol, m_request.url.host(), m_request.url.port());
01991 }
01992
01993 if (!connectOk) {
01994 return false;
01995 }
01996
01997 #if 0 // QTcpSocket doesn't support this
01998
01999 socket().setNoDelay(true);
02000 #endif
02001
02002 m_isFirstRequest = true;
02003 m_server.initFrom(m_request);
02004 connected();
02005 return true;
02006 }
02007
02008 bool HTTPProtocol::satisfyRequestFromCache(bool *success)
02009 {
02010 m_request.cacheTag.gzs = 0;
02011 m_request.cacheTag.readFromCache = false;
02012 m_request.cacheTag.writeToCache = false;
02013 m_request.cacheTag.isExpired = false;
02014 m_request.cacheTag.expireDate = 0;
02015 m_request.cacheTag.creationDate = 0;
02016
02017 if (m_request.cacheTag.useCache) {
02018
02019 m_request.cacheTag.gzs = checkCacheEntry();
02020 bool bCacheOnly = (m_request.cacheTag.policy == KIO::CC_CacheOnly);
02021 bool bOffline = isOffline(isValidProxy(m_request.proxyUrl) ? m_request.proxyUrl : m_request.url);
02022
02023 if (bOffline && m_request.cacheTag.policy != KIO::CC_Reload) {
02024 m_request.cacheTag.policy= KIO::CC_CacheOnly;
02025 }
02026
02027 if (m_request.cacheTag.policy == CC_Reload && m_request.cacheTag.gzs) {
02028 gzclose(m_request.cacheTag.gzs);
02029 m_request.cacheTag.gzs = 0;
02030 }
02031 if (m_request.cacheTag.policy == KIO::CC_CacheOnly ||
02032 m_request.cacheTag.policy == KIO::CC_Cache) {
02033 m_request.cacheTag.isExpired = false;
02034 }
02035
02036 m_request.cacheTag.writeToCache = true;
02037
02038 if (m_request.cacheTag.gzs && !m_request.cacheTag.isExpired) {
02039
02040 m_request.cacheTag.readFromCache = true;
02041 *success = true;
02042 return true;
02043 } else if (!m_request.cacheTag.gzs) {
02044
02045 m_request.cacheTag.isExpired = false;
02046 } else {
02047
02048 }
02049
02050 if (bCacheOnly) {
02051 error(ERR_DOES_NOT_EXIST, m_request.url.url());
02052 *success = false;
02053 return true;
02054 }
02055 if (bOffline) {
02056 error(ERR_COULD_NOT_CONNECT, m_request.url.url());
02057 *success = false;
02058 return true;
02059 }
02060 }
02061 *success = true;
02062 return false;
02063 }
02064
02065 QString HTTPProtocol::formatRequestUri() const
02066 {
02067
02068
02069
02070 if (isHttpProxy(m_request.proxyUrl) && !isAutoSsl()) {
02071 KUrl u;
02072
02073 QString protocol = m_protocol;
02074 if (protocol.startsWith("webdav")) {
02075 protocol.replace(0, strlen("webdav"), "http");
02076 }
02077 u.setProtocol(protocol);
02078
02079 u.setHost(m_request.url.host());
02080 if (m_request.url.port() != m_defaultPort) {
02081 u.setPort(m_request.url.port());
02082 }
02083 u.setEncodedPathAndQuery(m_request.url.encodedPathAndQuery(
02084 KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath));
02085 return u.url();
02086 } else {
02087 return m_request.url.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath);
02088 }
02089 }
02090
02106 bool HTTPProtocol::sendQuery()
02107 {
02108 kDebug(7113);
02109
02110
02111
02112 if (isEncryptedHttpVariety(m_protocol) && !isAutoSsl()) {
02113 error(ERR_UNSUPPORTED_PROTOCOL, m_protocol);
02114 return false;
02115 }
02116
02117 bool cacheHasPage = false;
02118 if (satisfyRequestFromCache(&cacheHasPage)) {
02119 return cacheHasPage;
02120 }
02121
02122 QString header;
02123
02124 bool hasBodyData = false;
02125 bool hasDavData = false;
02126
02127 {
02128 header = methodString(m_request.method);
02129 QString davHeader;
02130
02131
02132 switch (m_request.method)
02133 {
02134 case HTTP_GET:
02135 case HTTP_HEAD:
02136 break;
02137 case HTTP_PUT:
02138 case HTTP_POST:
02139 hasBodyData = true;
02140 m_request.cacheTag.writeToCache = false;
02141 break;
02142 case HTTP_DELETE:
02143 case HTTP_OPTIONS:
02144 m_request.cacheTag.writeToCache = false;
02145 break;
02146 case DAV_PROPFIND:
02147 hasDavData = true;
02148 davHeader = "Depth: ";
02149 if ( hasMetaData( "davDepth" ) )
02150 {
02151 kDebug(7113) << "Reading DAV depth from metadata: " << metaData( "davDepth" );
02152 davHeader += metaData( "davDepth" );
02153 }
02154 else
02155 {
02156 if ( m_request.davData.depth == 2 )
02157 davHeader += "infinity";
02158 else
02159 davHeader += QString("%1").arg( m_request.davData.depth );
02160 }
02161 davHeader += "\r\n";
02162 m_request.cacheTag.writeToCache = false;
02163 break;
02164 case DAV_PROPPATCH:
02165 hasDavData = true;
02166 m_request.cacheTag.writeToCache = false;
02167 break;
02168 case DAV_MKCOL:
02169 m_request.cacheTag.writeToCache = false;
02170 break;
02171 case DAV_COPY:
02172 case DAV_MOVE:
02173 davHeader = "Destination: " + m_request.davData.desturl;
02174
02175
02176 davHeader += "\r\nDepth: infinity\r\nOverwrite: ";
02177 davHeader += m_request.davData.overwrite ? "T" : "F";
02178 davHeader += "\r\n";
02179 m_request.cacheTag.writeToCache = false;
02180 break;
02181 case DAV_LOCK:
02182 davHeader = "Timeout: ";
02183 {
02184 uint timeout = 0;
02185 if ( hasMetaData( "davTimeout" ) )
02186 timeout = metaData( "davTimeout" ).toUInt();
02187 if ( timeout == 0 )
02188 davHeader += "Infinite";
02189 else
02190 davHeader += QString("Seconds-%1").arg(timeout);
02191 }
02192 davHeader += "\r\n";
02193 m_request.cacheTag.writeToCache = false;
02194 hasDavData = true;
02195 break;
02196 case DAV_UNLOCK:
02197 davHeader = "Lock-token: " + metaData("davLockToken") + "\r\n";
02198 m_request.cacheTag.writeToCache = false;
02199 break;
02200 case DAV_SEARCH:
02201 hasDavData = true;
02202
02203 case DAV_SUBSCRIBE:
02204 case DAV_UNSUBSCRIBE:
02205 case DAV_POLL:
02206 m_request.cacheTag.writeToCache = false;
02207 break;
02208 default:
02209 error (ERR_UNSUPPORTED_ACTION, QString());
02210 return false;
02211 }
02212
02213
02214 header += formatRequestUri() + " HTTP/1.1\r\n";
02215
02216
02217 header += "Host: " + m_request.encoded_hostname;
02218 if (m_request.url.port() != m_defaultPort) {
02219 header += QString(":%1").arg(m_request.url.port());
02220 }
02221 header += "\r\n";
02222
02223
02224
02225
02226
02227 if (isHttpProxy(m_request.proxyUrl) && !isAutoSsl()) {
02228 header += "Proxy-Connection: ";
02229 } else {
02230 header += "Connection: ";
02231 }
02232 if (m_request.isKeepAlive) {
02233 header += "Keep-Alive\r\n";
02234 } else {
02235 header += "close\r\n";
02236 }
02237
02238 if (!m_request.userAgent.isEmpty())
02239 {
02240 header += "User-Agent: ";
02241 header += m_request.userAgent;
02242 header += "\r\n";
02243 }
02244
02245 if (!m_request.referrer.isEmpty())
02246 {
02247 header += "Referer: ";
02248 header += m_request.referrer;
02249 header += "\r\n";
02250 }
02251
02252 if ( m_request.endoffset > m_request.offset )
02253 {
02254 header += QString("Range: bytes=%1-%2\r\n").arg(KIO::number(m_request.offset))
02255 .arg(KIO::number(m_request.endoffset));
02256 kDebug(7103) << "kio_http : Range = " << KIO::number(m_request.offset) <<
02257 " - " << KIO::number(m_request.endoffset);
02258 }
02259 else if ( m_request.offset > 0 && m_request.endoffset == 0 )
02260 {
02261 header += QString("Range: bytes=%1-\r\n").arg(KIO::number(m_request.offset));
02262 kDebug(7103) << "kio_http : Range = " << KIO::number(m_request.offset);
02263 }
02264
02265 if ( m_request.cacheTag.policy== CC_Reload )
02266 {
02267
02268 header += "Pragma: no-cache\r\n";
02269 header += "Cache-control: no-cache\r\n";
02270 }
02271
02272 if (m_request.cacheTag.isExpired)
02273 {
02274
02275 if (!m_request.cacheTag.etag.isEmpty())
02276 header += "If-None-Match: "+m_request.cacheTag.etag+"\r\n";
02277 if (!m_request.cacheTag.lastModified.isEmpty())
02278 header += "If-Modified-Since: "+m_request.cacheTag.lastModified+"\r\n";
02279 }
02280
02281 header += "Accept: ";
02282 QString acceptHeader = metaData("accept");
02283 if (!acceptHeader.isEmpty())
02284 header += acceptHeader;
02285 else
02286 header += DEFAULT_ACCEPT_HEADER;
02287 header += "\r\n";
02288
02289 #ifdef DO_GZIP
02290 if (m_request.allowTransferCompression)
02291 header += "Accept-Encoding: x-gzip, x-deflate, gzip, deflate\r\n";
02292 #endif
02293
02294 if (!m_request.charsets.isEmpty())
02295 header += "Accept-Charset: " + m_request.charsets + "\r\n";
02296
02297 if (!m_request.languages.isEmpty())
02298 header += "Accept-Language: " + m_request.languages + "\r\n";
02299
02300 QString cookieStr;
02301 QString cookieMode = metaData("cookies").toLower();
02302 if (cookieMode == "none")
02303 {
02304 m_request.cookieMode = HTTPRequest::CookiesNone;
02305 }
02306 else if (cookieMode == "manual")
02307 {
02308 m_request.cookieMode = HTTPRequest::CookiesManual;
02309 cookieStr = metaData("setcookies");
02310 }
02311 else
02312 {
02313 m_request.cookieMode = HTTPRequest::CookiesAuto;
02314 if (m_request.useCookieJar)
02315 cookieStr = findCookies(m_request.url.url());
02316 }
02317
02318 if (!cookieStr.isEmpty())
02319 header += cookieStr + "\r\n";
02320
02321 QString customHeader = metaData( "customHTTPHeader" );
02322 if (!customHeader.isEmpty())
02323 {
02324 header += sanitizeCustomHTTPHeader(customHeader);
02325 header += "\r\n";
02326 }
02327
02328 QString contentType = metaData("content-type");
02329 if ((m_request.method == HTTP_POST || m_request.method == HTTP_PUT)
02330 && !contentType.isEmpty())
02331 {
02332 header += contentType;
02333 header += "\r\n";
02334 }
02335
02336
02337
02338
02339
02340
02341
02342 header += authenticationHeader();
02343
02344 if ( m_protocol == "webdav" || m_protocol == "webdavs" )
02345 {
02346 header += davProcessLocks();
02347
02348
02349 davHeader += metaData("davHeader");
02350
02351
02352 if (hasDavData)
02353 davHeader += "Content-Type: text/xml; charset=utf-8\r\n";
02354
02355
02356 header += davHeader;
02357 }
02358 }
02359
02360 kDebug(7103) << "============ Sending Header:";
02361 foreach (const QString &s, header.split("\r\n", QString::SkipEmptyParts)) {
02362 kDebug(7103) << s;
02363 }
02364
02365
02366
02367 if (!hasBodyData && !hasDavData)
02368 header += "\r\n";
02369
02370
02371 if (httpShouldCloseConnection()) {
02372 httpCloseConnection();
02373 }
02374
02375
02376
02377
02378 if ( !isConnected() )
02379 {
02380 if (!httpOpenConnection())
02381 {
02382 kDebug(7113) << "Couldn't connect, oopsie!";
02383 return false;
02384 }
02385 }
02386
02387
02388 resetConnectionSettings();
02389
02390
02391
02392 ssize_t written = write(header.toLatin1(), header.length());
02393 bool sendOk = (written == (ssize_t) header.length());
02394 if (!sendOk)
02395 {
02396 kDebug(7113) << "Connection broken! (" << m_request.url.host() << ")"
02397 << " -- intended to write" << header.length()
02398 << "bytes but wrote" << (int)written << ".";
02399
02400
02401
02402 if (m_request.isKeepAlive)
02403 {
02404 httpCloseConnection();
02405 return true;
02406 }
02407
02408 kDebug(7113) << "sendOk == false. Connection broken !"
02409 << " -- intended to write" << header.length()
02410 << "bytes but wrote" << (int)written << ".";
02411 error( ERR_CONNECTION_BROKEN, m_request.url.host() );
02412 return false;
02413 }
02414 else
02415 kDebug(7113) << "sent it!";
02416
02417 bool res = true;
02418 if (hasBodyData || hasDavData)
02419 res = sendBody();
02420
02421 infoMessage(i18n("%1 contacted. Waiting for reply...", m_request.url.host()));
02422
02423 return res;
02424 }
02425
02426 void HTTPProtocol::forwardHttpResponseHeader()
02427 {
02428
02429 if ( config()->readEntry("PropagateHttpHeader", false) )
02430 {
02431 setMetaData("HTTP-Headers", m_responseHeaders.join("\n"));
02432 sendMetaData();
02433 }
02434 }
02435
02436 bool HTTPProtocol::readHeaderFromCache() {
02437 m_responseHeaders.clear();
02438
02439
02440 char buffer[4097];
02441 if (!gzgets(m_request.cacheTag.gzs, buffer, 4096) )
02442 {
02443
02444 kDebug(7113) << "Could not access cache to obtain mimetype!";
02445 error( ERR_CONNECTION_BROKEN, m_request.url.host() );
02446 return false;
02447 }
02448
02449 m_mimeType = QString::fromLatin1(buffer).trimmed();
02450
02451 kDebug(7113) << "cached data mimetype: " << m_mimeType;
02452
02453
02454 if (!gzgets(m_request.cacheTag.gzs, buffer, 4096) )
02455 {
02456
02457 kDebug(7113) << "Could not access cached data! ";
02458 error( ERR_CONNECTION_BROKEN, m_request.url.host() );
02459 return false;
02460 }
02461 m_responseHeaders << buffer;
02462
02463 while(true) {
02464 if (!gzgets(m_request.cacheTag.gzs, buffer, 8192) )
02465 {
02466
02467 kDebug(7113) << "Could not access cached data!";
02468 error( ERR_CONNECTION_BROKEN, m_request.url.host() );
02469 return false;
02470 }
02471 m_responseHeaders << buffer;
02472 QString header = QString::fromLatin1(buffer).trimmed().toLower();
02473 if (header.isEmpty()) {
02474 break;
02475 }
02476 if (header.startsWith("content-type: ")) {
02477 int pos = header.indexOf("charset=");
02478 if (pos != -1) {
02479 QString charset = header.mid(pos+8);
02480 m_request.cacheTag.charset = charset;
02481 setMetaData("charset", charset);
02482 }
02483 } else
02484 if (header.startsWith("content-language: ")) {
02485 QString language = header.mid(18);
02486 setMetaData("content-language", language);
02487 } else
02488 if (header.startsWith("content-disposition:")) {
02489 parseContentDisposition(header.mid(20));
02490 }
02491 }
02492 forwardHttpResponseHeader();
02493
02494 if (!m_request.cacheTag.lastModified.isEmpty())
02495 setMetaData("modified", m_request.cacheTag.lastModified);
02496
02497 setMetaData("expire-date", QString::number(m_request.cacheTag.expireDate));
02498 setMetaData("cache-creation-date", QString::number(m_request.cacheTag.creationDate));
02499
02500 mimeType(m_mimeType);
02501 return true;
02502 }
02503
02504 void HTTPProtocol::fixupResponseMimetype()
02505 {
02506
02507 if (m_mimeType == "application/x-targz")
02508 m_mimeType = QString::fromLatin1("application/x-compressed-tar");
02509 else if (m_mimeType == "image/x-png")
02510 m_mimeType = QString::fromLatin1("image/png");
02511 else if (m_mimeType == "audio/x-mp3" || m_mimeType == "audio/x-mpeg" || m_mimeType == "audio/mp3")
02512 m_mimeType = QString::fromLatin1("audio/mpeg");
02513 else if (m_mimeType == "audio/microsoft-wave")
02514 m_mimeType = QString::fromLatin1("audio/x-wav");
02515
02516
02517 else if (m_mimeType == "application/pkix-cert" ||
02518 m_mimeType == "application/binary-certificate") {
02519 m_mimeType = QString::fromLatin1("application/x-x509-ca-cert");
02520 }
02521
02522
02523 else if (m_mimeType == "application/x-gzip") {
02524 if ((m_request.url.path().endsWith(".tar.gz")) ||
02525 (m_request.url.path().endsWith(".tar")))
02526 m_mimeType = QString::fromLatin1("application/x-compressed-tar");
02527 if ((m_request.url.path().endsWith(".ps.gz")))
02528 m_mimeType = QString::fromLatin1("application/x-gzpostscript");
02529 }
02530
02531
02532 else if ((m_mimeType == "text/plain") || (m_mimeType == "application/octet-stream")) {
02533 QString ext = m_request.url.path().right(4).toUpper();
02534 if (ext == ".BZ2")
02535 m_mimeType = QString::fromLatin1("application/x-bzip");
02536 else if (ext == ".PEM")
02537 m_mimeType = QString::fromLatin1("application/x-x509-ca-cert");
02538 else if (ext == ".SWF")
02539 m_mimeType = QString::fromLatin1("application/x-shockwave-flash");
02540 else if (ext == ".PLS")
02541 m_mimeType = QString::fromLatin1("audio/x-scpls");
02542 else if (ext == ".WMV")
02543 m_mimeType = QString::fromLatin1("video/x-ms-wmv");
02544 }
02545 }
02546
02547
02554 bool HTTPProtocol::readResponseHeader()
02555 {
02556 resetResponseParsing();
02557 try_again:
02558 kDebug(7113);
02559
02560 if (m_request.cacheTag.readFromCache) {
02561 return readHeaderFromCache();
02562 }
02563
02564
02565
02566 QString locationStr;
02567 QByteArray cookieStr;
02568
02569 QString mediaValue;
02570 QString mediaAttribute;
02571
02572 QStringList upgradeOffers;
02573
02574 bool upgradeRequired = false;
02575
02576
02577
02578 bool canUpgrade = false;
02579
02580
02581 m_request.cacheTag.etag.clear();
02582 m_request.cacheTag.lastModified.clear();
02583 m_request.cacheTag.charset.clear();
02584 m_responseHeaders.clear();
02585
02586 time_t dateHeader = 0;
02587 time_t expireDate = 0;
02588 int currentAge = 0;
02589 int maxAge = -1;
02590 static const int maxHeaderSize = 128 * 1024;
02591
02592 char buffer[maxHeaderSize];
02593 bool cont = false;
02594 bool cacheValidated = false;
02595 bool mayCache = true;
02596 bool hasCacheDirective = false;
02597 bool bCanResume = false;
02598
02599 if (!isConnected()) {
02600 kDebug(7113) << "No connection.";
02601 return false;
02602 }
02603
02604 if (!waitForResponse(m_remoteRespTimeout)) {
02605
02606 error(ERR_SERVER_TIMEOUT , m_request.url.host());
02607 return false;
02608 }
02609
02610 int bufPos = 0;
02611 bool foundDelimiter = readDelimitedText(buffer, &bufPos, maxHeaderSize, 1);
02612 if (!foundDelimiter && bufPos < maxHeaderSize) {
02613 kDebug(7113) << "EOF while waiting for header start.";
02614 if (m_request.isKeepAlive) {
02615
02616 httpCloseConnection();
02617 return false;
02618 }
02619
02620 if (m_request.method == HTTP_HEAD) {
02621
02622
02623
02624
02625 kDebug(7113) << "HEAD -> returned mimetype: " << DEFAULT_MIME_TYPE;
02626 mimeType(QString::fromLatin1(DEFAULT_MIME_TYPE));
02627 return true;
02628 }
02629
02630 kDebug(7113) << "Connection broken !";
02631 error( ERR_CONNECTION_BROKEN, m_request.url.host() );
02632 return false;
02633 }
02634 if (!foundDelimiter) {
02635
02636 Q_ASSERT(0);
02637 }
02638
02639 kDebug(7103) << "============ Received Status Response:";
02640 kDebug(7103) << QByteArray(buffer, bufPos);
02641
02642 bool noHeader = true;
02643 HTTP_REV httpRev = HTTP_None;
02644 int headerSize = 0;
02645
02646 int idx = 0;
02647
02648 if (idx != bufPos && buffer[idx] == '<') {
02649 kDebug(7103) << "No valid HTTP header found! Document starts with XML/HTML tag";
02650
02651 m_mimeType = "text/html";
02652
02653 unread(buffer, bufPos);
02654 goto endParsing;
02655 }
02656
02657
02658 if (consume(buffer, &idx, bufPos, "ICY ")) {
02659 httpRev = SHOUTCAST;
02660 m_request.isKeepAlive = false;
02661 } else if (consume(buffer, &idx, bufPos, "HTTP/")) {
02662 if (consume(buffer, &idx, bufPos, "1.0")) {
02663 httpRev = HTTP_10;
02664 m_request.isKeepAlive = false;
02665 } else if (consume(buffer, &idx, bufPos, "1.1")) {
02666 httpRev = HTTP_11;
02667 }
02668 }
02669
02670 if (httpRev == HTTP_None && bufPos != 0) {
02671
02672
02673 kDebug(7113) << "DO NOT WANT." << bufPos;
02674 unread(buffer, bufPos);
02675 if (m_request.responseCode) {
02676 m_request.prevResponseCode = m_request.responseCode;
02677 }
02678 m_request.responseCode = 200;
02679 httpRev = HTTP_Unknown;
02680 m_request.isKeepAlive = false;
02681 goto endParsing;
02682 }
02683
02684
02685
02686 if (m_request.responseCode) {
02687 m_request.prevResponseCode = m_request.responseCode;
02688 }
02689 skipSpace(buffer, &idx, bufPos);
02690
02691 if (idx != bufPos) {
02692 m_request.responseCode = atoi(&buffer[idx]);
02693 } else {
02694 m_request.responseCode = 200;
02695 }
02696
02697 idx = bufPos;
02698
02699
02700
02701
02702 if (m_request.responseCode >= 500 && m_request.responseCode <= 599) {
02703
02704
02705 if (m_request.method == HTTP_HEAD) {
02706 ;
02707 } else {
02708 if (m_request.preferErrorPage) {
02709 errorPage();
02710 } else {
02711 error(ERR_INTERNAL_SERVER, m_request.url.url());
02712 return false;
02713 }
02714 }
02715 m_request.cacheTag.writeToCache = false;
02716 mayCache = false;
02717 } else if (m_request.responseCode == 401 || m_request.responseCode == 407) {
02718
02719 m_request.cacheTag.writeToCache = false;
02720 mayCache = false;
02721 } else if (m_request.responseCode == 416) {
02722
02723 m_request.offset = 0;
02724 return false;
02725 } else if (m_request.responseCode == 426) {
02726
02727 upgradeRequired = true;
02728 } else if (m_request.responseCode >= 400 && m_request.responseCode <= 499) {
02729
02730
02731 if (m_request.preferErrorPage) {
02732 errorPage();
02733 } else {
02734 error(ERR_DOES_NOT_EXIST, m_request.url.url());
02735 return false;
02736 }
02737 m_request.cacheTag.writeToCache = false;
02738 mayCache = false;
02739 } else if (m_request.responseCode == 307) {
02740
02741 m_request.cacheTag.writeToCache = false;
02742 mayCache = false;
02743 } else if (m_request.responseCode == 304) {
02744
02745
02746 cacheValidated = true;
02747
02748 } else if (m_request.responseCode >= 301 && m_request.responseCode<= 303) {
02749
02750 if (m_request.responseCode == 301) {
02751 setMetaData("permanent-redirect", "true");
02752 }
02753
02754
02755 if (m_request.method != HTTP_HEAD && m_request.method != HTTP_GET) {
02756 #if 0
02757
02758
02759 if (m_request.method == HTTP_POST) {
02760 m_POSTbuf.resize(0);
02761 }
02762 #endif
02763
02764
02765
02766
02767
02768
02769
02770
02771
02772 m_request.method = HTTP_GET;
02773 }
02774 m_request.cacheTag.writeToCache = false;
02775 mayCache = false;
02776 } else if ( m_request.responseCode == 207 ) {
02777
02778
02779 } else if (m_request.responseCode == 204) {
02780
02781
02782
02783
02784
02785
02786
02787
02788
02789 } else if (m_request.responseCode == 206) {
02790 if (m_request.offset) {
02791 bCanResume = true;
02792 }
02793 } else if (m_request.responseCode == 102) {
02794
02795
02796
02797
02798
02799
02800 infoMessage( i18n( "Server processing request, please wait..." ) );
02801 cont = true;
02802 } else if (m_request.responseCode == 100) {
02803
02804 cont = true;
02805 }
02806
02807
02808 {
02809 const bool wasAuthError = m_request.prevResponseCode == 401 || m_request.prevResponseCode == 407;
02810 const bool isAuthError = m_request.responseCode == 401 || m_request.responseCode == 407;
02811
02812
02813 if (wasAuthError && (m_request.responseCode < 400 ||
02814 (isAuthError && m_request.responseCode != m_request.prevResponseCode))) {
02815 KIO::AuthInfo authi;
02816 KAbstractHttpAuthentication *auth;
02817 if (m_request.prevResponseCode == 401) {
02818 auth = m_wwwAuth;
02819 } else {
02820 auth = m_proxyAuth;
02821 }
02822 Q_ASSERT(auth);
02823 if (auth) {
02824 auth->fillKioAuthInfo(&authi);
02825 cacheAuthentication(authi);
02826 }
02827 }
02828 }
02829
02830
02831
02832 endParsing:
02833
02834
02835
02836 foundDelimiter = readDelimitedText(buffer, &bufPos, maxHeaderSize, 2);
02837 kDebug(7113) << " -- full response:" << QByteArray(buffer, bufPos);
02838 Q_ASSERT(foundDelimiter);
02839
02840
02841
02842
02843
02844 HeaderTokenizer tokenizer(buffer);
02845 headerSize = tokenizer.tokenize(idx, sizeof(buffer));
02846
02847
02848
02849 TokenIterator tIt = tokenizer.iterator("accept-ranges");
02850 if (tIt.hasNext() && tIt.next().toLower().startsWith("none")) {
02851 bCanResume = false;
02852 }
02853
02854 tIt = tokenizer.iterator("keep-alive");
02855 while (tIt.hasNext()) {
02856 if (tIt.next().startsWith("timeout=")) {
02857 m_request.keepAliveTimeout = tIt.current().mid(strlen("timeout=")).trimmed().toInt();
02858 }
02859 }
02860
02861 tIt = tokenizer.iterator("cache-control");
02862 while (tIt.hasNext()) {
02863 QByteArray cacheStr = tIt.next().toLower();
02864 if (cacheStr.startsWith("no-cache") || cacheStr.startsWith("no-store")) {
02865
02866 m_request.cacheTag.writeToCache = false;
02867 mayCache = false;
02868 hasCacheDirective = true;
02869 } else if (cacheStr.startsWith("max-age=")) {
02870 QByteArray age = cacheStr.mid(strlen("max-age=")).trimmed();
02871 if (!age.isEmpty()) {
02872 maxAge = STRTOLL(age.constData(), 0, 10);
02873 hasCacheDirective = true;
02874 }
02875 }
02876 }
02877
02878
02879 tIt = tokenizer.iterator("content-length");
02880 if (tIt.hasNext()) {
02881 m_iSize = STRTOLL(tIt.next().constData(), 0, 10);
02882 }
02883
02884 tIt = tokenizer.iterator("content-location");
02885 if (tIt.hasNext()) {
02886 setMetaData("content-location", QString::fromLatin1(tIt.next().trimmed()));
02887 }
02888
02889
02890 tIt = tokenizer.iterator("content-type");
02891 if (tIt.hasNext()) {
02892 QList<QByteArray> l = tIt.next().split(';');
02893 if (!l.isEmpty()) {
02894
02895 m_mimeType = QString::fromLatin1(l.first().trimmed().toLower());
02896 kDebug(7113) << "Content-type: " << m_mimeType;
02897 l.removeFirst();
02898 }
02899
02900
02901
02902 foreach (const QByteArray &statement, l) {
02903 QList<QByteArray> parts = statement.split('=');
02904 if (parts.count() != 2) {
02905 continue;
02906 }
02907 mediaAttribute = parts[0].trimmed().toLower();
02908 mediaValue = parts[1].trimmed();
02909 if (mediaValue.length() && (mediaValue[0] == '"') &&
02910 (mediaValue[mediaValue.length() - 1] == '"')) {
02911 mediaValue = mediaValue.mid(1, mediaValue.length() - 2);
02912 }
02913 kDebug (7113) << "Encoding-type: " << mediaAttribute
02914 << "=" << mediaValue;
02915
02916 if (mediaAttribute == "charset") {
02917 mediaValue = mediaValue.toLower();
02918 m_request.cacheTag.charset = mediaValue;
02919 setMetaData("charset", mediaValue);
02920 } else {
02921 setMetaData("media-" + mediaAttribute, mediaValue);
02922 }
02923 }
02924 }
02925
02926
02927 tIt = tokenizer.iterator("date");
02928 if (tIt.hasNext()) {
02929 dateHeader = KDateTime::fromString(tIt.next(), KDateTime::RFCDate).toTime_t();
02930 }
02931
02932
02933 tIt = tokenizer.iterator("date");
02934 if (tIt.hasNext()) {
02935
02936 m_request.cacheTag.etag = QString(tIt.next());
02937 }
02938
02939 tIt = tokenizer.iterator("date");
02940 if (tIt.hasNext()) {
02941
02942 m_request.cacheTag.etag = QString(tIt.next());
02943 }
02944
02945 tIt = tokenizer.iterator("expires");
02946 if (tIt.hasNext()) {
02947 expireDate = KDateTime::fromString(tIt.next(), KDateTime::RFCDate).toTime_t();
02948 if (!expireDate) {
02949 expireDate = 1;
02950 }
02951 }
02952
02953 tIt = tokenizer.iterator("last-modified");
02954 if (tIt.hasNext()) {
02955 m_request.cacheTag.lastModified = QString(tIt.next());
02956 }
02957
02958
02959 tIt = tokenizer.iterator("warning");
02960 if (tIt.hasNext()) {
02961
02962
02963 infoMessage(tIt.next());
02964 }
02965
02966
02967 tIt = tokenizer.iterator("pragma");
02968 while (tIt.hasNext()) {
02969 if (tIt.next().toLower().startsWith("no-cache")) {
02970 m_request.cacheTag.writeToCache = false;
02971 mayCache = false;
02972 hasCacheDirective = true;
02973 }
02974 }
02975
02976
02977 tIt = tokenizer.iterator("refresh");
02978 if (tIt.hasNext()) {
02979 mayCache = false;
02980 setMetaData("http-refresh", QString::fromLatin1(tIt.next().trimmed()));
02981 }
02982
02983
02984 tIt = tokenizer.iterator("location");
02985 if (tIt.hasNext() && m_request.responseCode > 299 && m_request.responseCode < 400) {
02986 locationStr = tIt.next().trimmed();
02987 }
02988
02989
02990 tIt = tokenizer.iterator("set-cookie");
02991 while (tIt.hasNext()) {
02992 cookieStr += "Set-Cookie: ";
02993 cookieStr += tIt.next();
02994 cookieStr += '\n';
02995 }
02996
02997 tIt = tokenizer.iterator("upgrade");
02998 if (tIt.hasNext()) {
02999
03000 QString offered = QString::fromLatin1(tIt.next());
03001 upgradeOffers = offered.split(QRegExp("[ \n,\r\t]"), QString::SkipEmptyParts);
03002 }
03003
03004
03005 tIt = tokenizer.iterator("content-encoding");
03006 while (tIt.hasNext()) {
03007
03008
03009
03010
03011
03012
03013
03014
03015
03016
03017
03018
03019
03020 addEncoding(tIt.next(), m_contentEncodings);
03021 }
03022
03023 tIt = tokenizer.iterator("content-disposition");
03024 if (tIt.hasNext()) {
03025 parseContentDisposition(QString::fromLatin1(tIt.next()));
03026 }
03027 tIt = tokenizer.iterator("content-language");
03028 if (tIt.hasNext()) {
03029 QString language = QString::fromLatin1(tIt.next().trimmed());
03030 if (!language.isEmpty()) {
03031 setMetaData("content-language", language);
03032 }
03033 }
03034
03035 tIt = tokenizer.iterator("proxy-connection");
03036 if (tIt.hasNext() && isHttpProxy(m_request.proxyUrl) && !isAutoSsl()) {
03037 QByteArray pc = tIt.next().toLower();
03038 if (pc.startsWith("close")) {
03039 m_request.isKeepAlive = false;
03040 } else if (pc.startsWith("keep-alive")) {
03041 m_request.isKeepAlive = true;
03042 }
03043 }
03044
03045 tIt = tokenizer.iterator("link");
03046 if (tIt.hasNext()) {
03047
03048 QStringList link = QString::fromLatin1(tIt.next()).split(';', QString::SkipEmptyParts);
03049 if (link.count() == 2) {
03050 QString rel = link[1].trimmed();
03051 if (rel.startsWith("rel=\"")) {
03052 rel = rel.mid(5, rel.length() - 6);
03053 if (rel.toLower() == "pageservices") {
03054
03055 QString url = link[0].remove(QRegExp("[<>]")).trimmed();
03056 setMetaData("PageServices", url);
03057 }
03058 }
03059 }
03060 }
03061
03062 tIt = tokenizer.iterator("p3p");
03063 if (tIt.hasNext()) {
03064
03065 QStringList policyrefs, compact;
03066 while (tIt.hasNext()) {
03067 QStringList policy = QString::fromLatin1(tIt.next().simplified())
03068 .split('=', QString::SkipEmptyParts);
03069 if (policy.count() == 2) {
03070 if (policy[0].toLower() == "policyref") {
03071 policyrefs << policy[1].remove(QRegExp("[\"\']")).trimmed();
03072 } else if (policy[0].toLower() == "cp") {
03073
03074
03075
03076 const QString s = policy[1].remove(QRegExp("[\"\']"));
03077 const QStringList cps = s.split(' ', QString::SkipEmptyParts);
03078 compact << cps;
03079 }
03080 }
03081 }
03082 if (!policyrefs.isEmpty()) {
03083 setMetaData("PrivacyPolicy", policyrefs.join("\n"));
03084 }
03085 if (!compact.isEmpty()) {
03086 setMetaData("PrivacyCompactPolicy", compact.join("\n"));
03087 }
03088 }
03089
03090
03091 if (httpRev == HTTP_11 || httpRev == HTTP_10) {
03092
03093 tIt = tokenizer.iterator("connection");
03094 while (tIt.hasNext()) {
03095 QByteArray connection = tIt.next().toLower();
03096 if (!(isHttpProxy(m_request.proxyUrl) && !isAutoSsl())) {
03097 if (connection.startsWith("close")) {
03098 m_request.isKeepAlive = false;
03099 } else if (connection.startsWith("keep-alive")) {
03100 m_request.isKeepAlive = true;
03101 }
03102 }
03103 if (connection.startsWith("upgrade")) {
03104 if (m_request.responseCode == 101) {
03105
03106 upgradeRequired = true;
03107 } else if (upgradeRequired) {
03108
03109 } else {
03110
03111 canUpgrade = true;
03112 }
03113 }
03114 }
03115
03116 tIt = tokenizer.iterator("transfer-encoding");
03117 while (tIt.hasNext()) {
03118
03119
03120
03121 addEncoding(tIt.next().trimmed(), m_transferEncodings);
03122 }
03123
03124
03125 tIt = tokenizer.iterator("content-md5");
03126 if (tIt.hasNext()) {
03127 m_contentMD5 = QString::fromLatin1(tIt.next().trimmed());
03128 }
03129
03130
03131
03132 tIt = tokenizer.iterator("dav");
03133 while (tIt.hasNext()) {
03134 m_davCapabilities << QString::fromLatin1(tIt.next());
03135 }
03136
03137 }
03138
03139
03140
03141 foreach (const QString &opt, upgradeOffers) {
03142 if (opt == "TLS/1.0") {
03143 if (!startSsl() && upgradeRequired) {
03144 error(ERR_UPGRADE_REQUIRED, opt);
03145 return false;
03146 }
03147 } else if (opt == "HTTP/1.1") {
03148 httpRev = HTTP_11;
03149 } else if (upgradeRequired) {
03150
03151 error(ERR_UPGRADE_REQUIRED, opt);
03152 return false;
03153 }
03154 }
03155
03156
03157 if (expireDate && (expireDate <= dateHeader))
03158 expireDate = 1;
03159
03160
03161 if (maxAge == 0)
03162 expireDate = 1;
03163 else if (maxAge > 0)
03164 {
03165 if (currentAge)
03166 maxAge -= currentAge;
03167 if (maxAge <=0)
03168 maxAge = 0;
03169 expireDate = time(0) + maxAge;
03170 }
03171
03172 if (!expireDate)
03173 {
03174 time_t lastModifiedDate = 0;
03175 if (!m_request.cacheTag.lastModified.isEmpty())
03176 lastModifiedDate = KDateTime::fromString(m_request.cacheTag.lastModified, KDateTime::RFCDate).toTime_t();
03177
03178 if (lastModifiedDate)
03179 {
03180 long diff = static_cast<long>(difftime(dateHeader, lastModifiedDate));
03181 if (diff < 0)
03182 expireDate = time(0) + 1;
03183 else
03184 expireDate = time(0) + (diff / 10);
03185 }
03186 else
03187 {
03188 expireDate = time(0) + DEFAULT_CACHE_EXPIRE;
03189 }
03190 }
03191
03192
03193 if (!cookieStr.isEmpty())
03194 {
03195 if ((m_request.cookieMode == HTTPRequest::CookiesAuto) && m_request.useCookieJar)
03196 {
03197
03198 QString domain = config()->readEntry("cross-domain");
03199 if (!domain.isEmpty() && isCrossDomainRequest(m_request.url.host(), domain))
03200 cookieStr = "Cross-Domain\n" + cookieStr;
03201 addCookies( m_request.url.url(), cookieStr );
03202 }
03203 else if (m_request.cookieMode == HTTPRequest::CookiesManual)
03204 {
03205
03206 setMetaData("setcookies", cookieStr);
03207 }
03208 }
03209
03210 if (m_request.cacheTag.isExpired)
03211 {
03212 m_request.cacheTag.isExpired = false;
03213 if (cacheValidated)
03214 {
03215
03216
03217 gzclose(m_request.cacheTag.gzs);
03218 m_request.cacheTag.gzs = 0;
03219 updateExpireDate( expireDate, true );
03220 m_request.cacheTag.gzs = checkCacheEntry( );
03221
03222 if (m_request.cacheTag.gzs)
03223 {
03224 m_request.cacheTag.readFromCache = true;
03225 goto try_again;
03226 }
03227 else
03228 {
03229
03230 }
03231 }
03232 else
03233 {
03234
03235 gzclose(m_request.cacheTag.gzs);
03236 m_request.cacheTag.gzs = 0;
03237 }
03238 }
03239
03240
03241 if ( cont )
03242 {
03243 kDebug(7113) << "cont; returning to mark try_again";
03244 goto try_again;
03245 }
03246
03247
03248
03249 if (!m_isChunked && (m_iSize == NO_SIZE)) {
03250 m_request.isKeepAlive = false;
03251 }
03252
03253 if ( m_request.responseCode == 204 )
03254 {
03255 return true;
03256 }
03257
03258
03259
03260
03261 bool authRequiresAnotherRoundtrip = false;
03262 if (!m_request.doNotAuthenticate && (m_request.responseCode == 401 ||
03263 m_request.responseCode == 407)) {
03264 authRequiresAnotherRoundtrip = true;
03265
03266 KAbstractHttpAuthentication **auth = &m_wwwAuth;
03267 tIt = tokenizer.iterator("www-authenticate");
03268 KUrl resource = m_request.url;
03269 if (m_request.responseCode == 407) {
03270 auth = &m_proxyAuth;
03271 tIt = tokenizer.iterator("proxy-authenticate");
03272 resource = m_request.proxyUrl;
03273 }
03274
03275 kDebug(7113) << "parsing authentication request; response code =" << m_request.responseCode;
03276
03277 QByteArray bestOffer = KAbstractHttpAuthentication::bestOffer(tIt.all());
03278 if (*auth) {
03279 if (!bestOffer.toLower().startsWith((*auth)->scheme().toLower())) {
03280
03281 kDebug(7113) << "deleting old auth class, scheme mismatch.";
03282 delete *auth;
03283 *auth = 0;
03284 }
03285 }
03286 kDebug(7113) << "strongest authentication scheme offered is" << bestOffer;
03287 if (!(*auth)) {
03288 *auth = KAbstractHttpAuthentication::newAuth(bestOffer);
03289 }
03290 kDebug(7113) << "pointer to auth class is now" << *auth;
03291 if (!(*auth)) {
03292 if (m_request.preferErrorPage) {
03293 errorPage();
03294 } else {
03295 error(ERR_UNSUPPORTED_ACTION, "Unknown Authorization method!");
03296 return false;
03297 }
03298
03299 }
03300
03301
03302 QByteArray requestMethod = methodString(m_request.method).toLatin1().trimmed();
03303 (*auth)->setChallenge(bestOffer, resource, requestMethod);
03304
03305
03306 QString username;
03307 QString password;
03308 if ((*auth)->retryWithSameCredentials()) {
03309
03310 } else {
03311
03312 KIO::AuthInfo authi;
03313 fillPromptInfo(&authi);
03314 bool obtained = checkCachedAuthentication(authi);
03315 const bool probablyWrong = m_request.responseCode == m_request.prevResponseCode;
03316 if (!obtained || probablyWrong) {
03317 QString msg = (m_request.responseCode == 401) ?
03318 i18n("Authentication Failed.") :
03319 i18n("Proxy Authentication Failed.");
03320 obtained = openPasswordDialog(authi, msg);
03321 if (!obtained) {
03322 error(ERR_USER_CANCELED, resource.host());
03323 return false;
03324 }
03325 }
03326 if (!obtained) {
03327 kDebug(7103) << "could not obtain authentication credentials from cache or user!";
03328 }
03329 username = authi.username;
03330 password = authi.password;
03331 }
03332
03333 (*auth)->generateResponse(username, password);
03334
03335 kDebug(7113) << "auth state: isError" << (*auth)->isError()
03336 << "retryWithSameCredentials" << (*auth)->retryWithSameCredentials()
03337 << "forceKeepAlive" << (*auth)->forceKeepAlive()
03338 << "forceDisconnect" << (*auth)->forceDisconnect()
03339 << "headerFragment" << (*auth)->headerFragment();
03340
03341 if ((*auth)->isError()) {
03342 if (m_request.preferErrorPage) {
03343 errorPage();
03344 } else {
03345 error(ERR_UNSUPPORTED_ACTION, "Authorization failed!");
03346 return false;
03347 }
03348
03349 } else if ((*auth)->forceKeepAlive()) {
03350
03351 m_request.isKeepAlive = true;
03352 } else if ((*auth)->forceDisconnect()) {
03353
03354 m_request.isKeepAlive = false;
03355 httpCloseConnection();
03356 }
03357 if (m_request.isKeepAlive) {
03358
03359 readBody(true);
03360 }
03361
03362 }
03363
03364
03365 if (!locationStr.isEmpty())
03366 {
03367 KUrl u(m_request.url, locationStr);
03368 if(!u.isValid())
03369 {
03370 error(ERR_MALFORMED_URL, u.url());
03371 return false;
03372 }
03373 if ((u.protocol() != "http") && (u.protocol() != "https") &&
03374 (u.protocol() != "webdav") && (u.protocol() != "webdavs"))
03375 {
03376 redirection(u);
03377 error(ERR_ACCESS_DENIED, u.url());
03378 return false;
03379 }
03380
03381
03382
03383
03384
03385 if (m_request.url.hasRef() && !u.hasRef() &&
03386 (m_request.url.host() == u.host()) &&
03387 (m_request.url.protocol() == u.protocol()))
03388 u.setRef(m_request.url.ref());
03389
03390 m_isRedirection = true;
03391
03392 if (!m_request.id.isEmpty())
03393 {
03394 sendMetaData();
03395 }
03396
03397
03398 if (m_protocol == "webdav" || m_protocol == "webdavs")
03399 u.setProtocol(m_protocol);
03400
03401 kDebug(7113) << "Re-directing from" << m_request.url.url()
03402 << "to" << u.url();
03403
03404 redirection(u);
03405 m_request.cacheTag.writeToCache = false;
03406 mayCache = false;
03407 }
03408
03409
03410 if ( bCanResume && m_request.offset )
03411 canResume();
03412 else
03413 m_request.offset = 0;
03414
03415
03416 if (m_mimeType.startsWith("text/") &&
03417 (m_mimeType != "text/css") &&
03418 (m_mimeType != "text/x-javascript") &&
03419 !hasCacheDirective)
03420 {
03421
03422
03423
03424 if (isUsingSsl() || m_wwwAuth)
03425 {
03426 m_request.cacheTag.writeToCache = false;
03427 mayCache = false;
03428 }
03429 }
03430
03431
03432
03433
03434
03435
03436 if (!m_contentEncodings.isEmpty() && m_contentEncodings.last() == "gzip")
03437 {
03438 if (m_mimeType == "application/x-tar")
03439 {
03440 m_contentEncodings.removeLast();
03441 m_mimeType = QString::fromLatin1("application/x-compressed-tar");
03442 }
03443 else if (m_mimeType == "application/postscript")
03444 {
03445
03446
03447 m_contentEncodings.removeLast();
03448 m_mimeType = QString::fromLatin1("application/x-gzpostscript");
03449 }
03450 else if ( (m_request.allowTransferCompression &&
03451 m_mimeType == "text/html")
03452 ||
03453 (m_request.allowTransferCompression &&
03454 m_mimeType != "application/x-compressed-tar" &&
03455 m_mimeType != "application/x-tgz" &&
03456 m_mimeType != "application/x-targz" &&
03457 m_mimeType != "application/x-gzip" &&
03458 !m_request.url.path().endsWith(QLatin1String(".gz")))
03459 )
03460 {
03461
03462 }
03463 else
03464 {
03465 m_contentEncodings.removeLast();
03466 m_mimeType = QString::fromLatin1("application/x-gzip");
03467 }
03468 }
03469
03470
03471
03472
03473
03474
03475
03476 if (!m_contentEncodings.isEmpty() && m_contentEncodings.last() == "bzip2")
03477 {
03478 m_contentEncodings.removeLast();
03479 m_mimeType = QString::fromLatin1("application/x-bzip");
03480 }
03481
03482
03483 fixupResponseMimetype();
03484
03485 if (!m_request.cacheTag.lastModified.isEmpty())
03486 setMetaData("modified", m_request.cacheTag.lastModified);
03487
03488 if (!mayCache)
03489 {
03490 setMetaData("no-cache", "true");
03491 setMetaData("expire-date", "1");
03492 }
03493 else
03494 {
03495 QString tmp;
03496 tmp.setNum(expireDate);
03497 setMetaData("expire-date", tmp);
03498 tmp.setNum(time(0));
03499 setMetaData("cache-creation-date", tmp);
03500 }
03501
03502
03503
03504 if (locationStr.isEmpty() && (!m_mimeType.isEmpty() ||
03505 m_request.method == HTTP_HEAD))
03506 {
03507 kDebug(7113) << "Emitting mimetype " << m_mimeType;
03508 mimeType( m_mimeType );
03509 }
03510
03511 if (config()->readEntry("PropagateHttpHeader", false) ||
03512 (m_request.cacheTag.useCache) && m_request.cacheTag.writeToCache) {
03513
03514
03515 int nextLinePos = 0;
03516 int prevLinePos = 0;
03517 bool haveMore = true;
03518 while (haveMore) {
03519 haveMore = nextLine(buffer, &nextLinePos, bufPos);
03520 int prevLineEnd = nextLinePos;
03521 while (buffer[prevLineEnd - 1] == '\r' || buffer[prevLineEnd - 1] == '\n') {
03522 prevLineEnd--;
03523 }
03524 m_responseHeaders.append(QString::fromLatin1(&buffer[prevLinePos],
03525 prevLineEnd - prevLinePos));
03526 prevLinePos = nextLinePos;
03527 }
03528 }
03529
03530
03531
03532 forwardHttpResponseHeader();
03533
03534 if (m_request.method == HTTP_HEAD)
03535 return true;
03536
03537
03538 if (m_request.cacheTag.useCache)
03539 {
03540 ::unlink(QFile::encodeName(m_request.cacheTag.file));
03541 if ( m_request.cacheTag.writeToCache && !m_mimeType.isEmpty() )
03542 {
03543 kDebug(7113) << "Cache, adding" << m_request.url.url();
03544 createCacheEntry(m_mimeType, expireDate);
03545 if (!m_request.cacheTag.gzs)
03546 {
03547 m_request.cacheTag.writeToCache = false;
03548 kDebug(7113) << "Error creating cache entry for " << m_request.url.url()<<"!\n";
03549 }
03550 m_request.cacheTag.expireDate = expireDate;
03551 m_maxCacheSize = config()->readEntry("MaxCacheSize", DEFAULT_MAX_CACHE_SIZE) / 2;
03552 }
03553 }
03554
03555 return !authRequiresAnotherRoundtrip;
03556 }
03557
03558 static void skipLWS(const QString &str, int &pos)
03559 {
03560 while (pos < str.length() && (str[pos] == ' ' || str[pos] == '\t'))
03561 ++pos;
03562 }
03563
03564
03565
03566 static QString extractUntil(const QString &str, unsigned char term, int &pos)
03567 {
03568 QString out;
03569 skipLWS(str, pos);
03570 while (pos < str.length() && (str[pos] != term)) {
03571 out += str[pos];
03572 ++pos;
03573 }
03574
03575 if (pos < str.length())
03576 ++pos;
03577
03578
03579 while (out.endsWith(' ') || out.endsWith('\t'))
03580 out.chop(1);
03581
03582 return out;
03583 }
03584
03585
03586 static QString extractMaybeQuotedUntil(const QString &str, unsigned char term, int &pos)
03587 {
03588 skipLWS(str, pos);
03589
03590
03591 if (pos < str.length() && str[pos] == '"') {
03592 QString out;
03593
03594
03595 ++pos;
03596
03597
03598 while (pos < str.length()) {
03599 if (str[pos] == '\\' && pos + 1 < str.length()) {
03600
03601 out += str[pos + 1];
03602 pos += 2;
03603 } else if (str[pos] == '"') {
03604 ++pos;
03605 break;
03606 } else {
03607 out += str[pos];
03608 ++pos;
03609 }
03610 }
03611
03612
03613 while (pos < str.length() && (str[pos] != term))
03614 ++pos;
03615
03616 if (pos < str.length())
03617 ++pos;
03618
03619 return out;
03620 } else {
03621 return extractUntil(str, term, pos);
03622 }
03623 }
03624
03625 void HTTPProtocol::parseContentDisposition(const QString &disposition)
03626 {
03627 kDebug(7113) << "disposition: " << disposition;
03628 QString strDisposition;
03629 QString strFilename;
03630
03631 int pos = 0;
03632
03633 strDisposition = extractUntil(disposition, ';', pos);
03634
03635 while (pos < disposition.length()) {
03636 QString key = extractUntil(disposition, '=', pos);
03637 QString val = extractMaybeQuotedUntil(disposition, ';', pos);
03638 if (key == "filename")
03639 strFilename = val;
03640 }
03641
03642
03643
03644 if ( !strFilename.isEmpty() )
03645 {
03646 int pos = strFilename.lastIndexOf( '/' );
03647
03648 if( pos > -1 )
03649 strFilename = strFilename.mid(pos+1);
03650
03651 kDebug(7113) << "Content-Disposition: filename=" << strFilename;
03652 }
03653 setMetaData("content-disposition-type", strDisposition);
03654 if (!strFilename.isEmpty())
03655 setMetaData("content-disposition-filename", KCodecs::decodeRFC2047String(strFilename));
03656 }
03657
03658 void HTTPProtocol::addEncoding(const QString &_encoding, QStringList &encs)
03659 {
03660 QString encoding = _encoding.trimmed().toLower();
03661
03662 if (encoding == "identity") {
03663 return;
03664 } else if (encoding == "8bit") {
03665
03666 return;
03667 } else if (encoding == "chunked") {
03668 m_isChunked = true;
03669
03670
03671 m_iSize = NO_SIZE;
03672 } else if ((encoding == "x-gzip") || (encoding == "gzip")) {
03673 encs.append(QString::fromLatin1("gzip"));
03674 } else if ((encoding == "x-bzip2") || (encoding == "bzip2")) {
03675 encs.append(QString::fromLatin1("bzip2"));
03676 } else if ((encoding == "x-deflate") || (encoding == "deflate")) {
03677 encs.append(QString::fromLatin1("deflate"));
03678 } else {
03679 kDebug(7113) << "Unknown encoding encountered. "
03680 << "Please write code. Encoding =" << encoding;
03681 }
03682 }
03683
03684 bool HTTPProtocol::sendBody()
03685 {
03686 infoMessage( i18n( "Requesting data to send" ) );
03687
03688 int readFromApp = -1;
03689
03690
03691
03692
03693 if (m_POSTbuf.isEmpty())
03694 {
03695 kDebug(7113) << "POST'ing live data...";
03696
03697 QByteArray buffer;
03698
03699 do {
03700 m_POSTbuf.append(buffer);
03701 buffer.clear();
03702 dataReq();
03703 readFromApp = readData(buffer);
03704 } while (readFromApp > 0);
03705 }
03706 else
03707 {
03708 kDebug(7113) << "POST'ing saved data...";
03709 readFromApp = 0;
03710 }
03711
03712 if (readFromApp < 0)
03713 {
03714 error(ERR_ABORTED, m_request.url.host());
03715 return false;
03716 }
03717
03718 infoMessage(i18n("Sending data to %1" , m_request.url.host()));
03719
03720 QString cLength = QString("Content-Length: %1\r\n\r\n").arg(m_POSTbuf.size());
03721 kDebug( 7113 ) << cLength;
03722
03723
03724 bool sendOk = (write(cLength.toLatin1(), cLength.length()) == (ssize_t) cLength.length());
03725 if (!sendOk)
03726 {
03727 kDebug( 7113 ) << "Connection broken when sending "
03728 << "content length: (" << m_request.url.host() << ")";
03729 error( ERR_CONNECTION_BROKEN, m_request.url.host() );
03730 return false;
03731 }
03732
03733
03734
03735 sendOk = (write(m_POSTbuf.data(), m_POSTbuf.size()) == (ssize_t) m_POSTbuf.size());
03736 if (!sendOk)
03737 {
03738 kDebug(7113) << "Connection broken when sending message body: ("
03739 << m_request.url.host() << ")";
03740 error( ERR_CONNECTION_BROKEN, m_request.url.host() );
03741 return false;
03742 }
03743
03744 return true;
03745 }
03746
03747 void HTTPProtocol::httpClose( bool keepAlive )
03748 {
03749 kDebug(7113) << "keepAlive =" << keepAlive;
03750
03751 if (m_request.cacheTag.gzs)
03752 {
03753 gzclose(m_request.cacheTag.gzs);
03754 m_request.cacheTag.gzs = 0;
03755 if (m_request.cacheTag.writeToCache)
03756 {
03757 QString filename = m_request.cacheTag.file + ".new";
03758 ::unlink( QFile::encodeName(filename) );
03759 }
03760 }
03761
03762
03763
03764
03765
03766 if (keepAlive) {
03767 if (!m_request.keepAliveTimeout)
03768 m_request.keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
03769 else if (m_request.keepAliveTimeout > 2*DEFAULT_KEEP_ALIVE_TIMEOUT)
03770 m_request.keepAliveTimeout = 2*DEFAULT_KEEP_ALIVE_TIMEOUT;
03771
03772 kDebug(7113) << "keep alive (" << m_request.keepAliveTimeout << ")";
03773 QByteArray data;
03774 QDataStream stream( &data, QIODevice::WriteOnly );
03775 stream << int(99);
03776 setTimeoutSpecialCommand(m_request.keepAliveTimeout, data);
03777
03778 return;
03779 }
03780
03781 httpCloseConnection();
03782 }
03783
03784 void HTTPProtocol::closeConnection()
03785 {
03786 kDebug(7113);
03787 httpCloseConnection();
03788 }
03789
03790 void HTTPProtocol::httpCloseConnection()
03791 {
03792 kDebug(7113);
03793 m_request.isKeepAlive = false;
03794 m_server.clear();
03795 disconnectFromHost();
03796 clearUnreadBuffer();
03797 setTimeoutSpecialCommand(-1);
03798 }
03799
03800 void HTTPProtocol::slave_status()
03801 {
03802 kDebug(7113);
03803
03804 if ( !isConnected() )
03805 httpCloseConnection();
03806
03807 slaveStatus( m_server.url.host(), isConnected() );
03808 }
03809
03810 void HTTPProtocol::mimetype( const KUrl& url )
03811 {
03812 kDebug(7113) << url.url();
03813
03814 if (!maybeSetRequestUrl(url))
03815 return;
03816 resetSessionSettings();
03817
03818 m_request.method = HTTP_HEAD;
03819 m_request.cacheTag.policy= CC_Cache;
03820
03821 proceedUntilResponseHeader();
03822 httpClose(m_request.isKeepAlive);
03823 finished();
03824
03825 kDebug(7113) << "http: mimetype = " << m_mimeType;
03826 }
03827
03828 void HTTPProtocol::special( const QByteArray &data )
03829 {
03830 kDebug(7113);
03831
03832 int tmp;
03833 QDataStream stream(data);
03834
03835 stream >> tmp;
03836 switch (tmp) {
03837 case 1:
03838 {
03839 KUrl url;
03840 stream >> url;
03841 post( url );
03842 break;
03843 }
03844 case 2:
03845 {
03846 KUrl url;
03847 bool no_cache;
03848 qlonglong expireDate;
03849 stream >> url >> no_cache >> expireDate;
03850 cacheUpdate( url, no_cache, time_t(expireDate) );
03851 break;
03852 }
03853 case 5:
03854 {
03855 KUrl url;
03856 QString scope, type, owner;
03857 stream >> url >> scope >> type >> owner;
03858 davLock( url, scope, type, owner );
03859 break;
03860 }
03861 case 6:
03862 {
03863 KUrl url;
03864 stream >> url;
03865 davUnlock( url );
03866 break;
03867 }
03868 case 7:
03869 {
03870 KUrl url;
03871 int method;
03872 stream >> url >> method;
03873 davGeneric( url, (KIO::HTTP_METHOD) method );
03874 break;
03875 }
03876 case 99:
03877 {
03878 httpCloseConnection();
03879 break;
03880 }
03881 default:
03882
03883
03884 break;
03885 }
03886 }
03887
03891 int HTTPProtocol::readChunked()
03892 {
03893 if ((m_iBytesLeft == 0) || (m_iBytesLeft == NO_SIZE))
03894 {
03895
03896
03897 int bufPos = 0;
03898 m_receiveBuf.resize(4096);
03899
03900 bool foundCrLf = readDelimitedText(m_receiveBuf.data(), &bufPos, m_receiveBuf.size(), 1);
03901
03902 if (foundCrLf && bufPos == 2) {
03903
03904
03905 bufPos = 0;
03906 foundCrLf = readDelimitedText(m_receiveBuf.data(), &bufPos, m_receiveBuf.size(), 1);
03907 }
03908 if (!foundCrLf) {
03909 kDebug(7113) << "Failed to read chunk header.";
03910 return -1;
03911 }
03912 Q_ASSERT(bufPos > 2);
03913
03914 long long nextChunkSize = STRTOLL(m_receiveBuf.data(), 0, 16);
03915 if (nextChunkSize < 0)
03916 {
03917 kDebug(7113) << "Negative chunk size";
03918 return -1;
03919 }
03920 m_iBytesLeft = nextChunkSize;
03921
03922 kDebug(7113) << "Chunk size = " << m_iBytesLeft << " bytes";
03923
03924 if (m_iBytesLeft == 0)
03925 {
03926
03927
03928
03929
03930
03931 char trash[4096];
03932 trash[0] = m_receiveBuf.constData()[bufPos - 2];
03933 trash[1] = m_receiveBuf.constData()[bufPos - 1];
03934 int trashBufPos = 2;
03935 bool done = false;
03936 while (!done && !m_isEOF) {
03937 if (trashBufPos > 3) {
03938
03939 for (int i = 0; i < 3; i++) {
03940 trash[i] = trash[trashBufPos - 3 + i];
03941 }
03942 trashBufPos = 3;
03943 }
03944 done = readDelimitedText(trash, &trashBufPos, 4096, 2);
03945 }
03946 if (m_isEOF && !done) {
03947 kDebug(7113) << "Failed to read chunk trailer.";
03948 return -1;
03949 }
03950
03951 return 0;
03952 }
03953 }
03954
03955 int bytesReceived = readLimited();
03956 if (!m_iBytesLeft) {
03957 m_iBytesLeft = NO_SIZE;
03958 }
03959 return bytesReceived;
03960 }
03961
03962 int HTTPProtocol::readLimited()
03963 {
03964 if (!m_iBytesLeft)
03965 return 0;
03966
03967 m_receiveBuf.resize(4096);
03968
03969 int bytesToReceive;
03970 if (m_iBytesLeft > KIO::filesize_t(m_receiveBuf.size()))
03971 bytesToReceive = m_receiveBuf.size();
03972 else
03973 bytesToReceive = m_iBytesLeft;
03974
03975 int bytesReceived = readBuffered(m_receiveBuf.data(), bytesToReceive);
03976
03977 if (bytesReceived <= 0)
03978 return -1;
03979
03980 m_iBytesLeft -= bytesReceived;
03981 return bytesReceived;
03982 }
03983
03984 int HTTPProtocol::readUnlimited()
03985 {
03986 if (m_request.isKeepAlive)
03987 {
03988 kDebug(7113) << "Unbounded datastream on a Keep-alive connection!";
03989 m_request.isKeepAlive = false;
03990 }
03991
03992 m_receiveBuf.resize(4096);
03993
03994 int result = readBuffered(m_receiveBuf.data(), m_receiveBuf.size());
03995 if (result > 0)
03996 return result;
03997
03998 m_isEOF = true;
03999 m_iBytesLeft = 0;
04000 return 0;
04001 }
04002
04003 void HTTPProtocol::slotData(const QByteArray &_d)
04004 {
04005 if (!_d.size())
04006 {
04007 m_isEOD = true;
04008 return;
04009 }
04010
04011 if (m_iContentLeft != NO_SIZE)
04012 {
04013 if (m_iContentLeft >= KIO::filesize_t(_d.size()))
04014 m_iContentLeft -= _d.size();
04015 else
04016 m_iContentLeft = NO_SIZE;
04017 }
04018
04019 QByteArray d = _d;
04020 if ( !m_dataInternal )
04021 {
04022
04023
04024
04025 if ( m_mimeType.isEmpty() && !m_isRedirection &&
04026 !( m_request.responseCode >= 300 && m_request.responseCode <=399) )
04027 {
04028 kDebug(7113) << "Determining mime-type from content...";
04029 int old_size = m_mimeTypeBuffer.size();
04030 m_mimeTypeBuffer.resize( old_size + d.size() );
04031 memcpy( m_mimeTypeBuffer.data() + old_size, d.data(), d.size() );
04032 if ( (m_iBytesLeft != NO_SIZE) && (m_iBytesLeft > 0)
04033 && (m_mimeTypeBuffer.size() < 1024) )
04034 {
04035 m_cpMimeBuffer = true;
04036 return;
04037 }
04038
04039 kDebug(7113) << "Mimetype buffer size: " << m_mimeTypeBuffer.size();
04040
04041 KMimeType::Ptr mime = KMimeType::findByNameAndContent(m_request.url.fileName(), m_mimeTypeBuffer);
04042 if( mime && !mime->isDefault() )
04043 {
04044 m_mimeType = mime->name();
04045 kDebug(7113) << "Mimetype from content: " << m_mimeType;
04046 }
04047
04048 if ( m_mimeType.isEmpty() )
04049 {
04050 m_mimeType = QString::fromLatin1( DEFAULT_MIME_TYPE );
04051 kDebug(7113) << "Using default mimetype: " << m_mimeType;
04052 }
04053
04054 if ( m_request.cacheTag.writeToCache )
04055 {
04056 createCacheEntry( m_mimeType, m_request.cacheTag.expireDate );
04057 if (!m_request.cacheTag.gzs)
04058 m_request.cacheTag.writeToCache = false;
04059 }
04060
04061 if ( m_cpMimeBuffer )
04062 {
04063 d.resize(0);
04064 d.resize(m_mimeTypeBuffer.size());
04065 memcpy( d.data(), m_mimeTypeBuffer.data(),
04066 d.size() );
04067 }
04068 mimeType(m_mimeType);
04069 m_mimeTypeBuffer.resize(0);
04070 }
04071
04072 data( d );
04073 if (m_request.cacheTag.writeToCache && m_request.cacheTag.gzs)
04074 writeCacheEntry(d.data(), d.size());
04075 }
04076 else
04077 {
04078 uint old_size = m_webDavDataBuf.size();
04079 m_webDavDataBuf.resize (old_size + d.size());
04080 memcpy (m_webDavDataBuf.data() + old_size, d.data(), d.size());
04081 }
04082 }
04083
04093 bool HTTPProtocol::readBody( bool dataInternal )
04094 {
04095 if (m_request.responseCode == 204)
04096 return true;
04097
04098 m_isEOD = false;
04099
04100
04101
04102
04103
04104 m_dataInternal = dataInternal;
04105 if (dataInternal) {
04106 m_webDavDataBuf.clear();
04107 }
04108
04109
04110
04111 bool useMD5 = !m_contentMD5.isEmpty();
04112
04113
04114 KIO::filesize_t sz = m_request.offset;
04115 if ( sz )
04116 m_iSize += sz;
04117
04118
04119
04120
04121
04122 if ( !dataInternal ) {
04123 if ( (m_iSize > 0) && (m_iSize != NO_SIZE)) {
04124 totalSize(m_iSize);
04125 infoMessage(i18n("Retrieving %1 from %2...", KIO::convertSize(m_iSize),
04126 m_request.url.host()));
04127 } else {
04128 totalSize (0);
04129 }
04130 } else {
04131 infoMessage( i18n( "Retrieving from %1..." , m_request.url.host() ) );
04132 }
04133
04134 if (m_request.cacheTag.readFromCache)
04135 {
04136 kDebug(7113) << "read data from cache!";
04137 m_request.cacheTag.writeToCache = false;
04138
04139 char buffer[ MAX_IPC_SIZE ];
04140
04141 m_iContentLeft = NO_SIZE;
04142
04143
04144
04145 while (!gzeof(m_request.cacheTag.gzs))
04146 {
04147 int nbytes = gzread( m_request.cacheTag.gzs, buffer, MAX_IPC_SIZE);
04148
04149 if (nbytes > 0)
04150 {
04151 slotData( QByteArray::fromRawData( buffer, nbytes ) );
04152 sz += nbytes;
04153 }
04154 }
04155
04156 m_receiveBuf.resize( 0 );
04157
04158 if ( !dataInternal )
04159 {
04160 processedSize( sz );
04161 data( QByteArray() );
04162 }
04163
04164 return true;
04165 }
04166
04167
04168 if (m_iSize != NO_SIZE)
04169 m_iBytesLeft = m_iSize - sz;
04170 else
04171 m_iBytesLeft = NO_SIZE;
04172
04173 m_iContentLeft = m_iBytesLeft;
04174
04175 if (m_isChunked)
04176 m_iBytesLeft = NO_SIZE;
04177
04178 kDebug(7113) << "retrieve data."<<KIO::number(m_iBytesLeft)<<"left.";
04179
04180
04181 m_cpMimeBuffer = false;
04182 m_mimeTypeBuffer.resize(0);
04183 struct timeval last_tv;
04184 gettimeofday( &last_tv, 0L );
04185
04186 HTTPFilterChain chain;
04187
04188 QObject::connect(&chain, SIGNAL(output(const QByteArray &)),
04189 this, SLOT(slotData(const QByteArray &)));
04190 QObject::connect(&chain, SIGNAL(error(int, const QString &)),
04191 this, SLOT(error(int, const QString &)));
04192
04193
04194 while (!m_transferEncodings.isEmpty())
04195 {
04196 QString enc = m_transferEncodings.takeLast();
04197 if ( enc == "gzip" )
04198 chain.addFilter(new HTTPFilterGZip);
04199 else if ( enc == "deflate" )
04200 chain.addFilter(new HTTPFilterDeflate);
04201 }
04202
04203
04204
04205
04206
04207
04208
04209 HTTPFilterMD5 *md5Filter = 0;
04210 if ( useMD5 )
04211 {
04212 md5Filter = new HTTPFilterMD5;
04213 chain.addFilter(md5Filter);
04214 }
04215
04216
04217
04218
04219
04220
04221
04222
04223
04224 while (!m_contentEncodings.isEmpty())
04225 {
04226 QString enc = m_contentEncodings.takeLast();
04227 if ( enc == "gzip" )
04228 chain.addFilter(new HTTPFilterGZip);
04229 else if ( enc == "deflate" )
04230 chain.addFilter(new HTTPFilterDeflate);
04231 }
04232
04233 while (!m_isEOF)
04234 {
04235 int bytesReceived;
04236
04237 if (m_isChunked)
04238 bytesReceived = readChunked();
04239 else if (m_iSize != NO_SIZE)
04240 bytesReceived = readLimited();
04241 else
04242 bytesReceived = readUnlimited();
04243
04244
04245
04246
04247
04248 if (bytesReceived == -1)
04249 {
04250 if (m_iContentLeft == 0)
04251 {
04252
04253
04254 m_iBytesLeft = 0;
04255 break;
04256 }
04257
04258 kDebug(7113) << "bytesReceived==-1 sz=" << (int)sz
04259 << " Connection broken !";
04260 error(ERR_CONNECTION_BROKEN, m_request.url.host());
04261 return false;
04262 }
04263
04264
04265
04266 if (bytesReceived > 0)
04267 {
04268
04269
04270 m_receiveBuf.truncate( bytesReceived );
04271
04272 chain.slotInput(m_receiveBuf);
04273
04274 if (m_isError)
04275 return false;
04276
04277 sz += bytesReceived;
04278 if (!dataInternal)
04279 processedSize( sz );
04280 }
04281 m_receiveBuf.resize(0);
04282
04283 if (m_iBytesLeft && m_isEOD && !m_isChunked)
04284 {
04285
04286
04287 m_iBytesLeft = 0;
04288 }
04289
04290 if (m_iBytesLeft == 0)
04291 {
04292 kDebug(7113) << "EOD received! Left = "<< KIO::number(m_iBytesLeft);
04293 break;
04294 }
04295 }
04296 chain.slotInput(QByteArray());
04297
04298 if ( useMD5 )
04299 {
04300 QString calculatedMD5 = md5Filter->md5();
04301
04302 if ( m_contentMD5 != calculatedMD5 )
04303 kWarning(7113) << "MD5 checksum MISMATCH! Expected: "
04304 << calculatedMD5 << ", Got: " << m_contentMD5;
04305 }
04306
04307
04308 if (m_iBytesLeft == 0)
04309 {
04310 if (m_request.cacheTag.writeToCache && m_request.cacheTag.gzs)
04311 closeCacheEntry();
04312 }
04313
04314 if (sz <= 1)
04315 {
04316 if (m_request.responseCode >= 500 && m_request.responseCode <= 599) {
04317 error(ERR_INTERNAL_SERVER, m_request.url.host());
04318 return false;
04319 } else if (m_request.responseCode >= 400 && m_request.responseCode <= 499) {
04320 error(ERR_DOES_NOT_EXIST, m_request.url.host());
04321 return false;
04322 }
04323 }
04324
04325 if (!dataInternal)
04326 data( QByteArray() );
04327 return true;
04328 }
04329
04330
04331 void HTTPProtocol::error( int _err, const QString &_text )
04332 {
04333 httpClose(false);
04334
04335 if (!m_request.id.isEmpty())
04336 {
04337 forwardHttpResponseHeader();
04338 sendMetaData();
04339 }
04340
04341
04342 m_POSTbuf.clear();
04343
04344 SlaveBase::error( _err, _text );
04345 m_isError = true;
04346 }
04347
04348
04349 void HTTPProtocol::addCookies( const QString &url, const QByteArray &cookieHeader )
04350 {
04351 qlonglong windowId = m_request.windowId.toLongLong();
04352 QDBusInterface kcookiejar( "org.kde.kded", "/modules/kcookiejar", "org.kde.KCookieServer" );
04353 (void)kcookiejar.call( QDBus::NoBlock, "addCookies", url,
04354 cookieHeader, windowId );
04355 }
04356
04357 QString HTTPProtocol::findCookies( const QString &url)
04358 {
04359 qlonglong windowId = m_request.windowId.toLongLong();
04360 QDBusInterface kcookiejar( "org.kde.kded", "/modules/kcookiejar", "org.kde.KCookieServer" );
04361 QDBusReply<QString> reply = kcookiejar.call( "findCookies", url, windowId );
04362
04363 if ( !reply.isValid() )
04364 {
04365 kWarning(7113) << "Can't communicate with kded_kcookiejar!";
04366 return QString();
04367 }
04368 return reply;
04369 }
04370
04371
04372
04373
04374 void HTTPProtocol::cacheUpdate( const KUrl& url, bool no_cache, time_t expireDate)
04375 {
04376 if (!maybeSetRequestUrl(url))
04377 return;
04378
04379
04380 resetSessionSettings();
04381
04382 m_request.cacheTag.policy= CC_Reload;
04383
04384 if (no_cache)
04385 {
04386 m_request.cacheTag.gzs = checkCacheEntry( );
04387 if (m_request.cacheTag.gzs)
04388 {
04389 gzclose(m_request.cacheTag.gzs);
04390 m_request.cacheTag.gzs = 0;
04391 ::unlink( QFile::encodeName(m_request.cacheTag.file) );
04392 }
04393 }
04394 else
04395 {
04396 updateExpireDate( expireDate );
04397 }
04398 finished();
04399 }
04400
04401
04402
04403
04404
04405 gzFile HTTPProtocol::checkCacheEntry( bool readWrite)
04406 {
04407 const QChar separator = '_';
04408
04409 QString CEF = m_request.url.path();
04410
04411 int p = CEF.indexOf('/');
04412
04413 while(p != -1)
04414 {
04415 CEF[p] = separator;
04416 p = CEF.indexOf('/', p);
04417 }
04418
04419 QString host = m_request.url.host().toLower();
04420 CEF = host + CEF + '_';
04421
04422 QString dir = m_strCacheDir;
04423 if (dir[dir.length()-1] != '/')
04424 dir += '/';
04425
04426 int l = host.length();
04427 for(int i = 0; i < l; i++)
04428 {
04429 if (host[i].isLetter() && (host[i] != 'w'))
04430 {
04431 dir += host[i];
04432 break;
04433 }
04434 }
04435 if (dir[dir.length()-1] == '/')
04436 dir += '0';
04437
04438 unsigned long hash = 0x00000000;
04439 QByteArray u = m_request.url.url().toLatin1();
04440 for(int i = u.length(); i--;)
04441 {
04442 hash = (hash * 12211 + u.at(i)) % 2147483563;
04443 }
04444
04445 QString hashString;
04446 hashString.sprintf("%08lx", hash);
04447
04448 CEF = CEF + hashString;
04449
04450 CEF = dir + '/' + CEF;
04451
04452 m_request.cacheTag.file = CEF;
04453
04454 const char *mode = (readWrite ? "r+b" : "rb");
04455
04456 gzFile fs = gzopen( QFile::encodeName(CEF), mode);
04457 if (!fs)
04458 return 0;
04459
04460 char buffer[401];
04461 bool ok = true;
04462
04463
04464 if (ok && (!gzgets(fs, buffer, 400)))
04465 ok = false;
04466 if (ok && (strcmp(buffer, CACHE_REVISION) != 0))
04467 ok = false;
04468
04469 time_t date;
04470 time_t currentDate = time(0);
04471
04472
04473 if (ok && (!gzgets(fs, buffer, 400)))
04474 ok = false;
04475 if (ok)
04476 {
04477 int l = strlen(buffer);
04478 if (l>0)
04479 buffer[l-1] = 0;
04480 if (m_request.url.url() != buffer)
04481 {
04482 ok = false;
04483 }
04484 }
04485
04486
04487 if (ok && (!gzgets(fs, buffer, 400)))
04488 ok = false;
04489 if (ok)
04490 {
04491 date = (time_t) strtoul(buffer, 0, 10);
04492 m_request.cacheTag.creationDate = date;
04493 if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge))
04494 {
04495 m_request.cacheTag.isExpired = true;
04496 m_request.cacheTag.expireDate = currentDate;
04497 }
04498 }
04499
04500
04501 m_request.cacheTag.expireDateOffset = gztell(fs);
04502 if (ok && (!gzgets(fs, buffer, 400)))
04503 ok = false;
04504 if (ok)
04505 {
04506 if (m_request.cacheTag.policy== CC_Verify)
04507 {
04508 date = (time_t) strtoul(buffer, 0, 10);
04509
04510 if (!date || difftime(currentDate, date) >= 0)
04511 m_request.cacheTag.isExpired = true;
04512 m_request.cacheTag.expireDate = date;
04513 }
04514 else if (m_request.cacheTag.policy== CC_Refresh)
04515 {
04516 m_request.cacheTag.isExpired = true;
04517 m_request.cacheTag.expireDate = currentDate;
04518 }
04519 }
04520
04521
04522 if (ok && (!gzgets(fs, buffer, 400)))
04523 ok = false;
04524 if (ok)
04525 {
04526 m_request.cacheTag.etag = QString(buffer).trimmed();
04527 }
04528
04529
04530 if (ok && (!gzgets(fs, buffer, 400)))
04531 ok = false;
04532 if (ok)
04533 {
04534 m_request.cacheTag.bytesCached=0;
04535 m_request.cacheTag.lastModified = QString(buffer).trimmed();
04536
04537
04538
04539
04540
04541
04542 int freq=0;
04543 FILE* hitdata = fopen( QFile::encodeName(CEF+"_freq"), "r+");
04544 if (hitdata)
04545 {
04546 freq=fgetc(hitdata);
04547 if (freq!=EOF)
04548 freq+=fgetc(hitdata)<<8;
04549 else
04550 freq=0;
04551 KDE_fseek(hitdata,0,SEEK_SET);
04552 }
04553 if (hitdata||(hitdata=fopen(QFile::encodeName(CEF+"_freq"), "w")))
04554 {
04555 fputc(++freq,hitdata);
04556 fputc(freq>>8,hitdata);
04557 fclose(hitdata);
04558 }
04559
04560 return fs;
04561 }
04562
04563 gzclose(fs);
04564 unlink( QFile::encodeName(CEF));
04565 return 0;
04566 }
04567
04568 void HTTPProtocol::updateExpireDate(time_t expireDate, bool updateCreationDate)
04569 {
04570 bool ok = true;
04571
04572 gzFile fs = checkCacheEntry(true);
04573 if (fs)
04574 {
04575 QString date;
04576 char buffer[401];
04577 time_t creationDate;
04578
04579 gzseek(fs, 0, SEEK_SET);
04580 if (ok && !gzgets(fs, buffer, 400))
04581 ok = false;
04582 if (ok && !gzgets(fs, buffer, 400))
04583 ok = false;
04584 long cacheCreationDateOffset = gztell(fs);
04585 if (ok && !gzgets(fs, buffer, 400))
04586 ok = false;
04587 creationDate = strtoul(buffer, 0, 10);
04588 if (!creationDate)
04589 ok = false;
04590
04591 if (updateCreationDate)
04592 {
04593 if (!ok || gzseek(fs, cacheCreationDateOffset, SEEK_SET))
04594 return;
04595 QString date;
04596 date.setNum( time(0) );
04597 date = date.leftJustified(16);
04598 gzputs(fs, date.toLatin1());
04599 gzputc(fs, '\n');
04600 }
04601
04602 if (expireDate > (30 * 365 * 24 * 60 * 60))
04603 {
04604
04605
04606 date.setNum( expireDate );
04607 }
04608 else
04609 {
04610
04611
04612
04613
04614
04615 date.setNum( creationDate + expireDate );
04616 }
04617 date = date.leftJustified(16);
04618 if (!ok || gzseek(fs, m_request.cacheTag.expireDateOffset, SEEK_SET))
04619 return;
04620 gzputs(fs, date.toLatin1());
04621 gzseek(fs, 0, SEEK_END);
04622 gzclose(fs);
04623 }
04624 }
04625
04626 void HTTPProtocol::createCacheEntry( const QString &mimetype, time_t expireDate)
04627 {
04628 QString dir = m_request.cacheTag.file;
04629 int p = dir.lastIndexOf('/');
04630 if (p == -1) return;
04631 dir.truncate(p);
04632
04633
04634 KDE_mkdir( QFile::encodeName(dir), 0700 );
04635
04636 QString filename = m_request.cacheTag.file + ".new";
04637
04638
04639
04640 m_request.cacheTag.gzs = gzopen( QFile::encodeName(filename), "wb");
04641 if (!m_request.cacheTag.gzs)
04642 {
04643 kWarning(7113) << "opening" << filename << "failed.";
04644 return;
04645 }
04646
04647 gzputs(m_request.cacheTag.gzs, CACHE_REVISION);
04648
04649 gzputs(m_request.cacheTag.gzs, m_request.url.url().toLatin1());
04650 gzputc(m_request.cacheTag.gzs, '\n');
04651
04652 QString date;
04653 m_request.cacheTag.creationDate = time(0);
04654 date.setNum( m_request.cacheTag.creationDate );
04655 date = date.leftJustified(16);
04656 gzputs(m_request.cacheTag.gzs, date.toLatin1());
04657 gzputc(m_request.cacheTag.gzs, '\n');
04658
04659 date.setNum( expireDate );
04660 date = date.leftJustified(16);
04661 gzputs(m_request.cacheTag.gzs, date.toLatin1());
04662 gzputc(m_request.cacheTag.gzs, '\n');
04663
04664 if (!m_request.cacheTag.etag.isEmpty())
04665 gzputs(m_request.cacheTag.gzs, m_request.cacheTag.etag.toLatin1());
04666 gzputc(m_request.cacheTag.gzs, '\n');
04667
04668 if (!m_request.cacheTag.lastModified.isEmpty())
04669 gzputs(m_request.cacheTag.gzs, m_request.cacheTag.lastModified.toLatin1());
04670 gzputc(m_request.cacheTag.gzs, '\n');
04671
04672 gzputs(m_request.cacheTag.gzs, mimetype.toLatin1());
04673 gzputc(m_request.cacheTag.gzs, '\n');
04674
04675 gzputs(m_request.cacheTag.gzs, m_responseHeaders.join("\n").toLatin1());
04676 gzputc(m_request.cacheTag.gzs, '\n');
04677
04678 gzputc(m_request.cacheTag.gzs, '\n');
04679
04680 return;
04681 }
04682
04683
04684
04685
04686 void HTTPProtocol::writeCacheEntry( const char *buffer, int nbytes)
04687 {
04688
04689
04690
04691 if (gzwrite(m_request.cacheTag.gzs, const_cast<void *>(static_cast<const void *>(buffer)), nbytes) == 0)
04692 {
04693 kWarning(7113) << "writeCacheEntry: writing " << nbytes << " bytes failed.";
04694 gzclose(m_request.cacheTag.gzs);
04695 m_request.cacheTag.gzs = 0;
04696 QString filename = m_request.cacheTag.file + ".new";
04697 ::unlink( QFile::encodeName(filename) );
04698 return;
04699 }
04700 m_request.cacheTag.bytesCached+=nbytes;
04701 if ( m_request.cacheTag.bytesCached>>10 > m_maxCacheSize )
04702 {
04703 kDebug(7113) << "writeCacheEntry: File size reaches " << (m_request.cacheTag.bytesCached>>10)
04704 << "Kb, exceeds cache limits. (" << m_maxCacheSize << "Kb)";
04705 gzclose(m_request.cacheTag.gzs);
04706 m_request.cacheTag.gzs = 0;
04707 QString filename = m_request.cacheTag.file + ".new";
04708 ::unlink( QFile::encodeName(filename) );
04709 return;
04710 }
04711 }
04712
04713 void HTTPProtocol::closeCacheEntry()
04714 {
04715 QString filename = m_request.cacheTag.file + ".new";
04716 int result = gzclose( m_request.cacheTag.gzs);
04717 m_request.cacheTag.gzs = 0;
04718 if (result == 0)
04719 {
04720 #ifdef Q_OS_WIN
04721 if ( MoveFileExW( (LPCWSTR)filename.utf16(),
04722 (LPCWSTR)m_request.cacheTag.file.utf16(),
04723 MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED ) != 0 )
04724 return;
04725 #else
04726 if (KDE_rename( QFile::encodeName(filename), QFile::encodeName(m_request.cacheTag.file)) == 0)
04727 return;
04728 #endif
04729 kWarning(7113) << "closeCacheEntry: error renaming "
04730 << "cache entry. (" << filename << " -> " << m_request.cacheTag.file
04731 << ")";
04732 }
04733
04734 kWarning(7113) << "closeCacheEntry: error closing cache "
04735 << "entry. (" << filename<< ")";
04736 }
04737
04738 void HTTPProtocol::cleanCache()
04739 {
04740 const time_t maxAge = DEFAULT_CLEAN_CACHE_INTERVAL;
04741 bool doClean = false;
04742 QString cleanFile = m_strCacheDir;
04743 if (cleanFile[cleanFile.length()-1] != '/')
04744 cleanFile += '/';
04745 cleanFile += "cleaned";
04746
04747 KDE_struct_stat stat_buf;
04748
04749 int result = KDE_stat(QFile::encodeName(cleanFile), &stat_buf);
04750 if (result == -1)
04751 {
04752 int fd = creat( QFile::encodeName(cleanFile), 0600);
04753 if (fd != -1)
04754 {
04755 doClean = true;
04756 ::close(fd);
04757 }
04758 }
04759 else
04760 {
04761 time_t age = (time_t) difftime( time(0), stat_buf.st_mtime );
04762 if (age > maxAge)
04763 doClean = true;
04764 }
04765 if (doClean)
04766 {
04767
04768 utime(QFile::encodeName(cleanFile), 0);
04769 KToolInvocation::startServiceByDesktopPath("http_cache_cleaner.desktop");
04770 }
04771 }
04772
04773
04774
04775
04776
04777
04778 void HTTPProtocol::fillPromptInfo(AuthInfo *inf)
04779 {
04780 AuthInfo &info = *inf;
04781
04782 info.keepPassword = true;
04783 info.verifyPath = false;
04784
04785 if ( m_request.responseCode == 401 )
04786 {
04787
04788 info.url = m_request.url;
04789 if ( !m_server.url.user().isEmpty() )
04790 info.username = m_server.url.user();
04791 info.prompt = i18n( "You need to supply a username and a "
04792 "password to access this site." );
04793 Q_ASSERT(m_wwwAuth);
04794 if (m_wwwAuth)
04795 {
04796 info.realmValue = m_wwwAuth->realm();
04797
04798 info.commentLabel = i18n("Site:");
04799 info.comment = i18n("<b>%1</b> at <b>%2</b>", info.realmValue, m_request.url.host());
04800 }
04801 }
04802 else if ( m_request.responseCode == 407 )
04803 {
04804 info.url = m_request.proxyUrl;
04805 info.username = m_request.proxyUrl.user();
04806 info.prompt = i18n( "You need to supply a username and a password for "
04807 "the proxy server listed below before you are allowed "
04808 "to access any sites." );
04809 Q_ASSERT(m_proxyAuth);
04810 if (m_proxyAuth)
04811 {
04812 info.realmValue = m_proxyAuth->realm();
04813
04814 info.commentLabel = i18n("Proxy:");
04815 info.comment = i18n("<b>%1</b> at <b>%2</b>", info.realmValue, m_request.proxyUrl.host());
04816 }
04817 }
04818 }
04819
04820
04821 QString HTTPProtocol::authenticationHeader()
04822 {
04823 QString ret;
04824
04825 if (m_wwwAuth && !m_wwwAuth->isError()) {
04826 ret += "Authorization: ";
04827 ret += m_wwwAuth->headerFragment();
04828 }
04829 if (m_proxyAuth && !m_proxyAuth->isError()) {
04830 ret += "Proxy-Authorization: ";
04831 ret += m_proxyAuth->headerFragment();
04832 }
04833 return ret;
04834 }
04835
04836 #if 0
04837 QString HTTPProtocol::proxyAuthenticationHeader()
04838 {
04839 kDebug(7113) << m_proxyAuth.authorization << m_proxyAuth.scheme
04840 << m_proxyAuth.realm << m_proxyAuth.authCount;
04841
04842
04843
04844
04845 if (m_proxyAuth.realm.isEmpty()) {
04846 AuthInfo info;
04847 info.url = m_request.proxyUrl;
04848 info.username = m_request.proxyUrl.user();
04849 info.password = m_request.proxyUrl.pass();
04850 info.verifyPath = true;
04851 kDebug(7113) << info.url.url() << info.username << info.password
04852 << info.digestInfo << info.realmValue;
04853
04854
04855
04856
04857
04858 if (!info.username.isEmpty() && !info.password.isEmpty()) {
04859 if( m_proxyAuth.authorization.isEmpty() )
04860 m_proxyAuth.scheme = AUTH_None;
04861 else if( m_proxyAuth.authorization.startsWith("Basic") )
04862 m_proxyAuth.scheme = AUTH_Basic;
04863 else if( m_proxyAuth.authorization.startsWith("NTLM") )
04864 m_proxyAuth.scheme = AUTH_NTLM;
04865 else
04866 m_proxyAuth.scheme = AUTH_Digest;
04867 } else {
04868 const bool ccaRes = checkCachedAuthentication(info);
04869 kDebug(7113) << info.url.url() << info.username << info.password
04870 << info.digestInfo << info.realmValue;
04871 kDebug(7113) << ccaRes;
04872 kDebug(7113) << info.url.url() << info.username << info.password
04873 << info.digestInfo << info.realmValue;
04874
04875 if (ccaRes && !info.digestInfo.isEmpty()) {
04876 m_request.proxyUrl.setUser(info.username);
04877 m_request.proxyUrl.setPass(info.password);
04878 m_proxyAuth.realm = info.realmValue;
04879 m_proxyAuth.authorization = info.digestInfo;
04880 if (m_proxyAuth.authorization.startsWith("Basic")) {
04881 m_proxyAuth.scheme = AUTH_Basic;
04882 } else if (m_proxyAuth.authorization.startsWith("NTLM")) {
04883 m_proxyAuth.scheme = AUTH_NTLM;
04884 } else {
04885 m_proxyAuth.scheme = AUTH_Digest;
04886 }
04887 } else {
04888 m_proxyAuth.scheme = AUTH_None;
04889 }
04890 }
04891 }
04892
04893 if (m_proxyAuth.scheme != AUTH_None) {
04894 kDebug(7113) << "Using Proxy Authentication: ";
04895 kDebug(7113) << " HOST =" << m_request.proxyUrl.host();
04896 kDebug(7113) << " PORT =" << m_request.proxyUrl.port();
04897 kDebug(7113) << " USER =" << m_request.proxyUrl.user();
04898 kDebug(7113) << " PASSWORD = [protected]";
04899 kDebug(7113) << " REALM =" << m_proxyAuth.realm;
04900 kDebug(7113) << " EXTRA =" << m_proxyAuth.authorization;
04901 } else {
04902 kDebug(7113) << "m_proxyAuth.scheme = AUTH_None.";
04903 }
04904
04905 switch (m_proxyAuth.scheme) {
04906 case AUTH_Basic:
04907 return createBasicAuth(true);
04908 break;
04909 case AUTH_Digest:
04910 return createDigestAuth(true);
04911 break;
04912 case AUTH_NTLM:
04913 if (m_isFirstRequest) {
04914 return createNTLMAuth(true);
04915 }
04916 break;
04917 case AUTH_None:
04918 default:
04919 break;
04920 }
04921 return QString();
04922 }
04923
04924 QString HTTPProtocol::wwwAuthenticationHeader()
04925 {
04926 kDebug(7113);
04927
04928
04929
04930
04931
04932
04933
04934
04935 if (!m_request.doNotAuthenticate && m_request.responseCode != 401
04936 && m_request.responseCode != 407
04937 && m_wwwAuth.scheme != AUTH_Negotiate) {
04938
04939 AuthInfo info;
04940 info.url = m_request.url;
04941 info.verifyPath = true;
04942 if (!m_request.url.user().isEmpty()) {
04943 info.username = m_request.url.user();
04944 }
04945
04946 kDebug(7113) << "Calling checkCachedAuthentication";
04947
04948 if (checkCachedAuthentication(info) && !info.digestInfo.isEmpty()) {
04949 m_wwwAuth.scheme = AUTH_Digest;
04950 if (info.digestInfo.startsWith("Basic")) {
04951 m_wwwAuth.scheme = AUTH_Basic;
04952 } else if (info.digestInfo.startsWith("NTLM")) {
04953 m_wwwAuth.scheme = AUTH_NTLM;
04954 } else if (info.digestInfo.startsWith("Negotiate")) {
04955 m_wwwAuth.scheme = AUTH_Negotiate;
04956 }
04957
04958 m_server.url.user() = info.username;
04959 m_server.url.pass() = info.password;
04960 m_wwwAuth.realm = info.realmValue;
04961 if (m_wwwAuth.scheme != AUTH_NTLM && m_wwwAuth.scheme != AUTH_Negotiate) {
04962 m_wwwAuth.authorization = info.digestInfo;
04963 }
04964 }
04965 } else {
04966 kDebug(7113) << "Not calling checkCachedAuthentication";
04967 }
04968
04969 if (m_wwwAuth.scheme != AUTH_None) {
04970 kDebug(7113) << "Using Authentication: ";
04971 kDebug(7113) << " HOST =" << m_server.url.host();
04972 kDebug(7113) << " PORT =" << m_server.url.port();
04973 kDebug(7113) << " USER =" << m_server.url.user();
04974 kDebug(7113) << " PASSWORD = [protected]";
04975 kDebug(7113) << " REALM =" << m_wwwAuth.realm;
04976 kDebug(7113) << " EXTRA =" << m_wwwAuth.authorization;
04977 }
04978
04979 switch (m_wwwAuth.scheme) {
04980 case AUTH_Basic:
04981 return createBasicAuth();
04982 case AUTH_Digest:
04983 return createDigestAuth();
04984 #ifdef HAVE_LIBGSSAPI
04985 case AUTH_Negotiate:
04986 return createNegotiateAuth();
04987 #endif
04988 case AUTH_NTLM:
04989 return createNTLMAuth();
04990 case AUTH_None:
04991 default:
04992 break;
04993 }
04994 return QString();
04995 }
04996 #endif
04997 void HTTPProtocol::proxyAuthenticationForSocket(const QNetworkProxy &proxy, QAuthenticator *authenticator)
04998 {
04999 kDebug(7113) << "Authenticator received -- realm: " << authenticator->realm() << "user:"
05000 << authenticator->user();
05001
05002 AuthInfo info;
05003 info.url = m_request.proxyUrl;
05004 info.realmValue = authenticator->realm();
05005 info.verifyPath = true;
05006 info.username = authenticator->user();
05007 info.password = authenticator->password();
05008
05009 const bool haveCachedCredentials = checkCachedAuthentication(info);
05010
05011
05012
05013 if (!haveCachedCredentials || m_socketProxyAuth) {
05014
05015
05016 connect(socket(), SIGNAL(connected()),
05017 this, SLOT(saveProxyAuthenticationForSocket()));
05018
05019 info.prompt = i18n("You need to supply a username and a password for "
05020 "the proxy server listed below before you are allowed "
05021 "to access any sites.");
05022 info.keepPassword = true;
05023 info.commentLabel = i18n("Proxy:");
05024 info.comment = i18n("<b>%1</b> at <b>%2</b>", info.realmValue, m_request.proxyUrl.host());
05025 const bool dataEntered = openPasswordDialog(info, i18n("Proxy Authentication Failed."));
05026 if (!dataEntered) {
05027 error(ERR_USER_CANCELED, m_request.proxyUrl.host());
05028 }
05029 }
05030 authenticator->setUser(info.username);
05031 authenticator->setPassword(info.password);
05032
05033 if (m_socketProxyAuth) {
05034 *m_socketProxyAuth = *authenticator;
05035 } else {
05036 m_socketProxyAuth = new QAuthenticator(*authenticator);
05037 }
05038
05039 m_request.proxyUrl.setUser(info.username);
05040 m_request.proxyUrl.setPassword(info.password);
05041 }
05042
05043 void HTTPProtocol::saveProxyAuthenticationForSocket()
05044 {
05045 kDebug(7113) << "Saving authenticator";
05046 disconnect(socket(), SIGNAL(connected()),
05047 this, SLOT(saveProxyAuthenticationForSocket()));
05048 Q_ASSERT(m_socketProxyAuth);
05049 if (m_socketProxyAuth) {
05050 kDebug(7113) << "-- realm: " << m_socketProxyAuth->realm() << "user:"
05051 << m_socketProxyAuth->user();
05052 KIO::AuthInfo a;
05053 a.verifyPath = true;
05054 a.url = m_request.proxyUrl;
05055 a.realmValue = m_socketProxyAuth->realm();
05056 a.username = m_socketProxyAuth->user();
05057 a.password = m_socketProxyAuth->password();
05058 cacheAuthentication(a);
05059 }
05060 delete m_socketProxyAuth;
05061 m_socketProxyAuth = 0;
05062 }
05063
05064 #include "http.moc"