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