diff options
Diffstat (limited to 'tdeioslave/http/http.cc')
-rw-r--r-- | tdeioslave/http/http.cc | 6131 |
1 files changed, 0 insertions, 6131 deletions
diff --git a/tdeioslave/http/http.cc b/tdeioslave/http/http.cc deleted file mode 100644 index 8cd0f7a64..000000000 --- a/tdeioslave/http/http.cc +++ /dev/null @@ -1,6131 +0,0 @@ -/* - Copyright (C) 2000-2003 Waldo Bastian <[email protected]> - Copyright (C) 2000-2002 George Staikos <[email protected]> - Copyright (C) 2000-2002 Dawit Alemayehu <[email protected]> - Copyright (C) 2001,2002 Hamish Rodda <[email protected]> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License (LGPL) as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later - version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include <config.h> - -#include <errno.h> -#include <fcntl.h> -#include <utime.h> -#include <stdlib.h> -#include <signal.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <netinet/in.h> // Required for AIX -#include <netinet/tcp.h> -#include <unistd.h> // must be explicitly included for MacOSX - -/* -#include <netdb.h> -#include <sys/time.h> -#include <sys/wait.h> -*/ - -#include <tqdom.h> -#include <tqfile.h> -#include <tqregexp.h> -#include <tqdatetime.h> -#include <tqstringlist.h> -#include <tqurl.h> - -#include <kurl.h> -#include <kidna.h> -#include <ksocks.h> -#include <kdebug.h> -#include <tdelocale.h> -#include <tdeconfig.h> -#include <kextsock.h> -#include <kservice.h> -#include <krfcdate.h> -#include <kmdcodec.h> -#include <kinstance.h> -#include <kresolver.h> -#include <kmimemagic.h> -#include <dcopclient.h> -#include <kdatastream.h> -#include <tdeapplication.h> -#include <kstandarddirs.h> -#include <kstringhandler.h> -#include <kremoteencoding.h> - -#include "tdeio/ioslave_defaults.h" -#include "tdeio/http_slave_defaults.h" - -#include "httpfilter.h" -#include "http.h" - -#ifdef HAVE_LIBGSSAPI -#ifdef GSSAPI_MIT -#include <gssapi/gssapi.h> -#else -#include <gssapi.h> -#endif /* GSSAPI_MIT */ - -// Catch uncompatible crap (BR86019) -#if defined(GSS_RFC_COMPLIANT_OIDS) && (GSS_RFC_COMPLIANT_OIDS == 0) -#include <gssapi/gssapi_generic.h> -#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name -#endif - -#endif /* HAVE_LIBGSSAPI */ - -#include <misc/tdentlm/tdentlm.h> - -using namespace TDEIO; - -extern "C" { - KDE_EXPORT int kdemain(int argc, char **argv); -} - -int kdemain( int argc, char **argv ) -{ - TDELocale::setMainCatalogue("tdelibs"); - TDEInstance instance( "tdeio_http" ); - ( void ) TDEGlobal::locale(); - - if (argc != 4) - { - fprintf(stderr, "Usage: tdeio_http protocol domain-socket1 domain-socket2\n"); - exit(-1); - } - - HTTPProtocol slave(argv[1], argv[2], argv[3]); - slave.dispatchLoop(); - return 0; -} - -/*********************************** Generic utility functions ********************/ - -static char * trimLead (char *orig_string) -{ - while (*orig_string == ' ') - orig_string++; - return orig_string; -} - -static bool isCrossDomainRequest( const TQString& fqdn, const TQString& originURL ) -{ - if (originURL == "true") // Backwards compatibility - return true; - - KURL url ( originURL ); - - // Document Origin domain - TQString a = url.host(); - - // Current request domain - TQString b = fqdn; - - if (a == b) - return false; - - TQStringList l1 = TQStringList::split('.', a); - TQStringList l2 = TQStringList::split('.', b); - - while(l1.count() > l2.count()) - l1.pop_front(); - - while(l2.count() > l1.count()) - l2.pop_front(); - - while(l2.count() >= 2) - { - if (l1 == l2) - return false; - - l1.pop_front(); - l2.pop_front(); - } - - return true; -} - -/* - Eliminates any custom header that could potentically alter the request -*/ -static TQString sanitizeCustomHTTPHeader(const TQString& _header) -{ - TQString sanitizedHeaders; - TQStringList headers = TQStringList::split(TQRegExp("[\r\n]"), _header); - - for(TQStringList::Iterator it = headers.begin(); it != headers.end(); ++it) - { - TQString header = (*it).lower(); - // Do not allow Request line to be specified and ignore - // the other HTTP headers. - if (header.find(':') == -1 || - header.startsWith("host") || - header.startsWith("via")) - continue; - - sanitizedHeaders += (*it); - sanitizedHeaders += "\r\n"; - } - - return sanitizedHeaders.stripWhiteSpace(); -} - -static TQString htmlEscape(const TQString &plain) -{ - TQString rich; - rich.reserve(uint(plain.length() * 1.1)); - for (uint i = 0; i < plain.length(); ++i) { - if (plain.at(i) == '<') { - rich += "<"; - } else if (plain.at(i) == '>') { - rich += ">"; - } else if (plain.at(i) == '&') { - rich += "&"; - } else if (plain.at(i) == '"') { - rich += """; - } else { - rich += plain.at(i); - } - } - rich.squeeze(); - return rich; -} - - -#define NO_SIZE ((TDEIO::filesize_t) -1) - -#ifdef HAVE_STRTOLL -#define STRTOLL strtoll -#else -#define STRTOLL strtol -#endif - - -/************************************** HTTPProtocol **********************************************/ - -HTTPProtocol::HTTPProtocol( const TQCString &protocol, const TQCString &pool, - const TQCString &app ) - :TCPSlaveBase( 0, protocol , pool, app, - (protocol == "https" || protocol == "webdavs") ) -{ - m_requestQueue.setAutoDelete(true); - - m_bBusy = false; - m_bFirstRequest = false; - m_bProxyAuthValid = false; - - m_iSize = NO_SIZE; - m_lineBufUnget = 0; - - m_protocol = protocol; - - m_maxCacheAge = DEFAULT_MAX_CACHE_AGE; - m_maxCacheSize = DEFAULT_MAX_CACHE_SIZE / 2; - m_remoteConnTimeout = DEFAULT_CONNECT_TIMEOUT; - m_remoteRespTimeout = DEFAULT_RESPONSE_TIMEOUT; - m_proxyConnTimeout = DEFAULT_PROXY_CONNECT_TIMEOUT; - - m_pid = getpid(); - - setMultipleAuthCaching( true ); - reparseConfiguration(); -} - -HTTPProtocol::~HTTPProtocol() -{ - httpClose(false); -} - -void HTTPProtocol::reparseConfiguration() -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::reparseConfiguration" << endl; - - m_strProxyRealm = TQString::null; - m_strProxyAuthorization = TQString::null; - ProxyAuthentication = AUTH_None; - m_bUseProxy = false; - - if (m_protocol == "https" || m_protocol == "webdavs") - m_iDefaultPort = DEFAULT_HTTPS_PORT; - else if (m_protocol == "ftp") - m_iDefaultPort = DEFAULT_FTP_PORT; - else - m_iDefaultPort = DEFAULT_HTTP_PORT; -} - -void HTTPProtocol::resetConnectionSettings() -{ - m_bEOF = false; - m_bError = false; - m_lineCount = 0; - m_iWWWAuthCount = 0; - m_lineCountUnget = 0; - m_iProxyAuthCount = 0; - -} - -void HTTPProtocol::resetResponseSettings() -{ - m_bRedirect = false; - m_redirectLocation = KURL(); - m_bChunked = false; - m_iSize = NO_SIZE; - - m_responseHeader.clear(); - m_qContentEncodings.clear(); - m_qTransferEncodings.clear(); - m_sContentMD5 = TQString::null; - m_strMimeType = TQString::null; - - setMetaData("request-id", m_request.id); -} - -void HTTPProtocol::resetSessionSettings() -{ - // Do not reset the URL on redirection if the proxy - // URL, username or password has not changed! - KURL proxy ( config()->readEntry("UseProxy") ); - - if ( m_strProxyRealm.isEmpty() || !proxy.isValid() || - m_proxyURL.host() != proxy.host() || - (!proxy.user().isNull() && proxy.user() != m_proxyURL.user()) || - (!proxy.pass().isNull() && proxy.pass() != m_proxyURL.pass()) ) - { - m_bProxyAuthValid = false; - m_proxyURL = proxy; - m_bUseProxy = m_proxyURL.isValid(); - - kdDebug(7113) << "(" << m_pid << ") Using proxy: " << m_bUseProxy << - " URL: " << m_proxyURL.prettyURL() << - " Realm: " << m_strProxyRealm << endl; - } - - m_bPersistentProxyConnection = config()->readBoolEntry("PersistentProxyConnection", false); - kdDebug(7113) << "(" << m_pid << ") Enable Persistent Proxy Connection: " - << m_bPersistentProxyConnection << endl; - - m_request.bUseCookiejar = config()->readBoolEntry("Cookies"); - m_request.bUseCache = config()->readBoolEntry("UseCache", true); - m_request.bErrorPage = config()->readBoolEntry("errorPage", true); - m_request.bNoAuth = config()->readBoolEntry("no-auth"); - m_strCacheDir = config()->readPathEntry("CacheDir"); - m_maxCacheAge = config()->readNumEntry("MaxCacheAge", DEFAULT_MAX_CACHE_AGE); - m_request.window = config()->readEntry("window-id"); - - kdDebug(7113) << "(" << m_pid << ") Window Id = " << m_request.window << endl; - kdDebug(7113) << "(" << m_pid << ") ssl_was_in_use = " - << metaData ("ssl_was_in_use") << endl; - - m_request.referrer = TQString::null; - if ( config()->readBoolEntry("SendReferrer", true) && - (m_protocol == "https" || m_protocol == "webdavs" || - metaData ("ssl_was_in_use") != "TRUE" ) ) - { - KURL referrerURL ( metaData("referrer") ); - if (referrerURL.isValid()) - { - // Sanitize - TQString protocol = referrerURL.protocol(); - if (protocol.startsWith("webdav")) - { - protocol.replace(0, 6, "http"); - referrerURL.setProtocol(protocol); - } - - if (protocol.startsWith("http")) - { - referrerURL.setRef(TQString::null); - referrerURL.setUser(TQString::null); - referrerURL.setPass(TQString::null); - m_request.referrer = referrerURL.url(); - } - } - } - - if ( config()->readBoolEntry("SendLanguageSettings", true) ) - { - m_request.charsets = config()->readEntry( "Charsets", "iso-8859-1" ); - - if ( !m_request.charsets.isEmpty() ) - m_request.charsets += DEFAULT_PARTIAL_CHARSET_HEADER; - - m_request.languages = config()->readEntry( "Languages", DEFAULT_LANGUAGE_HEADER ); - } - else - { - m_request.charsets = TQString::null; - m_request.languages = TQString::null; - } - - // Adjust the offset value based on the "resume" meta-data. - TQString resumeOffset = metaData("resume"); - if ( !resumeOffset.isEmpty() ) - m_request.offset = resumeOffset.toInt(); // TODO: Convert to 64 bit - else - m_request.offset = 0; - - m_request.disablePassDlg = config()->readBoolEntry("DisablePassDlg", false); - m_request.allowCompressedPage = config()->readBoolEntry("AllowCompressedPage", true); - m_request.id = metaData("request-id"); - - // Store user agent for this host. - if ( config()->readBoolEntry("SendUserAgent", true) ) - m_request.userAgent = metaData("UserAgent"); - else - m_request.userAgent = TQString::null; - - // Deal with cache cleaning. - // TODO: Find a smarter way to deal with cleaning the - // cache ? - if ( m_request.bUseCache ) - cleanCache(); - - // Deal with HTTP tunneling - if ( m_bIsSSL && m_bUseProxy && m_proxyURL.protocol() != "https" && - m_proxyURL.protocol() != "webdavs") - { - m_bNeedTunnel = true; - setRealHost( m_request.hostname ); - kdDebug(7113) << "(" << m_pid << ") SSL tunnel: Setting real hostname to: " - << m_request.hostname << endl; - } - else - { - m_bNeedTunnel = false; - setRealHost( TQString::null); - } - - m_responseCode = 0; - m_prevResponseCode = 0; - - m_strRealm = TQString::null; - m_strAuthorization = TQString::null; - Authentication = AUTH_None; - - // Obtain the proxy and remote server timeout values - m_proxyConnTimeout = proxyConnectTimeout(); - m_remoteConnTimeout = connectTimeout(); - m_remoteRespTimeout = responseTimeout(); - - // Set the SSL meta-data here... - setSSLMetaData(); - - // Bounce back the actual referrer sent - setMetaData("referrer", m_request.referrer); - - // Follow HTTP/1.1 spec and enable keep-alive by default - // unless the remote side tells us otherwise or we determine - // the persistent link has been terminated by the remote end. - m_bKeepAlive = true; - m_keepAliveTimeout = 0; - m_bUnauthorized = false; - - // A single request can require multiple exchanges with the remote - // server due to authentication challenges or SSL tunneling. - // m_bFirstRequest is a flag that indicates whether we are - // still processing the first request. This is important because we - // should not force a close of a keep-alive connection in the middle - // of the first request. - // m_bFirstRequest is set to "true" whenever a new connection is - // made in httpOpenConnection() - m_bFirstRequest = false; -} - -void HTTPProtocol::setHost( const TQString& host, int port, - const TQString& user, const TQString& pass ) -{ - // Reset the webdav-capable flags for this host - if ( m_request.hostname != host ) - m_davHostOk = m_davHostUnsupported = false; - - // is it an IPv6 address? - if (host.find(':') == -1) - { - m_request.hostname = host; - m_request.encoded_hostname = KIDNA::toAscii(host); - } - else - { - m_request.hostname = host; - int pos = host.find('%'); - if (pos == -1) - m_request.encoded_hostname = '[' + host + ']'; - else - // don't send the scope-id in IPv6 addresses to the server - m_request.encoded_hostname = '[' + host.left(pos) + ']'; - } - m_request.port = (port == 0) ? m_iDefaultPort : port; - m_request.user = user; - m_request.passwd = pass; - - m_bIsTunneled = false; - - kdDebug(7113) << "(" << m_pid << ") Hostname is now: " << m_request.hostname << - " (" << m_request.encoded_hostname << ")" <<endl; -} - -bool HTTPProtocol::checkRequestURL( const KURL& u ) -{ - kdDebug (7113) << "(" << m_pid << ") HTTPProtocol::checkRequestURL: " << u.prettyURL() << endl; - - m_request.url = u; - - if (m_request.hostname.isEmpty()) - { - error( TDEIO::ERR_UNKNOWN_HOST, i18n("No host specified.")); - return false; - } - - if (u.path().isEmpty()) - { - KURL newUrl(u); - newUrl.setPath("/"); - redirection(newUrl); - finished(); - return false; - } - - if ( m_protocol != u.protocol().latin1() ) - { - short unsigned int oldDefaultPort = m_iDefaultPort; - m_protocol = u.protocol().latin1(); - reparseConfiguration(); - if ( m_iDefaultPort != oldDefaultPort && - m_request.port == oldDefaultPort ) - m_request.port = m_iDefaultPort; - } - - resetSessionSettings(); - return true; -} - -void HTTPProtocol::retrieveContent( bool dataInternal /* = false */ ) -{ - kdDebug (7113) << "(" << m_pid << ") HTTPProtocol::retrieveContent " << endl; - if ( !retrieveHeader( false ) ) - { - if ( m_bError ) - return; - } - else - { - if ( !readBody( dataInternal ) && m_bError ) - return; - } - - httpClose(m_bKeepAlive); - - // if data is required internally, don't finish, - // it is processed before we finish() - if ( !dataInternal ) - { - if ((m_responseCode == 204) && - ((m_request.method == HTTP_GET) || (m_request.method == HTTP_POST))) - error(ERR_NO_CONTENT, ""); - else - finished(); - } -} - -bool HTTPProtocol::retrieveHeader( bool close_connection ) -{ - kdDebug (7113) << "(" << m_pid << ") HTTPProtocol::retrieveHeader " << endl; - while ( 1 ) - { - if (!httpOpen()) - return false; - - resetResponseSettings(); - if (!readHeader()) - { - if ( m_bError ) - return false; - - if (m_bIsTunneled) - { - kdDebug(7113) << "(" << m_pid << ") Re-establishing SSL tunnel..." << endl; - httpCloseConnection(); - } - } - else - { - // Do not save authorization if the current response code is - // 4xx (client error) or 5xx (server error). - kdDebug(7113) << "(" << m_pid << ") Previous Response: " - << m_prevResponseCode << endl; - kdDebug(7113) << "(" << m_pid << ") Current Response: " - << m_responseCode << endl; - - if (isSSLTunnelEnabled() && m_bIsSSL && !m_bUnauthorized && !m_bError) - { - // If there is no error, disable tunneling - if ( m_responseCode < 400 ) - { - kdDebug(7113) << "(" << m_pid << ") Unset tunneling flag!" << endl; - setEnableSSLTunnel( false ); - m_bIsTunneled = true; - // Reset the CONNECT response code... - m_responseCode = m_prevResponseCode; - continue; - } - else - { - if ( !m_request.bErrorPage ) - { - kdDebug(7113) << "(" << m_pid << ") Sending an error message!" << endl; - error( ERR_UNKNOWN_PROXY_HOST, m_proxyURL.host() ); - return false; - } - - kdDebug(7113) << "(" << m_pid << ") Sending an error page!" << endl; - } - } - - if (m_responseCode < 400 && (m_prevResponseCode == 401 || - m_prevResponseCode == 407)) - saveAuthorization(); - break; - } - } - - // Clear of the temporary POST buffer if it is not empty... - if (!m_bufPOST.isEmpty()) - { - m_bufPOST.resize(0); - kdDebug(7113) << "(" << m_pid << ") HTTP::retreiveHeader: Cleared POST " - "buffer..." << endl; - } - - if ( close_connection ) - { - httpClose(m_bKeepAlive); - finished(); - } - - return true; -} - -void HTTPProtocol::stat(const KURL& url) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::stat " << url.prettyURL() - << endl; - - if ( !checkRequestURL( url ) ) - return; - - if ( m_protocol != "webdav" && m_protocol != "webdavs" ) - { - TQString statSide = metaData(TQString::fromLatin1("statSide")); - if ( statSide != "source" ) - { - // When uploading we assume the file doesn't exit - error( ERR_DOES_NOT_EXIST, url.prettyURL() ); - return; - } - - // When downloading we assume it exists - UDSEntry entry; - UDSAtom atom; - atom.m_uds = TDEIO::UDS_NAME; - atom.m_str = url.fileName(); - entry.append( atom ); - - atom.m_uds = TDEIO::UDS_FILE_TYPE; - atom.m_long = S_IFREG; // a file - entry.append( atom ); - - atom.m_uds = TDEIO::UDS_ACCESS; - atom.m_long = S_IRUSR | S_IRGRP | S_IROTH; // readable by everybody - entry.append( atom ); - - statEntry( entry ); - finished(); - return; - } - - davStatList( url ); -} - -void HTTPProtocol::listDir( const KURL& url ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::listDir " << url.prettyURL() - << endl; - - if ( !checkRequestURL( url ) ) - return; - - if (!url.protocol().startsWith("webdav")) { - error(ERR_UNSUPPORTED_ACTION, url.prettyURL()); - return; - } - - davStatList( url, false ); -} - -void HTTPProtocol::davSetRequest( const TQCString& requestXML ) -{ - // insert the document into the POST buffer, kill trailing zero byte - m_bufPOST = requestXML; - - if (m_bufPOST.size()) - m_bufPOST.truncate( m_bufPOST.size() - 1 ); -} - -void HTTPProtocol::davStatList( const KURL& url, bool stat ) -{ - UDSEntry entry; - UDSAtom atom; - - // check to make sure this host supports WebDAV - if ( !davHostOk() ) - return; - - // Maybe it's a disguised SEARCH... - TQString query = metaData("davSearchQuery"); - if ( !query.isEmpty() ) - { - TQCString request = "<?xml version=\"1.0\"?>\r\n"; - request.append( "<D:searchrequest xmlns:D=\"DAV:\">\r\n" ); - request.append( query.utf8() ); - request.append( "</D:searchrequest>\r\n" ); - - davSetRequest( request ); - } else { - // We are only after certain features... - TQCString request; - request = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" - "<D:propfind xmlns:D=\"DAV:\">"; - - // insert additional XML request from the davRequestResponse metadata - if ( hasMetaData( "davRequestResponse" ) ) - request += metaData( "davRequestResponse" ).utf8(); - else { - // No special request, ask for default properties - request += "<D:prop>" - "<D:creationdate/>" - "<D:getcontentlength/>" - "<D:displayname/>" - "<D:source/>" - "<D:getcontentlanguage/>" - "<D:getcontenttype/>" - "<D:executable/>" - "<D:getlastmodified/>" - "<D:getetag/>" - "<D:supportedlock/>" - "<D:lockdiscovery/>" - "<D:resourcetype/>" - "</D:prop>"; - } - request += "</D:propfind>"; - - davSetRequest( request ); - } - - // WebDAV Stat or List... - m_request.method = query.isEmpty() ? DAV_PROPFIND : DAV_SEARCH; - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - m_request.davData.depth = stat ? 0 : 1; - if (!stat) - m_request.url.adjustPath(+1); - - retrieveContent( true ); - - // Has a redirection already been called? If so, we're done. - if (m_bRedirect) { - finished(); - return; - } - - TQDomDocument multiResponse; - multiResponse.setContent( m_bufWebDavData, true ); - - bool hasResponse = false; - - for ( TQDomNode n = multiResponse.documentElement().firstChild(); - !n.isNull(); n = n.nextSibling()) - { - TQDomElement thisResponse = n.toElement(); - if (thisResponse.isNull()) - continue; - - hasResponse = true; - - TQDomElement href = thisResponse.namedItem( "href" ).toElement(); - if ( !href.isNull() ) - { - entry.clear(); - - TQString urlStr = href.text(); -#if 0 - int encoding = remoteEncoding()->encodingMib(); - if ((encoding == 106) && (!KStringHandler::isUtf8(KURL::decode_string(urlStr, 4).latin1()))) - encoding = 4; // Use latin1 if the file is not actually utf-8 -#else - TQUrl::decode(urlStr); - int encoding = 106; -#endif - - KURL thisURL ( urlStr, encoding ); - - atom.m_uds = TDEIO::UDS_NAME; - - if ( thisURL.isValid() ) { - // don't list the base dir of a listDir() - if ( !stat && thisURL.path(+1).length() == url.path(+1).length() ) - continue; - - atom.m_str = thisURL.fileName(); - } else { - // This is a relative URL. - atom.m_str = href.text(); - } - - entry.append( atom ); - - TQDomNodeList propstats = thisResponse.elementsByTagName( "propstat" ); - - davParsePropstats( propstats, entry ); - - if ( stat ) - { - // return an item - statEntry( entry ); - finished(); - return; - } - else - { - listEntry( entry, false ); - } - } - else - { - kdDebug(7113) << "Error: no URL contained in response to PROPFIND on " - << url.prettyURL() << endl; - } - } - - if ( stat || !hasResponse ) - { - error( ERR_DOES_NOT_EXIST, url.prettyURL() ); - } - else - { - listEntry( entry, true ); - finished(); - } -} - -void HTTPProtocol::davGeneric( const KURL& url, TDEIO::HTTP_METHOD method ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::davGeneric " << url.prettyURL() - << endl; - - if ( !checkRequestURL( url ) ) - return; - - // check to make sure this host supports WebDAV - if ( !davHostOk() ) - return; - - // WebDAV method - m_request.method = method; - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - retrieveContent( false ); -} - -int HTTPProtocol::codeFromResponse( const TQString& response ) -{ - int firstSpace = response.find( ' ' ); - int secondSpace = response.find( ' ', firstSpace + 1 ); - return response.mid( firstSpace + 1, secondSpace - firstSpace - 1 ).toInt(); -} - -void HTTPProtocol::davParsePropstats( const TQDomNodeList& propstats, UDSEntry& entry ) -{ - TQString mimeType; - UDSAtom atom; - bool foundExecutable = false; - bool isDirectory = false; - uint lockCount = 0; - uint supportedLockCount = 0; - - for ( uint i = 0; i < propstats.count(); i++) - { - TQDomElement propstat = propstats.item(i).toElement(); - - TQDomElement status = propstat.namedItem( "status" ).toElement(); - if ( status.isNull() ) - { - // error, no status code in this propstat - kdDebug(7113) << "Error, no status code in this propstat" << endl; - return; - } - - int code = codeFromResponse( status.text() ); - - if ( code != 200 ) - { - kdDebug(7113) << "Warning: status code " << code << " (this may mean that some properties are unavailable" << endl; - continue; - } - - TQDomElement prop = propstat.namedItem( "prop" ).toElement(); - if ( prop.isNull() ) - { - kdDebug(7113) << "Error: no prop segment in this propstat." << endl; - return; - } - - if ( hasMetaData( "davRequestResponse" ) ) - { - atom.m_uds = TDEIO::UDS_XML_PROPERTIES; - TQDomDocument doc; - doc.appendChild(prop); - atom.m_str = doc.toString(); - entry.append( atom ); - } - - for ( TQDomNode n = prop.firstChild(); !n.isNull(); n = n.nextSibling() ) - { - TQDomElement property = n.toElement(); - if (property.isNull()) - continue; - - if ( property.namespaceURI() != "DAV:" ) - { - // break out - we're only interested in properties from the DAV namespace - continue; - } - - if ( property.tagName() == "creationdate" ) - { - // Resource creation date. Should be is ISO 8601 format. - atom.m_uds = TDEIO::UDS_CREATION_TIME; - atom.m_long = parseDateTime( property.text(), property.attribute("dt") ); - entry.append( atom ); - } - else if ( property.tagName() == "getcontentlength" ) - { - // Content length (file size) - atom.m_uds = TDEIO::UDS_SIZE; - atom.m_long = property.text().toULong(); - entry.append( atom ); - } - else if ( property.tagName() == "displayname" ) - { - // Name suitable for presentation to the user - setMetaData( "davDisplayName", property.text() ); - } - else if ( property.tagName() == "source" ) - { - // Source template location - TQDomElement source = property.namedItem( "link" ).toElement() - .namedItem( "dst" ).toElement(); - if ( !source.isNull() ) - setMetaData( "davSource", source.text() ); - } - else if ( property.tagName() == "getcontentlanguage" ) - { - // equiv. to Content-Language header on a GET - setMetaData( "davContentLanguage", property.text() ); - } - else if ( property.tagName() == "getcontenttype" ) - { - // Content type (mime type) - // This may require adjustments for other server-side webdav implementations - // (tested with Apache + mod_dav 1.0.3) - if ( property.text() == "httpd/unix-directory" ) - { - isDirectory = true; - } - else - { - mimeType = property.text(); - } - } - else if ( property.tagName() == "executable" ) - { - // File executable status - if ( property.text() == "T" ) - foundExecutable = true; - - } - else if ( property.tagName() == "getlastmodified" ) - { - // Last modification date - atom.m_uds = TDEIO::UDS_MODIFICATION_TIME; - atom.m_long = parseDateTime( property.text(), property.attribute("dt") ); - entry.append( atom ); - - } - else if ( property.tagName() == "getetag" ) - { - // Entity tag - setMetaData( "davEntityTag", property.text() ); - } - else if ( property.tagName() == "supportedlock" ) - { - // Supported locking specifications - for ( TQDomNode n2 = property.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) - { - TQDomElement lockEntry = n2.toElement(); - if ( lockEntry.tagName() == "lockentry" ) - { - TQDomElement lockScope = lockEntry.namedItem( "lockscope" ).toElement(); - TQDomElement lockType = lockEntry.namedItem( "locktype" ).toElement(); - if ( !lockScope.isNull() && !lockType.isNull() ) - { - // Lock type was properly specified - supportedLockCount++; - TQString scope = lockScope.firstChild().toElement().tagName(); - TQString type = lockType.firstChild().toElement().tagName(); - - setMetaData( TQString("davSupportedLockScope%1").arg(supportedLockCount), scope ); - setMetaData( TQString("davSupportedLockType%1").arg(supportedLockCount), type ); - } - } - } - } - else if ( property.tagName() == "lockdiscovery" ) - { - // Lists the available locks - davParseActiveLocks( property.elementsByTagName( "activelock" ), lockCount ); - } - else if ( property.tagName() == "resourcetype" ) - { - // Resource type. "Specifies the nature of the resource." - if ( !property.namedItem( "collection" ).toElement().isNull() ) - { - // This is a collection (directory) - isDirectory = true; - } - } - else - { - kdDebug(7113) << "Found unknown webdav property: " << property.tagName() << endl; - } - } - } - - setMetaData( "davLockCount", TQString("%1").arg(lockCount) ); - setMetaData( "davSupportedLockCount", TQString("%1").arg(supportedLockCount) ); - - atom.m_uds = TDEIO::UDS_FILE_TYPE; - atom.m_long = isDirectory ? S_IFDIR : S_IFREG; - entry.append( atom ); - - if ( foundExecutable || isDirectory ) - { - // File was executable, or is a directory. - atom.m_uds = TDEIO::UDS_ACCESS; - atom.m_long = 0700; - entry.append(atom); - } - else - { - atom.m_uds = TDEIO::UDS_ACCESS; - atom.m_long = 0600; - entry.append(atom); - } - - if ( !isDirectory && !mimeType.isEmpty() ) - { - atom.m_uds = TDEIO::UDS_MIME_TYPE; - atom.m_str = mimeType; - entry.append( atom ); - } -} - -void HTTPProtocol::davParseActiveLocks( const TQDomNodeList& activeLocks, - uint& lockCount ) -{ - for ( uint i = 0; i < activeLocks.count(); i++ ) - { - TQDomElement activeLock = activeLocks.item(i).toElement(); - - lockCount++; - // required - TQDomElement lockScope = activeLock.namedItem( "lockscope" ).toElement(); - TQDomElement lockType = activeLock.namedItem( "locktype" ).toElement(); - TQDomElement lockDepth = activeLock.namedItem( "depth" ).toElement(); - // optional - TQDomElement lockOwner = activeLock.namedItem( "owner" ).toElement(); - TQDomElement lockTimeout = activeLock.namedItem( "timeout" ).toElement(); - TQDomElement lockToken = activeLock.namedItem( "locktoken" ).toElement(); - - if ( !lockScope.isNull() && !lockType.isNull() && !lockDepth.isNull() ) - { - // lock was properly specified - lockCount++; - TQString scope = lockScope.firstChild().toElement().tagName(); - TQString type = lockType.firstChild().toElement().tagName(); - TQString depth = lockDepth.text(); - - setMetaData( TQString("davLockScope%1").arg( lockCount ), scope ); - setMetaData( TQString("davLockType%1").arg( lockCount ), type ); - setMetaData( TQString("davLockDepth%1").arg( lockCount ), depth ); - - if ( !lockOwner.isNull() ) - setMetaData( TQString("davLockOwner%1").arg( lockCount ), lockOwner.text() ); - - if ( !lockTimeout.isNull() ) - setMetaData( TQString("davLockTimeout%1").arg( lockCount ), lockTimeout.text() ); - - if ( !lockToken.isNull() ) - { - TQDomElement tokenVal = lockScope.namedItem( "href" ).toElement(); - if ( !tokenVal.isNull() ) - setMetaData( TQString("davLockToken%1").arg( lockCount ), tokenVal.text() ); - } - } - } -} - -long HTTPProtocol::parseDateTime( const TQString& input, const TQString& type ) -{ - if ( type == "dateTime.tz" ) - { - return KRFCDate::parseDateISO8601( input ); - } - else if ( type == "dateTime.rfc1123" ) - { - return KRFCDate::parseDate( input ); - } - - // format not advertised... try to parse anyway - time_t time = KRFCDate::parseDate( input ); - if ( time != 0 ) - return time; - - return KRFCDate::parseDateISO8601( input ); -} - -TQString HTTPProtocol::davProcessLocks() -{ - if ( hasMetaData( "davLockCount" ) ) - { - TQString response("If:"); - int numLocks; - numLocks = metaData( "davLockCount" ).toInt(); - bool bracketsOpen = false; - for ( int i = 0; i < numLocks; i++ ) - { - if ( hasMetaData( TQString("davLockToken%1").arg(i) ) ) - { - if ( hasMetaData( TQString("davLockURL%1").arg(i) ) ) - { - if ( bracketsOpen ) - { - response += ")"; - bracketsOpen = false; - } - response += " <" + metaData( TQString("davLockURL%1").arg(i) ) + ">"; - } - - if ( !bracketsOpen ) - { - response += " ("; - bracketsOpen = true; - } - else - { - response += " "; - } - - if ( hasMetaData( TQString("davLockNot%1").arg(i) ) ) - response += "Not "; - - response += "<" + metaData( TQString("davLockToken%1").arg(i) ) + ">"; - } - } - - if ( bracketsOpen ) - response += ")"; - - response += "\r\n"; - return response; - } - - return TQString::null; -} - -bool HTTPProtocol::davHostOk() -{ - // FIXME needs to be reworked. Switched off for now. - return true; - - // cached? - if ( m_davHostOk ) - { - kdDebug(7113) << "(" << m_pid << ") " << k_funcinfo << " true" << endl; - return true; - } - else if ( m_davHostUnsupported ) - { - kdDebug(7113) << "(" << m_pid << ") " << k_funcinfo << " false" << endl; - davError( -2 ); - return false; - } - - m_request.method = HTTP_OPTIONS; - - // query the server's capabilities generally, not for a specific URL - m_request.path = "*"; - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - // clear davVersions variable, which holds the response to the DAV: header - m_davCapabilities.clear(); - - retrieveHeader(false); - - if (m_davCapabilities.count()) - { - for (uint i = 0; i < m_davCapabilities.count(); i++) - { - bool ok; - uint verNo = m_davCapabilities[i].toUInt(&ok); - if (ok && verNo > 0 && verNo < 3) - { - m_davHostOk = true; - kdDebug(7113) << "Server supports DAV version " << verNo << "." << endl; - } - } - - if ( m_davHostOk ) - return true; - } - - m_davHostUnsupported = true; - davError( -2 ); - return false; -} - -// This function is for closing retrieveHeader( false ); requests -// Required because there may or may not be further info expected -void HTTPProtocol::davFinished() -{ - // TODO: Check with the DAV extension developers - httpClose(m_bKeepAlive); - finished(); -} - -void HTTPProtocol::mkdir( const KURL& url, int ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::mkdir " << url.prettyURL() - << endl; - - if ( !checkRequestURL( url ) ) - return; - - m_request.method = DAV_MKCOL; - m_request.path = url.path(); - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - retrieveHeader( false ); - - if ( m_responseCode == 201 ) - davFinished(); - else - davError(); -} - -void HTTPProtocol::get( const KURL& url ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::get " << url.prettyURL() - << endl; - - if ( !checkRequestURL( url ) ) - return; - - m_request.method = HTTP_GET; - m_request.path = url.path(); - m_request.query = url.query(); - - TQString tmp = metaData("cache"); - if (!tmp.isEmpty()) - m_request.cache = parseCacheControl(tmp); - else - m_request.cache = DEFAULT_CACHE_CONTROL; - - m_request.passwd = url.pass(); - m_request.user = url.user(); - m_request.doProxy = m_bUseProxy; - - retrieveContent(); -} - -void HTTPProtocol::put( const KURL &url, int, bool overwrite, bool) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::put " << url.prettyURL() - << endl; - - if ( !checkRequestURL( url ) ) - return; - - // Webdav hosts are capable of observing overwrite == false - if (!overwrite && m_protocol.left(6) == "webdav") { - // check to make sure this host supports WebDAV - if ( !davHostOk() ) - return; - - TQCString request; - request = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" - "<D:propfind xmlns:D=\"DAV:\"><D:prop>" - "<D:creationdate/>" - "<D:getcontentlength/>" - "<D:displayname/>" - "<D:resourcetype/>" - "</D:prop></D:propfind>"; - - davSetRequest( request ); - - // WebDAV Stat or List... - m_request.method = DAV_PROPFIND; - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - m_request.davData.depth = 0; - - retrieveContent(true); - - if (m_responseCode == 207) { - error(ERR_FILE_ALREADY_EXIST, TQString::null); - return; - } - - m_bError = false; - } - - m_request.method = HTTP_PUT; - m_request.path = url.path(); - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - retrieveHeader( false ); - - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::put error = " << m_bError << endl; - if (m_bError) - return; - - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::put responseCode = " << m_responseCode << endl; - - httpClose(false); // Always close connection. - - if ( (m_responseCode >= 200) && (m_responseCode < 300) ) - finished(); - else - httpError(); -} - -void HTTPProtocol::copy( const KURL& src, const KURL& dest, int, bool overwrite ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::copy " << src.prettyURL() - << " -> " << dest.prettyURL() << endl; - - if ( !checkRequestURL( dest ) || !checkRequestURL( src ) ) - return; - - // destination has to be "http(s)://..." - KURL newDest = dest; - if (newDest.protocol() == "webdavs") - newDest.setProtocol("https"); - else - newDest.setProtocol("http"); - - m_request.method = DAV_COPY; - m_request.path = src.path(); - m_request.davData.desturl = newDest.url(); - m_request.davData.overwrite = overwrite; - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - retrieveHeader( false ); - - // The server returns a HTTP/1.1 201 Created or 204 No Content on successful completion - if ( m_responseCode == 201 || m_responseCode == 204 ) - davFinished(); - else - davError(); -} - -void HTTPProtocol::rename( const KURL& src, const KURL& dest, bool overwrite ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::rename " << src.prettyURL() - << " -> " << dest.prettyURL() << endl; - - if ( !checkRequestURL( dest ) || !checkRequestURL( src ) ) - return; - - // destination has to be "http://..." - KURL newDest = dest; - if (newDest.protocol() == "webdavs") - newDest.setProtocol("https"); - else - newDest.setProtocol("http"); - - m_request.method = DAV_MOVE; - m_request.path = src.path(); - m_request.davData.desturl = newDest.url(); - m_request.davData.overwrite = overwrite; - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - retrieveHeader( false ); - - if ( m_responseCode == 301 ) - { - // Work around strict Apache-2 WebDAV implementation which refuses to cooperate - // with webdav://host/directory, instead requiring webdav://host/directory/ - // (strangely enough it accepts Destination: without a trailing slash) - - if (m_redirectLocation.protocol() == "https") - m_redirectLocation.setProtocol("webdavs"); - else - m_redirectLocation.setProtocol("webdav"); - - if ( !checkRequestURL( m_redirectLocation ) ) - return; - - m_request.method = DAV_MOVE; - m_request.path = m_redirectLocation.path(); - m_request.davData.desturl = newDest.url(); - m_request.davData.overwrite = overwrite; - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - retrieveHeader( false ); - } - - if ( m_responseCode == 201 ) - davFinished(); - else - davError(); -} - -void HTTPProtocol::del( const KURL& url, bool ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::del " << url.prettyURL() - << endl; - - if ( !checkRequestURL( url ) ) - return; - - m_request.method = HTTP_DELETE; - m_request.path = url.path(); - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - retrieveHeader( false ); - - // The server returns a HTTP/1.1 200 Ok or HTTP/1.1 204 No Content - // on successful completion - if ( m_responseCode == 200 || m_responseCode == 204 ) - davFinished(); - else - davError(); -} - -void HTTPProtocol::post( const KURL& url ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::post " - << url.prettyURL() << endl; - - if ( !checkRequestURL( url ) ) - return; - - m_request.method = HTTP_POST; - m_request.path = url.path(); - m_request.query = url.query(); - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - retrieveContent(); -} - -void HTTPProtocol::davLock( const KURL& url, const TQString& scope, - const TQString& type, const TQString& owner ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::davLock " - << url.prettyURL() << endl; - - if ( !checkRequestURL( url ) ) - return; - - m_request.method = DAV_LOCK; - m_request.path = url.path(); - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - /* Create appropriate lock XML request. */ - TQDomDocument lockReq; - - TQDomElement lockInfo = lockReq.createElementNS( "DAV:", "lockinfo" ); - lockReq.appendChild( lockInfo ); - - TQDomElement lockScope = lockReq.createElement( "lockscope" ); - lockInfo.appendChild( lockScope ); - - lockScope.appendChild( lockReq.createElement( scope ) ); - - TQDomElement lockType = lockReq.createElement( "locktype" ); - lockInfo.appendChild( lockType ); - - lockType.appendChild( lockReq.createElement( type ) ); - - if ( !owner.isNull() ) { - TQDomElement ownerElement = lockReq.createElement( "owner" ); - lockReq.appendChild( ownerElement ); - - TQDomElement ownerHref = lockReq.createElement( "href" ); - ownerElement.appendChild( ownerHref ); - - ownerHref.appendChild( lockReq.createTextNode( owner ) ); - } - - // insert the document into the POST buffer - m_bufPOST = lockReq.toCString(); - - retrieveContent( true ); - - if ( m_responseCode == 200 ) { - // success - TQDomDocument multiResponse; - multiResponse.setContent( m_bufWebDavData, true ); - - TQDomElement prop = multiResponse.documentElement().namedItem( "prop" ).toElement(); - - TQDomElement lockdiscovery = prop.namedItem( "lockdiscovery" ).toElement(); - - uint lockCount = 0; - davParseActiveLocks( lockdiscovery.elementsByTagName( "activelock" ), lockCount ); - - setMetaData( "davLockCount", TQString("%1").arg( lockCount ) ); - - finished(); - - } else - davError(); -} - -void HTTPProtocol::davUnlock( const KURL& url ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::davUnlock " - << url.prettyURL() << endl; - - if ( !checkRequestURL( url ) ) - return; - - m_request.method = DAV_UNLOCK; - m_request.path = url.path(); - m_request.query = TQString::null; - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - retrieveContent( true ); - - if ( m_responseCode == 200 ) - finished(); - else - davError(); -} - -TQString HTTPProtocol::davError( int code /* = -1 */, TQString url ) -{ - bool callError = false; - if ( code == -1 ) { - code = m_responseCode; - callError = true; - } - if ( code == -2 ) { - callError = true; - } - - // Huh? This looks like inverted logic to me (it doesn't make sense to me as - // written), but I'm only fixing the CVE now. -- Kevin Kofler - if ( !url.isNull() ) - url = m_request.url.prettyURL(); - - TQString action, errorString; - TDEIO::Error kError; - - // for 412 Precondition Failed - TQString ow = i18n( "Otherwise, the request would have succeeded." ); - - switch ( m_request.method ) { - case DAV_PROPFIND: - action = i18n( "retrieve property values" ); - break; - case DAV_PROPPATCH: - action = i18n( "set property values" ); - break; - case DAV_MKCOL: - action = i18n( "create the requested folder" ); - break; - case DAV_COPY: - action = i18n( "copy the specified file or folder" ); - break; - case DAV_MOVE: - action = i18n( "move the specified file or folder" ); - break; - case DAV_SEARCH: - action = i18n( "search in the specified folder" ); - break; - case DAV_LOCK: - action = i18n( "lock the specified file or folder" ); - break; - case DAV_UNLOCK: - action = i18n( "unlock the specified file or folder" ); - break; - case HTTP_DELETE: - action = i18n( "delete the specified file or folder" ); - break; - case HTTP_OPTIONS: - action = i18n( "query the server's capabilities" ); - break; - case HTTP_GET: - action = i18n( "retrieve the contents of the specified file or folder" ); - break; - case HTTP_PUT: - case HTTP_POST: - case HTTP_HEAD: - default: - // this should not happen, this function is for webdav errors only - Q_ASSERT(0); - } - - // default error message if the following code fails - kError = ERR_INTERNAL; - errorString = i18n("An unexpected error (%1) occurred while attempting to %2.") - .arg( code ).arg( action ); - - switch ( code ) - { - case -2: - // internal error: OPTIONS request did not specify DAV compliance - kError = ERR_UNSUPPORTED_PROTOCOL; - errorString = i18n("The server does not support the WebDAV protocol."); - break; - case 207: - // 207 Multi-status - { - // our error info is in the returned XML document. - // retrieve the XML document - - // there was an error retrieving the XML document. - // ironic, eh? - if ( !readBody( true ) && m_bError ) - return TQString::null; - - TQStringList errors; - TQDomDocument multiResponse; - - multiResponse.setContent( m_bufWebDavData, true ); - - TQDomElement multistatus = multiResponse.documentElement().namedItem( "multistatus" ).toElement(); - - TQDomNodeList responses = multistatus.elementsByTagName( "response" ); - - for (uint i = 0; i < responses.count(); i++) - { - int errCode; - TQString errUrl; - - TQDomElement response = responses.item(i).toElement(); - TQDomElement code = response.namedItem( "status" ).toElement(); - - if ( !code.isNull() ) - { - errCode = codeFromResponse( code.text() ); - TQDomElement href = response.namedItem( "href" ).toElement(); - if ( !href.isNull() ) - errUrl = href.text(); - errors << davError( errCode, errUrl ); - } - } - - //kError = ERR_SLAVE_DEFINED; - errorString = i18n("An error occurred while attempting to %1, %2. A " - "summary of the reasons is below.<ul>").arg( action ).arg( url ); - - for ( TQStringList::Iterator it = errors.begin(); it != errors.end(); ++it ) - errorString += "<li>" + *it + "</li>"; - - errorString += "</ul>"; - } - case 403: - case 500: // hack: Apache mod_dav returns this instead of 403 (!) - // 403 Forbidden - kError = ERR_ACCESS_DENIED; - errorString = i18n("Access was denied while attempting to %1.").arg( action ); - break; - case 405: - // 405 Method Not Allowed - if ( m_request.method == DAV_MKCOL ) - { - kError = ERR_DIR_ALREADY_EXIST; - errorString = i18n("The specified folder already exists."); - } - break; - case 409: - // 409 Conflict - kError = ERR_ACCESS_DENIED; - errorString = i18n("A resource cannot be created at the destination " - "until one or more intermediate collections (folders) " - "have been created."); - break; - case 412: - // 412 Precondition failed - if ( m_request.method == DAV_COPY || m_request.method == DAV_MOVE ) - { - kError = ERR_ACCESS_DENIED; - errorString = i18n("The server was unable to maintain the liveness of " - "the properties listed in the propertybehavior XML " - "element or you attempted to overwrite a file while " - "requesting that files are not overwritten. %1") - .arg( ow ); - - } - else if ( m_request.method == DAV_LOCK ) - { - kError = ERR_ACCESS_DENIED; - errorString = i18n("The requested lock could not be granted. %1").arg( ow ); - } - break; - case 415: - // 415 Unsupported Media Type - kError = ERR_ACCESS_DENIED; - errorString = i18n("The server does not support the request type of the body."); - break; - case 423: - // 423 Locked - kError = ERR_ACCESS_DENIED; - errorString = i18n("Unable to %1 because the resource is locked.").arg( action ); - break; - case 425: - // 424 Failed Dependency - errorString = i18n("This action was prevented by another error."); - break; - case 502: - // 502 Bad Gateway - if ( m_request.method == DAV_COPY || m_request.method == DAV_MOVE ) - { - kError = ERR_WRITE_ACCESS_DENIED; - errorString = i18n("Unable to %1 because the destination server refuses " - "to accept the file or folder.").arg( action ); - } - break; - case 507: - // 507 Insufficient Storage - kError = ERR_DISK_FULL; - errorString = i18n("The destination resource does not have sufficient space " - "to record the state of the resource after the execution " - "of this method."); - break; - } - - // if ( kError != ERR_SLAVE_DEFINED ) - //errorString += " (" + url + ")"; - - if ( callError ) - error( ERR_SLAVE_DEFINED, errorString ); - - return errorString; -} - -void HTTPProtocol::httpError() -{ - TQString action, errorString; - TDEIO::Error kError; - - switch ( m_request.method ) { - case HTTP_PUT: - action = i18n( "upload %1" ).arg(m_request.url.prettyURL()); - break; - default: - // this should not happen, this function is for http errors only - Q_ASSERT(0); - } - - // default error message if the following code fails - kError = ERR_INTERNAL; - errorString = i18n("An unexpected error (%1) occurred while attempting to %2.") - .arg( m_responseCode ).arg( action ); - - switch ( m_responseCode ) - { - case 403: - case 405: - case 500: // hack: Apache mod_dav returns this instead of 403 (!) - // 403 Forbidden - // 405 Method Not Allowed - kError = ERR_ACCESS_DENIED; - errorString = i18n("Access was denied while attempting to %1.").arg( action ); - break; - case 409: - // 409 Conflict - kError = ERR_ACCESS_DENIED; - errorString = i18n("A resource cannot be created at the destination " - "until one or more intermediate collections (folders) " - "have been created."); - break; - case 423: - // 423 Locked - kError = ERR_ACCESS_DENIED; - errorString = i18n("Unable to %1 because the resource is locked.").arg( action ); - break; - case 502: - // 502 Bad Gateway - kError = ERR_WRITE_ACCESS_DENIED; - errorString = i18n("Unable to %1 because the destination server refuses " - "to accept the file or folder.").arg( action ); - break; - case 507: - // 507 Insufficient Storage - kError = ERR_DISK_FULL; - errorString = i18n("The destination resource does not have sufficient space " - "to record the state of the resource after the execution " - "of this method."); - break; - } - - // if ( kError != ERR_SLAVE_DEFINED ) - //errorString += " (" + url + ")"; - - error( ERR_SLAVE_DEFINED, errorString ); -} - -bool HTTPProtocol::isOffline(const KURL &url) -{ - const int NetWorkStatusUnknown = 1; - const int NetWorkStatusOnline = 8; - TQCString replyType; - TQByteArray params; - TQByteArray reply; - - TQDataStream stream(params, IO_WriteOnly); - - if ( url.host() == TQString::fromLatin1("localhost") || url.host() == TQString::fromLatin1("127.0.0.1") || url.host() == TQString::fromLatin1("::") ) { - return false; - } - if ( dcopClient()->call( "kded", "networkstatus", "status()", - params, replyType, reply ) && (replyType == "int") ) - { - int result; - TQDataStream stream2( reply, IO_ReadOnly ); - stream2 >> result; - kdDebug(7113) << "(" << m_pid << ") networkstatus status = " << result << endl; - return (result != NetWorkStatusUnknown) && (result != NetWorkStatusOnline); - } - kdDebug(7113) << "(" << m_pid << ") networkstatus <unreachable>" << endl; - return false; // On error, assume we are online -} - -void HTTPProtocol::multiGet(const TQByteArray &data) -{ - TQDataStream stream(data, IO_ReadOnly); - TQ_UINT32 n; - stream >> n; - - kdDebug(7113) << "(" << m_pid << ") HTTPProtcool::multiGet n = " << n << endl; - - HTTPRequest saveRequest; - if (m_bBusy) - saveRequest = m_request; - -// m_requestQueue.clear(); - for(unsigned i = 0; i < n; i++) - { - KURL url; - stream >> url >> mIncomingMetaData; - - if ( !checkRequestURL( url ) ) - continue; - - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::multi_get " << url.prettyURL() << endl; - - m_request.method = HTTP_GET; - m_request.path = url.path(); - m_request.query = url.query(); - TQString tmp = metaData("cache"); - if (!tmp.isEmpty()) - m_request.cache = parseCacheControl(tmp); - else - m_request.cache = DEFAULT_CACHE_CONTROL; - - m_request.passwd = url.pass(); - m_request.user = url.user(); - m_request.doProxy = m_bUseProxy; - - HTTPRequest *newRequest = new HTTPRequest(m_request); - m_requestQueue.append(newRequest); - } - - if (m_bBusy) - m_request = saveRequest; - - if (!m_bBusy) - { - m_bBusy = true; - while(!m_requestQueue.isEmpty()) - { - HTTPRequest *request = m_requestQueue.take(0); - m_request = *request; - delete request; - retrieveContent(); - } - m_bBusy = false; - } -} - -ssize_t HTTPProtocol::write (const void *_buf, size_t nbytes) -{ - int bytes_sent = 0; - const char* buf = static_cast<const char*>(_buf); - while ( nbytes > 0 ) - { - int n = TCPSlaveBase::write(buf, nbytes); - - if ( n <= 0 ) - { - // remote side closed connection ? - if ( n == 0 ) - break; - // a valid exception(s) occurred, let's retry... - if (n < 0 && ((errno == EINTR) || (errno == EAGAIN))) - continue; - // some other error occurred ? - return -1; - } - - nbytes -= n; - buf += n; - bytes_sent += n; - } - - return bytes_sent; -} - -void HTTPProtocol::setRewindMarker() -{ - m_rewindCount = 0; -} - -void HTTPProtocol::rewind() -{ - m_linePtrUnget = m_rewindBuf, - m_lineCountUnget = m_rewindCount; - m_rewindCount = 0; -} - - -char *HTTPProtocol::gets (char *s, int size) -{ - int len=0; - char *buf=s; - char mybuf[2]={0,0}; - - while (len < size) - { - read(mybuf, 1); - if (m_bEOF) - break; - - if (m_rewindCount < sizeof(m_rewindBuf)) - m_rewindBuf[m_rewindCount++] = *mybuf; - - if (*mybuf == '\r') // Ignore! - continue; - - if ((*mybuf == '\n') || !*mybuf) - break; - - *buf++ = *mybuf; - len++; - } - - *buf=0; - return s; -} - -ssize_t HTTPProtocol::read (void *b, size_t nbytes) -{ - ssize_t ret = 0; - - if (m_lineCountUnget > 0) - { - ret = ( nbytes < m_lineCountUnget ? nbytes : m_lineCountUnget ); - m_lineCountUnget -= ret; - memcpy(b, m_linePtrUnget, ret); - m_linePtrUnget += ret; - - return ret; - } - - if (m_lineCount > 0) - { - ret = ( nbytes < m_lineCount ? nbytes : m_lineCount ); - m_lineCount -= ret; - memcpy(b, m_linePtr, ret); - m_linePtr += ret; - return ret; - } - - if (nbytes == 1) - { - ret = read(m_lineBuf, 1024); // Read into buffer - m_linePtr = m_lineBuf; - if (ret <= 0) - { - m_lineCount = 0; - return ret; - } - m_lineCount = ret; - return read(b, 1); // Read from buffer - } - - do - { - ret = TCPSlaveBase::read( b, nbytes); - if (ret == 0) - m_bEOF = true; - - } while ((ret == -1) && (errno == EAGAIN || errno == EINTR)); - - return ret; -} - -void HTTPProtocol::httpCheckConnection() -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpCheckConnection: " << - " Socket status: " << m_iSock << - " Keep Alive: " << m_bKeepAlive << - " First: " << m_bFirstRequest << endl; - - if ( !m_bFirstRequest && (m_iSock != -1) ) - { - bool closeDown = false; - if ( !isConnectionValid()) - { - kdDebug(7113) << "(" << m_pid << ") Connection lost!" << endl; - closeDown = true; - } - else if ( m_request.method != HTTP_GET ) - { - closeDown = true; - } - else if ( !m_state.doProxy && !m_request.doProxy ) - { - if (m_state.hostname != m_request.hostname || - m_state.port != m_request.port || - m_state.user != m_request.user || - m_state.passwd != m_request.passwd) - closeDown = true; - } - else - { - // Keep the connection to the proxy. - if ( !(m_request.doProxy && m_state.doProxy) ) - closeDown = true; - } - - if (closeDown) - httpCloseConnection(); - } - - // Let's update our current state - m_state.hostname = m_request.hostname; - m_state.encoded_hostname = m_request.encoded_hostname; - m_state.port = m_request.port; - m_state.user = m_request.user; - m_state.passwd = m_request.passwd; - m_state.doProxy = m_request.doProxy; -} - -bool HTTPProtocol::httpOpenConnection() -{ - int errCode; - TQString errMsg; - - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpOpenConnection" << endl; - - setBlockConnection( true ); - // tdeio_http uses its own proxying: - KSocks::self()->disableSocks(); - - if ( m_state.doProxy ) - { - TQString proxy_host = m_proxyURL.host(); - int proxy_port = m_proxyURL.port(); - - kdDebug(7113) << "(" << m_pid << ") Connecting to proxy server: " - << proxy_host << ", port: " << proxy_port << endl; - - infoMessage( i18n("Connecting to %1...").arg(m_state.hostname) ); - - setConnectTimeout( m_proxyConnTimeout ); - - if ( !connectToHost(proxy_host, proxy_port, false) ) - { - if (userAborted()) { - error(ERR_NO_CONTENT, ""); - return false; - } - - switch ( connectResult() ) - { - case IO_LookupError: - errMsg = proxy_host; - errCode = ERR_UNKNOWN_PROXY_HOST; - break; - case IO_TimeOutError: - errMsg = i18n("Proxy %1 at port %2").arg(proxy_host).arg(proxy_port); - errCode = ERR_SERVER_TIMEOUT; - break; - default: - errMsg = i18n("Proxy %1 at port %2").arg(proxy_host).arg(proxy_port); - errCode = ERR_COULD_NOT_CONNECT; - } - error( errCode, errMsg ); - return false; - } - } - else - { - // Apparently we don't want a proxy. let's just connect directly - setConnectTimeout(m_remoteConnTimeout); - - if ( !connectToHost(m_state.hostname, m_state.port, false ) ) - { - if (userAborted()) { - error(ERR_NO_CONTENT, ""); - return false; - } - - switch ( connectResult() ) - { - case IO_LookupError: - errMsg = m_state.hostname; - errCode = ERR_UNKNOWN_HOST; - break; - case IO_TimeOutError: - errMsg = i18n("Connection was to %1 at port %2").arg(m_state.hostname).arg(m_state.port); - errCode = ERR_SERVER_TIMEOUT; - break; - default: - errCode = ERR_COULD_NOT_CONNECT; - if (m_state.port != m_iDefaultPort) - errMsg = i18n("%1 (port %2)").arg(m_state.hostname).arg(m_state.port); - else - errMsg = m_state.hostname; - } - error( errCode, errMsg ); - return false; - } - } - - // Set our special socket option!! - int on = 1; - (void) setsockopt( m_iSock, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on) ); - - m_bFirstRequest = true; - - connected(); - return true; -} - - -/** - * This function is responsible for opening up the connection to the remote - * HTTP server and sending the header. If this requires special - * authentication or other such fun stuff, then it will handle it. This - * function will NOT receive anything from the server, however. This is in - * contrast to previous incarnations of 'httpOpen'. - * - * The reason for the change is due to one small fact: some requests require - * data to be sent in addition to the header (POST requests) and there is no - * way for this function to get that data. This function is called in the - * slotPut() or slotGet() functions which, in turn, are called (indirectly) as - * a result of a TDEIOJob::put() or TDEIOJob::get(). It is those latter functions - * which are responsible for starting up this ioslave in the first place. - * This means that 'httpOpen' is called (essentially) as soon as the ioslave - * is created -- BEFORE any data gets to this slave. - * - * The basic process now is this: - * - * 1) Open up the socket and port - * 2) Format our request/header - * 3) Send the header to the remote server - */ -bool HTTPProtocol::httpOpen() -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpOpen" << endl; - - // Cannot have an https request without the m_bIsSSL being set! This can - // only happen if TCPSlaveBase::InitializeSSL() function failed in which it - // means the current installation does not support SSL... - if ( (m_protocol == "https" || m_protocol == "webdavs") && !m_bIsSSL ) - { - error( ERR_UNSUPPORTED_PROTOCOL, m_protocol ); - return false; - } - - m_request.fcache = 0; - m_request.bCachedRead = false; - m_request.bCachedWrite = false; - m_request.bMustRevalidate = false; - m_request.expireDate = 0; - m_request.creationDate = 0; - - if (m_request.bUseCache) - { - m_request.fcache = checkCacheEntry( ); - - bool bCacheOnly = (m_request.cache == TDEIO::CC_CacheOnly); - bool bOffline = isOffline(m_request.doProxy ? m_proxyURL : m_request.url); - if (bOffline && (m_request.cache != TDEIO::CC_Reload)) - m_request.cache = TDEIO::CC_CacheOnly; - - if (m_request.cache == CC_Reload && m_request.fcache) - { - if (m_request.fcache) - fclose(m_request.fcache); - m_request.fcache = 0; - } - if ((m_request.cache == TDEIO::CC_CacheOnly) || (m_request.cache == TDEIO::CC_Cache)) - m_request.bMustRevalidate = false; - - m_request.bCachedWrite = true; - - if (m_request.fcache && !m_request.bMustRevalidate) - { - // Cache entry is OK. - m_request.bCachedRead = true; // Cache hit. - return true; - } - else if (!m_request.fcache) - { - m_request.bMustRevalidate = false; // Cache miss - } - else - { - // Conditional cache hit. (Validate) - } - - if (bCacheOnly && bOffline) - { - error( ERR_OFFLINE_MODE, m_request.url.prettyURL() ); - return false; - } - if (bCacheOnly) - { - error( ERR_DOES_NOT_EXIST, m_request.url.prettyURL() ); - return false; - } - if (bOffline) - { - error( ERR_OFFLINE_MODE, m_request.url.prettyURL() ); - return false; - } - } - - TQString header; - TQString davHeader; - - bool moreData = false; - bool davData = false; - - // Clear out per-connection settings... - resetConnectionSettings (); - - // Check the validity of the current connection, if one exists. - httpCheckConnection(); - - if ( !m_bIsTunneled && m_bNeedTunnel ) - { - setEnableSSLTunnel( true ); - // We send a HTTP 1.0 header since some proxies refuse HTTP 1.1 and we don't - // need any HTTP 1.1 capabilities for CONNECT - Waba - header = TQString("CONNECT %1:%2 HTTP/1.0" - "\r\n").arg( m_request.encoded_hostname).arg(m_request.port); - - // Identify who you are to the proxy server! - if (!m_request.userAgent.isEmpty()) - header += "User-Agent: " + m_request.userAgent + "\r\n"; - - /* Add hostname information */ - header += "Host: " + m_state.encoded_hostname; - - if (m_state.port != m_iDefaultPort) - header += TQString(":%1").arg(m_state.port); - header += "\r\n"; - - header += proxyAuthenticationHeader(); - } - else - { - // Determine if this is a POST or GET method - switch (m_request.method) - { - case HTTP_GET: - header = "GET "; - break; - case HTTP_PUT: - header = "PUT "; - moreData = true; - m_request.bCachedWrite = false; // Do not put any result in the cache - break; - case HTTP_POST: - header = "POST "; - moreData = true; - m_request.bCachedWrite = false; // Do not put any result in the cache - break; - case HTTP_HEAD: - header = "HEAD "; - break; - case HTTP_DELETE: - header = "DELETE "; - m_request.bCachedWrite = false; // Do not put any result in the cache - break; - case HTTP_OPTIONS: - header = "OPTIONS "; - m_request.bCachedWrite = false; // Do not put any result in the cache - break; - case DAV_PROPFIND: - header = "PROPFIND "; - davData = true; - davHeader = "Depth: "; - if ( hasMetaData( "davDepth" ) ) - { - kdDebug(7113) << "Reading DAV depth from metadata: " << metaData( "davDepth" ) << endl; - davHeader += metaData( "davDepth" ); - } - else - { - if ( m_request.davData.depth == 2 ) - davHeader += "infinity"; - else - davHeader += TQString("%1").arg( m_request.davData.depth ); - } - davHeader += "\r\n"; - m_request.bCachedWrite = false; // Do not put any result in the cache - break; - case DAV_PROPPATCH: - header = "PROPPATCH "; - davData = true; - m_request.bCachedWrite = false; // Do not put any result in the cache - break; - case DAV_MKCOL: - header = "MKCOL "; - m_request.bCachedWrite = false; // Do not put any result in the cache - break; - case DAV_COPY: - case DAV_MOVE: - header = ( m_request.method == DAV_COPY ) ? "COPY " : "MOVE "; - davHeader = "Destination: " + m_request.davData.desturl; - // infinity depth means copy recursively - // (optional for copy -> but is the desired action) - davHeader += "\r\nDepth: infinity\r\nOverwrite: "; - davHeader += m_request.davData.overwrite ? "T" : "F"; - davHeader += "\r\n"; - m_request.bCachedWrite = false; // Do not put any result in the cache - break; - case DAV_LOCK: - header = "LOCK "; - davHeader = "Timeout: "; - { - uint timeout = 0; - if ( hasMetaData( "davTimeout" ) ) - timeout = metaData( "davTimeout" ).toUInt(); - if ( timeout == 0 ) - davHeader += "Infinite"; - else - davHeader += TQString("Seconds-%1").arg(timeout); - } - davHeader += "\r\n"; - m_request.bCachedWrite = false; // Do not put any result in the cache - davData = true; - break; - case DAV_UNLOCK: - header = "UNLOCK "; - davHeader = "Lock-token: " + metaData("davLockToken") + "\r\n"; - m_request.bCachedWrite = false; // Do not put any result in the cache - break; - case DAV_SEARCH: - header = "SEARCH "; - davData = true; - m_request.bCachedWrite = false; - break; - case DAV_SUBSCRIBE: - header = "SUBSCRIBE "; - m_request.bCachedWrite = false; - break; - case DAV_UNSUBSCRIBE: - header = "UNSUBSCRIBE "; - m_request.bCachedWrite = false; - break; - case DAV_POLL: - header = "POLL "; - m_request.bCachedWrite = false; - break; - default: - error (ERR_UNSUPPORTED_ACTION, TQString::null); - return false; - } - // DAV_POLL; DAV_NOTIFY - - // format the URI - if (m_state.doProxy && !m_bIsTunneled) - { - KURL u; - - if (m_protocol == "webdav") - u.setProtocol( "http" ); - else if (m_protocol == "webdavs" ) - u.setProtocol( "https" ); - else - u.setProtocol( m_protocol ); - - // For all protocols other than the once handled by this io-slave - // append the username. This fixes a long standing bug of ftp io-slave - // logging in anonymously in proxied connections even when the username - // is explicitly specified. - if (m_protocol != "http" && m_protocol != "https" && - !m_state.user.isEmpty()) - u.setUser (m_state.user); - - u.setHost( m_state.hostname ); - if (m_state.port != m_iDefaultPort) - u.setPort( m_state.port ); - u.setEncodedPathAndQuery( m_request.url.encodedPathAndQuery(0,true) ); - header += u.url(); - } - else - { - header += m_request.url.encodedPathAndQuery(0, true); - } - - header += " HTTP/1.1\r\n"; /* start header */ - - if (!m_request.userAgent.isEmpty()) - { - header += "User-Agent: "; - header += m_request.userAgent; - header += "\r\n"; - } - - if (!m_request.referrer.isEmpty()) - { - header += "Referer: "; //Don't try to correct spelling! - header += m_request.referrer; - header += "\r\n"; - } - - if ( m_request.offset > 0 ) - { - header += TQString("Range: bytes=%1-\r\n").arg(TDEIO::number(m_request.offset)); - kdDebug(7103) << "tdeio_http : Range = " << TDEIO::number(m_request.offset) << endl; - } - - if ( m_request.cache == CC_Reload ) - { - /* No caching for reload */ - header += "Pragma: no-cache\r\n"; /* for HTTP/1.0 caches */ - header += "Cache-control: no-cache\r\n"; /* for HTTP >=1.1 caches */ - } - - if (m_request.bMustRevalidate) - { - /* conditional get */ - if (!m_request.etag.isEmpty()) - header += "If-None-Match: "+m_request.etag+"\r\n"; - if (!m_request.lastModified.isEmpty()) - header += "If-Modified-Since: "+m_request.lastModified+"\r\n"; - } - - header += "Accept: "; - TQString acceptHeader = metaData("accept"); - if (!acceptHeader.isEmpty()) - header += acceptHeader; - else - header += DEFAULT_ACCEPT_HEADER; - header += "\r\n"; - -#ifdef DO_GZIP - if (m_request.allowCompressedPage) - header += "Accept-Encoding: x-gzip, x-deflate, gzip, deflate\r\n"; -#endif - - if (!m_request.charsets.isEmpty()) - header += "Accept-Charset: " + m_request.charsets + "\r\n"; - - if (!m_request.languages.isEmpty()) - header += "Accept-Language: " + m_request.languages + "\r\n"; - - - /* support for virtual hosts and required by HTTP 1.1 */ - header += "Host: " + m_state.encoded_hostname; - - if (m_state.port != m_iDefaultPort) - header += TQString(":%1").arg(m_state.port); - header += "\r\n"; - - TQString cookieStr; - TQString cookieMode = metaData("cookies").lower(); - if (cookieMode == "none") - { - m_request.cookieMode = HTTPRequest::CookiesNone; - } - else if (cookieMode == "manual") - { - m_request.cookieMode = HTTPRequest::CookiesManual; - cookieStr = metaData("setcookies"); - } - else - { - m_request.cookieMode = HTTPRequest::CookiesAuto; - if (m_request.bUseCookiejar) - cookieStr = findCookies( m_request.url.url()); - } - - if (!cookieStr.isEmpty()) - header += cookieStr + "\r\n"; - - TQString customHeader = metaData( "customHTTPHeader" ); - if (!customHeader.isEmpty()) - { - header += sanitizeCustomHTTPHeader(customHeader); - header += "\r\n"; - } - - if (m_request.method == HTTP_POST) - { - header += metaData("content-type"); - header += "\r\n"; - } - - // Only check for a cached copy if the previous - // response was NOT a 401 or 407. - // no caching for Negotiate auth. - if ( !m_request.bNoAuth && m_responseCode != 401 && m_responseCode != 407 && Authentication != AUTH_Negotiate ) - { - kdDebug(7113) << "(" << m_pid << ") Calling checkCachedAuthentication " << endl; - AuthInfo info; - info.url = m_request.url; - info.verifyPath = true; - if ( !m_request.user.isEmpty() ) - info.username = m_request.user; - if ( checkCachedAuthentication( info ) && !info.digestInfo.isEmpty() ) - { - Authentication = info.digestInfo.startsWith("Basic") ? AUTH_Basic : info.digestInfo.startsWith("NTLM") ? AUTH_NTLM : info.digestInfo.startsWith("Negotiate") ? AUTH_Negotiate : AUTH_Digest ; - m_state.user = info.username; - m_state.passwd = info.password; - m_strRealm = info.realmValue; - if ( Authentication != AUTH_NTLM && Authentication != AUTH_Negotiate ) // don't use the cached challenge - m_strAuthorization = info.digestInfo; - } - } - else - { - kdDebug(7113) << "(" << m_pid << ") Not calling checkCachedAuthentication " << endl; - } - - switch ( Authentication ) - { - case AUTH_Basic: - header += createBasicAuth(); - break; - case AUTH_Digest: - header += createDigestAuth(); - break; -#ifdef HAVE_LIBGSSAPI - case AUTH_Negotiate: - header += createNegotiateAuth(); - break; -#endif - case AUTH_NTLM: - header += createNTLMAuth(); - break; - case AUTH_None: - default: - break; - } - - /********* Only for debugging purpose *********/ - if ( Authentication != AUTH_None ) - { - kdDebug(7113) << "(" << m_pid << ") Using Authentication: " << endl; - kdDebug(7113) << "(" << m_pid << ") HOST= " << m_state.hostname << endl; - kdDebug(7113) << "(" << m_pid << ") PORT= " << m_state.port << endl; - kdDebug(7113) << "(" << m_pid << ") USER= " << m_state.user << endl; - kdDebug(7113) << "(" << m_pid << ") PASSWORD= [protected]" << endl; - kdDebug(7113) << "(" << m_pid << ") REALM= " << m_strRealm << endl; - kdDebug(7113) << "(" << m_pid << ") EXTRA= " << m_strAuthorization << endl; - } - - // Do we need to authorize to the proxy server ? - if ( m_state.doProxy && !m_bIsTunneled ) - header += proxyAuthenticationHeader(); - - // Support old HTTP/1.0 style keep-alive header for compatability - // purposes as well as performance improvements while giving end - // users the ability to disable this feature proxy servers that - // don't not support such feature, e.g. junkbuster proxy server. - if (!m_bUseProxy || m_bPersistentProxyConnection || m_bIsTunneled) - header += "Connection: Keep-Alive\r\n"; - else - header += "Connection: close\r\n"; - - if ( m_protocol == "webdav" || m_protocol == "webdavs" ) - { - header += davProcessLocks(); - - // add extra webdav headers, if supplied - TQString davExtraHeader = metaData("davHeader"); - if ( !davExtraHeader.isEmpty() ) - davHeader += davExtraHeader; - - // Set content type of webdav data - if (davData) - davHeader += "Content-Type: text/xml; charset=utf-8\r\n"; - - // add extra header elements for WebDAV - if ( !davHeader.isNull() ) - header += davHeader; - } - } - - kdDebug(7103) << "(" << m_pid << ") ============ Sending Header:" << endl; - - TQStringList headerOutput = TQStringList::split("\r\n", header); - TQStringList::Iterator it = headerOutput.begin(); - - for (; it != headerOutput.end(); it++) - kdDebug(7103) << "(" << m_pid << ") " << (*it) << endl; - - if ( !moreData && !davData) - header += "\r\n"; /* end header */ - - // Now that we have our formatted header, let's send it! - // Create a new connection to the remote machine if we do - // not already have one... - if ( m_iSock == -1) - { - if (!httpOpenConnection()) - return false; - } - - // Send the data to the remote machine... - bool sendOk = (write(header.latin1(), header.length()) == (ssize_t) header.length()); - if (!sendOk) - { - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpOpen: " - "Connection broken! (" << m_state.hostname << ")" << endl; - - // With a Keep-Alive connection this can happen. - // Just reestablish the connection. - if (m_bKeepAlive) - { - httpCloseConnection(); - return true; // Try again - } - - if (!sendOk) - { - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpOpen: sendOk==false." - " Connnection broken !" << endl; - error( ERR_CONNECTION_BROKEN, m_state.hostname ); - return false; - } - } - - bool res = true; - - if ( moreData || davData ) - res = sendBody(); - - infoMessage(i18n("%1 contacted. Waiting for reply...").arg(m_request.hostname)); - - return res; -} - -void HTTPProtocol::forwardHttpResponseHeader() -{ - // Send the response header if it was requested - if ( config()->readBoolEntry("PropagateHttpHeader", false) ) - { - setMetaData("HTTP-Headers", m_responseHeader.join("\n")); - sendMetaData(); - } - m_responseHeader.clear(); -} - -/** - * This function will read in the return header from the server. It will - * not read in the body of the return message. It will also not transmit - * the header to our client as the client doesn't need to know the gory - * details of HTTP headers. - */ -bool HTTPProtocol::readHeader() -{ -try_again: - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readHeader" << endl; - - // Check - if (m_request.bCachedRead) - { - m_responseHeader << "HTTP-CACHE"; - // Read header from cache... - char buffer[4097]; - if (!fgets(buffer, 4096, m_request.fcache) ) - { - // Error, delete cache entry - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readHeader: " - << "Could not access cache to obtain mimetype!" << endl; - error( ERR_CONNECTION_BROKEN, m_state.hostname ); - return false; - } - - m_strMimeType = TQString(TQString::fromUtf8( buffer)).stripWhiteSpace(); - - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readHeader: cached " - << "data mimetype: " << m_strMimeType << endl; - - if (!fgets(buffer, 4096, m_request.fcache) ) - { - // Error, delete cache entry - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readHeader: " - << "Could not access cached data! " << endl; - error( ERR_CONNECTION_BROKEN, m_state.hostname ); - return false; - } - - m_request.strCharset = TQString(TQString::fromUtf8( buffer)).stripWhiteSpace().lower(); - setMetaData("charset", m_request.strCharset); - if (!m_request.lastModified.isEmpty()) - setMetaData("modified", m_request.lastModified); - TQString tmp; - tmp.setNum(m_request.expireDate); - setMetaData("expire-date", tmp); - tmp.setNum(m_request.creationDate); - setMetaData("cache-creation-date", tmp); - mimeType(m_strMimeType); - forwardHttpResponseHeader(); - return true; - } - - TQCString locationStr; // In case we get a redirect. - TQCString cookieStr; // In case we get a cookie. - - TQString dispositionType; // In case we get a Content-Disposition type - TQString dispositionFilename; // In case we get a Content-Disposition filename - - TQString mediaValue; - TQString mediaAttribute; - - TQStringList upgradeOffers; - - bool upgradeRequired = false; // Server demands that we upgrade to something - // This is also true if we ask to upgrade and - // the server accepts, since we are now - // committed to doing so - bool canUpgrade = false; // The server offered an upgrade - - - m_request.etag = TQString::null; - m_request.lastModified = TQString::null; - m_request.strCharset = TQString::null; - - time_t dateHeader = 0; - time_t expireDate = 0; // 0 = no info, 1 = already expired, > 1 = actual date - int currentAge = 0; - int maxAge = -1; // -1 = no max age, 0 already expired, > 0 = actual time - int maxHeaderSize = 64*1024; // 64Kb to catch DOS-attacks - - // read in 8192 bytes at a time (HTTP cookies can be quite large.) - int len = 0; - char buffer[8193]; - bool cont = false; - bool cacheValidated = false; // Revalidation was successful - bool mayCache = true; - bool hasCacheDirective = false; - bool bCanResume = false; - - if (m_iSock == -1) - { - kdDebug(7113) << "HTTPProtocol::readHeader: No connection." << endl; - return false; // Restablish connection and try again - } - - if (!waitForResponse(m_remoteRespTimeout)) - { - // No response error - error( ERR_SERVER_TIMEOUT , m_state.hostname ); - return false; - } - - setRewindMarker(); - - gets(buffer, sizeof(buffer)-1); - - if (m_bEOF || *buffer == '\0') - { - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readHeader: " - << "EOF while waiting for header start." << endl; - if (m_bKeepAlive) // Try to reestablish connection. - { - httpCloseConnection(); - return false; // Reestablish connection and try again. - } - - if (m_request.method == HTTP_HEAD) - { - // HACK - // Some web-servers fail to respond properly to a HEAD request. - // We compensate for their failure to properly implement the HTTP standard - // by assuming that they will be sending html. - kdDebug(7113) << "(" << m_pid << ") HTTPPreadHeader: HEAD -> returned " - << "mimetype: " << DEFAULT_MIME_TYPE << endl; - mimeType(TQString::fromLatin1(DEFAULT_MIME_TYPE)); - return true; - } - - kdDebug(7113) << "HTTPProtocol::readHeader: Connection broken !" << endl; - error( ERR_CONNECTION_BROKEN, m_state.hostname ); - return false; - } - - kdDebug(7103) << "(" << m_pid << ") ============ Received Response:"<< endl; - - bool noHeader = true; - HTTP_REV httpRev = HTTP_None; - int headerSize = 0; - - do - { - // strip off \r and \n if we have them - len = strlen(buffer); - - while(len && (buffer[len-1] == '\n' || buffer[len-1] == '\r')) - buffer[--len] = 0; - - // if there was only a newline then continue - if (!len) - { - kdDebug(7103) << "(" << m_pid << ") --empty--" << endl; - continue; - } - - headerSize += len; - - // We have a response header. This flag is a work around for - // servers that append a "\r\n" before the beginning of the HEADER - // response!!! It only catches x number of \r\n being placed at the - // top of the reponse... - noHeader = false; - - kdDebug(7103) << "(" << m_pid << ") \"" << buffer << "\"" << endl; - - // Save broken servers from damnation!! - char* buf = buffer; - while( *buf == ' ' ) - buf++; - - - if (buf[0] == '<') - { - // We get XML / HTTP without a proper header - // put string back - kdDebug(7103) << "tdeio_http: No valid HTTP header found! Document starts with XML/HTML tag" << endl; - - // Document starts with a tag, assume html instead of text/plain - m_strMimeType = "text/html"; - - rewind(); - break; - } - - // Store the the headers so they can be passed to the - // calling application later - m_responseHeader << TQString::fromLatin1(buf); - - if ((strncasecmp(buf, "HTTP/", 5) == 0) || - (strncasecmp(buf, "ICY ", 4) == 0)) // Shoutcast support - { - if (strncasecmp(buf, "ICY ", 4) == 0) - { - // Shoutcast support - httpRev = SHOUTCAST; - m_bKeepAlive = false; - } - else if (strncmp((buf + 5), "1.0",3) == 0) - { - httpRev = HTTP_10; - // For 1.0 servers, the server itself has to explicitly - // tell us whether it supports persistent connection or - // not. By default, we assume it does not, but we do - // send the old style header "Connection: Keep-Alive" to - // inform it that we support persistence. - m_bKeepAlive = false; - } - else if (strncmp((buf + 5), "1.1",3) == 0) - { - httpRev = HTTP_11; - } - else - { - httpRev = HTTP_Unknown; - } - - if (m_responseCode) - m_prevResponseCode = m_responseCode; - - const char* rptr = buf; - while ( *rptr && *rptr > ' ' ) - ++rptr; - m_responseCode = atoi(rptr); - - // server side errors - if (m_responseCode >= 500 && m_responseCode <= 599) - { - if (m_request.method == HTTP_HEAD) - { - ; // Ignore error - } - else - { - if (m_request.bErrorPage) - errorPage(); - else - { - error(ERR_INTERNAL_SERVER, m_request.url.prettyURL()); - return false; - } - } - m_request.bCachedWrite = false; // Don't put in cache - mayCache = false; - } - // Unauthorized access - else if (m_responseCode == 401 || m_responseCode == 407) - { - // Double authorization requests, i.e. a proxy auth - // request followed immediately by a regular auth request. - if ( m_prevResponseCode != m_responseCode && - (m_prevResponseCode == 401 || m_prevResponseCode == 407) ) - saveAuthorization(); - - m_bUnauthorized = true; - m_request.bCachedWrite = false; // Don't put in cache - mayCache = false; - } - // - else if (m_responseCode == 416) // Range not supported - { - m_request.offset = 0; - httpCloseConnection(); - return false; // Try again. - } - // Upgrade Required - else if (m_responseCode == 426) - { - upgradeRequired = true; - } - // Any other client errors - else if (m_responseCode >= 400 && m_responseCode <= 499) - { - // Tell that we will only get an error page here. - if (m_request.bErrorPage) - errorPage(); - else - { - error(ERR_DOES_NOT_EXIST, m_request.url.prettyURL()); - return false; - } - m_request.bCachedWrite = false; // Don't put in cache - mayCache = false; - } - else if (m_responseCode == 307) - { - // 307 Temporary Redirect - m_request.bCachedWrite = false; // Don't put in cache - mayCache = false; - } - else if (m_responseCode == 304) - { - // 304 Not Modified - // The value in our cache is still valid. - cacheValidated = true; - } - else if (m_responseCode >= 301 && m_responseCode<= 303) - { - // 301 Moved permanently - if (m_responseCode == 301) - setMetaData("permanent-redirect", "true"); - - // 302 Found (temporary location) - // 303 See Other - if (m_request.method != HTTP_HEAD && m_request.method != HTTP_GET) - { -#if 0 - // Reset the POST buffer to avoid a double submit - // on redirection - if (m_request.method == HTTP_POST) - m_bufPOST.resize(0); -#endif - - // NOTE: This is wrong according to RFC 2616. However, - // because most other existing user agent implementations - // treat a 301/302 response as a 303 response and preform - // a GET action regardless of what the previous method was, - // many servers have simply adapted to this way of doing - // things!! Thus, we are forced to do the same thing or we - // won't be able to retrieve these pages correctly!! See RFC - // 2616 sections 10.3.[2/3/4/8] - m_request.method = HTTP_GET; // Force a GET - } - m_request.bCachedWrite = false; // Don't put in cache - mayCache = false; - } - else if ( m_responseCode == 207 ) // Multi-status (for WebDav) - { - - } - else if ( m_responseCode == 204 ) // No content - { - // error(ERR_NO_CONTENT, i18n("Data have been successfully sent.")); - // Short circuit and do nothing! - - // The original handling here was wrong, this is not an error: eg. in the - // example of a 204 No Content response to a PUT completing. - // m_bError = true; - // return false; - } - else if ( m_responseCode == 206 ) - { - if ( m_request.offset ) - bCanResume = true; - } - else if (m_responseCode == 102) // Processing (for WebDAV) - { - /*** - * This status code is given when the server expects the - * command to take significant time to complete. So, inform - * the user. - */ - infoMessage( i18n( "Server processing request, please wait..." ) ); - cont = true; - } - else if (m_responseCode == 100) - { - // We got 'Continue' - ignore it - cont = true; - } - } - - // are we allowd to resume? this will tell us - else if (strncasecmp(buf, "Accept-Ranges:", 14) == 0) { - if (strncasecmp(trimLead(buf + 14), "none", 4) == 0) - bCanResume = false; - } - // Keep Alive - else if (strncasecmp(buf, "Keep-Alive:", 11) == 0) { - TQStringList options = TQStringList::split(',', - TQString::fromLatin1(trimLead(buf+11))); - for(TQStringList::ConstIterator it = options.begin(); - it != options.end(); - it++) - { - TQString option = (*it).stripWhiteSpace().lower(); - if (option.startsWith("timeout=")) - { - m_keepAliveTimeout = option.mid(8).toInt(); - } - } - } - - // Cache control - else if (strncasecmp(buf, "Cache-Control:", 14) == 0) { - TQStringList cacheControls = TQStringList::split(',', - TQString::fromLatin1(trimLead(buf+14))); - for(TQStringList::ConstIterator it = cacheControls.begin(); - it != cacheControls.end(); - it++) - { - TQString cacheControl = (*it).stripWhiteSpace(); - if (strncasecmp(cacheControl.latin1(), "no-cache", 8) == 0) - { - m_request.bCachedWrite = false; // Don't put in cache - mayCache = false; - } - else if (strncasecmp(cacheControl.latin1(), "no-store", 8) == 0) - { - m_request.bCachedWrite = false; // Don't put in cache - mayCache = false; - } - else if (strncasecmp(cacheControl.latin1(), "max-age=", 8) == 0) - { - TQString age = cacheControl.mid(8).stripWhiteSpace(); - if (!age.isNull()) - maxAge = STRTOLL(age.latin1(), 0, 10); - } - } - hasCacheDirective = true; - } - - // get the size of our data - else if (strncasecmp(buf, "Content-length:", 15) == 0) { - char* len = trimLead(buf + 15); - if (len) - m_iSize = STRTOLL(len, 0, 10); - } - - else if (strncasecmp(buf, "Content-location:", 17) == 0) { - setMetaData ("content-location", - TQString::fromLatin1(trimLead(buf+17)).stripWhiteSpace()); - } - - // what type of data do we have? - else if (strncasecmp(buf, "Content-type:", 13) == 0) { - char *start = trimLead(buf + 13); - char *pos = start; - - // Increment until we encounter ";" or the end of the buffer - while ( *pos && *pos != ';' ) pos++; - - // Assign the mime-type. - m_strMimeType = TQString::fromLatin1(start, pos-start).stripWhiteSpace().lower(); - kdDebug(7113) << "(" << m_pid << ") Content-type: " << m_strMimeType << endl; - - // If we still have text, then it means we have a mime-type with a - // parameter (eg: charset=iso-8851) ; so let's get that... - while (*pos) - { - start = ++pos; - while ( *pos && *pos != '=' ) pos++; - - char *end = pos; - while ( *end && *end != ';' ) end++; - - if (*pos) - { - mediaAttribute = TQString::fromLatin1(start, pos-start).stripWhiteSpace().lower(); - mediaValue = TQString::fromLatin1(pos+1, end-pos-1).stripWhiteSpace(); - pos = end; - if (mediaValue.length() && - (mediaValue[0] == '"') && - (mediaValue[mediaValue.length()-1] == '"')) - mediaValue = mediaValue.mid(1, mediaValue.length()-2); - - kdDebug (7113) << "(" << m_pid << ") Media-Parameter Attribute: " - << mediaAttribute << endl; - kdDebug (7113) << "(" << m_pid << ") Media-Parameter Value: " - << mediaValue << endl; - - if ( mediaAttribute == "charset") - { - mediaValue = mediaValue.lower(); - m_request.strCharset = mediaValue; - } - else - { - setMetaData("media-"+mediaAttribute, mediaValue); - } - } - } - } - - // Date - else if (strncasecmp(buf, "Date:", 5) == 0) { - dateHeader = KRFCDate::parseDate(trimLead(buf+5)); - } - - // Cache management - else if (strncasecmp(buf, "ETag:", 5) == 0) { - m_request.etag = trimLead(buf+5); - } - - // Cache management - else if (strncasecmp(buf, "Expires:", 8) == 0) { - expireDate = KRFCDate::parseDate(trimLead(buf+8)); - if (!expireDate) - expireDate = 1; // Already expired - } - - // Cache management - else if (strncasecmp(buf, "Last-Modified:", 14) == 0) { - m_request.lastModified = (TQString::fromLatin1(trimLead(buf+14))).stripWhiteSpace(); - } - - // whoops.. we received a warning - else if (strncasecmp(buf, "Warning:", 8) == 0) { - //Don't use warning() here, no need to bother the user. - //Those warnings are mostly about caches. - infoMessage(trimLead(buf + 8)); - } - - // Cache management (HTTP 1.0) - else if (strncasecmp(buf, "Pragma:", 7) == 0) { - TQCString pragma = TQCString(trimLead(buf+7)).stripWhiteSpace().lower(); - if (pragma == "no-cache") - { - m_request.bCachedWrite = false; // Don't put in cache - mayCache = false; - hasCacheDirective = true; - } - } - - // The deprecated Refresh Response - else if (strncasecmp(buf,"Refresh:", 8) == 0) { - mayCache = false; // Do not cache page as it defeats purpose of Refresh tag! - setMetaData( "http-refresh", TQString::fromLatin1(trimLead(buf+8)).stripWhiteSpace() ); - } - - // In fact we should do redirection only if we got redirection code - else if (strncasecmp(buf, "Location:", 9) == 0) { - // Redirect only for 3xx status code, will ya! Thanks, pal! - if ( m_responseCode > 299 && m_responseCode < 400 ) - locationStr = TQCString(trimLead(buf+9)).stripWhiteSpace(); - } - - // Check for cookies - else if (strncasecmp(buf, "Set-Cookie", 10) == 0) { - cookieStr += buf; - cookieStr += '\n'; - } - - // check for direct authentication - else if (strncasecmp(buf, "WWW-Authenticate:", 17) == 0) { - configAuth(trimLead(buf + 17), false); - } - - // check for proxy-based authentication - else if (strncasecmp(buf, "Proxy-Authenticate:", 19) == 0) { - configAuth(trimLead(buf + 19), true); - } - - else if (strncasecmp(buf, "Upgrade:", 8) == 0) { - // Now we have to check to see what is offered for the upgrade - TQString offered = &(buf[8]); - upgradeOffers = TQStringList::split(TQRegExp("[ \n,\r\t]"), offered); - } - - // content? - else if (strncasecmp(buf, "Content-Encoding:", 17) == 0) { - // This is so wrong !! No wonder tdeio_http is stripping the - // gzip encoding from downloaded files. This solves multiple - // bug reports and caitoo's problem with downloads when such a - // header is encountered... - - // A quote from RFC 2616: - // " When present, its (Content-Encoding) value indicates what additional - // content have been applied to the entity body, and thus what decoding - // mechanism must be applied to obtain the media-type referenced by the - // Content-Type header field. Content-Encoding is primarily used to allow - // a document to be compressed without loosing the identity of its underlying - // media type. Simply put if it is specified, this is the actual mime-type - // we should use when we pull the resource !!! - addEncoding(trimLead(buf + 17), m_qContentEncodings); - } - // Refer to RFC 2616 sec 15.5/19.5.1 and RFC 2183 - else if(strncasecmp(buf, "Content-Disposition:", 20) == 0) { - char* dispositionBuf = trimLead(buf + 20); - while ( *dispositionBuf ) - { - if ( strncasecmp( dispositionBuf, "filename", 8 ) == 0 ) - { - dispositionBuf += 8; - - while ( *dispositionBuf == ' ' || *dispositionBuf == '=' ) - dispositionBuf++; - - char* bufStart = dispositionBuf; - - while ( *dispositionBuf && *dispositionBuf != ';' ) - dispositionBuf++; - - if ( dispositionBuf > bufStart ) - { - // Skip any leading quotes... - while ( *bufStart == '"' ) - bufStart++; - - // Skip any trailing quotes as well as white spaces... - while ( *(dispositionBuf-1) == ' ' || *(dispositionBuf-1) == '"') - dispositionBuf--; - - if ( dispositionBuf > bufStart ) - dispositionFilename = TQString::fromLatin1( bufStart, dispositionBuf-bufStart ); - - break; - } - } - else - { - char *bufStart = dispositionBuf; - - while ( *dispositionBuf && *dispositionBuf != ';' ) - dispositionBuf++; - - if ( dispositionBuf > bufStart ) - dispositionType = TQString::fromLatin1( bufStart, dispositionBuf-bufStart ).stripWhiteSpace(); - - while ( *dispositionBuf == ';' || *dispositionBuf == ' ' ) - dispositionBuf++; - } - } - - // Content-Dispostion is not allowed to dictate directory - // path, thus we extract the filename only. - if ( !dispositionFilename.isEmpty() ) - { - int pos = dispositionFilename.findRev( '/' ); - - if( pos > -1 ) - dispositionFilename = dispositionFilename.mid(pos+1); - - kdDebug(7113) << "(" << m_pid << ") Content-Disposition: filename=" - << dispositionFilename<< endl; - } - } - else if(strncasecmp(buf, "Content-Language:", 17) == 0) { - TQString language = TQString::fromLatin1(trimLead(buf+17)).stripWhiteSpace(); - if (!language.isEmpty()) - setMetaData("content-language", language); - } - else if (strncasecmp(buf, "Proxy-Connection:", 17) == 0) - { - if (strncasecmp(trimLead(buf + 17), "Close", 5) == 0) - m_bKeepAlive = false; - else if (strncasecmp(trimLead(buf + 17), "Keep-Alive", 10)==0) - m_bKeepAlive = true; - } - else if (strncasecmp(buf, "Link:", 5) == 0) { - // We only support Link: <url>; rel="type" so far - TQStringList link = TQStringList::split(";", TQString(buf) - .replace(TQRegExp("^Link:[ ]*"), - "")); - if (link.count() == 2) { - TQString rel = link[1].stripWhiteSpace(); - if (rel.startsWith("rel=\"")) { - rel = rel.mid(5, rel.length() - 6); - if (rel.lower() == "pageservices") { - TQString url = TQString(link[0].replace(TQRegExp("[<>]"),"")).stripWhiteSpace(); - setMetaData("PageServices", url); - } - } - } - } - else if (strncasecmp(buf, "P3P:", 4) == 0) { - TQString p3pstr = buf; - p3pstr = p3pstr.mid(4).simplifyWhiteSpace(); - TQStringList policyrefs, compact; - TQStringList policyfields = TQStringList::split(TQRegExp(",[ ]*"), p3pstr); - for (TQStringList::Iterator it = policyfields.begin(); - it != policyfields.end(); - ++it) { - TQStringList policy = TQStringList::split("=", *it); - - if (policy.count() == 2) { - if (policy[0].lower() == "policyref") { - policyrefs << TQString(policy[1].replace(TQRegExp("[\"\']"), "")) - .stripWhiteSpace(); - } else if (policy[0].lower() == "cp") { - // We convert to cp\ncp\ncp\n[...]\ncp to be consistent with - // other metadata sent in strings. This could be a bit more - // efficient but I'm going for correctness right now. - TQStringList cps = TQStringList::split(" ", - TQString(policy[1].replace(TQRegExp("[\"\']"), "")) - .simplifyWhiteSpace()); - - for (TQStringList::Iterator j = cps.begin(); j != cps.end(); ++j) - compact << *j; - } - } - } - - if (!policyrefs.isEmpty()) - setMetaData("PrivacyPolicy", policyrefs.join("\n")); - - if (!compact.isEmpty()) - setMetaData("PrivacyCompactPolicy", compact.join("\n")); - } - // let them tell us if we should stay alive or not - else if (strncasecmp(buf, "Connection:", 11) == 0) - { - if (strncasecmp(trimLead(buf + 11), "Close", 5) == 0) - m_bKeepAlive = false; - else if (strncasecmp(trimLead(buf + 11), "Keep-Alive", 10)==0) - m_bKeepAlive = true; - else if (strncasecmp(trimLead(buf + 11), "Upgrade", 7)==0) - { - if (m_responseCode == 101) { - // Ok, an upgrade was accepted, now we must do it - upgradeRequired = true; - } else if (upgradeRequired) { // 426 - // Nothing to do since we did it above already - } else { - // Just an offer to upgrade - no need to take it - canUpgrade = true; - } - } - } - // continue only if we know that we're HTTP/1.1 - else if ( httpRev == HTTP_11) { - // what kind of encoding do we have? transfer? - if (strncasecmp(buf, "Transfer-Encoding:", 18) == 0) { - // If multiple encodings have been applied to an entity, the - // transfer-codings MUST be listed in the order in which they - // were applied. - addEncoding(trimLead(buf + 18), m_qTransferEncodings); - } - - // md5 signature - else if (strncasecmp(buf, "Content-MD5:", 12) == 0) { - m_sContentMD5 = TQString::fromLatin1(trimLead(buf + 12)); - } - - // *** Responses to the HTTP OPTIONS method follow - // WebDAV capabilities - else if (strncasecmp(buf, "DAV:", 4) == 0) { - if (m_davCapabilities.isEmpty()) { - m_davCapabilities << TQString::fromLatin1(trimLead(buf + 4)); - } - else { - m_davCapabilities << TQString::fromLatin1(trimLead(buf + 4)); - } - } - // *** Responses to the HTTP OPTIONS method finished - } - else if ((httpRev == HTTP_None) && (strlen(buf) != 0)) - { - // Remote server does not seem to speak HTTP at all - // Put the crap back into the buffer and hope for the best - rewind(); - if (m_responseCode) - m_prevResponseCode = m_responseCode; - - m_responseCode = 200; // Fake it - httpRev = HTTP_Unknown; - m_bKeepAlive = false; - break; - } - setRewindMarker(); - - // Clear out our buffer for further use. - memset(buffer, 0, sizeof(buffer)); - - } while (!m_bEOF && (len || noHeader) && (headerSize < maxHeaderSize) && (gets(buffer, sizeof(buffer)-1))); - - // Now process the HTTP/1.1 upgrade - TQStringList::Iterator opt = upgradeOffers.begin(); - for( ; opt != upgradeOffers.end(); ++opt) { - if (*opt == "TLS/1.0") { - if(upgradeRequired) { - if (!startTLS() && !usingTLS()) { - error(ERR_UPGRADE_REQUIRED, *opt); - return false; - } - } - } else if (*opt == "HTTP/1.1") { - httpRev = HTTP_11; - } else { - // unknown - if (upgradeRequired) { - error(ERR_UPGRADE_REQUIRED, *opt); - return false; - } - } - } - - setMetaData("charset", m_request.strCharset); - - // If we do not support the requested authentication method... - if ( (m_responseCode == 401 && Authentication == AUTH_None) || - (m_responseCode == 407 && ProxyAuthentication == AUTH_None) ) - { - m_bUnauthorized = false; - if (m_request.bErrorPage) - errorPage(); - else - { - error( ERR_UNSUPPORTED_ACTION, "Unknown Authorization method!" ); - return false; - } - } - - // Fixup expire date for clock drift. - if (expireDate && (expireDate <= dateHeader)) - expireDate = 1; // Already expired. - - // Convert max-age into expireDate (overriding previous set expireDate) - if (maxAge == 0) - expireDate = 1; // Already expired. - else if (maxAge > 0) - { - if (currentAge) - maxAge -= currentAge; - if (maxAge <=0) - maxAge = 0; - expireDate = time(0) + maxAge; - } - - if (!expireDate) - { - time_t lastModifiedDate = 0; - if (!m_request.lastModified.isEmpty()) - lastModifiedDate = KRFCDate::parseDate(m_request.lastModified); - - if (lastModifiedDate) - { - long diff = static_cast<long>(difftime(dateHeader, lastModifiedDate)); - if (diff < 0) - expireDate = time(0) + 1; - else - expireDate = time(0) + (diff / 10); - } - else - { - expireDate = time(0) + DEFAULT_CACHE_EXPIRE; - } - } - - // DONE receiving the header! - if (!cookieStr.isEmpty()) - { - if ((m_request.cookieMode == HTTPRequest::CookiesAuto) && m_request.bUseCookiejar) - { - // Give cookies to the cookiejar. - TQString domain = config()->readEntry("cross-domain"); - if (!domain.isEmpty() && isCrossDomainRequest(m_request.url.host(), domain)) - cookieStr = "Cross-Domain\n" + cookieStr; - addCookies( m_request.url.url(), cookieStr ); - } - else if (m_request.cookieMode == HTTPRequest::CookiesManual) - { - // Pass cookie to application - setMetaData("setcookies", cookieStr); - } - } - - if (m_request.bMustRevalidate) - { - m_request.bMustRevalidate = false; // Reset just in case. - if (cacheValidated) - { - // Yippie, we can use the cached version. - // Update the cache with new "Expire" headers. - fclose(m_request.fcache); - m_request.fcache = 0; - updateExpireDate( expireDate, true ); - m_request.fcache = checkCacheEntry( ); // Re-read cache entry - - if (m_request.fcache) - { - m_request.bCachedRead = true; - goto try_again; // Read header again, but now from cache. - } - else - { - // Where did our cache entry go??? - } - } - else - { - // Validation failed. Close cache. - fclose(m_request.fcache); - m_request.fcache = 0; - } - } - - // We need to reread the header if we got a '100 Continue' or '102 Processing' - if ( cont ) - { - goto try_again; - } - - // Do not do a keep-alive connection if the size of the - // response is not known and the response is not Chunked. - if (!m_bChunked && (m_iSize == NO_SIZE)) - m_bKeepAlive = false; - - if ( m_responseCode == 204 ) - { - return true; - } - - // We need to try to login again if we failed earlier - if ( m_bUnauthorized ) - { - if ( (m_responseCode == 401) || - (m_bUseProxy && (m_responseCode == 407)) - ) - { - if ( getAuthorization() ) - { - // for NTLM Authentication we have to keep the connection open! - if ( Authentication == AUTH_NTLM && m_strAuthorization.length() > 4 ) - { - m_bKeepAlive = true; - readBody( true ); - } - else if (ProxyAuthentication == AUTH_NTLM && m_strProxyAuthorization.length() > 4) - { - readBody( true ); - } - else - httpCloseConnection(); - return false; // Try again. - } - - if (m_bError) - return false; // Error out - - // Show error page... - } - m_bUnauthorized = false; - } - - // We need to do a redirect - if (!locationStr.isEmpty()) - { - KURL u(m_request.url, locationStr); - if(!u.isValid()) - { - error(ERR_MALFORMED_URL, u.prettyURL()); - return false; - } - if ((u.protocol() != "http") && (u.protocol() != "https") && - (u.protocol() != "ftp") && (u.protocol() != "webdav") && - (u.protocol() != "webdavs")) - { - redirection(u); - error(ERR_ACCESS_DENIED, u.prettyURL()); - return false; - } - - // preserve #ref: (bug 124654) - // if we were at http://host/resource1#ref, we sent a GET for "/resource1" - // if we got redirected to http://host/resource2, then we have to re-add - // the fragment: - if (m_request.url.hasRef() && !u.hasRef() && - (m_request.url.host() == u.host()) && - (m_request.url.protocol() == u.protocol())) - u.setRef(m_request.url.ref()); - - m_bRedirect = true; - m_redirectLocation = u; - - if (!m_request.id.isEmpty()) - { - sendMetaData(); - } - - kdDebug(7113) << "(" << m_pid << ") request.url: " << m_request.url.prettyURL() - << endl << "LocationStr: " << locationStr.data() << endl; - - kdDebug(7113) << "(" << m_pid << ") Requesting redirection to: " << u.prettyURL() - << endl; - - // If we're redirected to a http:// url, remember that we're doing webdav... - if (m_protocol == "webdav" || m_protocol == "webdavs") - u.setProtocol(m_protocol); - - redirection(u); - m_request.bCachedWrite = false; // Turn off caching on re-direction (DA) - mayCache = false; - } - - // Inform the job that we can indeed resume... - if ( bCanResume && m_request.offset ) - canResume(); - else - m_request.offset = 0; - - // We don't cache certain text objects - if (m_strMimeType.startsWith("text/") && - (m_strMimeType != "text/css") && - (m_strMimeType != "text/x-javascript") && - !hasCacheDirective) - { - // Do not cache secure pages or pages - // originating from password protected sites - // unless the webserver explicitly allows it. - if ( m_bIsSSL || (Authentication != AUTH_None) ) - { - m_request.bCachedWrite = false; - mayCache = false; - } - } - - // WABA: Correct for tgz files with a gzip-encoding. - // They really shouldn't put gzip in the Content-Encoding field! - // Web-servers really shouldn't do this: They let Content-Size refer - // to the size of the tgz file, not to the size of the tar file, - // while the Content-Type refers to "tar" instead of "tgz". - if (m_qContentEncodings.last() == "gzip") - { - if (m_strMimeType == "application/x-tar") - { - m_qContentEncodings.remove(m_qContentEncodings.fromLast()); - m_strMimeType = TQString::fromLatin1("application/x-tgz"); - } - else if (m_strMimeType == "application/postscript") - { - // LEONB: Adding another exception for psgz files. - // Could we use the mimelnk files instead of hardcoding all this? - m_qContentEncodings.remove(m_qContentEncodings.fromLast()); - m_strMimeType = TQString::fromLatin1("application/x-gzpostscript"); - } - else if ( m_request.allowCompressedPage && - m_strMimeType != "application/x-tgz" && - m_strMimeType != "application/x-targz" && - m_strMimeType != "application/x-gzip" && - m_request.url.path().right(6) == ".ps.gz" ) - { - m_qContentEncodings.remove(m_qContentEncodings.fromLast()); - m_strMimeType = TQString::fromLatin1("application/x-gzpostscript"); - } - else if ( (m_request.allowCompressedPage && - m_strMimeType == "text/html") - || - (m_request.allowCompressedPage && - m_strMimeType != "application/x-tgz" && - m_strMimeType != "application/x-targz" && - m_strMimeType != "application/x-gzip" && - m_request.url.path().right(3) != ".gz") - ) - { - // Unzip! - } - else - { - m_qContentEncodings.remove(m_qContentEncodings.fromLast()); - m_strMimeType = TQString::fromLatin1("application/x-gzip"); - } - } - - // We can't handle "bzip2" encoding (yet). So if we get something with - // bzip2 encoding, we change the mimetype to "application/x-bzip2". - // Note for future changes: some web-servers send both "bzip2" as - // encoding and "application/x-bzip2" as mimetype. That is wrong. - // currently that doesn't bother us, because we remove the encoding - // and set the mimetype to x-bzip2 anyway. - if (m_qContentEncodings.last() == "bzip2") - { - m_qContentEncodings.remove(m_qContentEncodings.fromLast()); - m_strMimeType = TQString::fromLatin1("application/x-bzip2"); - } - - // Convert some common mimetypes to standard KDE mimetypes - if (m_strMimeType == "application/x-targz") - m_strMimeType = TQString::fromLatin1("application/x-tgz"); - else if (m_strMimeType == "application/zip") - m_strMimeType = TQString::fromLatin1("application/x-zip"); - else if (m_strMimeType == "image/x-png") - m_strMimeType = TQString::fromLatin1("image/png"); - else if (m_strMimeType == "image/bmp") - m_strMimeType = TQString::fromLatin1("image/x-bmp"); - else if (m_strMimeType == "audio/mpeg" || m_strMimeType == "audio/x-mpeg" || m_strMimeType == "audio/mp3") - m_strMimeType = TQString::fromLatin1("audio/x-mp3"); - else if (m_strMimeType == "audio/microsoft-wave") - m_strMimeType = TQString::fromLatin1("audio/x-wav"); - else if (m_strMimeType == "audio/midi") - m_strMimeType = TQString::fromLatin1("audio/x-midi"); - else if (m_strMimeType == "image/x-xpixmap") - m_strMimeType = TQString::fromLatin1("image/x-xpm"); - else if (m_strMimeType == "application/rtf") - m_strMimeType = TQString::fromLatin1("text/rtf"); - - // Crypto ones.... - else if (m_strMimeType == "application/pkix-cert" || - m_strMimeType == "application/binary-certificate") - { - m_strMimeType = TQString::fromLatin1("application/x-x509-ca-cert"); - } - - // Prefer application/x-tgz or x-gzpostscript over application/x-gzip. - else if (m_strMimeType == "application/x-gzip") - { - if ((m_request.url.path().right(7) == ".tar.gz") || - (m_request.url.path().right(4) == ".tar")) - m_strMimeType = TQString::fromLatin1("application/x-tgz"); - if ((m_request.url.path().right(6) == ".ps.gz")) - m_strMimeType = TQString::fromLatin1("application/x-gzpostscript"); - } - - // Some webservers say "text/plain" when they mean "application/x-bzip2" - else if ((m_strMimeType == "text/plain") || (m_strMimeType == "application/octet-stream")) - { - TQString ext = m_request.url.path().right(4).upper(); - if (ext == ".BZ2") - m_strMimeType = TQString::fromLatin1("application/x-bzip2"); - else if (ext == ".PEM") - m_strMimeType = TQString::fromLatin1("application/x-x509-ca-cert"); - else if (ext == ".SWF") - m_strMimeType = TQString::fromLatin1("application/x-shockwave-flash"); - else if (ext == ".PLS") - m_strMimeType = TQString::fromLatin1("audio/x-scpls"); - else if (ext == ".WMV") - m_strMimeType = TQString::fromLatin1("video/x-ms-wmv"); - } - -#if 0 - // Even if we can't rely on content-length, it seems that we should - // never get more data than content-length. Maybe less, if the - // content-length refers to the unzipped data. - if (!m_qContentEncodings.isEmpty()) - { - // If we still have content encoding we can't rely on the Content-Length. - m_iSize = NO_SIZE; - } -#endif - - if( !dispositionType.isEmpty() ) - { - kdDebug(7113) << "(" << m_pid << ") Setting Content-Disposition type to: " - << dispositionType << endl; - setMetaData("content-disposition-type", dispositionType); - } - if( !dispositionFilename.isEmpty() ) - { - kdDebug(7113) << "(" << m_pid << ") Setting Content-Disposition filename to: " - << dispositionFilename << endl; - // ### KDE4: setting content-disposition to filename for pre 3.5.2 compatability - setMetaData("content-disposition", dispositionFilename); - setMetaData("content-disposition-filename", dispositionFilename); - } - - if (!m_request.lastModified.isEmpty()) - setMetaData("modified", m_request.lastModified); - - if (!mayCache) - { - setMetaData("no-cache", "true"); - setMetaData("expire-date", "1"); // Expired - } - else - { - TQString tmp; - tmp.setNum(expireDate); - setMetaData("expire-date", tmp); - tmp.setNum(time(0)); // Cache entry will be created shortly. - setMetaData("cache-creation-date", tmp); - } - - // Let the app know about the mime-type iff this is not - // a redirection and the mime-type string is not empty. - if (locationStr.isEmpty() && (!m_strMimeType.isEmpty() || - m_request.method == HTTP_HEAD)) - { - kdDebug(7113) << "(" << m_pid << ") Emitting mimetype " << m_strMimeType << endl; - mimeType( m_strMimeType ); - } - - // Do not move send response header before any redirection as it seems - // to screw up some sites. See BR# 150904. - forwardHttpResponseHeader(); - - if (m_request.method == HTTP_HEAD) - return true; - - // Do we want to cache this request? - if (m_request.bUseCache) - { - ::unlink( TQFile::encodeName(m_request.cef)); - if ( m_request.bCachedWrite && !m_strMimeType.isEmpty() ) - { - // Check... - createCacheEntry(m_strMimeType, expireDate); // Create a cache entry - if (!m_request.fcache) - { - m_request.bCachedWrite = false; // Error creating cache entry. - kdDebug(7113) << "(" << m_pid << ") Error creating cache entry for " << m_request.url.prettyURL()<<"!\n"; - } - m_request.expireDate = expireDate; - m_maxCacheSize = config()->readNumEntry("MaxCacheSize", DEFAULT_MAX_CACHE_SIZE) / 2; - } - } - - if (m_request.bCachedWrite && !m_strMimeType.isEmpty()) - kdDebug(7113) << "(" << m_pid << ") Cache, adding \"" << m_request.url.prettyURL() << "\"" << endl; - else if (m_request.bCachedWrite && m_strMimeType.isEmpty()) - kdDebug(7113) << "(" << m_pid << ") Cache, pending \"" << m_request.url.prettyURL() << "\"" << endl; - else - kdDebug(7113) << "(" << m_pid << ") Cache, not adding \"" << m_request.url.prettyURL() << "\"" << endl; - return true; -} - - -void HTTPProtocol::addEncoding(TQString encoding, TQStringList &encs) -{ - encoding = encoding.stripWhiteSpace().lower(); - // Identity is the same as no encoding - if (encoding == "identity") { - return; - } else if (encoding == "8bit") { - // Strange encoding returned by http://linac.ikp.physik.tu-darmstadt.de - return; - } else if (encoding == "chunked") { - m_bChunked = true; - // Anyone know of a better way to handle unknown sizes possibly/ideally with unsigned ints? - //if ( m_cmd != CMD_COPY ) - m_iSize = NO_SIZE; - } else if ((encoding == "x-gzip") || (encoding == "gzip")) { - encs.append(TQString::fromLatin1("gzip")); - } else if ((encoding == "x-bzip2") || (encoding == "bzip2")) { - encs.append(TQString::fromLatin1("bzip2")); // Not yet supported! - } else if ((encoding == "x-deflate") || (encoding == "deflate")) { - encs.append(TQString::fromLatin1("deflate")); - } else { - kdDebug(7113) << "(" << m_pid << ") Unknown encoding encountered. " - << "Please write code. Encoding = \"" << encoding - << "\"" << endl; - } -} - -bool HTTPProtocol::sendBody() -{ - int result=-1; - int length=0; - - infoMessage( i18n( "Requesting data to send" ) ); - - // m_bufPOST will NOT be empty iff authentication was required before posting - // the data OR a re-connect is requested from ::readHeader because the - // connection was lost for some reason. - if ( !m_bufPOST.isNull() ) - { - kdDebug(7113) << "(" << m_pid << ") POST'ing saved data..." << endl; - - result = 0; - length = m_bufPOST.size(); - } - else - { - kdDebug(7113) << "(" << m_pid << ") POST'ing live data..." << endl; - - TQByteArray buffer; - int old_size; - - m_bufPOST.resize(0); - do - { - dataReq(); // Request for data - result = readData( buffer ); - if ( result > 0 ) - { - length += result; - old_size = m_bufPOST.size(); - m_bufPOST.resize( old_size+result ); - memcpy( m_bufPOST.data()+ old_size, buffer.data(), buffer.size() ); - buffer.resize(0); - } - } while ( result > 0 ); - } - - if ( result < 0 ) - { - error( ERR_ABORTED, m_request.hostname ); - return false; - } - - infoMessage( i18n( "Sending data to %1" ).arg( m_request.hostname ) ); - - TQString size = TQString ("Content-Length: %1\r\n\r\n").arg(length); - kdDebug( 7113 ) << "(" << m_pid << ")" << size << endl; - - // Send the content length... - bool sendOk = (write(size.latin1(), size.length()) == (ssize_t) size.length()); - if (!sendOk) - { - kdDebug( 7113 ) << "(" << m_pid << ") Connection broken when sending " - << "content length: (" << m_state.hostname << ")" << endl; - error( ERR_CONNECTION_BROKEN, m_state.hostname ); - return false; - } - - // Send the data... - // kdDebug( 7113 ) << "(" << m_pid << ") POST DATA: " << TQCString(m_bufPOST) << endl; - sendOk = (write(m_bufPOST.data(), m_bufPOST.size()) == (ssize_t) m_bufPOST.size()); - if (!sendOk) - { - kdDebug(7113) << "(" << m_pid << ") Connection broken when sending message body: (" - << m_state.hostname << ")" << endl; - error( ERR_CONNECTION_BROKEN, m_state.hostname ); - return false; - } - - return true; -} - -void HTTPProtocol::httpClose( bool keepAlive ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpClose" << endl; - - if (m_request.fcache) - { - fclose(m_request.fcache); - m_request.fcache = 0; - if (m_request.bCachedWrite) - { - TQString filename = m_request.cef + ".new"; - ::unlink( TQFile::encodeName(filename) ); - } - } - - // Only allow persistent connections for GET requests. - // NOTE: we might even want to narrow this down to non-form - // based submit requests which will require a meta-data from - // tdehtml. - if (keepAlive && (!m_bUseProxy || - m_bPersistentProxyConnection || m_bIsTunneled)) - { - if (!m_keepAliveTimeout) - m_keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT; - else if (m_keepAliveTimeout > 2*DEFAULT_KEEP_ALIVE_TIMEOUT) - m_keepAliveTimeout = 2*DEFAULT_KEEP_ALIVE_TIMEOUT; - - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpClose: keep alive (" << m_keepAliveTimeout << ")" << endl; - TQByteArray data; - TQDataStream stream( data, IO_WriteOnly ); - stream << int(99); // special: Close connection - setTimeoutSpecialCommand(m_keepAliveTimeout, data); - return; - } - - httpCloseConnection(); -} - -void HTTPProtocol::closeConnection() -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::closeConnection" << endl; - httpCloseConnection (); -} - -void HTTPProtocol::httpCloseConnection () -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpCloseConnection" << endl; - m_bIsTunneled = false; - m_bKeepAlive = false; - closeDescriptor(); - setTimeoutSpecialCommand(-1); // Cancel any connection timeout -} - -void HTTPProtocol::slave_status() -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::slave_status" << endl; - - if ( m_iSock != -1 && !isConnectionValid() ) - httpCloseConnection(); - - slaveStatus( m_state.hostname, (m_iSock != -1) ); -} - -void HTTPProtocol::mimetype( const KURL& url ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::mimetype: " - << url.prettyURL() << endl; - - if ( !checkRequestURL( url ) ) - return; - - m_request.method = HTTP_HEAD; - m_request.path = url.path(); - m_request.query = url.query(); - m_request.cache = CC_Cache; - m_request.doProxy = m_bUseProxy; - - retrieveHeader(); - - kdDebug(7113) << "(" << m_pid << ") http: mimetype = " << m_strMimeType - << endl; -} - -void HTTPProtocol::special( const TQByteArray &data ) -{ - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::special" << endl; - - int tmp; - TQDataStream stream(data, IO_ReadOnly); - - stream >> tmp; - switch (tmp) { - case 1: // HTTP POST - { - KURL url; - stream >> url; - post( url ); - break; - } - case 2: // cache_update - { - KURL url; - bool no_cache; - time_t expireDate; - stream >> url >> no_cache >> expireDate; - cacheUpdate( url, no_cache, expireDate ); - break; - } - case 5: // WebDAV lock - { - KURL url; - TQString scope, type, owner; - stream >> url >> scope >> type >> owner; - davLock( url, scope, type, owner ); - break; - } - case 6: // WebDAV unlock - { - KURL url; - stream >> url; - davUnlock( url ); - break; - } - case 7: // Generic WebDAV - { - KURL url; - int method; - stream >> url >> method; - davGeneric( url, (TDEIO::HTTP_METHOD) method ); - break; - } - case 99: // Close Connection - { - httpCloseConnection(); - break; - } - default: - // Some command we don't understand. - // Just ignore it, it may come from some future version of KDE. - break; - } -} - -/** - * Read a chunk from the data stream. - */ -int HTTPProtocol::readChunked() -{ - if ((m_iBytesLeft == 0) || (m_iBytesLeft == NO_SIZE)) - { - setRewindMarker(); - - m_bufReceive.resize(4096); - - if (!gets(m_bufReceive.data(), m_bufReceive.size()-1)) - { - kdDebug(7113) << "(" << m_pid << ") gets() failure on Chunk header" << endl; - return -1; - } - // We could have got the CRLF of the previous chunk. - // If so, try again. - if (m_bufReceive[0] == '\0') - { - if (!gets(m_bufReceive.data(), m_bufReceive.size()-1)) - { - kdDebug(7113) << "(" << m_pid << ") gets() failure on Chunk header" << endl; - return -1; - } - } - - // m_bEOF is set to true when read called from gets returns 0. For chunked reading 0 - // means end of chunked transfer and not error. See RFC 2615 section 3.6.1 - #if 0 - if (m_bEOF) - { - kdDebug(7113) << "(" << m_pid << ") EOF on Chunk header" << endl; - return -1; - } - #endif - - long long trunkSize = STRTOLL(m_bufReceive.data(), 0, 16); - if (trunkSize < 0) - { - kdDebug(7113) << "(" << m_pid << ") Negative chunk size" << endl; - return -1; - } - m_iBytesLeft = trunkSize; - - // kdDebug(7113) << "(" << m_pid << ") Chunk size = " << m_iBytesLeft << " bytes" << endl; - - if (m_iBytesLeft == 0) - { - // Last chunk. - // Skip trailers. - do { - // Skip trailer of last chunk. - if (!gets(m_bufReceive.data(), m_bufReceive.size()-1)) - { - kdDebug(7113) << "(" << m_pid << ") gets() failure on Chunk trailer" << endl; - return -1; - } - // kdDebug(7113) << "(" << m_pid << ") Chunk trailer = \"" << m_bufReceive.data() << "\"" << endl; - } - while (strlen(m_bufReceive.data()) != 0); - - return 0; - } - } - - int bytesReceived = readLimited(); - if (!m_iBytesLeft) - m_iBytesLeft = NO_SIZE; // Don't stop, continue with next chunk - - // kdDebug(7113) << "(" << m_pid << ") readChunked: BytesReceived=" << bytesReceived << endl; - return bytesReceived; -} - -int HTTPProtocol::readLimited() -{ - if (!m_iBytesLeft) - return 0; - - m_bufReceive.resize(4096); - - int bytesReceived; - int bytesToReceive; - - if (m_iBytesLeft > m_bufReceive.size()) - bytesToReceive = m_bufReceive.size(); - else - bytesToReceive = m_iBytesLeft; - - bytesReceived = read(m_bufReceive.data(), bytesToReceive); - - if (bytesReceived <= 0) - return -1; // Error: connection lost - - m_iBytesLeft -= bytesReceived; - return bytesReceived; -} - -int HTTPProtocol::readUnlimited() -{ - if (m_bKeepAlive) - { - kdDebug(7113) << "(" << m_pid << ") Unbounded datastream on a Keep " - << "alive connection!" << endl; - m_bKeepAlive = false; - } - - m_bufReceive.resize(4096); - - int result = read(m_bufReceive.data(), m_bufReceive.size()); - if (result > 0) - return result; - - m_bEOF = true; - m_iBytesLeft = 0; - return 0; -} - -void HTTPProtocol::slotData(const TQByteArray &_d) -{ - if (!_d.size()) - { - m_bEOD = true; - return; - } - - if (m_iContentLeft != NO_SIZE) - { - if (m_iContentLeft >= _d.size()) - m_iContentLeft -= _d.size(); - else - m_iContentLeft = NO_SIZE; - } - - TQByteArray d = _d; - if ( !m_dataInternal ) - { - // If a broken server does not send the mime-type, - // we try to id it from the content before dealing - // with the content itself. - if ( m_strMimeType.isEmpty() && !m_bRedirect && - !( m_responseCode >= 300 && m_responseCode <=399) ) - { - kdDebug(7113) << "(" << m_pid << ") Determining mime-type from content..." << endl; - int old_size = m_mimeTypeBuffer.size(); - m_mimeTypeBuffer.resize( old_size + d.size() ); - memcpy( m_mimeTypeBuffer.data() + old_size, d.data(), d.size() ); - if ( (m_iBytesLeft != NO_SIZE) && (m_iBytesLeft > 0) - && (m_mimeTypeBuffer.size() < 1024) ) - { - m_cpMimeBuffer = true; - return; // Do not send up the data since we do not yet know its mimetype! - } - - kdDebug(7113) << "(" << m_pid << ") Mimetype buffer size: " << m_mimeTypeBuffer.size() - << endl; - - KMimeMagicResult *result; - result = KMimeMagic::self()->findBufferFileType( m_mimeTypeBuffer, - m_request.url.fileName() ); - if( result ) - { - m_strMimeType = result->mimeType(); - kdDebug(7113) << "(" << m_pid << ") Mimetype from content: " - << m_strMimeType << endl; - } - - if ( m_strMimeType.isEmpty() ) - { - m_strMimeType = TQString::fromLatin1( DEFAULT_MIME_TYPE ); - kdDebug(7113) << "(" << m_pid << ") Using default mimetype: " - << m_strMimeType << endl; - } - - if ( m_request.bCachedWrite ) - { - createCacheEntry( m_strMimeType, m_request.expireDate ); - if (!m_request.fcache) - m_request.bCachedWrite = false; - } - - if ( m_cpMimeBuffer ) - { - // Do not make any assumption about the state of the TQByteArray we received. - // Fix the crash described by BR# 130104. - d.detach(); - d.resize(0); - d.resize(m_mimeTypeBuffer.size()); - memcpy( d.data(), m_mimeTypeBuffer.data(), - d.size() ); - } - mimeType(m_strMimeType); - m_mimeTypeBuffer.resize(0); - } - - data( d ); - if (m_request.bCachedWrite && m_request.fcache) - writeCacheEntry(d.data(), d.size()); - } - else - { - uint old_size = m_bufWebDavData.size(); - m_bufWebDavData.resize (old_size + d.size()); - memcpy (m_bufWebDavData.data() + old_size, d.data(), d.size()); - } -} - -/** - * This function is our "receive" function. It is responsible for - * downloading the message (not the header) from the HTTP server. It - * is called either as a response to a client's TDEIOJob::dataEnd() - * (meaning that the client is done sending data) or by 'httpOpen()' - * (if we are in the process of a PUT/POST request). It can also be - * called by a webDAV function, to receive stat/list/property/etc. - * data; in this case the data is stored in m_bufWebDavData. - */ -bool HTTPProtocol::readBody( bool dataInternal /* = false */ ) -{ - if (m_responseCode == 204) - return true; - - m_bEOD = false; - // Note that when dataInternal is true, we are going to: - // 1) save the body data to a member variable, m_bufWebDavData - // 2) _not_ advertise the data, speed, size, etc., through the - // corresponding functions. - // This is used for returning data to WebDAV. - m_dataInternal = dataInternal; - if ( dataInternal ) - m_bufWebDavData.resize (0); - - // Check if we need to decode the data. - // If we are in copy mode, then use only transfer decoding. - bool useMD5 = !m_sContentMD5.isEmpty(); - - // Deal with the size of the file. - TDEIO::filesize_t sz = m_request.offset; - if ( sz ) - m_iSize += sz; - - // Update the application with total size except when - // it is compressed, or when the data is to be handled - // internally (webDAV). If compressed we have to wait - // until we uncompress to find out the actual data size - if ( !dataInternal ) { - if ( (m_iSize > 0) && (m_iSize != NO_SIZE)) { - totalSize(m_iSize); - infoMessage( i18n( "Retrieving %1 from %2...").arg(TDEIO::convertSize(m_iSize)) - .arg( m_request.hostname ) ); - } - else - { - totalSize ( 0 ); - } - } - else - infoMessage( i18n( "Retrieving from %1..." ).arg( m_request.hostname ) ); - - if (m_request.bCachedRead) - { - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readBody: read data from cache!" << endl; - m_request.bCachedWrite = false; - - char buffer[ MAX_IPC_SIZE ]; - - m_iContentLeft = NO_SIZE; - - // Jippie! It's already in the cache :-) - while (!feof(m_request.fcache) && !ferror(m_request.fcache)) - { - int nbytes = fread( buffer, 1, MAX_IPC_SIZE, m_request.fcache); - - if (nbytes > 0) - { - m_bufReceive.setRawData( buffer, nbytes); - slotData( m_bufReceive ); - m_bufReceive.resetRawData( buffer, nbytes ); - sz += nbytes; - } - } - - m_bufReceive.resize( 0 ); - - if ( !dataInternal ) - { - processedSize( sz ); - data( TQByteArray() ); - } - - return true; - } - - - if (m_iSize != NO_SIZE) - m_iBytesLeft = m_iSize - sz; - else - m_iBytesLeft = NO_SIZE; - - m_iContentLeft = m_iBytesLeft; - - if (m_bChunked) - m_iBytesLeft = NO_SIZE; - - kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readBody: retrieve data. " - << TDEIO::number(m_iBytesLeft) << " left." << endl; - - // Main incoming loop... Gather everything while we can... - m_cpMimeBuffer = false; - m_mimeTypeBuffer.resize(0); - struct timeval last_tv; - gettimeofday( &last_tv, 0L ); - - HTTPFilterChain chain; - - TQObject::connect(&chain, TQT_SIGNAL(output(const TQByteArray &)), - this, TQT_SLOT(slotData(const TQByteArray &))); - TQObject::connect(&chain, TQT_SIGNAL(error(int, const TQString &)), - this, TQT_SLOT(error(int, const TQString &))); - - // decode all of the transfer encodings - while (!m_qTransferEncodings.isEmpty()) - { - TQString enc = m_qTransferEncodings.last(); - m_qTransferEncodings.remove(m_qTransferEncodings.fromLast()); - if ( enc == "gzip" ) - chain.addFilter(new HTTPFilterGZip); - else if ( enc == "deflate" ) - chain.addFilter(new HTTPFilterDeflate); - } - - // From HTTP 1.1 Draft 6: - // The MD5 digest is computed based on the content of the entity-body, - // including any content-coding that has been applied, but not including - // any transfer-encoding applied to the message-body. If the message is - // received with a transfer-encoding, that encoding MUST be removed - // prior to checking the Content-MD5 value against the received entity. - HTTPFilterMD5 *md5Filter = 0; - if ( useMD5 ) - { - md5Filter = new HTTPFilterMD5; - chain.addFilter(md5Filter); - } - - // now decode all of the content encodings - // -- Why ?? We are not - // -- a proxy server, be a client side implementation!! The applications - // -- are capable of determinig how to extract the encoded implementation. - // WB: That's a misunderstanding. We are free to remove the encoding. - // WB: Some braindead www-servers however, give .tgz files an encoding - // WB: of "gzip" (or even "x-gzip") and a content-type of "applications/tar" - // WB: They shouldn't do that. We can work around that though... - while (!m_qContentEncodings.isEmpty()) - { - TQString enc = m_qContentEncodings.last(); - m_qContentEncodings.remove(m_qContentEncodings.fromLast()); - if ( enc == "gzip" ) - chain.addFilter(new HTTPFilterGZip); - else if ( enc == "deflate" ) - chain.addFilter(new HTTPFilterDeflate); - } - - while (!m_bEOF) - { - int bytesReceived; - - if (m_bChunked) - bytesReceived = readChunked(); - else if (m_iSize != NO_SIZE) - bytesReceived = readLimited(); - else - bytesReceived = readUnlimited(); - - // make sure that this wasn't an error, first - // kdDebug(7113) << "(" << (int) m_pid << ") readBody: bytesReceived: " - // << (int) bytesReceived << " m_iSize: " << (int) m_iSize << " Chunked: " - // << (int) m_bChunked << " BytesLeft: "<< (int) m_iBytesLeft << endl; - if (bytesReceived == -1) - { - if (m_iContentLeft == 0) - { - // gzip'ed data sometimes reports a too long content-length. - // (The length of the unzipped data) - m_iBytesLeft = 0; - break; - } - // Oh well... log an error and bug out - kdDebug(7113) << "(" << m_pid << ") readBody: bytesReceived==-1 sz=" << (int)sz - << " Connnection broken !" << endl; - error(ERR_CONNECTION_BROKEN, m_state.hostname); - return false; - } - - // I guess that nbytes == 0 isn't an error.. but we certainly - // won't work with it! - if (bytesReceived > 0) - { - // Important: truncate the buffer to the actual size received! - // Otherwise garbage will be passed to the app - m_bufReceive.truncate( bytesReceived ); - - chain.slotInput(m_bufReceive); - - if (m_bError) - return false; - - sz += bytesReceived; - if (!dataInternal) - processedSize( sz ); - } - m_bufReceive.resize(0); // res - - if (m_iBytesLeft && m_bEOD && !m_bChunked) - { - // gzip'ed data sometimes reports a too long content-length. - // (The length of the unzipped data) - m_iBytesLeft = 0; - } - - if (m_iBytesLeft == 0) - { - kdDebug(7113) << "("<<m_pid<<") EOD received! Left = "<< TDEIO::number(m_iBytesLeft) << endl; - break; - } - } - chain.slotInput(TQByteArray()); // Flush chain. - - if ( useMD5 ) - { - TQString calculatedMD5 = md5Filter->md5(); - - if ( m_sContentMD5 == calculatedMD5 ) - kdDebug(7113) << "(" << m_pid << ") MD5 checksum MATCHED!!" << endl; - else - kdDebug(7113) << "(" << m_pid << ") MD5 checksum MISMATCH! Expected: " - << calculatedMD5 << ", Got: " << m_sContentMD5 << endl; - } - - // Close cache entry - if (m_iBytesLeft == 0) - { - if (m_request.bCachedWrite && m_request.fcache) - closeCacheEntry(); - else if (m_request.bCachedWrite) - kdDebug(7113) << "(" << m_pid << ") no cache file!\n"; - } - else - { - kdDebug(7113) << "(" << m_pid << ") still "<< TDEIO::number(m_iBytesLeft) - << " bytes left! can't close cache entry!\n"; - } - - if (sz <= 1) - { - /* kdDebug(7113) << "(" << m_pid << ") readBody: sz = " << TDEIO::number(sz) - << ", responseCode =" << m_responseCode << endl; */ - if (m_responseCode >= 500 && m_responseCode <= 599) - error(ERR_INTERNAL_SERVER, m_state.hostname); - else if (m_responseCode >= 400 && m_responseCode <= 499) - error(ERR_DOES_NOT_EXIST, m_state.hostname); - } - - if (!dataInternal) - data( TQByteArray() ); - - return true; -} - - -void HTTPProtocol::error( int _err, const TQString &_text ) -{ - httpClose(false); - - if (!m_request.id.isEmpty()) - { - forwardHttpResponseHeader(); - sendMetaData(); - } - - // Clear of the temporary POST buffer if it is not empty... - if (!m_bufPOST.isEmpty()) - { - m_bufPOST.resize(0); - kdDebug(7113) << "(" << m_pid << ") HTTP::retreiveHeader: Cleared POST " - "buffer..." << endl; - } - - SlaveBase::error( _err, _text ); - m_bError = true; -} - - -void HTTPProtocol::addCookies( const TQString &url, const TQCString &cookieHeader ) -{ - long windowId = m_request.window.toLong(); - TQByteArray params; - TQDataStream stream(params, IO_WriteOnly); - stream << url << cookieHeader << windowId; - - kdDebug(7113) << "(" << m_pid << ") " << cookieHeader << endl; - kdDebug(7113) << "(" << m_pid << ") " << "Window ID: " - << windowId << ", for host = " << url << endl; - - if ( !dcopClient()->send( "kded", "kcookiejar", "addCookies(TQString,TQCString,long int)", params ) ) - { - kdWarning(7113) << "(" << m_pid << ") Can't communicate with kded_kcookiejar!" << endl; - } -} - -TQString HTTPProtocol::findCookies( const TQString &url) -{ - TQCString replyType; - TQByteArray params; - TQByteArray reply; - TQString result; - - long windowId = m_request.window.toLong(); - result = TQString::null; - TQDataStream stream(params, IO_WriteOnly); - stream << url << windowId; - - if ( !dcopClient()->call( "kded", "kcookiejar", "findCookies(TQString,long int)", - params, replyType, reply ) ) - { - kdWarning(7113) << "(" << m_pid << ") Can't communicate with kded_kcookiejar!" << endl; - return result; - } - if ( replyType == "TQString" ) - { - TQDataStream stream2( reply, IO_ReadOnly ); - stream2 >> result; - } - else - { - kdError(7113) << "(" << m_pid << ") DCOP function findCookies(...) returns " - << replyType << ", expected TQString" << endl; - } - return result; -} - -/******************************* CACHING CODE ****************************/ - - -void HTTPProtocol::cacheUpdate( const KURL& url, bool no_cache, time_t expireDate) -{ - if ( !checkRequestURL( url ) ) - return; - - m_request.path = url.path(); - m_request.query = url.query(); - m_request.cache = CC_Reload; - m_request.doProxy = m_bUseProxy; - - if (no_cache) - { - m_request.fcache = checkCacheEntry( ); - if (m_request.fcache) - { - fclose(m_request.fcache); - m_request.fcache = 0; - ::unlink( TQFile::encodeName(m_request.cef) ); - } - } - else - { - updateExpireDate( expireDate ); - } - finished(); -} - -// !START SYNC! -// The following code should be kept in sync -// with the code in http_cache_cleaner.cpp - -FILE* HTTPProtocol::checkCacheEntry( bool readWrite) -{ - const TQChar separator = '_'; - - TQString CEF = m_request.path; - - int p = CEF.find('/'); - - while(p != -1) - { - CEF[p] = separator; - p = CEF.find('/', p); - } - - TQString host = m_request.hostname.lower(); - CEF = host + CEF + '_'; - - TQString dir = m_strCacheDir; - if (dir[dir.length()-1] != '/') - dir += "/"; - - int l = host.length(); - for(int i = 0; i < l; i++) - { - if (host[i].isLetter() && (host[i] != 'w')) - { - dir += host[i]; - break; - } - } - if (dir[dir.length()-1] == '/') - dir += "0"; - - unsigned long hash = 0x00000000; - TQCString u = m_request.url.url().latin1(); - for(int i = u.length(); i--;) - { - hash = (hash * 12211 + static_cast<const char>(u.at(i))) % 2147483563; - } - - TQString hashString; - hashString.sprintf("%08lx", hash); - - CEF = CEF + hashString; - - CEF = dir + "/" + CEF; - - m_request.cef = CEF; - - const char *mode = (readWrite ? "r+" : "r"); - - FILE *fs = fopen( TQFile::encodeName(CEF), mode); // Open for reading and writing - if (!fs) - return 0; - - char buffer[401]; - bool ok = true; - - // CacheRevision - if (ok && (!fgets(buffer, 400, fs))) - ok = false; - if (ok && (strcmp(buffer, CACHE_REVISION) != 0)) - ok = false; - - time_t date; - time_t currentDate = time(0); - - // URL - if (ok && (!fgets(buffer, 400, fs))) - ok = false; - if (ok) - { - int l = strlen(buffer); - if (l>0) - buffer[l-1] = 0; // Strip newline - if (m_request.url.url() != buffer) - { - ok = false; // Hash collision - } - } - - // Creation Date - if (ok && (!fgets(buffer, 400, fs))) - ok = false; - if (ok) - { - date = (time_t) strtoul(buffer, 0, 10); - m_request.creationDate = date; - if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge)) - { - m_request.bMustRevalidate = true; - m_request.expireDate = currentDate; - } - } - - // Expiration Date - m_request.cacheExpireDateOffset = ftell(fs); - if (ok && (!fgets(buffer, 400, fs))) - ok = false; - if (ok) - { - if (m_request.cache == CC_Verify) - { - date = (time_t) strtoul(buffer, 0, 10); - // After the expire date we need to revalidate. - if (!date || difftime(currentDate, date) >= 0) - m_request.bMustRevalidate = true; - m_request.expireDate = date; - } - else if (m_request.cache == CC_Refresh) - { - m_request.bMustRevalidate = true; - m_request.expireDate = currentDate; - } - } - - // ETag - if (ok && (!fgets(buffer, 400, fs))) - ok = false; - if (ok) - { - m_request.etag = TQString(buffer).stripWhiteSpace(); - } - - // Last-Modified - if (ok && (!fgets(buffer, 400, fs))) - ok = false; - if (ok) - { - m_request.lastModified = TQString(buffer).stripWhiteSpace(); - } - - if (ok) - return fs; - - fclose(fs); - unlink( TQFile::encodeName(CEF)); - return 0; -} - -void HTTPProtocol::updateExpireDate(time_t expireDate, bool updateCreationDate) -{ - bool ok = true; - - FILE *fs = checkCacheEntry(true); - if (fs) - { - TQString date; - char buffer[401]; - time_t creationDate; - - fseek(fs, 0, SEEK_SET); - if (ok && !fgets(buffer, 400, fs)) - ok = false; - if (ok && !fgets(buffer, 400, fs)) - ok = false; - long cacheCreationDateOffset = ftell(fs); - if (ok && !fgets(buffer, 400, fs)) - ok = false; - creationDate = strtoul(buffer, 0, 10); - if (!creationDate) - ok = false; - - if (updateCreationDate) - { - if (!ok || fseek(fs, cacheCreationDateOffset, SEEK_SET)) - return; - TQString date; - date.setNum( time(0) ); - date = date.leftJustify(16); - fputs(date.latin1(), fs); // Creation date - fputc('\n', fs); - } - - if (expireDate>(30*365*24*60*60)) - { - // expire date is a really a big number, it can't be - // a relative date. - date.setNum( expireDate ); - } - else - { - // expireDate before 2000. those values must be - // interpreted as relative expiration dates from - // <META http-equiv="Expires"> tags. - // so we have to scan the creation time and add - // it to the expiryDate - date.setNum( creationDate + expireDate ); - } - date = date.leftJustify(16); - if (!ok || fseek(fs, m_request.cacheExpireDateOffset, SEEK_SET)) - return; - fputs(date.latin1(), fs); // Expire date - fseek(fs, 0, SEEK_END); - fclose(fs); - } -} - -void HTTPProtocol::createCacheEntry( const TQString &mimetype, time_t expireDate) -{ - TQString dir = m_request.cef; - int p = dir.findRev('/'); - if (p == -1) return; // Error. - dir.truncate(p); - - // Create file - (void) ::mkdir( TQFile::encodeName(dir), 0700 ); - - TQString filename = m_request.cef + ".new"; // Create a new cache entryexpireDate - -// kdDebug( 7103 ) << "creating new cache entry: " << filename << endl; - - m_request.fcache = fopen( TQFile::encodeName(filename), "w"); - if (!m_request.fcache) - { - kdWarning(7113) << "(" << m_pid << ")createCacheEntry: opening " << filename << " failed." << endl; - return; // Error. - } - - fputs(CACHE_REVISION, m_request.fcache); // Revision - - fputs(m_request.url.url().latin1(), m_request.fcache); // Url - fputc('\n', m_request.fcache); - - TQString date; - m_request.creationDate = time(0); - date.setNum( m_request.creationDate ); - date = date.leftJustify(16); - fputs(date.latin1(), m_request.fcache); // Creation date - fputc('\n', m_request.fcache); - - date.setNum( expireDate ); - date = date.leftJustify(16); - fputs(date.latin1(), m_request.fcache); // Expire date - fputc('\n', m_request.fcache); - - if (!m_request.etag.isEmpty()) - fputs(m_request.etag.latin1(), m_request.fcache); //ETag - fputc('\n', m_request.fcache); - - if (!m_request.lastModified.isEmpty()) - fputs(m_request.lastModified.latin1(), m_request.fcache); // Last modified - fputc('\n', m_request.fcache); - - fputs(mimetype.latin1(), m_request.fcache); // Mimetype - fputc('\n', m_request.fcache); - - if (!m_request.strCharset.isEmpty()) - fputs(m_request.strCharset.latin1(), m_request.fcache); // Charset - fputc('\n', m_request.fcache); - - return; -} -// The above code should be kept in sync -// with the code in http_cache_cleaner.cpp -// !END SYNC! - -void HTTPProtocol::writeCacheEntry( const char *buffer, int nbytes) -{ - if (fwrite( buffer, nbytes, 1, m_request.fcache) != 1) - { - kdWarning(7113) << "(" << m_pid << ") writeCacheEntry: writing " << nbytes << " bytes failed." << endl; - fclose(m_request.fcache); - m_request.fcache = 0; - TQString filename = m_request.cef + ".new"; - ::unlink( TQFile::encodeName(filename) ); - return; - } - long file_pos = ftell( m_request.fcache ) / 1024; - if ( file_pos > m_maxCacheSize ) - { - kdDebug(7113) << "writeCacheEntry: File size reaches " << file_pos - << "Kb, exceeds cache limits. (" << m_maxCacheSize << "Kb)" << endl; - fclose(m_request.fcache); - m_request.fcache = 0; - TQString filename = m_request.cef + ".new"; - ::unlink( TQFile::encodeName(filename) ); - return; - } -} - -void HTTPProtocol::closeCacheEntry() -{ - TQString filename = m_request.cef + ".new"; - int result = fclose( m_request.fcache); - m_request.fcache = 0; - if (result == 0) - { - if (::rename( TQFile::encodeName(filename), TQFile::encodeName(m_request.cef)) == 0) - return; // Success - - kdWarning(7113) << "(" << m_pid << ") closeCacheEntry: error renaming " - << "cache entry. (" << filename << " -> " << m_request.cef - << ")" << endl; - } - - kdWarning(7113) << "(" << m_pid << ") closeCacheEntry: error closing cache " - << "entry. (" << filename<< ")" << endl; -} - -void HTTPProtocol::cleanCache() -{ - const time_t maxAge = DEFAULT_CLEAN_CACHE_INTERVAL; // 30 Minutes. - bool doClean = false; - TQString cleanFile = m_strCacheDir; - if (cleanFile[cleanFile.length()-1] != '/') - cleanFile += "/"; - cleanFile += "cleaned"; - - struct stat stat_buf; - - int result = ::stat(TQFile::encodeName(cleanFile), &stat_buf); - if (result == -1) - { - int fd = creat( TQFile::encodeName(cleanFile), 0600); - if (fd != -1) - { - doClean = true; - ::close(fd); - } - } - else - { - time_t age = (time_t) difftime( time(0), stat_buf.st_mtime ); - if (age > maxAge) // - doClean = true; - } - if (doClean) - { - // Touch file. - utime(TQFile::encodeName(cleanFile), 0); - TDEApplication::startServiceByDesktopPath("http_cache_cleaner.desktop"); - } -} - - - -//************************** AUTHENTICATION CODE ********************/ - - -void HTTPProtocol::configAuth( char *p, bool isForProxy ) -{ - HTTP_AUTH f = AUTH_None; - const char *strAuth = p; - - if ( strncasecmp( p, "Basic", 5 ) == 0 ) - { - f = AUTH_Basic; - p += 5; - strAuth = "Basic"; // Correct for upper-case variations. - } - else if ( strncasecmp (p, "Digest", 6) == 0 ) - { - f = AUTH_Digest; - memcpy((void *)p, "Digest", 6); // Correct for upper-case variations. - p += 6; - } - else if (strncasecmp( p, "MBS_PWD_COOKIE", 14 ) == 0) - { - // Found on http://www.webscription.net/baen/default.asp - f = AUTH_Basic; - p += 14; - strAuth = "Basic"; - } -#ifdef HAVE_LIBGSSAPI - else if ( strncasecmp( p, "Negotiate", 9 ) == 0 ) - { - // if we get two 401 in a row let's assume for now that - // Negotiate isn't working and ignore it - if ( !isForProxy && !(m_responseCode == 401 && m_prevResponseCode == 401) ) - { - f = AUTH_Negotiate; - memcpy((void *)p, "Negotiate", 9); // Correct for upper-case variations. - p += 9; - }; - } -#endif - else if ( strncasecmp( p, "NTLM", 4 ) == 0 ) - { - f = AUTH_NTLM; - memcpy((void *)p, "NTLM", 4); // Correct for upper-case variations. - p += 4; - m_strRealm = "NTLM"; // set a dummy realm - } - else - { - kdWarning(7113) << "(" << m_pid << ") Unsupported or invalid authorization " - << "type requested" << endl; - if (isForProxy) - kdWarning(7113) << "(" << m_pid << ") Proxy URL: " << m_proxyURL << endl; - else - kdWarning(7113) << "(" << m_pid << ") URL: " << m_request.url << endl; - kdWarning(7113) << "(" << m_pid << ") Request Authorization: " << p << endl; - } - - /* - This check ensures the following: - 1.) Rejection of any unknown/unsupported authentication schemes - 2.) Usage of the strongest possible authentication schemes if - and when multiple Proxy-Authenticate or WWW-Authenticate - header field is sent. - */ - if (isForProxy) - { - if ((f == AUTH_None) || - ((m_iProxyAuthCount > 0) && (f < ProxyAuthentication))) - { - // Since I purposefully made the Proxy-Authentication settings - // persistent to reduce the number of round-trips to tdesud we - // have to take special care when an unknown/unsupported auth- - // scheme is received. This check accomplishes just that... - if ( m_iProxyAuthCount == 0) - ProxyAuthentication = f; - kdDebug(7113) << "(" << m_pid << ") Rejected proxy auth method: " << f << endl; - return; - } - m_iProxyAuthCount++; - kdDebug(7113) << "(" << m_pid << ") Accepted proxy auth method: " << f << endl; - } - else - { - if ((f == AUTH_None) || - ((m_iWWWAuthCount > 0) && (f < Authentication))) - { - kdDebug(7113) << "(" << m_pid << ") Rejected auth method: " << f << endl; - return; - } - m_iWWWAuthCount++; - kdDebug(7113) << "(" << m_pid << ") Accepted auth method: " << f << endl; - } - - - while (*p) - { - int i = 0; - while( (*p == ' ') || (*p == ',') || (*p == '\t') ) { p++; } - if ( strncasecmp( p, "realm=", 6 ) == 0 ) - { - //for sites like lib.homelinux.org - TQTextCodec* oldCodec=TQTextCodec::codecForCStrings(); - if (TDEGlobal::locale()->language().contains("ru")) - TQTextCodec::setCodecForCStrings(TQTextCodec::codecForName("CP1251")); - - p += 6; - if (*p == '"') p++; - while( p[i] && p[i] != '"' ) i++; - if( isForProxy ) - m_strProxyRealm = TQString::fromAscii( p, i ); - else - m_strRealm = TQString::fromAscii( p, i ); - - TQTextCodec::setCodecForCStrings(oldCodec); - - if (!p[i]) break; - } - p+=(i+1); - } - - if( isForProxy ) - { - ProxyAuthentication = f; - m_strProxyAuthorization = TQString::fromLatin1( strAuth ); - } - else - { - Authentication = f; - m_strAuthorization = TQString::fromLatin1( strAuth ); - } -} - - -bool HTTPProtocol::retryPrompt() -{ - TQString prompt; - switch ( m_responseCode ) - { - case 401: - prompt = i18n("Authentication Failed."); - break; - case 407: - prompt = i18n("Proxy Authentication Failed."); - break; - default: - break; - } - prompt += i18n(" Do you want to retry?"); - return (messageBox(QuestionYesNo, prompt, i18n("Authentication")) == 3); -} - -void HTTPProtocol::promptInfo( AuthInfo& info ) -{ - if ( m_responseCode == 401 ) - { - info.url = m_request.url; - if ( !m_state.user.isEmpty() ) - info.username = m_state.user; - info.readOnly = !m_request.url.user().isEmpty(); - info.prompt = i18n( "You need to supply a username and a " - "password to access this site." ); - info.keepPassword = true; // Prompt the user for persistence as well. - if ( !m_strRealm.isEmpty() ) - { - info.realmValue = m_strRealm; - info.verifyPath = false; - info.digestInfo = m_strAuthorization; - info.commentLabel = i18n( "Site:" ); - info.comment = i18n("<b>%1</b> at <b>%2</b>").arg( htmlEscape(m_strRealm) ).arg( m_request.hostname ); - } - } - else if ( m_responseCode == 407 ) - { - info.url = m_proxyURL; - info.username = m_proxyURL.user(); - info.prompt = i18n( "You need to supply a username and a password for " - "the proxy server listed below before you are allowed " - "to access any sites." ); - info.keepPassword = true; - if ( !m_strProxyRealm.isEmpty() ) - { - info.realmValue = m_strProxyRealm; - info.verifyPath = false; - info.digestInfo = m_strProxyAuthorization; - info.commentLabel = i18n( "Proxy:" ); - info.comment = i18n("<b>%1</b> at <b>%2</b>").arg( htmlEscape(m_strProxyRealm) ).arg( m_proxyURL.host() ); - } - } -} - -bool HTTPProtocol::getAuthorization() -{ - AuthInfo info; - bool result = false; - - kdDebug (7113) << "(" << m_pid << ") HTTPProtocol::getAuthorization: " - << "Current Response: " << m_responseCode << ", " - << "Previous Response: " << m_prevResponseCode << ", " - << "Authentication: " << Authentication << ", " - << "ProxyAuthentication: " << ProxyAuthentication << endl; - - if (m_request.bNoAuth) - { - if (m_request.bErrorPage) - errorPage(); - else - error( ERR_COULD_NOT_LOGIN, i18n("Authentication needed for %1 but authentication is disabled.").arg(m_request.hostname)); - return false; - } - - bool repeatFailure = (m_prevResponseCode == m_responseCode); - - TQString errorMsg; - - if (repeatFailure) - { - bool prompt = true; - if ( Authentication == AUTH_Digest || ProxyAuthentication == AUTH_Digest ) - { - bool isStaleNonce = false; - TQString auth = ( m_responseCode == 401 ) ? m_strAuthorization : m_strProxyAuthorization; - int pos = auth.find("stale", 0, false); - if ( pos != -1 ) - { - pos += 5; - int len = auth.length(); - while( pos < len && (auth[pos] == ' ' || auth[pos] == '=') ) pos++; - if ( pos < len && auth.find("true", pos, false) != -1 ) - { - isStaleNonce = true; - kdDebug(7113) << "(" << m_pid << ") Stale nonce value. " - << "Will retry using same info..." << endl; - } - } - if ( isStaleNonce ) - { - prompt = false; - result = true; - if ( m_responseCode == 401 ) - { - info.username = m_request.user; - info.password = m_request.passwd; - info.realmValue = m_strRealm; - info.digestInfo = m_strAuthorization; - } - else if ( m_responseCode == 407 ) - { - info.username = m_proxyURL.user(); - info.password = m_proxyURL.pass(); - info.realmValue = m_strProxyRealm; - info.digestInfo = m_strProxyAuthorization; - } - } - } - - if ( Authentication == AUTH_NTLM || ProxyAuthentication == AUTH_NTLM ) - { - TQString auth = ( m_responseCode == 401 ) ? m_strAuthorization : m_strProxyAuthorization; - kdDebug(7113) << "auth: " << auth << endl; - if ( auth.length() > 4 ) - { - prompt = false; - result = true; - kdDebug(7113) << "(" << m_pid << ") NTLM auth second phase, " - << "sending response..." << endl; - if ( m_responseCode == 401 ) - { - info.username = m_request.user; - info.password = m_request.passwd; - info.realmValue = m_strRealm; - info.digestInfo = m_strAuthorization; - } - else if ( m_responseCode == 407 ) - { - info.username = m_proxyURL.user(); - info.password = m_proxyURL.pass(); - info.realmValue = m_strProxyRealm; - info.digestInfo = m_strProxyAuthorization; - } - } - } - - if ( prompt ) - { - switch ( m_responseCode ) - { - case 401: - errorMsg = i18n("Authentication Failed."); - break; - case 407: - errorMsg = i18n("Proxy Authentication Failed."); - break; - default: - break; - } - } - } - else - { - // At this point we know more details, so use it to find - // out if we have a cached version and avoid a re-prompt! - // We also do not use verify path unlike the pre-emptive - // requests because we already know the realm value... - - if (m_bProxyAuthValid) - { - // Reset cached proxy auth - m_bProxyAuthValid = false; - KURL proxy ( config()->readEntry("UseProxy") ); - m_proxyURL.setUser(proxy.user()); - m_proxyURL.setPass(proxy.pass()); - } - - info.verifyPath = false; - if ( m_responseCode == 407 ) - { - info.url = m_proxyURL; - info.username = m_proxyURL.user(); - info.password = m_proxyURL.pass(); - info.realmValue = m_strProxyRealm; - info.digestInfo = m_strProxyAuthorization; - } - else - { - info.url = m_request.url; - info.username = m_request.user; - info.password = m_request.passwd; - info.realmValue = m_strRealm; - info.digestInfo = m_strAuthorization; - } - - // If either username or password is not supplied - // with the request, check the password cache. - if ( info.username.isNull() || - info.password.isNull() ) - result = checkCachedAuthentication( info ); - - if ( Authentication == AUTH_Digest ) - { - TQString auth; - - if (m_responseCode == 401) - auth = m_strAuthorization; - else - auth = m_strProxyAuthorization; - - int pos = auth.find("stale", 0, false); - if ( pos != -1 ) - { - pos += 5; - int len = auth.length(); - while( pos < len && (auth[pos] == ' ' || auth[pos] == '=') ) pos++; - if ( pos < len && auth.find("true", pos, false) != -1 ) - { - info.digestInfo = (m_responseCode == 401) ? m_strAuthorization : m_strProxyAuthorization; - kdDebug(7113) << "(" << m_pid << ") Just a stale nonce value! " - << "Retrying using the new nonce sent..." << endl; - } - } - } - } - - if (!result ) - { - // Do not prompt if the username & password - // is already supplied and the login attempt - // did not fail before. - if ( !repeatFailure && - !info.username.isNull() && - !info.password.isNull() ) - result = true; - else - { - if (Authentication == AUTH_Negotiate) - { - if (!repeatFailure) - result = true; - } - else if ( m_request.disablePassDlg == false ) - { - kdDebug( 7113 ) << "(" << m_pid << ") Prompting the user for authorization..." << endl; - promptInfo( info ); - result = openPassDlg( info, errorMsg ); - } - } - } - - if ( result ) - { - switch (m_responseCode) - { - case 401: // Request-Authentication - m_request.user = info.username; - m_request.passwd = info.password; - m_strRealm = info.realmValue; - m_strAuthorization = info.digestInfo; - break; - case 407: // Proxy-Authentication - m_proxyURL.setUser( info.username ); - m_proxyURL.setPass( info.password ); - m_strProxyRealm = info.realmValue; - m_strProxyAuthorization = info.digestInfo; - break; - default: - break; - } - return true; - } - - if (m_request.bErrorPage) - errorPage(); - else - error( ERR_USER_CANCELED, TQString::null ); - return false; -} - -void HTTPProtocol::saveAuthorization() -{ - AuthInfo info; - if ( m_prevResponseCode == 407 ) - { - if (!m_bUseProxy) - return; - m_bProxyAuthValid = true; - info.url = m_proxyURL; - info.username = m_proxyURL.user(); - info.password = m_proxyURL.pass(); - info.realmValue = m_strProxyRealm; - info.digestInfo = m_strProxyAuthorization; - cacheAuthentication( info ); - } - else - { - info.url = m_request.url; - info.username = m_request.user; - info.password = m_request.passwd; - info.realmValue = m_strRealm; - info.digestInfo = m_strAuthorization; - cacheAuthentication( info ); - } -} - -#ifdef HAVE_LIBGSSAPI -TQCString HTTPProtocol::gssError( int major_status, int minor_status ) -{ - OM_uint32 new_status; - OM_uint32 msg_ctx = 0; - gss_buffer_desc major_string; - gss_buffer_desc minor_string; - OM_uint32 ret; - TQCString errorstr; - - errorstr = ""; - - do { - ret = gss_display_status(&new_status, major_status, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &major_string); - errorstr += (const char *)major_string.value; - errorstr += " "; - ret = gss_display_status(&new_status, minor_status, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &minor_string); - errorstr += (const char *)minor_string.value; - errorstr += " "; - } while (!GSS_ERROR(ret) && msg_ctx != 0); - - return errorstr; -} - -TQString HTTPProtocol::createNegotiateAuth() -{ - TQString auth; - TQCString servicename; - TQByteArray input; - OM_uint32 major_status, minor_status; - OM_uint32 req_flags = 0; - gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - gss_name_t server; - gss_ctx_id_t ctx; - gss_OID mech_oid; - static gss_OID_desc krb5_oid_desc = {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"}; - static gss_OID_desc spnego_oid_desc = {6, (void *) "\x2b\x06\x01\x05\x05\x02"}; - int found = 0; - unsigned int i; - gss_OID_set mech_set; - gss_OID tmp_oid; - - ctx = GSS_C_NO_CONTEXT; - mech_oid = &krb5_oid_desc; - - // see whether we can use the SPNEGO mechanism - major_status = gss_indicate_mechs(&minor_status, &mech_set); - if (GSS_ERROR(major_status)) { - kdDebug(7113) << "(" << m_pid << ") gss_indicate_mechs failed: " << gssError(major_status, minor_status) << endl; - } else { - for (i=0; i<mech_set->count && !found; i++) { - tmp_oid = &mech_set->elements[i]; - if (tmp_oid->length == spnego_oid_desc.length && - !memcmp(tmp_oid->elements, spnego_oid_desc.elements, tmp_oid->length)) { - kdDebug(7113) << "(" << m_pid << ") createNegotiateAuth: found SPNEGO mech" << endl; - found = 1; - mech_oid = &spnego_oid_desc; - break; - } - } - gss_release_oid_set(&minor_status, &mech_set); - } - - // the service name is "HTTP/f.q.d.n" - servicename = "HTTP@"; - servicename += m_state.hostname.ascii(); - - input_token.value = (void *)servicename.data(); - input_token.length = servicename.length() + 1; - - major_status = gss_import_name(&minor_status, &input_token, - GSS_C_NT_HOSTBASED_SERVICE, &server); - - input_token.value = NULL; - input_token.length = 0; - - if (GSS_ERROR(major_status)) { - kdDebug(7113) << "(" << m_pid << ") gss_import_name failed: " << gssError(major_status, minor_status) << endl; - // reset the auth string so that subsequent methods aren't confused - m_strAuthorization = TQString::null; - return TQString::null; - } - - major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, - &ctx, server, mech_oid, - req_flags, GSS_C_INDEFINITE, - GSS_C_NO_CHANNEL_BINDINGS, - GSS_C_NO_BUFFER, NULL, &output_token, - NULL, NULL); - - - if (GSS_ERROR(major_status) || (output_token.length == 0)) { - kdDebug(7113) << "(" << m_pid << ") gss_init_sec_context failed: " << gssError(major_status, minor_status) << endl; - gss_release_name(&minor_status, &server); - if (ctx != GSS_C_NO_CONTEXT) { - gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER); - ctx = GSS_C_NO_CONTEXT; - } - // reset the auth string so that subsequent methods aren't confused - m_strAuthorization = TQString::null; - return TQString::null; - } - - input.duplicate((const char *)output_token.value, output_token.length); - auth = "Authorization: Negotiate "; - auth += KCodecs::base64Encode( input ); - auth += "\r\n"; - - // free everything - gss_release_name(&minor_status, &server); - if (ctx != GSS_C_NO_CONTEXT) { - gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER); - ctx = GSS_C_NO_CONTEXT; - } - gss_release_buffer(&minor_status, &output_token); - - return auth; -} -#else - -// Dummy -TQCString HTTPProtocol::gssError( int, int ) -{ - return ""; -} - -// Dummy -TQString HTTPProtocol::createNegotiateAuth() -{ - return TQString::null; -} -#endif - -TQString HTTPProtocol::createNTLMAuth( bool isForProxy ) -{ - uint len; - TQString auth, user, domain, passwd; - TQCString strauth; - TQByteArray buf; - - if ( isForProxy ) - { - auth = "Proxy-Connection: Keep-Alive\r\n"; - auth += "Proxy-Authorization: NTLM "; - user = m_proxyURL.user(); - passwd = m_proxyURL.pass(); - strauth = m_strProxyAuthorization.latin1(); - len = m_strProxyAuthorization.length(); - } - else - { - auth = "Authorization: NTLM "; - user = m_state.user; - passwd = m_state.passwd; - strauth = m_strAuthorization.latin1(); - len = m_strAuthorization.length(); - } - if ( user.contains('\\') ) { - domain = user.section( '\\', 0, 0); - user = user.section( '\\', 1 ); - } - - kdDebug(7113) << "(" << m_pid << ") NTLM length: " << len << endl; - if ( user.isEmpty() || passwd.isEmpty() || len < 4 ) - return TQString::null; - - if ( len > 4 ) - { - // create a response - TQByteArray challenge; - KCodecs::base64Decode( strauth.right( len - 5 ), challenge ); - KNTLM::getAuth( buf, challenge, user, passwd, domain, - KNetwork::KResolver::localHostName(), false, false ); - } - else - { - KNTLM::getNegotiate( buf ); - } - - // remove the challenge to prevent reuse - if ( isForProxy ) - m_strProxyAuthorization = "NTLM"; - else - m_strAuthorization = "NTLM"; - - auth += KCodecs::base64Encode( buf ); - auth += "\r\n"; - - return auth; -} - -TQString HTTPProtocol::createBasicAuth( bool isForProxy ) -{ - TQString auth; - TQCString user, passwd; - if ( isForProxy ) - { - auth = "Proxy-Authorization: Basic "; - user = m_proxyURL.user().latin1(); - passwd = m_proxyURL.pass().latin1(); - } - else - { - auth = "Authorization: Basic "; - user = m_state.user.latin1(); - passwd = m_state.passwd.latin1(); - } - - if ( user.isEmpty() ) - user = ""; - if ( passwd.isEmpty() ) - passwd = ""; - - user += ':'; - user += passwd; - auth += KCodecs::base64Encode( user ); - auth += "\r\n"; - - return auth; -} - -void HTTPProtocol::calculateResponse( DigestAuthInfo& info, TQCString& Response ) -{ - KMD5 md; - TQCString HA1; - TQCString HA2; - - // Calculate H(A1) - TQCString authStr = info.username; - authStr += ':'; - authStr += info.realm; - authStr += ':'; - authStr += info.password; - md.update( authStr ); - - if ( info.algorithm.lower() == "md5-sess" ) - { - authStr = md.hexDigest(); - authStr += ':'; - authStr += info.nonce; - authStr += ':'; - authStr += info.cnonce; - md.reset(); - md.update( authStr ); - } - HA1 = md.hexDigest(); - - kdDebug(7113) << "(" << m_pid << ") calculateResponse(): A1 => " << HA1 << endl; - - // Calcualte H(A2) - authStr = info.method; - authStr += ':'; - authStr += m_request.url.encodedPathAndQuery(0, true).latin1(); - if ( info.qop == "auth-int" ) - { - authStr += ':'; - authStr += info.entityBody; - } - md.reset(); - md.update( authStr ); - HA2 = md.hexDigest(); - - kdDebug(7113) << "(" << m_pid << ") calculateResponse(): A2 => " - << HA2 << endl; - - // Calcualte the response. - authStr = HA1; - authStr += ':'; - authStr += info.nonce; - authStr += ':'; - if ( !info.qop.isEmpty() ) - { - authStr += info.nc; - authStr += ':'; - authStr += info.cnonce; - authStr += ':'; - authStr += info.qop; - authStr += ':'; - } - authStr += HA2; - md.reset(); - md.update( authStr ); - Response = md.hexDigest(); - - kdDebug(7113) << "(" << m_pid << ") calculateResponse(): Response => " - << Response << endl; -} - -TQString HTTPProtocol::createDigestAuth ( bool isForProxy ) -{ - const char *p; - - TQString auth; - TQCString opaque; - TQCString Response; - - DigestAuthInfo info; - - opaque = ""; - if ( isForProxy ) - { - auth = "Proxy-Authorization: Digest "; - info.username = m_proxyURL.user().latin1(); - info.password = m_proxyURL.pass().latin1(); - p = m_strProxyAuthorization.latin1(); - } - else - { - auth = "Authorization: Digest "; - info.username = m_state.user.latin1(); - info.password = m_state.passwd.latin1(); - p = m_strAuthorization.latin1(); - } - if (!p || !*p) - return TQString::null; - - p += 6; // Skip "Digest" - - if ( info.username.isEmpty() || info.password.isEmpty() || !p ) - return TQString::null; - - // info.entityBody = p; // FIXME: send digest of data for POST action ?? - info.realm = ""; - info.algorithm = "MD5"; - info.nonce = ""; - info.qop = ""; - - // cnonce is recommended to contain about 64 bits of entropy - info.cnonce = TDEApplication::randomString(16).latin1(); - - // HACK: Should be fixed according to RFC 2617 section 3.2.2 - info.nc = "00000001"; - - // Set the method used... - switch ( m_request.method ) - { - case HTTP_GET: - info.method = "GET"; - break; - case HTTP_PUT: - info.method = "PUT"; - break; - case HTTP_POST: - info.method = "POST"; - break; - case HTTP_HEAD: - info.method = "HEAD"; - break; - case HTTP_DELETE: - info.method = "DELETE"; - break; - case DAV_PROPFIND: - info.method = "PROPFIND"; - break; - case DAV_PROPPATCH: - info.method = "PROPPATCH"; - break; - case DAV_MKCOL: - info.method = "MKCOL"; - break; - case DAV_COPY: - info.method = "COPY"; - break; - case DAV_MOVE: - info.method = "MOVE"; - break; - case DAV_LOCK: - info.method = "LOCK"; - break; - case DAV_UNLOCK: - info.method = "UNLOCK"; - break; - case DAV_SEARCH: - info.method = "SEARCH"; - break; - case DAV_SUBSCRIBE: - info.method = "SUBSCRIBE"; - break; - case DAV_UNSUBSCRIBE: - info.method = "UNSUBSCRIBE"; - break; - case DAV_POLL: - info.method = "POLL"; - break; - default: - error( ERR_UNSUPPORTED_ACTION, i18n("Unsupported method: authentication will fail. Please submit a bug report.")); - break; - } - - // Parse the Digest response.... - while (*p) - { - int i = 0; - while ( (*p == ' ') || (*p == ',') || (*p == '\t')) { p++; } - if (strncasecmp(p, "realm=", 6 )==0) - { - p+=6; - while ( *p == '"' ) p++; // Go past any number of " mark(s) first - while ( p[i] != '"' ) i++; // Read everything until the last " mark - info.realm = TQCString( p, i+1 ); - } - else if (strncasecmp(p, "algorith=", 9)==0) - { - p+=9; - while ( *p == '"' ) p++; // Go past any number of " mark(s) first - while ( ( p[i] != '"' ) && ( p[i] != ',' ) && ( p[i] != '\0' ) ) i++; - info.algorithm = TQCString(p, i+1); - } - else if (strncasecmp(p, "algorithm=", 10)==0) - { - p+=10; - while ( *p == '"' ) p++; // Go past any " mark(s) first - while ( ( p[i] != '"' ) && ( p[i] != ',' ) && ( p[i] != '\0' ) ) i++; - info.algorithm = TQCString(p,i+1); - } - else if (strncasecmp(p, "domain=", 7)==0) - { - p+=7; - while ( *p == '"' ) p++; // Go past any " mark(s) first - while ( p[i] != '"' ) i++; // Read everything until the last " mark - int pos; - int idx = 0; - TQCString uri = TQCString(p,i+1); - do - { - pos = uri.find( ' ', idx ); - if ( pos != -1 ) - { - KURL u (m_request.url, uri.mid(idx, pos-idx)); - if (u.isValid ()) - info.digestURI.append( u.url().latin1() ); - } - else - { - KURL u (m_request.url, uri.mid(idx, uri.length()-idx)); - if (u.isValid ()) - info.digestURI.append( u.url().latin1() ); - } - idx = pos+1; - } while ( pos != -1 ); - } - else if (strncasecmp(p, "nonce=", 6)==0) - { - p+=6; - while ( *p == '"' ) p++; // Go past any " mark(s) first - while ( p[i] != '"' ) i++; // Read everything until the last " mark - info.nonce = TQCString(p,i+1); - } - else if (strncasecmp(p, "opaque=", 7)==0) - { - p+=7; - while ( *p == '"' ) p++; // Go past any " mark(s) first - while ( p[i] != '"' ) i++; // Read everything until the last " mark - opaque = TQCString(p,i+1); - } - else if (strncasecmp(p, "qop=", 4)==0) - { - p+=4; - while ( *p == '"' ) p++; // Go past any " mark(s) first - while ( p[i] != '"' ) i++; // Read everything until the last " mark - info.qop = TQCString(p,i+1); - } - p+=(i+1); - } - - if (info.realm.isEmpty() || info.nonce.isEmpty()) - return TQString::null; - - // If the "domain" attribute was not specified and the current response code - // is authentication needed, add the current request url to the list over which - // this credential can be automatically applied. - if (info.digestURI.isEmpty() && (m_responseCode == 401 || m_responseCode == 407)) - info.digestURI.append (m_request.url.url().latin1()); - else - { - // Verify whether or not we should send a cached credential to the - // server based on the stored "domain" attribute... - bool send = true; - - // Determine the path of the request url... - TQString requestPath = m_request.url.directory(false, false); - if (requestPath.isEmpty()) - requestPath = "/"; - - int count = info.digestURI.count(); - - for (int i = 0; i < count; i++ ) - { - KURL u ( info.digestURI.at(i) ); - - send &= (m_request.url.protocol().lower() == u.protocol().lower()); - send &= (m_request.hostname.lower() == u.host().lower()); - - if (m_request.port > 0 && u.port() > 0) - send &= (m_request.port == u.port()); - - TQString digestPath = u.directory (false, false); - if (digestPath.isEmpty()) - digestPath = "/"; - - send &= (requestPath.startsWith(digestPath)); - - if (send) - break; - } - - kdDebug(7113) << "(" << m_pid << ") createDigestAuth(): passed digest " - "authentication credential test: " << send << endl; - - if (!send) - return TQString::null; - } - - kdDebug(7113) << "(" << m_pid << ") RESULT OF PARSING:" << endl; - kdDebug(7113) << "(" << m_pid << ") algorithm: " << info.algorithm << endl; - kdDebug(7113) << "(" << m_pid << ") realm: " << info.realm << endl; - kdDebug(7113) << "(" << m_pid << ") nonce: " << info.nonce << endl; - kdDebug(7113) << "(" << m_pid << ") opaque: " << opaque << endl; - kdDebug(7113) << "(" << m_pid << ") qop: " << info.qop << endl; - - // Calculate the response... - calculateResponse( info, Response ); - - auth += "username=\""; - auth += info.username; - - auth += "\", realm=\""; - auth += info.realm; - auth += "\""; - - auth += ", nonce=\""; - auth += info.nonce; - - auth += "\", uri=\""; - auth += m_request.url.encodedPathAndQuery(0, true); - - auth += "\", algorithm=\""; - auth += info.algorithm; - auth +="\""; - - if ( !info.qop.isEmpty() ) - { - auth += ", qop=\""; - auth += info.qop; - auth += "\", cnonce=\""; - auth += info.cnonce; - auth += "\", nc="; - auth += info.nc; - } - - auth += ", response=\""; - auth += Response; - if ( !opaque.isEmpty() ) - { - auth += "\", opaque=\""; - auth += opaque; - } - auth += "\"\r\n"; - - return auth; -} - -TQString HTTPProtocol::proxyAuthenticationHeader() -{ - TQString header; - - // We keep proxy authentication locally until they are changed. - // Thus, no need to check with the password manager for every - // connection. - if ( m_strProxyRealm.isEmpty() ) - { - AuthInfo info; - info.url = m_proxyURL; - info.username = m_proxyURL.user(); - info.password = m_proxyURL.pass(); - info.verifyPath = true; - - // If the proxy URL already contains username - // and password simply attempt to retrieve it - // without prompting the user... - if ( !info.username.isNull() && !info.password.isNull() ) - { - if( m_strProxyAuthorization.isEmpty() ) - ProxyAuthentication = AUTH_None; - else if( m_strProxyAuthorization.startsWith("Basic") ) - ProxyAuthentication = AUTH_Basic; - else if( m_strProxyAuthorization.startsWith("NTLM") ) - ProxyAuthentication = AUTH_NTLM; - else - ProxyAuthentication = AUTH_Digest; - } - else - { - if ( checkCachedAuthentication(info) && !info.digestInfo.isEmpty() ) - { - m_proxyURL.setUser( info.username ); - m_proxyURL.setPass( info.password ); - m_strProxyRealm = info.realmValue; - m_strProxyAuthorization = info.digestInfo; - if( m_strProxyAuthorization.startsWith("Basic") ) - ProxyAuthentication = AUTH_Basic; - else if( m_strProxyAuthorization.startsWith("NTLM") ) - ProxyAuthentication = AUTH_NTLM; - else - ProxyAuthentication = AUTH_Digest; - } - else - { - ProxyAuthentication = AUTH_None; - } - } - } - - /********* Only for debugging purpose... *********/ - if ( ProxyAuthentication != AUTH_None ) - { - kdDebug(7113) << "(" << m_pid << ") Using Proxy Authentication: " << endl; - kdDebug(7113) << "(" << m_pid << ") HOST= " << m_proxyURL.host() << endl; - kdDebug(7113) << "(" << m_pid << ") PORT= " << m_proxyURL.port() << endl; - kdDebug(7113) << "(" << m_pid << ") USER= " << m_proxyURL.user() << endl; - kdDebug(7113) << "(" << m_pid << ") PASSWORD= [protected]" << endl; - kdDebug(7113) << "(" << m_pid << ") REALM= " << m_strProxyRealm << endl; - kdDebug(7113) << "(" << m_pid << ") EXTRA= " << m_strProxyAuthorization << endl; - } - - switch ( ProxyAuthentication ) - { - case AUTH_Basic: - header += createBasicAuth( true ); - break; - case AUTH_Digest: - header += createDigestAuth( true ); - break; - case AUTH_NTLM: - if ( m_bFirstRequest ) header += createNTLMAuth( true ); - break; - case AUTH_None: - default: - break; - } - - return header; -} - -#include "http.moc" |