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