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