• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kioslave/http
 

kioslave/http

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 += "&lt;";
00194         } else if (plain.at(i) == '>') {
00195             rich += "&gt;";
00196         } else if (plain.at(i) == '&') {
00197             rich += "&amp;";
00198         } else if (plain.at(i) == '"') {
00199             rich += "&quot;";
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"

kioslave/http

Skip menu "kioslave/http"
  • Main Page
  • Alphabetical List
  • Class List
  • File List

kioslave/http

Skip menu "kioslave/http"
  • arts
  • dcop
  • dnssd
  • interfaces
  •     interface
  •     library
  •   kspeech
  •   ktexteditor
  • kabc
  • kate
  • kcmshell
  • kdecore
  • kded
  • kdefx
  • kdeprint
  • kdesu
  • kdeui
  • kdoctools
  • khtml
  • kimgio
  • kinit
  • kio
  •   bookmarks
  •   httpfilter
  •   kfile
  •   kio
  •   kioexec
  •   kpasswdserver
  •   kssl
  • kioslave
  •   http
  • kjs
  • kmdi
  •   kmdi
  • knewstuff
  • kparts
  • krandr
  • kresources
  • kspell2
  • kunittest
  • kutils
  • kwallet
  • libkmid
  • libkscreensaver
Generated for kioslave/http by doxygen 1.7.6.1
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |