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