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