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