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