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

tdeioslave/http

  • tdeioslave
  • http
http.cpp
1 /*
2  Copyright (C) 2000-2003 Waldo Bastian <bastian@kde.org>
3  Copyright (C) 2000-2002 George Staikos <staikos@kde.org>
4  Copyright (C) 2000-2002 Dawit Alemayehu <adawit@kde.org>
5  Copyright (C) 2001,2002 Hamish Rodda <rodda@kde.org>
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Library General Public
9  License (LGPL) as published by the Free Software Foundation;
10  either version 2 of the License, or (at your option) any later
11  version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Library General Public License for more details.
17 
18  You should have received a copy of the GNU Library General Public License
19  along with this library; see the file COPYING.LIB. If not, write to
20  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  Boston, MA 02110-1301, USA.
22 */
23 
24 #include <config.h>
25 
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <utime.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <sys/stat.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h> // Required for AIX
34 #include <netinet/tcp.h>
35 #include <unistd.h> // must be explicitly included for MacOSX
36 
37 /*
38 #include <netdb.h>
39 #include <sys/time.h>
40 #include <sys/wait.h>
41 */
42 
43 #include <tqdom.h>
44 #include <tqfile.h>
45 #include <tqregexp.h>
46 #include <tqdatetime.h>
47 #include <tqstringlist.h>
48 #include <tqurl.h>
49 
50 #include <kurl.h>
51 #include <kidna.h>
52 #include <ksocks.h>
53 #include <kdebug.h>
54 #include <tdelocale.h>
55 #include <tdeconfig.h>
56 #include <kextsock.h>
57 #include <kservice.h>
58 #include <krfcdate.h>
59 #include <kmdcodec.h>
60 #include <kinstance.h>
61 #include <kresolver.h>
62 #include <kmimemagic.h>
63 #include <dcopclient.h>
64 #include <kdatastream.h>
65 #include <tdeapplication.h>
66 #include <kstandarddirs.h>
67 #include <kstringhandler.h>
68 #include <kremoteencoding.h>
69 
70 #include "tdeio/ioslave_defaults.h"
71 #include "tdeio/http_slave_defaults.h"
72 
73 #include "httpfilter.h"
74 #include "http.h"
75 
76 #ifdef HAVE_LIBGSSAPI
77 #ifdef GSSAPI_MIT
78 #include <gssapi/gssapi.h>
79 #else
80 #include <gssapi.h>
81 #endif /* GSSAPI_MIT */
82 
83 // Catch uncompatible crap (BR86019)
84 #if defined(GSS_RFC_COMPLIANT_OIDS) && (GSS_RFC_COMPLIANT_OIDS == 0)
85 #include <gssapi/gssapi_generic.h>
86 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
87 #endif
88 
89 #endif /* HAVE_LIBGSSAPI */
90 
91 #include <misc/tdentlm/tdentlm.h>
92 
93 using namespace TDEIO;
94 
95 extern "C" {
96  KDE_EXPORT int kdemain(int argc, char **argv);
97 }
98 
99 int kdemain( int argc, char **argv )
100 {
101  TDELocale::setMainCatalogue("tdelibs");
102  TDEInstance instance( "tdeio_http" );
103  ( void ) TDEGlobal::locale();
104 
105  if (argc != 4)
106  {
107  fprintf(stderr, "Usage: tdeio_http protocol domain-socket1 domain-socket2\n");
108  exit(-1);
109  }
110 
111  HTTPProtocol slave(argv[1], argv[2], argv[3]);
112  slave.dispatchLoop();
113  return 0;
114 }
115 
116 /*********************************** Generic utility functions ********************/
117 
118 static char * trimLead (char *orig_string)
119 {
120  while (*orig_string == ' ')
121  orig_string++;
122  return orig_string;
123 }
124 
125 static bool isCrossDomainRequest( const TQString& fqdn, const TQString& originURL )
126 {
127  if (originURL == "true") // Backwards compatibility
128  return true;
129 
130  KURL url ( originURL );
131 
132  // Document Origin domain
133  TQString a = url.host();
134 
135  // Current request domain
136  TQString b = fqdn;
137 
138  if (a == b)
139  return false;
140 
141  TQStringList l1 = TQStringList::split('.', a);
142  TQStringList l2 = TQStringList::split('.', b);
143 
144  while(l1.count() > l2.count())
145  l1.pop_front();
146 
147  while(l2.count() > l1.count())
148  l2.pop_front();
149 
150  while(l2.count() >= 2)
151  {
152  if (l1 == l2)
153  return false;
154 
155  l1.pop_front();
156  l2.pop_front();
157  }
158 
159  return true;
160 }
161 
162 /*
163  Eliminates any custom header that could potentically alter the request
164 */
165 static TQString sanitizeCustomHTTPHeader(const TQString& _header)
166 {
167  TQString sanitizedHeaders;
168  TQStringList headers = TQStringList::split(TQRegExp("[\r\n]"), _header);
169 
170  for(TQStringList::Iterator it = headers.begin(); it != headers.end(); ++it)
171  {
172  TQString header = (*it).lower();
173  // Do not allow Request line to be specified and ignore
174  // the other HTTP headers.
175  if (header.find(':') == -1 ||
176  header.startsWith("host") ||
177  header.startsWith("via"))
178  continue;
179 
180  sanitizedHeaders += (*it);
181  sanitizedHeaders += "\r\n";
182  }
183 
184  return sanitizedHeaders.stripWhiteSpace();
185 }
186 
187 static TQString htmlEscape(const TQString &plain)
188 {
189  TQString rich;
190  rich.reserve(uint(plain.length() * 1.1));
191  for (uint i = 0; i < plain.length(); ++i) {
192  if (plain.at(i) == '<') {
193  rich += "&lt;";
194  } else if (plain.at(i) == '>') {
195  rich += "&gt;";
196  } else if (plain.at(i) == '&') {
197  rich += "&amp;";
198  } else if (plain.at(i) == '"') {
199  rich += "&quot;";
200  } else {
201  rich += plain.at(i);
202  }
203  }
204  rich.squeeze();
205  return rich;
206 }
207 
208 
209 #define NO_SIZE ((TDEIO::filesize_t) -1)
210 
211 #ifdef HAVE_STRTOLL
212 #define STRTOLL strtoll
213 #else
214 #define STRTOLL strtol
215 #endif
216 
217 
218 /************************************** HTTPProtocol **********************************************/
219 
220 HTTPProtocol::HTTPProtocol( const TQCString &protocol, const TQCString &pool,
221  const TQCString &app )
222  :TCPSlaveBase( 0, protocol , pool, app,
223  (protocol == "https" || protocol == "webdavs") )
224 {
225  m_requestQueue.setAutoDelete(true);
226 
227  m_bBusy = false;
228  m_bFirstRequest = false;
229  m_bProxyAuthValid = false;
230 
231  m_iSize = NO_SIZE;
232  m_lineBufUnget = 0;
233 
234  m_protocol = protocol;
235 
236  m_maxCacheAge = DEFAULT_MAX_CACHE_AGE;
237  m_maxCacheSize = DEFAULT_MAX_CACHE_SIZE / 2;
238  m_remoteConnTimeout = DEFAULT_CONNECT_TIMEOUT;
239  m_remoteRespTimeout = DEFAULT_RESPONSE_TIMEOUT;
240  m_proxyConnTimeout = DEFAULT_PROXY_CONNECT_TIMEOUT;
241 
242  m_pid = getpid();
243 
244  setMultipleAuthCaching( true );
245  reparseConfiguration();
246 }
247 
248 HTTPProtocol::~HTTPProtocol()
249 {
250  httpClose(false);
251 }
252 
253 void HTTPProtocol::reparseConfiguration()
254 {
255  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::reparseConfiguration" << endl;
256 
257  m_strProxyRealm = TQString::null;
258  m_strProxyAuthorization = TQString::null;
259  ProxyAuthentication = AUTH_None;
260  m_bUseProxy = false;
261 
262  if (m_protocol == "https" || m_protocol == "webdavs")
263  m_iDefaultPort = DEFAULT_HTTPS_PORT;
264  else if (m_protocol == "ftp")
265  m_iDefaultPort = DEFAULT_FTP_PORT;
266  else
267  m_iDefaultPort = DEFAULT_HTTP_PORT;
268 }
269 
270 void HTTPProtocol::resetConnectionSettings()
271 {
272  m_bEOF = false;
273  m_bError = false;
274  m_lineCount = 0;
275  m_iWWWAuthCount = 0;
276  m_lineCountUnget = 0;
277  m_iProxyAuthCount = 0;
278 
279 }
280 
281 void HTTPProtocol::resetResponseSettings()
282 {
283  m_bRedirect = false;
284  m_redirectLocation = KURL();
285  m_bChunked = false;
286  m_iSize = NO_SIZE;
287 
288  m_responseHeader.clear();
289  m_qContentEncodings.clear();
290  m_qTransferEncodings.clear();
291  m_sContentMD5 = TQString::null;
292  m_strMimeType = TQString::null;
293 
294  setMetaData("request-id", m_request.id);
295 }
296 
297 void HTTPProtocol::resetSessionSettings()
298 {
299  // Do not reset the URL on redirection if the proxy
300  // URL, username or password has not changed!
301  KURL proxy ( config()->readEntry("UseProxy") );
302 
303  if ( m_strProxyRealm.isEmpty() || !proxy.isValid() ||
304  m_proxyURL.host() != proxy.host() ||
305  (!proxy.user().isNull() && proxy.user() != m_proxyURL.user()) ||
306  (!proxy.pass().isNull() && proxy.pass() != m_proxyURL.pass()) )
307  {
308  m_bProxyAuthValid = false;
309  m_proxyURL = proxy;
310  m_bUseProxy = m_proxyURL.isValid();
311 
312  kdDebug(7113) << "(" << m_pid << ") Using proxy: " << m_bUseProxy <<
313  " URL: " << m_proxyURL.prettyURL() <<
314  " Realm: " << m_strProxyRealm << endl;
315  }
316 
317  m_bPersistentProxyConnection = config()->readBoolEntry("PersistentProxyConnection", false);
318  kdDebug(7113) << "(" << m_pid << ") Enable Persistent Proxy Connection: "
319  << m_bPersistentProxyConnection << endl;
320 
321  m_request.bUseCookiejar = config()->readBoolEntry("Cookies");
322  m_request.bUseCache = config()->readBoolEntry("UseCache", true);
323  m_request.bErrorPage = config()->readBoolEntry("errorPage", true);
324  m_request.bNoAuth = config()->readBoolEntry("no-auth");
325  m_strCacheDir = config()->readPathEntry("CacheDir");
326  m_maxCacheAge = config()->readNumEntry("MaxCacheAge", DEFAULT_MAX_CACHE_AGE);
327  m_request.window = config()->readEntry("window-id");
328 
329  kdDebug(7113) << "(" << m_pid << ") Window Id = " << m_request.window << endl;
330  kdDebug(7113) << "(" << m_pid << ") ssl_was_in_use = "
331  << metaData ("ssl_was_in_use") << endl;
332 
333  m_request.referrer = TQString::null;
334  if ( config()->readBoolEntry("SendReferrer", true) &&
335  (m_protocol == "https" || m_protocol == "webdavs" ||
336  metaData ("ssl_was_in_use") != "TRUE" ) )
337  {
338  KURL referrerURL ( metaData("referrer") );
339  if (referrerURL.isValid())
340  {
341  // Sanitize
342  TQString protocol = referrerURL.protocol();
343  if (protocol.startsWith("webdav"))
344  {
345  protocol.replace(0, 6, "http");
346  referrerURL.setProtocol(protocol);
347  }
348 
349  if (protocol.startsWith("http"))
350  {
351  referrerURL.setRef(TQString::null);
352  referrerURL.setUser(TQString::null);
353  referrerURL.setPass(TQString::null);
354  m_request.referrer = referrerURL.url();
355  }
356  }
357  }
358 
359  if ( config()->readBoolEntry("SendLanguageSettings", true) )
360  {
361  m_request.charsets = config()->readEntry( "Charsets", "iso-8859-1" );
362 
363  if ( !m_request.charsets.isEmpty() )
364  m_request.charsets += DEFAULT_PARTIAL_CHARSET_HEADER;
365 
366  m_request.languages = config()->readEntry( "Languages", DEFAULT_LANGUAGE_HEADER );
367  }
368  else
369  {
370  m_request.charsets = TQString::null;
371  m_request.languages = TQString::null;
372  }
373 
374  // Adjust the offset value based on the "resume" meta-data.
375  TQString resumeOffset = metaData("resume");
376  if ( !resumeOffset.isEmpty() )
377  m_request.offset = resumeOffset.toInt(); // TODO: Convert to 64 bit
378  else
379  m_request.offset = 0;
380 
381  m_request.disablePassDlg = config()->readBoolEntry("DisablePassDlg", false);
382  m_request.allowCompressedPage = config()->readBoolEntry("AllowCompressedPage", true);
383  m_request.id = metaData("request-id");
384 
385  // Store user agent for this host.
386  if ( config()->readBoolEntry("SendUserAgent", true) )
387  m_request.userAgent = metaData("UserAgent");
388  else
389  m_request.userAgent = TQString::null;
390 
391  // Deal with cache cleaning.
392  // TODO: Find a smarter way to deal with cleaning the
393  // cache ?
394  if ( m_request.bUseCache )
395  cleanCache();
396 
397  // Deal with HTTP tunneling
398  if ( m_bIsSSL && m_bUseProxy && m_proxyURL.protocol() != "https" &&
399  m_proxyURL.protocol() != "webdavs")
400  {
401  m_bNeedTunnel = true;
402  setRealHost( m_request.hostname );
403  kdDebug(7113) << "(" << m_pid << ") SSL tunnel: Setting real hostname to: "
404  << m_request.hostname << endl;
405  }
406  else
407  {
408  m_bNeedTunnel = false;
409  setRealHost( TQString::null);
410  }
411 
412  m_responseCode = 0;
413  m_prevResponseCode = 0;
414 
415  m_strRealm = TQString::null;
416  m_strAuthorization = TQString::null;
417  Authentication = AUTH_None;
418 
419  // Obtain the proxy and remote server timeout values
420  m_proxyConnTimeout = proxyConnectTimeout();
421  m_remoteConnTimeout = connectTimeout();
422  m_remoteRespTimeout = responseTimeout();
423 
424  // Set the SSL meta-data here...
425  setSSLMetaData();
426 
427  // Bounce back the actual referrer sent
428  setMetaData("referrer", m_request.referrer);
429 
430  // Follow HTTP/1.1 spec and enable keep-alive by default
431  // unless the remote side tells us otherwise or we determine
432  // the persistent link has been terminated by the remote end.
433  m_bKeepAlive = true;
434  m_keepAliveTimeout = 0;
435  m_bUnauthorized = false;
436 
437  // A single request can require multiple exchanges with the remote
438  // server due to authentication challenges or SSL tunneling.
439  // m_bFirstRequest is a flag that indicates whether we are
440  // still processing the first request. This is important because we
441  // should not force a close of a keep-alive connection in the middle
442  // of the first request.
443  // m_bFirstRequest is set to "true" whenever a new connection is
444  // made in httpOpenConnection()
445  m_bFirstRequest = false;
446 }
447 
448 void HTTPProtocol::setHost( const TQString& host, int port,
449  const TQString& user, const TQString& pass )
450 {
451  // Reset the webdav-capable flags for this host
452  if ( m_request.hostname != host )
453  m_davHostOk = m_davHostUnsupported = false;
454 
455  // is it an IPv6 address?
456  if (host.find(':') == -1)
457  {
458  m_request.hostname = host;
459  m_request.encoded_hostname = KIDNA::toAscii(host);
460  }
461  else
462  {
463  m_request.hostname = host;
464  int pos = host.find('%');
465  if (pos == -1)
466  m_request.encoded_hostname = '[' + host + ']';
467  else
468  // don't send the scope-id in IPv6 addresses to the server
469  m_request.encoded_hostname = '[' + host.left(pos) + ']';
470  }
471  m_request.port = (port == 0) ? m_iDefaultPort : port;
472  m_request.user = user;
473  m_request.passwd = pass;
474 
475  m_bIsTunneled = false;
476 
477  kdDebug(7113) << "(" << m_pid << ") Hostname is now: " << m_request.hostname <<
478  " (" << m_request.encoded_hostname << ")" <<endl;
479 }
480 
481 bool HTTPProtocol::checkRequestURL( const KURL& u )
482 {
483  kdDebug (7113) << "(" << m_pid << ") HTTPProtocol::checkRequestURL: " << u.prettyURL() << endl;
484 
485  m_request.url = u;
486 
487  if (m_request.hostname.isEmpty())
488  {
489  error( TDEIO::ERR_UNKNOWN_HOST, i18n("No host specified."));
490  return false;
491  }
492 
493  if (u.path().isEmpty())
494  {
495  KURL newUrl(u);
496  newUrl.setPath("/");
497  redirection(newUrl);
498  finished();
499  return false;
500  }
501 
502  if ( m_protocol != u.protocol().latin1() )
503  {
504  short unsigned int oldDefaultPort = m_iDefaultPort;
505  m_protocol = u.protocol().latin1();
506  reparseConfiguration();
507  if ( m_iDefaultPort != oldDefaultPort &&
508  m_request.port == oldDefaultPort )
509  m_request.port = m_iDefaultPort;
510  }
511 
512  resetSessionSettings();
513  return true;
514 }
515 
516 void HTTPProtocol::retrieveContent( bool dataInternal /* = false */ )
517 {
518  kdDebug (7113) << "(" << m_pid << ") HTTPProtocol::retrieveContent " << endl;
519  if ( !retrieveHeader( false ) )
520  {
521  if ( m_bError )
522  return;
523  }
524  else
525  {
526  if ( !readBody( dataInternal ) && m_bError )
527  return;
528  }
529 
530  httpClose(m_bKeepAlive);
531 
532  // if data is required internally, don't finish,
533  // it is processed before we finish()
534  if ( !dataInternal )
535  {
536  if ((m_responseCode == 204) &&
537  ((m_request.method == HTTP_GET) || (m_request.method == HTTP_POST)))
538  error(ERR_NO_CONTENT, "");
539  else
540  finished();
541  }
542 }
543 
544 bool HTTPProtocol::retrieveHeader( bool close_connection )
545 {
546  kdDebug (7113) << "(" << m_pid << ") HTTPProtocol::retrieveHeader " << endl;
547  while ( 1 )
548  {
549  if (!httpOpen())
550  return false;
551 
552  resetResponseSettings();
553  if (!readHeader())
554  {
555  if ( m_bError )
556  return false;
557 
558  if (m_bIsTunneled)
559  {
560  kdDebug(7113) << "(" << m_pid << ") Re-establishing SSL tunnel..." << endl;
561  httpCloseConnection();
562  }
563  }
564  else
565  {
566  // Do not save authorization if the current response code is
567  // 4xx (client error) or 5xx (server error).
568  kdDebug(7113) << "(" << m_pid << ") Previous Response: "
569  << m_prevResponseCode << endl;
570  kdDebug(7113) << "(" << m_pid << ") Current Response: "
571  << m_responseCode << endl;
572 
573  if (isSSLTunnelEnabled() && m_bIsSSL && !m_bUnauthorized && !m_bError)
574  {
575  // If there is no error, disable tunneling
576  if ( m_responseCode < 400 )
577  {
578  kdDebug(7113) << "(" << m_pid << ") Unset tunneling flag!" << endl;
579  setEnableSSLTunnel( false );
580  m_bIsTunneled = true;
581  // Reset the CONNECT response code...
582  m_responseCode = m_prevResponseCode;
583  continue;
584  }
585  else
586  {
587  if ( !m_request.bErrorPage )
588  {
589  kdDebug(7113) << "(" << m_pid << ") Sending an error message!" << endl;
590  error( ERR_UNKNOWN_PROXY_HOST, m_proxyURL.host() );
591  return false;
592  }
593 
594  kdDebug(7113) << "(" << m_pid << ") Sending an error page!" << endl;
595  }
596  }
597 
598  if (m_responseCode < 400 && (m_prevResponseCode == 401 ||
599  m_prevResponseCode == 407))
600  saveAuthorization();
601  break;
602  }
603  }
604 
605  // Clear of the temporary POST buffer if it is not empty...
606  if (!m_bufPOST.isEmpty())
607  {
608  m_bufPOST.resize(0);
609  kdDebug(7113) << "(" << m_pid << ") HTTP::retreiveHeader: Cleared POST "
610  "buffer..." << endl;
611  }
612 
613  if ( close_connection )
614  {
615  httpClose(m_bKeepAlive);
616  finished();
617  }
618 
619  return true;
620 }
621 
622 void HTTPProtocol::stat(const KURL& url)
623 {
624  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::stat " << url.prettyURL()
625  << endl;
626 
627  if ( !checkRequestURL( url ) )
628  return;
629 
630  if ( m_protocol != "webdav" && m_protocol != "webdavs" )
631  {
632  TQString statSide = metaData(TQString::fromLatin1("statSide"));
633  if ( statSide != "source" )
634  {
635  // When uploading we assume the file doesn't exit
636  error( ERR_DOES_NOT_EXIST, url.prettyURL() );
637  return;
638  }
639 
640  // When downloading we assume it exists
641  UDSEntry entry;
642  UDSAtom atom;
643  atom.m_uds = TDEIO::UDS_NAME;
644  atom.m_str = url.fileName();
645  entry.append( atom );
646 
647  atom.m_uds = TDEIO::UDS_FILE_TYPE;
648  atom.m_long = S_IFREG; // a file
649  entry.append( atom );
650 
651  atom.m_uds = TDEIO::UDS_ACCESS;
652  atom.m_long = S_IRUSR | S_IRGRP | S_IROTH; // readable by everybody
653  entry.append( atom );
654 
655  statEntry( entry );
656  finished();
657  return;
658  }
659 
660  davStatList( url );
661 }
662 
663 void HTTPProtocol::listDir( const KURL& url )
664 {
665  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::listDir " << url.prettyURL()
666  << endl;
667 
668  if ( !checkRequestURL( url ) )
669  return;
670 
671  if (!url.protocol().startsWith("webdav")) {
672  error(ERR_UNSUPPORTED_ACTION, url.prettyURL());
673  return;
674  }
675 
676  davStatList( url, false );
677 }
678 
679 void HTTPProtocol::davSetRequest( const TQCString& requestXML )
680 {
681  // insert the document into the POST buffer, kill trailing zero byte
682  m_bufPOST = requestXML;
683 
684  if (m_bufPOST.size())
685  m_bufPOST.truncate( m_bufPOST.size() - 1 );
686 }
687 
688 void HTTPProtocol::davStatList( const KURL& url, bool stat )
689 {
690  UDSEntry entry;
691  UDSAtom atom;
692 
693  // check to make sure this host supports WebDAV
694  if ( !davHostOk() )
695  return;
696 
697  // Maybe it's a disguised SEARCH...
698  TQString query = metaData("davSearchQuery");
699  if ( !query.isEmpty() )
700  {
701  TQCString request = "<?xml version=\"1.0\"?>\r\n";
702  request.append( "<D:searchrequest xmlns:D=\"DAV:\">\r\n" );
703  request.append( query.utf8() );
704  request.append( "</D:searchrequest>\r\n" );
705 
706  davSetRequest( request );
707  } else {
708  // We are only after certain features...
709  TQCString request;
710  request = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
711  "<D:propfind xmlns:D=\"DAV:\">";
712 
713  // insert additional XML request from the davRequestResponse metadata
714  if ( hasMetaData( "davRequestResponse" ) )
715  request += metaData( "davRequestResponse" ).utf8();
716  else {
717  // No special request, ask for default properties
718  request += "<D:prop>"
719  "<D:creationdate/>"
720  "<D:getcontentlength/>"
721  "<D:displayname/>"
722  "<D:source/>"
723  "<D:getcontentlanguage/>"
724  "<D:getcontenttype/>"
725  "<D:executable/>"
726  "<D:getlastmodified/>"
727  "<D:getetag/>"
728  "<D:supportedlock/>"
729  "<D:lockdiscovery/>"
730  "<D:resourcetype/>"
731  "</D:prop>";
732  }
733  request += "</D:propfind>";
734 
735  davSetRequest( request );
736  }
737 
738  // WebDAV Stat or List...
739  m_request.method = query.isEmpty() ? DAV_PROPFIND : DAV_SEARCH;
740  m_request.query = TQString::null;
741  m_request.cache = CC_Reload;
742  m_request.doProxy = m_bUseProxy;
743  m_request.davData.depth = stat ? 0 : 1;
744  if (!stat)
745  m_request.url.adjustPath(+1);
746 
747  retrieveContent( true );
748 
749  // Has a redirection already been called? If so, we're done.
750  if (m_bRedirect) {
751  finished();
752  return;
753  }
754 
755  TQDomDocument multiResponse;
756  multiResponse.setContent( m_bufWebDavData, true );
757 
758  bool hasResponse = false;
759 
760  for ( TQDomNode n = multiResponse.documentElement().firstChild();
761  !n.isNull(); n = n.nextSibling())
762  {
763  TQDomElement thisResponse = n.toElement();
764  if (thisResponse.isNull())
765  continue;
766 
767  hasResponse = true;
768 
769  TQDomElement href = thisResponse.namedItem( "href" ).toElement();
770  if ( !href.isNull() )
771  {
772  entry.clear();
773 
774  TQString urlStr = href.text();
775 #if 0
776  int encoding = remoteEncoding()->encodingMib();
777  if ((encoding == 106) && (!KStringHandler::isUtf8(KURL::decode_string(urlStr, 4).latin1())))
778  encoding = 4; // Use latin1 if the file is not actually utf-8
779 #else
780  TQUrl::decode(urlStr);
781  int encoding = 106;
782 #endif
783 
784  KURL thisURL ( urlStr, encoding );
785 
786  atom.m_uds = TDEIO::UDS_NAME;
787 
788  if ( thisURL.isValid() ) {
789  // don't list the base dir of a listDir()
790  if ( !stat && thisURL.path(+1).length() == url.path(+1).length() )
791  continue;
792 
793  atom.m_str = thisURL.fileName();
794  } else {
795  // This is a relative URL.
796  atom.m_str = href.text();
797  }
798 
799  entry.append( atom );
800 
801  TQDomNodeList propstats = thisResponse.elementsByTagName( "propstat" );
802 
803  davParsePropstats( propstats, entry );
804 
805  if ( stat )
806  {
807  // return an item
808  statEntry( entry );
809  finished();
810  return;
811  }
812  else
813  {
814  listEntry( entry, false );
815  }
816  }
817  else
818  {
819  kdDebug(7113) << "Error: no URL contained in response to PROPFIND on "
820  << url.prettyURL() << endl;
821  }
822  }
823 
824  if ( stat || !hasResponse )
825  {
826  error( ERR_DOES_NOT_EXIST, url.prettyURL() );
827  }
828  else
829  {
830  listEntry( entry, true );
831  finished();
832  }
833 }
834 
835 void HTTPProtocol::davGeneric( const KURL& url, TDEIO::HTTP_METHOD method )
836 {
837  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::davGeneric " << url.prettyURL()
838  << endl;
839 
840  if ( !checkRequestURL( url ) )
841  return;
842 
843  // check to make sure this host supports WebDAV
844  if ( !davHostOk() )
845  return;
846 
847  // WebDAV method
848  m_request.method = method;
849  m_request.query = TQString::null;
850  m_request.cache = CC_Reload;
851  m_request.doProxy = m_bUseProxy;
852 
853  retrieveContent( false );
854 }
855 
856 int HTTPProtocol::codeFromResponse( const TQString& response )
857 {
858  int firstSpace = response.find( ' ' );
859  int secondSpace = response.find( ' ', firstSpace + 1 );
860  return response.mid( firstSpace + 1, secondSpace - firstSpace - 1 ).toInt();
861 }
862 
863 void HTTPProtocol::davParsePropstats( const TQDomNodeList& propstats, UDSEntry& entry )
864 {
865  TQString mimeType;
866  UDSAtom atom;
867  bool foundExecutable = false;
868  bool isDirectory = false;
869  uint lockCount = 0;
870  uint supportedLockCount = 0;
871 
872  for ( uint i = 0; i < propstats.count(); i++)
873  {
874  TQDomElement propstat = propstats.item(i).toElement();
875 
876  TQDomElement status = propstat.namedItem( "status" ).toElement();
877  if ( status.isNull() )
878  {
879  // error, no status code in this propstat
880  kdDebug(7113) << "Error, no status code in this propstat" << endl;
881  return;
882  }
883 
884  int code = codeFromResponse( status.text() );
885 
886  if ( code != 200 )
887  {
888  kdDebug(7113) << "Warning: status code " << code << " (this may mean that some properties are unavailable" << endl;
889  continue;
890  }
891 
892  TQDomElement prop = propstat.namedItem( "prop" ).toElement();
893  if ( prop.isNull() )
894  {
895  kdDebug(7113) << "Error: no prop segment in this propstat." << endl;
896  return;
897  }
898 
899  if ( hasMetaData( "davRequestResponse" ) )
900  {
901  atom.m_uds = TDEIO::UDS_XML_PROPERTIES;
902  TQDomDocument doc;
903  doc.appendChild(prop);
904  atom.m_str = doc.toString();
905  entry.append( atom );
906  }
907 
908  for ( TQDomNode n = prop.firstChild(); !n.isNull(); n = n.nextSibling() )
909  {
910  TQDomElement property = n.toElement();
911  if (property.isNull())
912  continue;
913 
914  if ( property.namespaceURI() != "DAV:" )
915  {
916  // break out - we're only interested in properties from the DAV namespace
917  continue;
918  }
919 
920  if ( property.tagName() == "creationdate" )
921  {
922  // Resource creation date. Should be is ISO 8601 format.
923  atom.m_uds = TDEIO::UDS_CREATION_TIME;
924  atom.m_long = parseDateTime( property.text(), property.attribute("dt") );
925  entry.append( atom );
926  }
927  else if ( property.tagName() == "getcontentlength" )
928  {
929  // Content length (file size)
930  atom.m_uds = TDEIO::UDS_SIZE;
931  atom.m_long = property.text().toULong();
932  entry.append( atom );
933  }
934  else if ( property.tagName() == "displayname" )
935  {
936  // Name suitable for presentation to the user
937  setMetaData( "davDisplayName", property.text() );
938  }
939  else if ( property.tagName() == "source" )
940  {
941  // Source template location
942  TQDomElement source = property.namedItem( "link" ).toElement()
943  .namedItem( "dst" ).toElement();
944  if ( !source.isNull() )
945  setMetaData( "davSource", source.text() );
946  }
947  else if ( property.tagName() == "getcontentlanguage" )
948  {
949  // equiv. to Content-Language header on a GET
950  setMetaData( "davContentLanguage", property.text() );
951  }
952  else if ( property.tagName() == "getcontenttype" )
953  {
954  // Content type (mime type)
955  // This may require adjustments for other server-side webdav implementations
956  // (tested with Apache + mod_dav 1.0.3)
957  if ( property.text() == "httpd/unix-directory" )
958  {
959  isDirectory = true;
960  }
961  else
962  {
963  mimeType = property.text();
964  }
965  }
966  else if ( property.tagName() == "executable" )
967  {
968  // File executable status
969  if ( property.text() == "T" )
970  foundExecutable = true;
971 
972  }
973  else if ( property.tagName() == "getlastmodified" )
974  {
975  // Last modification date
976  atom.m_uds = TDEIO::UDS_MODIFICATION_TIME;
977  atom.m_long = parseDateTime( property.text(), property.attribute("dt") );
978  entry.append( atom );
979 
980  }
981  else if ( property.tagName() == "getetag" )
982  {
983  // Entity tag
984  setMetaData( "davEntityTag", property.text() );
985  }
986  else if ( property.tagName() == "supportedlock" )
987  {
988  // Supported locking specifications
989  for ( TQDomNode n2 = property.firstChild(); !n2.isNull(); n2 = n2.nextSibling() )
990  {
991  TQDomElement lockEntry = n2.toElement();
992  if ( lockEntry.tagName() == "lockentry" )
993  {
994  TQDomElement lockScope = lockEntry.namedItem( "lockscope" ).toElement();
995  TQDomElement lockType = lockEntry.namedItem( "locktype" ).toElement();
996  if ( !lockScope.isNull() && !lockType.isNull() )
997  {
998  // Lock type was properly specified
999  supportedLockCount++;
1000  TQString scope = lockScope.firstChild().toElement().tagName();
1001  TQString type = lockType.firstChild().toElement().tagName();
1002 
1003  setMetaData( TQString("davSupportedLockScope%1").arg(supportedLockCount), scope );
1004  setMetaData( TQString("davSupportedLockType%1").arg(supportedLockCount), type );
1005  }
1006  }
1007  }
1008  }
1009  else if ( property.tagName() == "lockdiscovery" )
1010  {
1011  // Lists the available locks
1012  davParseActiveLocks( property.elementsByTagName( "activelock" ), lockCount );
1013  }
1014  else if ( property.tagName() == "resourcetype" )
1015  {
1016  // Resource type. "Specifies the nature of the resource."
1017  if ( !property.namedItem( "collection" ).toElement().isNull() )
1018  {
1019  // This is a collection (directory)
1020  isDirectory = true;
1021  }
1022  }
1023  else
1024  {
1025  kdDebug(7113) << "Found unknown webdav property: " << property.tagName() << endl;
1026  }
1027  }
1028  }
1029 
1030  setMetaData( "davLockCount", TQString("%1").arg(lockCount) );
1031  setMetaData( "davSupportedLockCount", TQString("%1").arg(supportedLockCount) );
1032 
1033  atom.m_uds = TDEIO::UDS_FILE_TYPE;
1034  atom.m_long = isDirectory ? S_IFDIR : S_IFREG;
1035  entry.append( atom );
1036 
1037  if ( foundExecutable || isDirectory )
1038  {
1039  // File was executable, or is a directory.
1040  atom.m_uds = TDEIO::UDS_ACCESS;
1041  atom.m_long = 0700;
1042  entry.append(atom);
1043  }
1044  else
1045  {
1046  atom.m_uds = TDEIO::UDS_ACCESS;
1047  atom.m_long = 0600;
1048  entry.append(atom);
1049  }
1050 
1051  if ( !isDirectory && !mimeType.isEmpty() )
1052  {
1053  atom.m_uds = TDEIO::UDS_MIME_TYPE;
1054  atom.m_str = mimeType;
1055  entry.append( atom );
1056  }
1057 }
1058 
1059 void HTTPProtocol::davParseActiveLocks( const TQDomNodeList& activeLocks,
1060  uint& lockCount )
1061 {
1062  for ( uint i = 0; i < activeLocks.count(); i++ )
1063  {
1064  TQDomElement activeLock = activeLocks.item(i).toElement();
1065 
1066  lockCount++;
1067  // required
1068  TQDomElement lockScope = activeLock.namedItem( "lockscope" ).toElement();
1069  TQDomElement lockType = activeLock.namedItem( "locktype" ).toElement();
1070  TQDomElement lockDepth = activeLock.namedItem( "depth" ).toElement();
1071  // optional
1072  TQDomElement lockOwner = activeLock.namedItem( "owner" ).toElement();
1073  TQDomElement lockTimeout = activeLock.namedItem( "timeout" ).toElement();
1074  TQDomElement lockToken = activeLock.namedItem( "locktoken" ).toElement();
1075 
1076  if ( !lockScope.isNull() && !lockType.isNull() && !lockDepth.isNull() )
1077  {
1078  // lock was properly specified
1079  lockCount++;
1080  TQString scope = lockScope.firstChild().toElement().tagName();
1081  TQString type = lockType.firstChild().toElement().tagName();
1082  TQString depth = lockDepth.text();
1083 
1084  setMetaData( TQString("davLockScope%1").arg( lockCount ), scope );
1085  setMetaData( TQString("davLockType%1").arg( lockCount ), type );
1086  setMetaData( TQString("davLockDepth%1").arg( lockCount ), depth );
1087 
1088  if ( !lockOwner.isNull() )
1089  setMetaData( TQString("davLockOwner%1").arg( lockCount ), lockOwner.text() );
1090 
1091  if ( !lockTimeout.isNull() )
1092  setMetaData( TQString("davLockTimeout%1").arg( lockCount ), lockTimeout.text() );
1093 
1094  if ( !lockToken.isNull() )
1095  {
1096  TQDomElement tokenVal = lockScope.namedItem( "href" ).toElement();
1097  if ( !tokenVal.isNull() )
1098  setMetaData( TQString("davLockToken%1").arg( lockCount ), tokenVal.text() );
1099  }
1100  }
1101  }
1102 }
1103 
1104 long HTTPProtocol::parseDateTime( const TQString& input, const TQString& type )
1105 {
1106  if ( type == "dateTime.tz" )
1107  {
1108  return KRFCDate::parseDateISO8601( input );
1109  }
1110  else if ( type == "dateTime.rfc1123" )
1111  {
1112  return KRFCDate::parseDate( input );
1113  }
1114 
1115  // format not advertised... try to parse anyway
1116  time_t time = KRFCDate::parseDate( input );
1117  if ( time != 0 )
1118  return time;
1119 
1120  return KRFCDate::parseDateISO8601( input );
1121 }
1122 
1123 TQString HTTPProtocol::davProcessLocks()
1124 {
1125  if ( hasMetaData( "davLockCount" ) )
1126  {
1127  TQString response("If:");
1128  int numLocks;
1129  numLocks = metaData( "davLockCount" ).toInt();
1130  bool bracketsOpen = false;
1131  for ( int i = 0; i < numLocks; i++ )
1132  {
1133  if ( hasMetaData( TQString("davLockToken%1").arg(i) ) )
1134  {
1135  if ( hasMetaData( TQString("davLockURL%1").arg(i) ) )
1136  {
1137  if ( bracketsOpen )
1138  {
1139  response += ")";
1140  bracketsOpen = false;
1141  }
1142  response += " <" + metaData( TQString("davLockURL%1").arg(i) ) + ">";
1143  }
1144 
1145  if ( !bracketsOpen )
1146  {
1147  response += " (";
1148  bracketsOpen = true;
1149  }
1150  else
1151  {
1152  response += " ";
1153  }
1154 
1155  if ( hasMetaData( TQString("davLockNot%1").arg(i) ) )
1156  response += "Not ";
1157 
1158  response += "<" + metaData( TQString("davLockToken%1").arg(i) ) + ">";
1159  }
1160  }
1161 
1162  if ( bracketsOpen )
1163  response += ")";
1164 
1165  response += "\r\n";
1166  return response;
1167  }
1168 
1169  return TQString::null;
1170 }
1171 
1172 bool HTTPProtocol::davHostOk()
1173 {
1174  // FIXME needs to be reworked. Switched off for now.
1175  return true;
1176 
1177  // cached?
1178  if ( m_davHostOk )
1179  {
1180  kdDebug(7113) << "(" << m_pid << ") " << k_funcinfo << " true" << endl;
1181  return true;
1182  }
1183  else if ( m_davHostUnsupported )
1184  {
1185  kdDebug(7113) << "(" << m_pid << ") " << k_funcinfo << " false" << endl;
1186  davError( -2 );
1187  return false;
1188  }
1189 
1190  m_request.method = HTTP_OPTIONS;
1191 
1192  // query the server's capabilities generally, not for a specific URL
1193  m_request.path = "*";
1194  m_request.query = TQString::null;
1195  m_request.cache = CC_Reload;
1196  m_request.doProxy = m_bUseProxy;
1197 
1198  // clear davVersions variable, which holds the response to the DAV: header
1199  m_davCapabilities.clear();
1200 
1201  retrieveHeader(false);
1202 
1203  if (m_davCapabilities.count())
1204  {
1205  for (uint i = 0; i < m_davCapabilities.count(); i++)
1206  {
1207  bool ok;
1208  uint verNo = m_davCapabilities[i].toUInt(&ok);
1209  if (ok && verNo > 0 && verNo < 3)
1210  {
1211  m_davHostOk = true;
1212  kdDebug(7113) << "Server supports DAV version " << verNo << "." << endl;
1213  }
1214  }
1215 
1216  if ( m_davHostOk )
1217  return true;
1218  }
1219 
1220  m_davHostUnsupported = true;
1221  davError( -2 );
1222  return false;
1223 }
1224 
1225 // This function is for closing retrieveHeader( false ); requests
1226 // Required because there may or may not be further info expected
1227 void HTTPProtocol::davFinished()
1228 {
1229  // TODO: Check with the DAV extension developers
1230  httpClose(m_bKeepAlive);
1231  finished();
1232 }
1233 
1234 void HTTPProtocol::mkdir( const KURL& url, int )
1235 {
1236  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::mkdir " << url.prettyURL()
1237  << endl;
1238 
1239  if ( !checkRequestURL( url ) )
1240  return;
1241 
1242  m_request.method = DAV_MKCOL;
1243  m_request.path = url.path();
1244  m_request.query = TQString::null;
1245  m_request.cache = CC_Reload;
1246  m_request.doProxy = m_bUseProxy;
1247 
1248  retrieveHeader( false );
1249 
1250  if ( m_responseCode == 201 )
1251  davFinished();
1252  else
1253  davError();
1254 }
1255 
1256 void HTTPProtocol::get( const KURL& url )
1257 {
1258  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::get " << url.prettyURL()
1259  << endl;
1260 
1261  if ( !checkRequestURL( url ) )
1262  return;
1263 
1264  m_request.method = HTTP_GET;
1265  m_request.path = url.path();
1266  m_request.query = url.query();
1267 
1268  TQString tmp = metaData("cache");
1269  if (!tmp.isEmpty())
1270  m_request.cache = parseCacheControl(tmp);
1271  else
1272  m_request.cache = DEFAULT_CACHE_CONTROL;
1273 
1274  m_request.passwd = url.pass();
1275  m_request.user = url.user();
1276  m_request.doProxy = m_bUseProxy;
1277 
1278  retrieveContent();
1279 }
1280 
1281 void HTTPProtocol::put( const KURL &url, int, bool overwrite, bool)
1282 {
1283  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::put " << url.prettyURL()
1284  << endl;
1285 
1286  if ( !checkRequestURL( url ) )
1287  return;
1288 
1289  // Webdav hosts are capable of observing overwrite == false
1290  if (!overwrite && m_protocol.left(6) == "webdav") {
1291  // check to make sure this host supports WebDAV
1292  if ( !davHostOk() )
1293  return;
1294 
1295  TQCString request;
1296  request = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
1297  "<D:propfind xmlns:D=\"DAV:\"><D:prop>"
1298  "<D:creationdate/>"
1299  "<D:getcontentlength/>"
1300  "<D:displayname/>"
1301  "<D:resourcetype/>"
1302  "</D:prop></D:propfind>";
1303 
1304  davSetRequest( request );
1305 
1306  // WebDAV Stat or List...
1307  m_request.method = DAV_PROPFIND;
1308  m_request.query = TQString::null;
1309  m_request.cache = CC_Reload;
1310  m_request.doProxy = m_bUseProxy;
1311  m_request.davData.depth = 0;
1312 
1313  retrieveContent(true);
1314 
1315  if (m_responseCode == 207) {
1316  error(ERR_FILE_ALREADY_EXIST, TQString::null);
1317  return;
1318  }
1319 
1320  m_bError = false;
1321  }
1322 
1323  m_request.method = HTTP_PUT;
1324  m_request.path = url.path();
1325  m_request.query = TQString::null;
1326  m_request.cache = CC_Reload;
1327  m_request.doProxy = m_bUseProxy;
1328 
1329  retrieveHeader( false );
1330 
1331  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::put error = " << m_bError << endl;
1332  if (m_bError)
1333  return;
1334 
1335  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::put responseCode = " << m_responseCode << endl;
1336 
1337  httpClose(false); // Always close connection.
1338 
1339  if ( (m_responseCode >= 200) && (m_responseCode < 300) )
1340  finished();
1341  else
1342  httpError();
1343 }
1344 
1345 void HTTPProtocol::copy( const KURL& src, const KURL& dest, int, bool overwrite )
1346 {
1347  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::copy " << src.prettyURL()
1348  << " -> " << dest.prettyURL() << endl;
1349 
1350  if ( !checkRequestURL( dest ) || !checkRequestURL( src ) )
1351  return;
1352 
1353  // destination has to be "http(s)://..."
1354  KURL newDest = dest;
1355  if (newDest.protocol() == "webdavs")
1356  newDest.setProtocol("https");
1357  else
1358  newDest.setProtocol("http");
1359 
1360  m_request.method = DAV_COPY;
1361  m_request.path = src.path();
1362  m_request.davData.desturl = newDest.url();
1363  m_request.davData.overwrite = overwrite;
1364  m_request.query = TQString::null;
1365  m_request.cache = CC_Reload;
1366  m_request.doProxy = m_bUseProxy;
1367 
1368  retrieveHeader( false );
1369 
1370  // The server returns a HTTP/1.1 201 Created or 204 No Content on successful completion
1371  if ( m_responseCode == 201 || m_responseCode == 204 )
1372  davFinished();
1373  else
1374  davError();
1375 }
1376 
1377 void HTTPProtocol::rename( const KURL& src, const KURL& dest, bool overwrite )
1378 {
1379  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::rename " << src.prettyURL()
1380  << " -> " << dest.prettyURL() << endl;
1381 
1382  if ( !checkRequestURL( dest ) || !checkRequestURL( src ) )
1383  return;
1384 
1385  // destination has to be "http://..."
1386  KURL newDest = dest;
1387  if (newDest.protocol() == "webdavs")
1388  newDest.setProtocol("https");
1389  else
1390  newDest.setProtocol("http");
1391 
1392  m_request.method = DAV_MOVE;
1393  m_request.path = src.path();
1394  m_request.davData.desturl = newDest.url();
1395  m_request.davData.overwrite = overwrite;
1396  m_request.query = TQString::null;
1397  m_request.cache = CC_Reload;
1398  m_request.doProxy = m_bUseProxy;
1399 
1400  retrieveHeader( false );
1401 
1402  if ( m_responseCode == 301 )
1403  {
1404  // Work around strict Apache-2 WebDAV implementation which refuses to cooperate
1405  // with webdav://host/directory, instead requiring webdav://host/directory/
1406  // (strangely enough it accepts Destination: without a trailing slash)
1407 
1408  if (m_redirectLocation.protocol() == "https")
1409  m_redirectLocation.setProtocol("webdavs");
1410  else
1411  m_redirectLocation.setProtocol("webdav");
1412 
1413  if ( !checkRequestURL( m_redirectLocation ) )
1414  return;
1415 
1416  m_request.method = DAV_MOVE;
1417  m_request.path = m_redirectLocation.path();
1418  m_request.davData.desturl = newDest.url();
1419  m_request.davData.overwrite = overwrite;
1420  m_request.query = TQString::null;
1421  m_request.cache = CC_Reload;
1422  m_request.doProxy = m_bUseProxy;
1423 
1424  retrieveHeader( false );
1425  }
1426 
1427  if ( m_responseCode == 201 )
1428  davFinished();
1429  else
1430  davError();
1431 }
1432 
1433 void HTTPProtocol::del( const KURL& url, bool )
1434 {
1435  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::del " << url.prettyURL()
1436  << endl;
1437 
1438  if ( !checkRequestURL( url ) )
1439  return;
1440 
1441  m_request.method = HTTP_DELETE;
1442  m_request.path = url.path();
1443  m_request.query = TQString::null;
1444  m_request.cache = CC_Reload;
1445  m_request.doProxy = m_bUseProxy;
1446 
1447  retrieveHeader( false );
1448 
1449  // The server returns a HTTP/1.1 200 Ok or HTTP/1.1 204 No Content
1450  // on successful completion
1451  if ( m_responseCode == 200 || m_responseCode == 204 )
1452  davFinished();
1453  else
1454  davError();
1455 }
1456 
1457 void HTTPProtocol::post( const KURL& url )
1458 {
1459  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::post "
1460  << url.prettyURL() << endl;
1461 
1462  if ( !checkRequestURL( url ) )
1463  return;
1464 
1465  m_request.method = HTTP_POST;
1466  m_request.path = url.path();
1467  m_request.query = url.query();
1468  m_request.cache = CC_Reload;
1469  m_request.doProxy = m_bUseProxy;
1470 
1471  retrieveContent();
1472 }
1473 
1474 void HTTPProtocol::davLock( const KURL& url, const TQString& scope,
1475  const TQString& type, const TQString& owner )
1476 {
1477  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::davLock "
1478  << url.prettyURL() << endl;
1479 
1480  if ( !checkRequestURL( url ) )
1481  return;
1482 
1483  m_request.method = DAV_LOCK;
1484  m_request.path = url.path();
1485  m_request.query = TQString::null;
1486  m_request.cache = CC_Reload;
1487  m_request.doProxy = m_bUseProxy;
1488 
1489  /* Create appropriate lock XML request. */
1490  TQDomDocument lockReq;
1491 
1492  TQDomElement lockInfo = lockReq.createElementNS( "DAV:", "lockinfo" );
1493  lockReq.appendChild( lockInfo );
1494 
1495  TQDomElement lockScope = lockReq.createElement( "lockscope" );
1496  lockInfo.appendChild( lockScope );
1497 
1498  lockScope.appendChild( lockReq.createElement( scope ) );
1499 
1500  TQDomElement lockType = lockReq.createElement( "locktype" );
1501  lockInfo.appendChild( lockType );
1502 
1503  lockType.appendChild( lockReq.createElement( type ) );
1504 
1505  if ( !owner.isNull() ) {
1506  TQDomElement ownerElement = lockReq.createElement( "owner" );
1507  lockReq.appendChild( ownerElement );
1508 
1509  TQDomElement ownerHref = lockReq.createElement( "href" );
1510  ownerElement.appendChild( ownerHref );
1511 
1512  ownerHref.appendChild( lockReq.createTextNode( owner ) );
1513  }
1514 
1515  // insert the document into the POST buffer
1516  m_bufPOST = lockReq.toCString();
1517 
1518  retrieveContent( true );
1519 
1520  if ( m_responseCode == 200 ) {
1521  // success
1522  TQDomDocument multiResponse;
1523  multiResponse.setContent( m_bufWebDavData, true );
1524 
1525  TQDomElement prop = multiResponse.documentElement().namedItem( "prop" ).toElement();
1526 
1527  TQDomElement lockdiscovery = prop.namedItem( "lockdiscovery" ).toElement();
1528 
1529  uint lockCount = 0;
1530  davParseActiveLocks( lockdiscovery.elementsByTagName( "activelock" ), lockCount );
1531 
1532  setMetaData( "davLockCount", TQString("%1").arg( lockCount ) );
1533 
1534  finished();
1535 
1536  } else
1537  davError();
1538 }
1539 
1540 void HTTPProtocol::davUnlock( const KURL& url )
1541 {
1542  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::davUnlock "
1543  << url.prettyURL() << endl;
1544 
1545  if ( !checkRequestURL( url ) )
1546  return;
1547 
1548  m_request.method = DAV_UNLOCK;
1549  m_request.path = url.path();
1550  m_request.query = TQString::null;
1551  m_request.cache = CC_Reload;
1552  m_request.doProxy = m_bUseProxy;
1553 
1554  retrieveContent( true );
1555 
1556  if ( m_responseCode == 200 )
1557  finished();
1558  else
1559  davError();
1560 }
1561 
1562 TQString HTTPProtocol::davError( int code /* = -1 */, TQString url )
1563 {
1564  bool callError = false;
1565  if ( code == -1 ) {
1566  code = m_responseCode;
1567  callError = true;
1568  }
1569  if ( code == -2 ) {
1570  callError = true;
1571  }
1572 
1573  // Huh? This looks like inverted logic to me (it doesn't make sense to me as
1574  // written), but I'm only fixing the CVE now. -- Kevin Kofler
1575  if ( !url.isNull() )
1576  url = m_request.url.prettyURL();
1577 
1578  TQString action, errorString;
1579  TDEIO::Error kError;
1580 
1581  // for 412 Precondition Failed
1582  TQString ow = i18n( "Otherwise, the request would have succeeded." );
1583 
1584  switch ( m_request.method ) {
1585  case DAV_PROPFIND:
1586  action = i18n( "retrieve property values" );
1587  break;
1588  case DAV_PROPPATCH:
1589  action = i18n( "set property values" );
1590  break;
1591  case DAV_MKCOL:
1592  action = i18n( "create the requested folder" );
1593  break;
1594  case DAV_COPY:
1595  action = i18n( "copy the specified file or folder" );
1596  break;
1597  case DAV_MOVE:
1598  action = i18n( "move the specified file or folder" );
1599  break;
1600  case DAV_SEARCH:
1601  action = i18n( "search in the specified folder" );
1602  break;
1603  case DAV_LOCK:
1604  action = i18n( "lock the specified file or folder" );
1605  break;
1606  case DAV_UNLOCK:
1607  action = i18n( "unlock the specified file or folder" );
1608  break;
1609  case HTTP_DELETE:
1610  action = i18n( "delete the specified file or folder" );
1611  break;
1612  case HTTP_OPTIONS:
1613  action = i18n( "query the server's capabilities" );
1614  break;
1615  case HTTP_GET:
1616  action = i18n( "retrieve the contents of the specified file or folder" );
1617  break;
1618  case HTTP_PUT:
1619  case HTTP_POST:
1620  case HTTP_HEAD:
1621  default:
1622  // this should not happen, this function is for webdav errors only
1623  Q_ASSERT(0);
1624  }
1625 
1626  // default error message if the following code fails
1627  kError = ERR_INTERNAL;
1628  errorString = i18n("An unexpected error (%1) occurred while attempting to %2.")
1629  .arg( code ).arg( action );
1630 
1631  switch ( code )
1632  {
1633  case -2:
1634  // internal error: OPTIONS request did not specify DAV compliance
1635  kError = ERR_UNSUPPORTED_PROTOCOL;
1636  errorString = i18n("The server does not support the WebDAV protocol.");
1637  break;
1638  case 207:
1639  // 207 Multi-status
1640  {
1641  // our error info is in the returned XML document.
1642  // retrieve the XML document
1643 
1644  // there was an error retrieving the XML document.
1645  // ironic, eh?
1646  if ( !readBody( true ) && m_bError )
1647  return TQString::null;
1648 
1649  TQStringList errors;
1650  TQDomDocument multiResponse;
1651 
1652  multiResponse.setContent( m_bufWebDavData, true );
1653 
1654  TQDomElement multistatus = multiResponse.documentElement().namedItem( "multistatus" ).toElement();
1655 
1656  TQDomNodeList responses = multistatus.elementsByTagName( "response" );
1657 
1658  for (uint i = 0; i < responses.count(); i++)
1659  {
1660  int errCode;
1661  TQString errUrl;
1662 
1663  TQDomElement response = responses.item(i).toElement();
1664  TQDomElement code = response.namedItem( "status" ).toElement();
1665 
1666  if ( !code.isNull() )
1667  {
1668  errCode = codeFromResponse( code.text() );
1669  TQDomElement href = response.namedItem( "href" ).toElement();
1670  if ( !href.isNull() )
1671  errUrl = href.text();
1672  errors << davError( errCode, errUrl );
1673  }
1674  }
1675 
1676  //kError = ERR_SLAVE_DEFINED;
1677  errorString = i18n("An error occurred while attempting to %1, %2. A "
1678  "summary of the reasons is below.<ul>").arg( action ).arg( url );
1679 
1680  for ( TQStringList::Iterator it = errors.begin(); it != errors.end(); ++it )
1681  errorString += "<li>" + *it + "</li>";
1682 
1683  errorString += "</ul>";
1684  }
1685  case 403:
1686  case 500: // hack: Apache mod_dav returns this instead of 403 (!)
1687  // 403 Forbidden
1688  kError = ERR_ACCESS_DENIED;
1689  errorString = i18n("Access was denied while attempting to %1.").arg( action );
1690  break;
1691  case 405:
1692  // 405 Method Not Allowed
1693  if ( m_request.method == DAV_MKCOL )
1694  {
1695  kError = ERR_DIR_ALREADY_EXIST;
1696  errorString = i18n("The specified folder already exists.");
1697  }
1698  break;
1699  case 409:
1700  // 409 Conflict
1701  kError = ERR_ACCESS_DENIED;
1702  errorString = i18n("A resource cannot be created at the destination "
1703  "until one or more intermediate collections (folders) "
1704  "have been created.");
1705  break;
1706  case 412:
1707  // 412 Precondition failed
1708  if ( m_request.method == DAV_COPY || m_request.method == DAV_MOVE )
1709  {
1710  kError = ERR_ACCESS_DENIED;
1711  errorString = i18n("The server was unable to maintain the liveness of "
1712  "the properties listed in the propertybehavior XML "
1713  "element or you attempted to overwrite a file while "
1714  "requesting that files are not overwritten. %1")
1715  .arg( ow );
1716 
1717  }
1718  else if ( m_request.method == DAV_LOCK )
1719  {
1720  kError = ERR_ACCESS_DENIED;
1721  errorString = i18n("The requested lock could not be granted. %1").arg( ow );
1722  }
1723  break;
1724  case 415:
1725  // 415 Unsupported Media Type
1726  kError = ERR_ACCESS_DENIED;
1727  errorString = i18n("The server does not support the request type of the body.");
1728  break;
1729  case 423:
1730  // 423 Locked
1731  kError = ERR_ACCESS_DENIED;
1732  errorString = i18n("Unable to %1 because the resource is locked.").arg( action );
1733  break;
1734  case 425:
1735  // 424 Failed Dependency
1736  errorString = i18n("This action was prevented by another error.");
1737  break;
1738  case 502:
1739  // 502 Bad Gateway
1740  if ( m_request.method == DAV_COPY || m_request.method == DAV_MOVE )
1741  {
1742  kError = ERR_WRITE_ACCESS_DENIED;
1743  errorString = i18n("Unable to %1 because the destination server refuses "
1744  "to accept the file or folder.").arg( action );
1745  }
1746  break;
1747  case 507:
1748  // 507 Insufficient Storage
1749  kError = ERR_DISK_FULL;
1750  errorString = i18n("The destination resource does not have sufficient space "
1751  "to record the state of the resource after the execution "
1752  "of this method.");
1753  break;
1754  }
1755 
1756  // if ( kError != ERR_SLAVE_DEFINED )
1757  //errorString += " (" + url + ")";
1758 
1759  if ( callError )
1760  error( ERR_SLAVE_DEFINED, errorString );
1761 
1762  return errorString;
1763 }
1764 
1765 void HTTPProtocol::httpError()
1766 {
1767  TQString action, errorString;
1768  TDEIO::Error kError;
1769 
1770  switch ( m_request.method ) {
1771  case HTTP_PUT:
1772  action = i18n( "upload %1" ).arg(m_request.url.prettyURL());
1773  break;
1774  default:
1775  // this should not happen, this function is for http errors only
1776  Q_ASSERT(0);
1777  }
1778 
1779  // default error message if the following code fails
1780  kError = ERR_INTERNAL;
1781  errorString = i18n("An unexpected error (%1) occurred while attempting to %2.")
1782  .arg( m_responseCode ).arg( action );
1783 
1784  switch ( m_responseCode )
1785  {
1786  case 403:
1787  case 405:
1788  case 500: // hack: Apache mod_dav returns this instead of 403 (!)
1789  // 403 Forbidden
1790  // 405 Method Not Allowed
1791  kError = ERR_ACCESS_DENIED;
1792  errorString = i18n("Access was denied while attempting to %1.").arg( action );
1793  break;
1794  case 409:
1795  // 409 Conflict
1796  kError = ERR_ACCESS_DENIED;
1797  errorString = i18n("A resource cannot be created at the destination "
1798  "until one or more intermediate collections (folders) "
1799  "have been created.");
1800  break;
1801  case 423:
1802  // 423 Locked
1803  kError = ERR_ACCESS_DENIED;
1804  errorString = i18n("Unable to %1 because the resource is locked.").arg( action );
1805  break;
1806  case 502:
1807  // 502 Bad Gateway
1808  kError = ERR_WRITE_ACCESS_DENIED;
1809  errorString = i18n("Unable to %1 because the destination server refuses "
1810  "to accept the file or folder.").arg( action );
1811  break;
1812  case 507:
1813  // 507 Insufficient Storage
1814  kError = ERR_DISK_FULL;
1815  errorString = i18n("The destination resource does not have sufficient space "
1816  "to record the state of the resource after the execution "
1817  "of this method.");
1818  break;
1819  }
1820 
1821  // if ( kError != ERR_SLAVE_DEFINED )
1822  //errorString += " (" + url + ")";
1823 
1824  error( ERR_SLAVE_DEFINED, errorString );
1825 }
1826 
1827 bool HTTPProtocol::isOffline(const KURL &url)
1828 {
1829  const int NetWorkStatusUnknown = 1;
1830  const int NetWorkStatusOnline = 8;
1831  TQCString replyType;
1832  TQByteArray params;
1833  TQByteArray reply;
1834 
1835  TQDataStream stream(params, IO_WriteOnly);
1836 
1837  if ( url.host() == TQString::fromLatin1("localhost") || url.host() == TQString::fromLatin1("127.0.0.1") || url.host() == TQString::fromLatin1("::") ) {
1838  return false;
1839  }
1840  if ( dcopClient()->call( "kded", "networkstatus", "status()",
1841  params, replyType, reply ) && (replyType == "int") )
1842  {
1843  int result;
1844  TQDataStream stream2( reply, IO_ReadOnly );
1845  stream2 >> result;
1846  kdDebug(7113) << "(" << m_pid << ") networkstatus status = " << result << endl;
1847  return (result != NetWorkStatusUnknown) && (result != NetWorkStatusOnline);
1848  }
1849  kdDebug(7113) << "(" << m_pid << ") networkstatus <unreachable>" << endl;
1850  return false; // On error, assume we are online
1851 }
1852 
1853 void HTTPProtocol::multiGet(const TQByteArray &data)
1854 {
1855  TQDataStream stream(data, IO_ReadOnly);
1856  TQ_UINT32 n;
1857  stream >> n;
1858 
1859  kdDebug(7113) << "(" << m_pid << ") HTTPProtcool::multiGet n = " << n << endl;
1860 
1861  HTTPRequest saveRequest;
1862  if (m_bBusy)
1863  saveRequest = m_request;
1864 
1865 // m_requestQueue.clear();
1866  for(unsigned i = 0; i < n; i++)
1867  {
1868  KURL url;
1869  stream >> url >> mIncomingMetaData;
1870 
1871  if ( !checkRequestURL( url ) )
1872  continue;
1873 
1874  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::multi_get " << url.prettyURL() << endl;
1875 
1876  m_request.method = HTTP_GET;
1877  m_request.path = url.path();
1878  m_request.query = url.query();
1879  TQString tmp = metaData("cache");
1880  if (!tmp.isEmpty())
1881  m_request.cache = parseCacheControl(tmp);
1882  else
1883  m_request.cache = DEFAULT_CACHE_CONTROL;
1884 
1885  m_request.passwd = url.pass();
1886  m_request.user = url.user();
1887  m_request.doProxy = m_bUseProxy;
1888 
1889  HTTPRequest *newRequest = new HTTPRequest(m_request);
1890  m_requestQueue.append(newRequest);
1891  }
1892 
1893  if (m_bBusy)
1894  m_request = saveRequest;
1895 
1896  if (!m_bBusy)
1897  {
1898  m_bBusy = true;
1899  while(!m_requestQueue.isEmpty())
1900  {
1901  HTTPRequest *request = m_requestQueue.take(0);
1902  m_request = *request;
1903  delete request;
1904  retrieveContent();
1905  }
1906  m_bBusy = false;
1907  }
1908 }
1909 
1910 ssize_t HTTPProtocol::write (const void *_buf, size_t nbytes)
1911 {
1912  int bytes_sent = 0;
1913  const char* buf = static_cast<const char*>(_buf);
1914  while ( nbytes > 0 )
1915  {
1916  int n = TCPSlaveBase::write(buf, nbytes);
1917 
1918  if ( n <= 0 )
1919  {
1920  // remote side closed connection ?
1921  if ( n == 0 )
1922  break;
1923  // a valid exception(s) occurred, let's retry...
1924  if (n < 0 && ((errno == EINTR) || (errno == EAGAIN)))
1925  continue;
1926  // some other error occurred ?
1927  return -1;
1928  }
1929 
1930  nbytes -= n;
1931  buf += n;
1932  bytes_sent += n;
1933  }
1934 
1935  return bytes_sent;
1936 }
1937 
1938 void HTTPProtocol::setRewindMarker()
1939 {
1940  m_rewindCount = 0;
1941 }
1942 
1943 void HTTPProtocol::rewind()
1944 {
1945  m_linePtrUnget = m_rewindBuf,
1946  m_lineCountUnget = m_rewindCount;
1947  m_rewindCount = 0;
1948 }
1949 
1950 
1951 char *HTTPProtocol::gets (char *s, int size)
1952 {
1953  int len=0;
1954  char *buf=s;
1955  char mybuf[2]={0,0};
1956 
1957  while (len < size)
1958  {
1959  read(mybuf, 1);
1960  if (m_bEOF)
1961  break;
1962 
1963  if (m_rewindCount < sizeof(m_rewindBuf))
1964  m_rewindBuf[m_rewindCount++] = *mybuf;
1965 
1966  if (*mybuf == '\r') // Ignore!
1967  continue;
1968 
1969  if ((*mybuf == '\n') || !*mybuf)
1970  break;
1971 
1972  *buf++ = *mybuf;
1973  len++;
1974  }
1975 
1976  *buf=0;
1977  return s;
1978 }
1979 
1980 ssize_t HTTPProtocol::read (void *b, size_t nbytes)
1981 {
1982  ssize_t ret = 0;
1983 
1984  if (m_lineCountUnget > 0)
1985  {
1986  ret = ( nbytes < m_lineCountUnget ? nbytes : m_lineCountUnget );
1987  m_lineCountUnget -= ret;
1988  memcpy(b, m_linePtrUnget, ret);
1989  m_linePtrUnget += ret;
1990 
1991  return ret;
1992  }
1993 
1994  if (m_lineCount > 0)
1995  {
1996  ret = ( nbytes < m_lineCount ? nbytes : m_lineCount );
1997  m_lineCount -= ret;
1998  memcpy(b, m_linePtr, ret);
1999  m_linePtr += ret;
2000  return ret;
2001  }
2002 
2003  if (nbytes == 1)
2004  {
2005  ret = read(m_lineBuf, 1024); // Read into buffer
2006  m_linePtr = m_lineBuf;
2007  if (ret <= 0)
2008  {
2009  m_lineCount = 0;
2010  return ret;
2011  }
2012  m_lineCount = ret;
2013  return read(b, 1); // Read from buffer
2014  }
2015 
2016  do
2017  {
2018  ret = TCPSlaveBase::read( b, nbytes);
2019  if (ret == 0)
2020  m_bEOF = true;
2021 
2022  } while ((ret == -1) && (errno == EAGAIN || errno == EINTR));
2023 
2024  return ret;
2025 }
2026 
2027 void HTTPProtocol::httpCheckConnection()
2028 {
2029  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpCheckConnection: " <<
2030  " Socket status: " << m_iSock <<
2031  " Keep Alive: " << m_bKeepAlive <<
2032  " First: " << m_bFirstRequest << endl;
2033 
2034  if ( !m_bFirstRequest && (m_iSock != -1) )
2035  {
2036  bool closeDown = false;
2037  if ( !isConnectionValid())
2038  {
2039  kdDebug(7113) << "(" << m_pid << ") Connection lost!" << endl;
2040  closeDown = true;
2041  }
2042  else if ( m_request.method != HTTP_GET )
2043  {
2044  closeDown = true;
2045  }
2046  else if ( !m_state.doProxy && !m_request.doProxy )
2047  {
2048  if (m_state.hostname != m_request.hostname ||
2049  m_state.port != m_request.port ||
2050  m_state.user != m_request.user ||
2051  m_state.passwd != m_request.passwd)
2052  closeDown = true;
2053  }
2054  else
2055  {
2056  // Keep the connection to the proxy.
2057  if ( !(m_request.doProxy && m_state.doProxy) )
2058  closeDown = true;
2059  }
2060 
2061  if (closeDown)
2062  httpCloseConnection();
2063  }
2064 
2065  // Let's update our current state
2066  m_state.hostname = m_request.hostname;
2067  m_state.encoded_hostname = m_request.encoded_hostname;
2068  m_state.port = m_request.port;
2069  m_state.user = m_request.user;
2070  m_state.passwd = m_request.passwd;
2071  m_state.doProxy = m_request.doProxy;
2072 }
2073 
2074 bool HTTPProtocol::httpOpenConnection()
2075 {
2076  int errCode;
2077  TQString errMsg;
2078 
2079  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpOpenConnection" << endl;
2080 
2081  setBlockConnection( true );
2082  // tdeio_http uses its own proxying:
2083  KSocks::self()->disableSocks();
2084 
2085  if ( m_state.doProxy )
2086  {
2087  TQString proxy_host = m_proxyURL.host();
2088  int proxy_port = m_proxyURL.port();
2089 
2090  kdDebug(7113) << "(" << m_pid << ") Connecting to proxy server: "
2091  << proxy_host << ", port: " << proxy_port << endl;
2092 
2093  infoMessage( i18n("Connecting to %1...").arg(m_state.hostname) );
2094 
2095  setConnectTimeout( m_proxyConnTimeout );
2096 
2097  if ( !connectToHost(proxy_host, proxy_port, false) )
2098  {
2099  if (userAborted()) {
2100  error(ERR_NO_CONTENT, "");
2101  return false;
2102  }
2103 
2104  switch ( connectResult() )
2105  {
2106  case IO_LookupError:
2107  errMsg = proxy_host;
2108  errCode = ERR_UNKNOWN_PROXY_HOST;
2109  break;
2110  case IO_TimeOutError:
2111  errMsg = i18n("Proxy %1 at port %2").arg(proxy_host).arg(proxy_port);
2112  errCode = ERR_SERVER_TIMEOUT;
2113  break;
2114  default:
2115  errMsg = i18n("Proxy %1 at port %2").arg(proxy_host).arg(proxy_port);
2116  errCode = ERR_COULD_NOT_CONNECT;
2117  }
2118  error( errCode, errMsg );
2119  return false;
2120  }
2121  }
2122  else
2123  {
2124  // Apparently we don't want a proxy. let's just connect directly
2125  setConnectTimeout(m_remoteConnTimeout);
2126 
2127  if ( !connectToHost(m_state.hostname, m_state.port, false ) )
2128  {
2129  if (userAborted()) {
2130  error(ERR_NO_CONTENT, "");
2131  return false;
2132  }
2133 
2134  switch ( connectResult() )
2135  {
2136  case IO_LookupError:
2137  errMsg = m_state.hostname;
2138  errCode = ERR_UNKNOWN_HOST;
2139  break;
2140  case IO_TimeOutError:
2141  errMsg = i18n("Connection was to %1 at port %2").arg(m_state.hostname).arg(m_state.port);
2142  errCode = ERR_SERVER_TIMEOUT;
2143  break;
2144  default:
2145  errCode = ERR_COULD_NOT_CONNECT;
2146  if (m_state.port != m_iDefaultPort)
2147  errMsg = i18n("%1 (port %2)").arg(m_state.hostname).arg(m_state.port);
2148  else
2149  errMsg = m_state.hostname;
2150  }
2151  error( errCode, errMsg );
2152  return false;
2153  }
2154  }
2155 
2156  // Set our special socket option!!
2157  int on = 1;
2158  (void) setsockopt( m_iSock, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on) );
2159 
2160  m_bFirstRequest = true;
2161 
2162  connected();
2163  return true;
2164 }
2165 
2166 
2189 bool HTTPProtocol::httpOpen()
2190 {
2191  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpOpen" << endl;
2192 
2193  // Cannot have an https request without the m_bIsSSL being set! This can
2194  // only happen if TCPSlaveBase::InitializeSSL() function failed in which it
2195  // means the current installation does not support SSL...
2196  if ( (m_protocol == "https" || m_protocol == "webdavs") && !m_bIsSSL )
2197  {
2198  error( ERR_UNSUPPORTED_PROTOCOL, m_protocol );
2199  return false;
2200  }
2201 
2202  m_request.fcache = 0;
2203  m_request.bCachedRead = false;
2204  m_request.bCachedWrite = false;
2205  m_request.bMustRevalidate = false;
2206  m_request.expireDate = 0;
2207  m_request.creationDate = 0;
2208 
2209  if (m_request.bUseCache)
2210  {
2211  m_request.fcache = checkCacheEntry( );
2212 
2213  bool bCacheOnly = (m_request.cache == TDEIO::CC_CacheOnly);
2214  bool bOffline = isOffline(m_request.doProxy ? m_proxyURL : m_request.url);
2215  if (bOffline && (m_request.cache != TDEIO::CC_Reload))
2216  m_request.cache = TDEIO::CC_CacheOnly;
2217 
2218  if (m_request.cache == CC_Reload && m_request.fcache)
2219  {
2220  if (m_request.fcache)
2221  fclose(m_request.fcache);
2222  m_request.fcache = 0;
2223  }
2224  if ((m_request.cache == TDEIO::CC_CacheOnly) || (m_request.cache == TDEIO::CC_Cache))
2225  m_request.bMustRevalidate = false;
2226 
2227  m_request.bCachedWrite = true;
2228 
2229  if (m_request.fcache && !m_request.bMustRevalidate)
2230  {
2231  // Cache entry is OK.
2232  m_request.bCachedRead = true; // Cache hit.
2233  return true;
2234  }
2235  else if (!m_request.fcache)
2236  {
2237  m_request.bMustRevalidate = false; // Cache miss
2238  }
2239  else
2240  {
2241  // Conditional cache hit. (Validate)
2242  }
2243 
2244  if (bCacheOnly && bOffline)
2245  {
2246  error( ERR_OFFLINE_MODE, m_request.url.prettyURL() );
2247  return false;
2248  }
2249  if (bCacheOnly)
2250  {
2251  error( ERR_DOES_NOT_EXIST, m_request.url.prettyURL() );
2252  return false;
2253  }
2254  if (bOffline)
2255  {
2256  error( ERR_OFFLINE_MODE, m_request.url.prettyURL() );
2257  return false;
2258  }
2259  }
2260 
2261  TQString header;
2262  TQString davHeader;
2263 
2264  bool moreData = false;
2265  bool davData = false;
2266 
2267  // Clear out per-connection settings...
2268  resetConnectionSettings ();
2269 
2270  // Check the validity of the current connection, if one exists.
2271  httpCheckConnection();
2272 
2273  if ( !m_bIsTunneled && m_bNeedTunnel )
2274  {
2275  setEnableSSLTunnel( true );
2276  // We send a HTTP 1.0 header since some proxies refuse HTTP 1.1 and we don't
2277  // need any HTTP 1.1 capabilities for CONNECT - Waba
2278  header = TQString("CONNECT %1:%2 HTTP/1.0"
2279  "\r\n").arg( m_request.encoded_hostname).arg(m_request.port);
2280 
2281  // Identify who you are to the proxy server!
2282  if (!m_request.userAgent.isEmpty())
2283  header += "User-Agent: " + m_request.userAgent + "\r\n";
2284 
2285  /* Add hostname information */
2286  header += "Host: " + m_state.encoded_hostname;
2287 
2288  if (m_state.port != m_iDefaultPort)
2289  header += TQString(":%1").arg(m_state.port);
2290  header += "\r\n";
2291 
2292  header += proxyAuthenticationHeader();
2293  }
2294  else
2295  {
2296  // Determine if this is a POST or GET method
2297  switch (m_request.method)
2298  {
2299  case HTTP_GET:
2300  header = "GET ";
2301  break;
2302  case HTTP_PUT:
2303  header = "PUT ";
2304  moreData = true;
2305  m_request.bCachedWrite = false; // Do not put any result in the cache
2306  break;
2307  case HTTP_POST:
2308  header = "POST ";
2309  moreData = true;
2310  m_request.bCachedWrite = false; // Do not put any result in the cache
2311  break;
2312  case HTTP_HEAD:
2313  header = "HEAD ";
2314  break;
2315  case HTTP_DELETE:
2316  header = "DELETE ";
2317  m_request.bCachedWrite = false; // Do not put any result in the cache
2318  break;
2319  case HTTP_OPTIONS:
2320  header = "OPTIONS ";
2321  m_request.bCachedWrite = false; // Do not put any result in the cache
2322  break;
2323  case DAV_PROPFIND:
2324  header = "PROPFIND ";
2325  davData = true;
2326  davHeader = "Depth: ";
2327  if ( hasMetaData( "davDepth" ) )
2328  {
2329  kdDebug(7113) << "Reading DAV depth from metadata: " << metaData( "davDepth" ) << endl;
2330  davHeader += metaData( "davDepth" );
2331  }
2332  else
2333  {
2334  if ( m_request.davData.depth == 2 )
2335  davHeader += "infinity";
2336  else
2337  davHeader += TQString("%1").arg( m_request.davData.depth );
2338  }
2339  davHeader += "\r\n";
2340  m_request.bCachedWrite = false; // Do not put any result in the cache
2341  break;
2342  case DAV_PROPPATCH:
2343  header = "PROPPATCH ";
2344  davData = true;
2345  m_request.bCachedWrite = false; // Do not put any result in the cache
2346  break;
2347  case DAV_MKCOL:
2348  header = "MKCOL ";
2349  m_request.bCachedWrite = false; // Do not put any result in the cache
2350  break;
2351  case DAV_COPY:
2352  case DAV_MOVE:
2353  header = ( m_request.method == DAV_COPY ) ? "COPY " : "MOVE ";
2354  davHeader = "Destination: " + m_request.davData.desturl;
2355  // infinity depth means copy recursively
2356  // (optional for copy -> but is the desired action)
2357  davHeader += "\r\nDepth: infinity\r\nOverwrite: ";
2358  davHeader += m_request.davData.overwrite ? "T" : "F";
2359  davHeader += "\r\n";
2360  m_request.bCachedWrite = false; // Do not put any result in the cache
2361  break;
2362  case DAV_LOCK:
2363  header = "LOCK ";
2364  davHeader = "Timeout: ";
2365  {
2366  uint timeout = 0;
2367  if ( hasMetaData( "davTimeout" ) )
2368  timeout = metaData( "davTimeout" ).toUInt();
2369  if ( timeout == 0 )
2370  davHeader += "Infinite";
2371  else
2372  davHeader += TQString("Seconds-%1").arg(timeout);
2373  }
2374  davHeader += "\r\n";
2375  m_request.bCachedWrite = false; // Do not put any result in the cache
2376  davData = true;
2377  break;
2378  case DAV_UNLOCK:
2379  header = "UNLOCK ";
2380  davHeader = "Lock-token: " + metaData("davLockToken") + "\r\n";
2381  m_request.bCachedWrite = false; // Do not put any result in the cache
2382  break;
2383  case DAV_SEARCH:
2384  header = "SEARCH ";
2385  davData = true;
2386  m_request.bCachedWrite = false;
2387  break;
2388  case DAV_SUBSCRIBE:
2389  header = "SUBSCRIBE ";
2390  m_request.bCachedWrite = false;
2391  break;
2392  case DAV_UNSUBSCRIBE:
2393  header = "UNSUBSCRIBE ";
2394  m_request.bCachedWrite = false;
2395  break;
2396  case DAV_POLL:
2397  header = "POLL ";
2398  m_request.bCachedWrite = false;
2399  break;
2400  default:
2401  error (ERR_UNSUPPORTED_ACTION, TQString::null);
2402  return false;
2403  }
2404  // DAV_POLL; DAV_NOTIFY
2405 
2406  // format the URI
2407  if (m_state.doProxy && !m_bIsTunneled)
2408  {
2409  KURL u;
2410 
2411  if (m_protocol == "webdav")
2412  u.setProtocol( "http" );
2413  else if (m_protocol == "webdavs" )
2414  u.setProtocol( "https" );
2415  else
2416  u.setProtocol( m_protocol );
2417 
2418  // For all protocols other than the once handled by this io-slave
2419  // append the username. This fixes a long standing bug of ftp io-slave
2420  // logging in anonymously in proxied connections even when the username
2421  // is explicitly specified.
2422  if (m_protocol != "http" && m_protocol != "https" &&
2423  !m_state.user.isEmpty())
2424  u.setUser (m_state.user);
2425 
2426  u.setHost( m_state.hostname );
2427  if (m_state.port != m_iDefaultPort)
2428  u.setPort( m_state.port );
2429  u.setEncodedPathAndQuery( m_request.url.encodedPathAndQuery(0,true) );
2430  header += u.url();
2431  }
2432  else
2433  {
2434  header += m_request.url.encodedPathAndQuery(0, true);
2435  }
2436 
2437  header += " HTTP/1.1\r\n"; /* start header */
2438 
2439  if (!m_request.userAgent.isEmpty())
2440  {
2441  header += "User-Agent: ";
2442  header += m_request.userAgent;
2443  header += "\r\n";
2444  }
2445 
2446  if (!m_request.referrer.isEmpty())
2447  {
2448  header += "Referer: "; //Don't try to correct spelling!
2449  header += m_request.referrer;
2450  header += "\r\n";
2451  }
2452 
2453  if ( m_request.offset > 0 )
2454  {
2455  header += TQString("Range: bytes=%1-\r\n").arg(TDEIO::number(m_request.offset));
2456  kdDebug(7103) << "tdeio_http : Range = " << TDEIO::number(m_request.offset) << endl;
2457  }
2458 
2459  if ( m_request.cache == CC_Reload )
2460  {
2461  /* No caching for reload */
2462  header += "Pragma: no-cache\r\n"; /* for HTTP/1.0 caches */
2463  header += "Cache-control: no-cache\r\n"; /* for HTTP >=1.1 caches */
2464  }
2465 
2466  if (m_request.bMustRevalidate)
2467  {
2468  /* conditional get */
2469  if (!m_request.etag.isEmpty())
2470  header += "If-None-Match: "+m_request.etag+"\r\n";
2471  if (!m_request.lastModified.isEmpty())
2472  header += "If-Modified-Since: "+m_request.lastModified+"\r\n";
2473  }
2474 
2475  header += "Accept: ";
2476  TQString acceptHeader = metaData("accept");
2477  if (!acceptHeader.isEmpty())
2478  header += acceptHeader;
2479  else
2480  header += DEFAULT_ACCEPT_HEADER;
2481  header += "\r\n";
2482 
2483 #ifdef DO_GZIP
2484  if (m_request.allowCompressedPage)
2485  header += "Accept-Encoding: x-gzip, x-deflate, gzip, deflate\r\n";
2486 #endif
2487 
2488  if (!m_request.charsets.isEmpty())
2489  header += "Accept-Charset: " + m_request.charsets + "\r\n";
2490 
2491  if (!m_request.languages.isEmpty())
2492  header += "Accept-Language: " + m_request.languages + "\r\n";
2493 
2494 
2495  /* support for virtual hosts and required by HTTP 1.1 */
2496  header += "Host: " + m_state.encoded_hostname;
2497 
2498  if (m_state.port != m_iDefaultPort)
2499  header += TQString(":%1").arg(m_state.port);
2500  header += "\r\n";
2501 
2502  TQString cookieStr;
2503  TQString cookieMode = metaData("cookies").lower();
2504  if (cookieMode == "none")
2505  {
2506  m_request.cookieMode = HTTPRequest::CookiesNone;
2507  }
2508  else if (cookieMode == "manual")
2509  {
2510  m_request.cookieMode = HTTPRequest::CookiesManual;
2511  cookieStr = metaData("setcookies");
2512  }
2513  else
2514  {
2515  m_request.cookieMode = HTTPRequest::CookiesAuto;
2516  if (m_request.bUseCookiejar)
2517  cookieStr = findCookies( m_request.url.url());
2518  }
2519 
2520  if (!cookieStr.isEmpty())
2521  header += cookieStr + "\r\n";
2522 
2523  TQString customHeader = metaData( "customHTTPHeader" );
2524  if (!customHeader.isEmpty())
2525  {
2526  header += sanitizeCustomHTTPHeader(customHeader);
2527  header += "\r\n";
2528  }
2529 
2530  if (m_request.method == HTTP_POST)
2531  {
2532  header += metaData("content-type");
2533  header += "\r\n";
2534  }
2535 
2536  // Only check for a cached copy if the previous
2537  // response was NOT a 401 or 407.
2538  // no caching for Negotiate auth.
2539  if ( !m_request.bNoAuth && m_responseCode != 401 && m_responseCode != 407 && Authentication != AUTH_Negotiate )
2540  {
2541  kdDebug(7113) << "(" << m_pid << ") Calling checkCachedAuthentication " << endl;
2542  AuthInfo info;
2543  info.url = m_request.url;
2544  info.verifyPath = true;
2545  if ( !m_request.user.isEmpty() )
2546  info.username = m_request.user;
2547  if ( checkCachedAuthentication( info ) && !info.digestInfo.isEmpty() )
2548  {
2549  Authentication = info.digestInfo.startsWith("Basic") ? AUTH_Basic : info.digestInfo.startsWith("NTLM") ? AUTH_NTLM : info.digestInfo.startsWith("Negotiate") ? AUTH_Negotiate : AUTH_Digest ;
2550  m_state.user = info.username;
2551  m_state.passwd = info.password;
2552  m_strRealm = info.realmValue;
2553  if ( Authentication != AUTH_NTLM && Authentication != AUTH_Negotiate ) // don't use the cached challenge
2554  m_strAuthorization = info.digestInfo;
2555  }
2556  }
2557  else
2558  {
2559  kdDebug(7113) << "(" << m_pid << ") Not calling checkCachedAuthentication " << endl;
2560  }
2561 
2562  switch ( Authentication )
2563  {
2564  case AUTH_Basic:
2565  header += createBasicAuth();
2566  break;
2567  case AUTH_Digest:
2568  header += createDigestAuth();
2569  break;
2570 #ifdef HAVE_LIBGSSAPI
2571  case AUTH_Negotiate:
2572  header += createNegotiateAuth();
2573  break;
2574 #endif
2575  case AUTH_NTLM:
2576  header += createNTLMAuth();
2577  break;
2578  case AUTH_None:
2579  default:
2580  break;
2581  }
2582 
2583  /********* Only for debugging purpose *********/
2584  if ( Authentication != AUTH_None )
2585  {
2586  kdDebug(7113) << "(" << m_pid << ") Using Authentication: " << endl;
2587  kdDebug(7113) << "(" << m_pid << ") HOST= " << m_state.hostname << endl;
2588  kdDebug(7113) << "(" << m_pid << ") PORT= " << m_state.port << endl;
2589  kdDebug(7113) << "(" << m_pid << ") USER= " << m_state.user << endl;
2590  kdDebug(7113) << "(" << m_pid << ") PASSWORD= [protected]" << endl;
2591  kdDebug(7113) << "(" << m_pid << ") REALM= " << m_strRealm << endl;
2592  kdDebug(7113) << "(" << m_pid << ") EXTRA= " << m_strAuthorization << endl;
2593  }
2594 
2595  // Do we need to authorize to the proxy server ?
2596  if ( m_state.doProxy && !m_bIsTunneled )
2597  header += proxyAuthenticationHeader();
2598 
2599  // Support old HTTP/1.0 style keep-alive header for compatability
2600  // purposes as well as performance improvements while giving end
2601  // users the ability to disable this feature proxy servers that
2602  // don't not support such feature, e.g. junkbuster proxy server.
2603  if (!m_bUseProxy || m_bPersistentProxyConnection || m_bIsTunneled)
2604  header += "Connection: Keep-Alive\r\n";
2605  else
2606  header += "Connection: close\r\n";
2607 
2608  if ( m_protocol == "webdav" || m_protocol == "webdavs" )
2609  {
2610  header += davProcessLocks();
2611 
2612  // add extra webdav headers, if supplied
2613  TQString davExtraHeader = metaData("davHeader");
2614  if ( !davExtraHeader.isEmpty() )
2615  davHeader += davExtraHeader;
2616 
2617  // Set content type of webdav data
2618  if (davData)
2619  davHeader += "Content-Type: text/xml; charset=utf-8\r\n";
2620 
2621  // add extra header elements for WebDAV
2622  if ( !davHeader.isNull() )
2623  header += davHeader;
2624  }
2625  }
2626 
2627  kdDebug(7103) << "(" << m_pid << ") ============ Sending Header:" << endl;
2628 
2629  TQStringList headerOutput = TQStringList::split("\r\n", header);
2630  TQStringList::Iterator it = headerOutput.begin();
2631 
2632  for (; it != headerOutput.end(); it++)
2633  kdDebug(7103) << "(" << m_pid << ") " << (*it) << endl;
2634 
2635  if ( !moreData && !davData)
2636  header += "\r\n"; /* end header */
2637 
2638  // Now that we have our formatted header, let's send it!
2639  // Create a new connection to the remote machine if we do
2640  // not already have one...
2641  if ( m_iSock == -1)
2642  {
2643  if (!httpOpenConnection())
2644  return false;
2645  }
2646 
2647  // Send the data to the remote machine...
2648  bool sendOk = (write(header.latin1(), header.length()) == (ssize_t) header.length());
2649  if (!sendOk)
2650  {
2651  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpOpen: "
2652  "Connection broken! (" << m_state.hostname << ")" << endl;
2653 
2654  // With a Keep-Alive connection this can happen.
2655  // Just reestablish the connection.
2656  if (m_bKeepAlive)
2657  {
2658  httpCloseConnection();
2659  return true; // Try again
2660  }
2661 
2662  if (!sendOk)
2663  {
2664  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpOpen: sendOk==false."
2665  " Connnection broken !" << endl;
2666  error( ERR_CONNECTION_BROKEN, m_state.hostname );
2667  return false;
2668  }
2669  }
2670 
2671  bool res = true;
2672 
2673  if ( moreData || davData )
2674  res = sendBody();
2675 
2676  infoMessage(i18n("%1 contacted. Waiting for reply...").arg(m_request.hostname));
2677 
2678  return res;
2679 }
2680 
2681 void HTTPProtocol::forwardHttpResponseHeader()
2682 {
2683  // Send the response header if it was requested
2684  if ( config()->readBoolEntry("PropagateHttpHeader", false) )
2685  {
2686  setMetaData("HTTP-Headers", m_responseHeader.join("\n"));
2687  sendMetaData();
2688  }
2689  m_responseHeader.clear();
2690 }
2691 
2698 bool HTTPProtocol::readHeader()
2699 {
2700 try_again:
2701  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readHeader" << endl;
2702 
2703  // Check
2704  if (m_request.bCachedRead)
2705  {
2706  m_responseHeader << "HTTP-CACHE";
2707  // Read header from cache...
2708  char buffer[4097];
2709  if (!fgets(buffer, 4096, m_request.fcache) )
2710  {
2711  // Error, delete cache entry
2712  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readHeader: "
2713  << "Could not access cache to obtain mimetype!" << endl;
2714  error( ERR_CONNECTION_BROKEN, m_state.hostname );
2715  return false;
2716  }
2717 
2718  m_strMimeType = TQString(TQString::fromUtf8( buffer)).stripWhiteSpace();
2719 
2720  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readHeader: cached "
2721  << "data mimetype: " << m_strMimeType << endl;
2722 
2723  if (!fgets(buffer, 4096, m_request.fcache) )
2724  {
2725  // Error, delete cache entry
2726  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readHeader: "
2727  << "Could not access cached data! " << endl;
2728  error( ERR_CONNECTION_BROKEN, m_state.hostname );
2729  return false;
2730  }
2731 
2732  m_request.strCharset = TQString(TQString::fromUtf8( buffer)).stripWhiteSpace().lower();
2733  setMetaData("charset", m_request.strCharset);
2734  if (!m_request.lastModified.isEmpty())
2735  setMetaData("modified", m_request.lastModified);
2736  TQString tmp;
2737  tmp.setNum(m_request.expireDate);
2738  setMetaData("expire-date", tmp);
2739  tmp.setNum(m_request.creationDate);
2740  setMetaData("cache-creation-date", tmp);
2741  mimeType(m_strMimeType);
2742  forwardHttpResponseHeader();
2743  return true;
2744  }
2745 
2746  TQCString locationStr; // In case we get a redirect.
2747  TQCString cookieStr; // In case we get a cookie.
2748 
2749  TQString dispositionType; // In case we get a Content-Disposition type
2750  TQString dispositionFilename; // In case we get a Content-Disposition filename
2751 
2752  TQString mediaValue;
2753  TQString mediaAttribute;
2754 
2755  TQStringList upgradeOffers;
2756 
2757  bool upgradeRequired = false; // Server demands that we upgrade to something
2758  // This is also true if we ask to upgrade and
2759  // the server accepts, since we are now
2760  // committed to doing so
2761  bool canUpgrade = false; // The server offered an upgrade
2762 
2763 
2764  m_request.etag = TQString::null;
2765  m_request.lastModified = TQString::null;
2766  m_request.strCharset = TQString::null;
2767 
2768  time_t dateHeader = 0;
2769  time_t expireDate = 0; // 0 = no info, 1 = already expired, > 1 = actual date
2770  int currentAge = 0;
2771  int maxAge = -1; // -1 = no max age, 0 already expired, > 0 = actual time
2772  int maxHeaderSize = 64*1024; // 64Kb to catch DOS-attacks
2773 
2774  // read in 8192 bytes at a time (HTTP cookies can be quite large.)
2775  int len = 0;
2776  char buffer[8193];
2777  bool cont = false;
2778  bool cacheValidated = false; // Revalidation was successful
2779  bool mayCache = true;
2780  bool hasCacheDirective = false;
2781  bool bCanResume = false;
2782 
2783  if (m_iSock == -1)
2784  {
2785  kdDebug(7113) << "HTTPProtocol::readHeader: No connection." << endl;
2786  return false; // Restablish connection and try again
2787  }
2788 
2789  if (!waitForResponse(m_remoteRespTimeout))
2790  {
2791  // No response error
2792  error( ERR_SERVER_TIMEOUT , m_state.hostname );
2793  return false;
2794  }
2795 
2796  setRewindMarker();
2797 
2798  gets(buffer, sizeof(buffer)-1);
2799 
2800  if (m_bEOF || *buffer == '\0')
2801  {
2802  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readHeader: "
2803  << "EOF while waiting for header start." << endl;
2804  if (m_bKeepAlive) // Try to reestablish connection.
2805  {
2806  httpCloseConnection();
2807  return false; // Reestablish connection and try again.
2808  }
2809 
2810  if (m_request.method == HTTP_HEAD)
2811  {
2812  // HACK
2813  // Some web-servers fail to respond properly to a HEAD request.
2814  // We compensate for their failure to properly implement the HTTP standard
2815  // by assuming that they will be sending html.
2816  kdDebug(7113) << "(" << m_pid << ") HTTPPreadHeader: HEAD -> returned "
2817  << "mimetype: " << DEFAULT_MIME_TYPE << endl;
2818  mimeType(TQString::fromLatin1(DEFAULT_MIME_TYPE));
2819  return true;
2820  }
2821 
2822  kdDebug(7113) << "HTTPProtocol::readHeader: Connection broken !" << endl;
2823  error( ERR_CONNECTION_BROKEN, m_state.hostname );
2824  return false;
2825  }
2826 
2827  kdDebug(7103) << "(" << m_pid << ") ============ Received Response:"<< endl;
2828 
2829  bool noHeader = true;
2830  HTTP_REV httpRev = HTTP_None;
2831  int headerSize = 0;
2832 
2833  do
2834  {
2835  // strip off \r and \n if we have them
2836  len = strlen(buffer);
2837 
2838  while(len && (buffer[len-1] == '\n' || buffer[len-1] == '\r'))
2839  buffer[--len] = 0;
2840 
2841  // if there was only a newline then continue
2842  if (!len)
2843  {
2844  kdDebug(7103) << "(" << m_pid << ") --empty--" << endl;
2845  continue;
2846  }
2847 
2848  headerSize += len;
2849 
2850  // We have a response header. This flag is a work around for
2851  // servers that append a "\r\n" before the beginning of the HEADER
2852  // response!!! It only catches x number of \r\n being placed at the
2853  // top of the reponse...
2854  noHeader = false;
2855 
2856  kdDebug(7103) << "(" << m_pid << ") \"" << buffer << "\"" << endl;
2857 
2858  // Save broken servers from damnation!!
2859  char* buf = buffer;
2860  while( *buf == ' ' )
2861  buf++;
2862 
2863 
2864  if (buf[0] == '<')
2865  {
2866  // We get XML / HTTP without a proper header
2867  // put string back
2868  kdDebug(7103) << "tdeio_http: No valid HTTP header found! Document starts with XML/HTML tag" << endl;
2869 
2870  // Document starts with a tag, assume html instead of text/plain
2871  m_strMimeType = "text/html";
2872 
2873  rewind();
2874  break;
2875  }
2876 
2877  // Store the the headers so they can be passed to the
2878  // calling application later
2879  m_responseHeader << TQString::fromLatin1(buf);
2880 
2881  if ((strncasecmp(buf, "HTTP/", 5) == 0) ||
2882  (strncasecmp(buf, "ICY ", 4) == 0)) // Shoutcast support
2883  {
2884  if (strncasecmp(buf, "ICY ", 4) == 0)
2885  {
2886  // Shoutcast support
2887  httpRev = SHOUTCAST;
2888  m_bKeepAlive = false;
2889  }
2890  else if (strncmp((buf + 5), "1.0",3) == 0)
2891  {
2892  httpRev = HTTP_10;
2893  // For 1.0 servers, the server itself has to explicitly
2894  // tell us whether it supports persistent connection or
2895  // not. By default, we assume it does not, but we do
2896  // send the old style header "Connection: Keep-Alive" to
2897  // inform it that we support persistence.
2898  m_bKeepAlive = false;
2899  }
2900  else if (strncmp((buf + 5), "1.1",3) == 0)
2901  {
2902  httpRev = HTTP_11;
2903  }
2904  else
2905  {
2906  httpRev = HTTP_Unknown;
2907  }
2908 
2909  if (m_responseCode)
2910  m_prevResponseCode = m_responseCode;
2911 
2912  const char* rptr = buf;
2913  while ( *rptr && *rptr > ' ' )
2914  ++rptr;
2915  m_responseCode = atoi(rptr);
2916 
2917  // server side errors
2918  if (m_responseCode >= 500 && m_responseCode <= 599)
2919  {
2920  if (m_request.method == HTTP_HEAD)
2921  {
2922  ; // Ignore error
2923  }
2924  else
2925  {
2926  if (m_request.bErrorPage)
2927  errorPage();
2928  else
2929  {
2930  error(ERR_INTERNAL_SERVER, m_request.url.prettyURL());
2931  return false;
2932  }
2933  }
2934  m_request.bCachedWrite = false; // Don't put in cache
2935  mayCache = false;
2936  }
2937  // Unauthorized access
2938  else if (m_responseCode == 401 || m_responseCode == 407)
2939  {
2940  // Double authorization requests, i.e. a proxy auth
2941  // request followed immediately by a regular auth request.
2942  if ( m_prevResponseCode != m_responseCode &&
2943  (m_prevResponseCode == 401 || m_prevResponseCode == 407) )
2944  saveAuthorization();
2945 
2946  m_bUnauthorized = true;
2947  m_request.bCachedWrite = false; // Don't put in cache
2948  mayCache = false;
2949  }
2950  //
2951  else if (m_responseCode == 416) // Range not supported
2952  {
2953  m_request.offset = 0;
2954  httpCloseConnection();
2955  return false; // Try again.
2956  }
2957  // Upgrade Required
2958  else if (m_responseCode == 426)
2959  {
2960  upgradeRequired = true;
2961  }
2962  // Any other client errors
2963  else if (m_responseCode >= 400 && m_responseCode <= 499)
2964  {
2965  // Tell that we will only get an error page here.
2966  if (m_request.bErrorPage)
2967  errorPage();
2968  else
2969  {
2970  error(ERR_DOES_NOT_EXIST, m_request.url.prettyURL());
2971  return false;
2972  }
2973  m_request.bCachedWrite = false; // Don't put in cache
2974  mayCache = false;
2975  }
2976  else if (m_responseCode == 307)
2977  {
2978  // 307 Temporary Redirect
2979  m_request.bCachedWrite = false; // Don't put in cache
2980  mayCache = false;
2981  }
2982  else if (m_responseCode == 304)
2983  {
2984  // 304 Not Modified
2985  // The value in our cache is still valid.
2986  cacheValidated = true;
2987  }
2988  else if (m_responseCode >= 301 && m_responseCode<= 303)
2989  {
2990  // 301 Moved permanently
2991  if (m_responseCode == 301)
2992  setMetaData("permanent-redirect", "true");
2993 
2994  // 302 Found (temporary location)
2995  // 303 See Other
2996  if (m_request.method != HTTP_HEAD && m_request.method != HTTP_GET)
2997  {
2998 #if 0
2999  // Reset the POST buffer to avoid a double submit
3000  // on redirection
3001  if (m_request.method == HTTP_POST)
3002  m_bufPOST.resize(0);
3003 #endif
3004 
3005  // NOTE: This is wrong according to RFC 2616. However,
3006  // because most other existing user agent implementations
3007  // treat a 301/302 response as a 303 response and preform
3008  // a GET action regardless of what the previous method was,
3009  // many servers have simply adapted to this way of doing
3010  // things!! Thus, we are forced to do the same thing or we
3011  // won't be able to retrieve these pages correctly!! See RFC
3012  // 2616 sections 10.3.[2/3/4/8]
3013  m_request.method = HTTP_GET; // Force a GET
3014  }
3015  m_request.bCachedWrite = false; // Don't put in cache
3016  mayCache = false;
3017  }
3018  else if ( m_responseCode == 207 ) // Multi-status (for WebDav)
3019  {
3020 
3021  }
3022  else if ( m_responseCode == 204 ) // No content
3023  {
3024  // error(ERR_NO_CONTENT, i18n("Data have been successfully sent."));
3025  // Short circuit and do nothing!
3026 
3027  // The original handling here was wrong, this is not an error: eg. in the
3028  // example of a 204 No Content response to a PUT completing.
3029  // m_bError = true;
3030  // return false;
3031  }
3032  else if ( m_responseCode == 206 )
3033  {
3034  if ( m_request.offset )
3035  bCanResume = true;
3036  }
3037  else if (m_responseCode == 102) // Processing (for WebDAV)
3038  {
3039  /***
3040  * This status code is given when the server expects the
3041  * command to take significant time to complete. So, inform
3042  * the user.
3043  */
3044  infoMessage( i18n( "Server processing request, please wait..." ) );
3045  cont = true;
3046  }
3047  else if (m_responseCode == 100)
3048  {
3049  // We got 'Continue' - ignore it
3050  cont = true;
3051  }
3052  }
3053 
3054  // are we allowd to resume? this will tell us
3055  else if (strncasecmp(buf, "Accept-Ranges:", 14) == 0) {
3056  if (strncasecmp(trimLead(buf + 14), "none", 4) == 0)
3057  bCanResume = false;
3058  }
3059  // Keep Alive
3060  else if (strncasecmp(buf, "Keep-Alive:", 11) == 0) {
3061  TQStringList options = TQStringList::split(',',
3062  TQString::fromLatin1(trimLead(buf+11)));
3063  for(TQStringList::ConstIterator it = options.begin();
3064  it != options.end();
3065  it++)
3066  {
3067  TQString option = (*it).stripWhiteSpace().lower();
3068  if (option.startsWith("timeout="))
3069  {
3070  m_keepAliveTimeout = option.mid(8).toInt();
3071  }
3072  }
3073  }
3074 
3075  // Cache control
3076  else if (strncasecmp(buf, "Cache-Control:", 14) == 0) {
3077  TQStringList cacheControls = TQStringList::split(',',
3078  TQString::fromLatin1(trimLead(buf+14)));
3079  for(TQStringList::ConstIterator it = cacheControls.begin();
3080  it != cacheControls.end();
3081  it++)
3082  {
3083  TQString cacheControl = (*it).stripWhiteSpace();
3084  if (strncasecmp(cacheControl.latin1(), "no-cache", 8) == 0)
3085  {
3086  m_request.bCachedWrite = false; // Don't put in cache
3087  mayCache = false;
3088  }
3089  else if (strncasecmp(cacheControl.latin1(), "no-store", 8) == 0)
3090  {
3091  m_request.bCachedWrite = false; // Don't put in cache
3092  mayCache = false;
3093  }
3094  else if (strncasecmp(cacheControl.latin1(), "max-age=", 8) == 0)
3095  {
3096  TQString age = cacheControl.mid(8).stripWhiteSpace();
3097  if (!age.isNull())
3098  maxAge = STRTOLL(age.latin1(), 0, 10);
3099  }
3100  }
3101  hasCacheDirective = true;
3102  }
3103 
3104  // get the size of our data
3105  else if (strncasecmp(buf, "Content-length:", 15) == 0) {
3106  char* len = trimLead(buf + 15);
3107  if (len)
3108  m_iSize = STRTOLL(len, 0, 10);
3109  }
3110 
3111  else if (strncasecmp(buf, "Content-location:", 17) == 0) {
3112  setMetaData ("content-location",
3113  TQString::fromLatin1(trimLead(buf+17)).stripWhiteSpace());
3114  }
3115 
3116  // what type of data do we have?
3117  else if (strncasecmp(buf, "Content-type:", 13) == 0) {
3118  char *start = trimLead(buf + 13);
3119  char *pos = start;
3120 
3121  // Increment until we encounter ";" or the end of the buffer
3122  while ( *pos && *pos != ';' ) pos++;
3123 
3124  // Assign the mime-type.
3125  m_strMimeType = TQString::fromLatin1(start, pos-start).stripWhiteSpace().lower();
3126  kdDebug(7113) << "(" << m_pid << ") Content-type: " << m_strMimeType << endl;
3127 
3128  // If we still have text, then it means we have a mime-type with a
3129  // parameter (eg: charset=iso-8851) ; so let's get that...
3130  while (*pos)
3131  {
3132  start = ++pos;
3133  while ( *pos && *pos != '=' ) pos++;
3134 
3135  char *end = pos;
3136  while ( *end && *end != ';' ) end++;
3137 
3138  if (*pos)
3139  {
3140  mediaAttribute = TQString::fromLatin1(start, pos-start).stripWhiteSpace().lower();
3141  mediaValue = TQString::fromLatin1(pos+1, end-pos-1).stripWhiteSpace();
3142  pos = end;
3143  if (mediaValue.length() &&
3144  (mediaValue[0] == '"') &&
3145  (mediaValue[mediaValue.length()-1] == '"'))
3146  mediaValue = mediaValue.mid(1, mediaValue.length()-2);
3147 
3148  kdDebug (7113) << "(" << m_pid << ") Media-Parameter Attribute: "
3149  << mediaAttribute << endl;
3150  kdDebug (7113) << "(" << m_pid << ") Media-Parameter Value: "
3151  << mediaValue << endl;
3152 
3153  if ( mediaAttribute == "charset")
3154  {
3155  mediaValue = mediaValue.lower();
3156  m_request.strCharset = mediaValue;
3157  }
3158  else
3159  {
3160  setMetaData("media-"+mediaAttribute, mediaValue);
3161  }
3162  }
3163  }
3164  }
3165 
3166  // Date
3167  else if (strncasecmp(buf, "Date:", 5) == 0) {
3168  dateHeader = KRFCDate::parseDate(trimLead(buf+5));
3169  }
3170 
3171  // Cache management
3172  else if (strncasecmp(buf, "ETag:", 5) == 0) {
3173  m_request.etag = trimLead(buf+5);
3174  }
3175 
3176  // Cache management
3177  else if (strncasecmp(buf, "Expires:", 8) == 0) {
3178  expireDate = KRFCDate::parseDate(trimLead(buf+8));
3179  if (!expireDate)
3180  expireDate = 1; // Already expired
3181  }
3182 
3183  // Cache management
3184  else if (strncasecmp(buf, "Last-Modified:", 14) == 0) {
3185  m_request.lastModified = (TQString::fromLatin1(trimLead(buf+14))).stripWhiteSpace();
3186  }
3187 
3188  // whoops.. we received a warning
3189  else if (strncasecmp(buf, "Warning:", 8) == 0) {
3190  //Don't use warning() here, no need to bother the user.
3191  //Those warnings are mostly about caches.
3192  infoMessage(trimLead(buf + 8));
3193  }
3194 
3195  // Cache management (HTTP 1.0)
3196  else if (strncasecmp(buf, "Pragma:", 7) == 0) {
3197  TQCString pragma = TQCString(trimLead(buf+7)).stripWhiteSpace().lower();
3198  if (pragma == "no-cache")
3199  {
3200  m_request.bCachedWrite = false; // Don't put in cache
3201  mayCache = false;
3202  hasCacheDirective = true;
3203  }
3204  }
3205 
3206  // The deprecated Refresh Response
3207  else if (strncasecmp(buf,"Refresh:", 8) == 0) {
3208  mayCache = false; // Do not cache page as it defeats purpose of Refresh tag!
3209  setMetaData( "http-refresh", TQString::fromLatin1(trimLead(buf+8)).stripWhiteSpace() );
3210  }
3211 
3212  // In fact we should do redirection only if we got redirection code
3213  else if (strncasecmp(buf, "Location:", 9) == 0) {
3214  // Redirect only for 3xx status code, will ya! Thanks, pal!
3215  if ( m_responseCode > 299 && m_responseCode < 400 )
3216  locationStr = TQCString(trimLead(buf+9)).stripWhiteSpace();
3217  }
3218 
3219  // Check for cookies
3220  else if (strncasecmp(buf, "Set-Cookie", 10) == 0) {
3221  cookieStr += buf;
3222  cookieStr += '\n';
3223  }
3224 
3225  // check for direct authentication
3226  else if (strncasecmp(buf, "WWW-Authenticate:", 17) == 0) {
3227  configAuth(trimLead(buf + 17), false);
3228  }
3229 
3230  // check for proxy-based authentication
3231  else if (strncasecmp(buf, "Proxy-Authenticate:", 19) == 0) {
3232  configAuth(trimLead(buf + 19), true);
3233  }
3234 
3235  else if (strncasecmp(buf, "Upgrade:", 8) == 0) {
3236  // Now we have to check to see what is offered for the upgrade
3237  TQString offered = &(buf[8]);
3238  upgradeOffers = TQStringList::split(TQRegExp("[ \n,\r\t]"), offered);
3239  }
3240 
3241  // content?
3242  else if (strncasecmp(buf, "Content-Encoding:", 17) == 0) {
3243  // This is so wrong !! No wonder tdeio_http is stripping the
3244  // gzip encoding from downloaded files. This solves multiple
3245  // bug reports and caitoo's problem with downloads when such a
3246  // header is encountered...
3247 
3248  // A quote from RFC 2616:
3249  // " When present, its (Content-Encoding) value indicates what additional
3250  // content have been applied to the entity body, and thus what decoding
3251  // mechanism must be applied to obtain the media-type referenced by the
3252  // Content-Type header field. Content-Encoding is primarily used to allow
3253  // a document to be compressed without loosing the identity of its underlying
3254  // media type. Simply put if it is specified, this is the actual mime-type
3255  // we should use when we pull the resource !!!
3256  addEncoding(trimLead(buf + 17), m_qContentEncodings);
3257  }
3258  // Refer to RFC 2616 sec 15.5/19.5.1 and RFC 2183
3259  else if(strncasecmp(buf, "Content-Disposition:", 20) == 0) {
3260  char* dispositionBuf = trimLead(buf + 20);
3261  while ( *dispositionBuf )
3262  {
3263  if ( strncasecmp( dispositionBuf, "filename", 8 ) == 0 )
3264  {
3265  dispositionBuf += 8;
3266 
3267  while ( *dispositionBuf == ' ' || *dispositionBuf == '=' )
3268  dispositionBuf++;
3269 
3270  char* bufStart = dispositionBuf;
3271 
3272  while ( *dispositionBuf && *dispositionBuf != ';' )
3273  dispositionBuf++;
3274 
3275  if ( dispositionBuf > bufStart )
3276  {
3277  // Skip any leading quotes...
3278  while ( *bufStart == '"' )
3279  bufStart++;
3280 
3281  // Skip any trailing quotes as well as white spaces...
3282  while ( *(dispositionBuf-1) == ' ' || *(dispositionBuf-1) == '"')
3283  dispositionBuf--;
3284 
3285  if ( dispositionBuf > bufStart )
3286  dispositionFilename = TQString::fromLatin1( bufStart, dispositionBuf-bufStart );
3287 
3288  break;
3289  }
3290  }
3291  else
3292  {
3293  char *bufStart = dispositionBuf;
3294 
3295  while ( *dispositionBuf && *dispositionBuf != ';' )
3296  dispositionBuf++;
3297 
3298  if ( dispositionBuf > bufStart )
3299  dispositionType = TQString::fromLatin1( bufStart, dispositionBuf-bufStart ).stripWhiteSpace();
3300 
3301  while ( *dispositionBuf == ';' || *dispositionBuf == ' ' )
3302  dispositionBuf++;
3303  }
3304  }
3305 
3306  // Content-Dispostion is not allowed to dictate directory
3307  // path, thus we extract the filename only.
3308  if ( !dispositionFilename.isEmpty() )
3309  {
3310  int pos = dispositionFilename.findRev( '/' );
3311 
3312  if( pos > -1 )
3313  dispositionFilename = dispositionFilename.mid(pos+1);
3314 
3315  kdDebug(7113) << "(" << m_pid << ") Content-Disposition: filename="
3316  << dispositionFilename<< endl;
3317  }
3318  }
3319  else if(strncasecmp(buf, "Content-Language:", 17) == 0) {
3320  TQString language = TQString::fromLatin1(trimLead(buf+17)).stripWhiteSpace();
3321  if (!language.isEmpty())
3322  setMetaData("content-language", language);
3323  }
3324  else if (strncasecmp(buf, "Proxy-Connection:", 17) == 0)
3325  {
3326  if (strncasecmp(trimLead(buf + 17), "Close", 5) == 0)
3327  m_bKeepAlive = false;
3328  else if (strncasecmp(trimLead(buf + 17), "Keep-Alive", 10)==0)
3329  m_bKeepAlive = true;
3330  }
3331  else if (strncasecmp(buf, "Link:", 5) == 0) {
3332  // We only support Link: <url>; rel="type" so far
3333  TQStringList link = TQStringList::split(";", TQString(buf)
3334  .replace(TQRegExp("^Link:[ ]*"),
3335  ""));
3336  if (link.count() == 2) {
3337  TQString rel = link[1].stripWhiteSpace();
3338  if (rel.startsWith("rel=\"")) {
3339  rel = rel.mid(5, rel.length() - 6);
3340  if (rel.lower() == "pageservices") {
3341  TQString url = TQString(link[0].replace(TQRegExp("[<>]"),"")).stripWhiteSpace();
3342  setMetaData("PageServices", url);
3343  }
3344  }
3345  }
3346  }
3347  else if (strncasecmp(buf, "P3P:", 4) == 0) {
3348  TQString p3pstr = buf;
3349  p3pstr = p3pstr.mid(4).simplifyWhiteSpace();
3350  TQStringList policyrefs, compact;
3351  TQStringList policyfields = TQStringList::split(TQRegExp(",[ ]*"), p3pstr);
3352  for (TQStringList::Iterator it = policyfields.begin();
3353  it != policyfields.end();
3354  ++it) {
3355  TQStringList policy = TQStringList::split("=", *it);
3356 
3357  if (policy.count() == 2) {
3358  if (policy[0].lower() == "policyref") {
3359  policyrefs << TQString(policy[1].replace(TQRegExp("[\"\']"), ""))
3360  .stripWhiteSpace();
3361  } else if (policy[0].lower() == "cp") {
3362  // We convert to cp\ncp\ncp\n[...]\ncp to be consistent with
3363  // other metadata sent in strings. This could be a bit more
3364  // efficient but I'm going for correctness right now.
3365  TQStringList cps = TQStringList::split(" ",
3366  TQString(policy[1].replace(TQRegExp("[\"\']"), ""))
3367  .simplifyWhiteSpace());
3368 
3369  for (TQStringList::Iterator j = cps.begin(); j != cps.end(); ++j)
3370  compact << *j;
3371  }
3372  }
3373  }
3374 
3375  if (!policyrefs.isEmpty())
3376  setMetaData("PrivacyPolicy", policyrefs.join("\n"));
3377 
3378  if (!compact.isEmpty())
3379  setMetaData("PrivacyCompactPolicy", compact.join("\n"));
3380  }
3381  // let them tell us if we should stay alive or not
3382  else if (strncasecmp(buf, "Connection:", 11) == 0)
3383  {
3384  if (strncasecmp(trimLead(buf + 11), "Close", 5) == 0)
3385  m_bKeepAlive = false;
3386  else if (strncasecmp(trimLead(buf + 11), "Keep-Alive", 10)==0)
3387  m_bKeepAlive = true;
3388  else if (strncasecmp(trimLead(buf + 11), "Upgrade", 7)==0)
3389  {
3390  if (m_responseCode == 101) {
3391  // Ok, an upgrade was accepted, now we must do it
3392  upgradeRequired = true;
3393  } else if (upgradeRequired) { // 426
3394  // Nothing to do since we did it above already
3395  } else {
3396  // Just an offer to upgrade - no need to take it
3397  canUpgrade = true;
3398  }
3399  }
3400  }
3401  // continue only if we know that we're HTTP/1.1
3402  else if ( httpRev == HTTP_11) {
3403  // what kind of encoding do we have? transfer?
3404  if (strncasecmp(buf, "Transfer-Encoding:", 18) == 0) {
3405  // If multiple encodings have been applied to an entity, the
3406  // transfer-codings MUST be listed in the order in which they
3407  // were applied.
3408  addEncoding(trimLead(buf + 18), m_qTransferEncodings);
3409  }
3410 
3411  // md5 signature
3412  else if (strncasecmp(buf, "Content-MD5:", 12) == 0) {
3413  m_sContentMD5 = TQString::fromLatin1(trimLead(buf + 12));
3414  }
3415 
3416  // *** Responses to the HTTP OPTIONS method follow
3417  // WebDAV capabilities
3418  else if (strncasecmp(buf, "DAV:", 4) == 0) {
3419  if (m_davCapabilities.isEmpty()) {
3420  m_davCapabilities << TQString::fromLatin1(trimLead(buf + 4));
3421  }
3422  else {
3423  m_davCapabilities << TQString::fromLatin1(trimLead(buf + 4));
3424  }
3425  }
3426  // *** Responses to the HTTP OPTIONS method finished
3427  }
3428  else if ((httpRev == HTTP_None) && (strlen(buf) != 0))
3429  {
3430  // Remote server does not seem to speak HTTP at all
3431  // Put the crap back into the buffer and hope for the best
3432  rewind();
3433  if (m_responseCode)
3434  m_prevResponseCode = m_responseCode;
3435 
3436  m_responseCode = 200; // Fake it
3437  httpRev = HTTP_Unknown;
3438  m_bKeepAlive = false;
3439  break;
3440  }
3441  setRewindMarker();
3442 
3443  // Clear out our buffer for further use.
3444  memset(buffer, 0, sizeof(buffer));
3445 
3446  } while (!m_bEOF && (len || noHeader) && (headerSize < maxHeaderSize) && (gets(buffer, sizeof(buffer)-1)));
3447 
3448  // Now process the HTTP/1.1 upgrade
3449  TQStringList::Iterator opt = upgradeOffers.begin();
3450  for( ; opt != upgradeOffers.end(); ++opt) {
3451  if (*opt == "TLS/1.0") {
3452  if(upgradeRequired) {
3453  if (!startTLS() && !usingTLS()) {
3454  error(ERR_UPGRADE_REQUIRED, *opt);
3455  return false;
3456  }
3457  }
3458  } else if (*opt == "HTTP/1.1") {
3459  httpRev = HTTP_11;
3460  } else {
3461  // unknown
3462  if (upgradeRequired) {
3463  error(ERR_UPGRADE_REQUIRED, *opt);
3464  return false;
3465  }
3466  }
3467  }
3468 
3469  setMetaData("charset", m_request.strCharset);
3470 
3471  // If we do not support the requested authentication method...
3472  if ( (m_responseCode == 401 && Authentication == AUTH_None) ||
3473  (m_responseCode == 407 && ProxyAuthentication == AUTH_None) )
3474  {
3475  m_bUnauthorized = false;
3476  if (m_request.bErrorPage)
3477  errorPage();
3478  else
3479  {
3480  error( ERR_UNSUPPORTED_ACTION, "Unknown Authorization method!" );
3481  return false;
3482  }
3483  }
3484 
3485  // Fixup expire date for clock drift.
3486  if (expireDate && (expireDate <= dateHeader))
3487  expireDate = 1; // Already expired.
3488 
3489  // Convert max-age into expireDate (overriding previous set expireDate)
3490  if (maxAge == 0)
3491  expireDate = 1; // Already expired.
3492  else if (maxAge > 0)
3493  {
3494  if (currentAge)
3495  maxAge -= currentAge;
3496  if (maxAge <=0)
3497  maxAge = 0;
3498  expireDate = time(0) + maxAge;
3499  }
3500 
3501  if (!expireDate)
3502  {
3503  time_t lastModifiedDate = 0;
3504  if (!m_request.lastModified.isEmpty())
3505  lastModifiedDate = KRFCDate::parseDate(m_request.lastModified);
3506 
3507  if (lastModifiedDate)
3508  {
3509  long diff = static_cast<long>(difftime(dateHeader, lastModifiedDate));
3510  if (diff < 0)
3511  expireDate = time(0) + 1;
3512  else
3513  expireDate = time(0) + (diff / 10);
3514  }
3515  else
3516  {
3517  expireDate = time(0) + DEFAULT_CACHE_EXPIRE;
3518  }
3519  }
3520 
3521  // DONE receiving the header!
3522  if (!cookieStr.isEmpty())
3523  {
3524  if ((m_request.cookieMode == HTTPRequest::CookiesAuto) && m_request.bUseCookiejar)
3525  {
3526  // Give cookies to the cookiejar.
3527  TQString domain = config()->readEntry("cross-domain");
3528  if (!domain.isEmpty() && isCrossDomainRequest(m_request.url.host(), domain))
3529  cookieStr = "Cross-Domain\n" + cookieStr;
3530  addCookies( m_request.url.url(), cookieStr );
3531  }
3532  else if (m_request.cookieMode == HTTPRequest::CookiesManual)
3533  {
3534  // Pass cookie to application
3535  setMetaData("setcookies", cookieStr);
3536  }
3537  }
3538 
3539  if (m_request.bMustRevalidate)
3540  {
3541  m_request.bMustRevalidate = false; // Reset just in case.
3542  if (cacheValidated)
3543  {
3544  // Yippie, we can use the cached version.
3545  // Update the cache with new "Expire" headers.
3546  fclose(m_request.fcache);
3547  m_request.fcache = 0;
3548  updateExpireDate( expireDate, true );
3549  m_request.fcache = checkCacheEntry( ); // Re-read cache entry
3550 
3551  if (m_request.fcache)
3552  {
3553  m_request.bCachedRead = true;
3554  goto try_again; // Read header again, but now from cache.
3555  }
3556  else
3557  {
3558  // Where did our cache entry go???
3559  }
3560  }
3561  else
3562  {
3563  // Validation failed. Close cache.
3564  fclose(m_request.fcache);
3565  m_request.fcache = 0;
3566  }
3567  }
3568 
3569  // We need to reread the header if we got a '100 Continue' or '102 Processing'
3570  if ( cont )
3571  {
3572  goto try_again;
3573  }
3574 
3575  // Do not do a keep-alive connection if the size of the
3576  // response is not known and the response is not Chunked.
3577  if (!m_bChunked && (m_iSize == NO_SIZE))
3578  m_bKeepAlive = false;
3579 
3580  if ( m_responseCode == 204 )
3581  {
3582  return true;
3583  }
3584 
3585  // We need to try to login again if we failed earlier
3586  if ( m_bUnauthorized )
3587  {
3588  if ( (m_responseCode == 401) ||
3589  (m_bUseProxy && (m_responseCode == 407))
3590  )
3591  {
3592  if ( getAuthorization() )
3593  {
3594  // for NTLM Authentication we have to keep the connection open!
3595  if ( Authentication == AUTH_NTLM && m_strAuthorization.length() > 4 )
3596  {
3597  m_bKeepAlive = true;
3598  readBody( true );
3599  }
3600  else if (ProxyAuthentication == AUTH_NTLM && m_strProxyAuthorization.length() > 4)
3601  {
3602  readBody( true );
3603  }
3604  else
3605  httpCloseConnection();
3606  return false; // Try again.
3607  }
3608 
3609  if (m_bError)
3610  return false; // Error out
3611 
3612  // Show error page...
3613  }
3614  m_bUnauthorized = false;
3615  }
3616 
3617  // We need to do a redirect
3618  if (!locationStr.isEmpty())
3619  {
3620  KURL u(m_request.url, locationStr);
3621  if(!u.isValid())
3622  {
3623  error(ERR_MALFORMED_URL, u.prettyURL());
3624  return false;
3625  }
3626  if ((u.protocol() != "http") && (u.protocol() != "https") &&
3627  (u.protocol() != "ftp") && (u.protocol() != "webdav") &&
3628  (u.protocol() != "webdavs"))
3629  {
3630  redirection(u);
3631  error(ERR_ACCESS_DENIED, u.prettyURL());
3632  return false;
3633  }
3634 
3635  // preserve #ref: (bug 124654)
3636  // if we were at http://host/resource1#ref, we sent a GET for "/resource1"
3637  // if we got redirected to http://host/resource2, then we have to re-add
3638  // the fragment:
3639  if (m_request.url.hasRef() && !u.hasRef() &&
3640  (m_request.url.host() == u.host()) &&
3641  (m_request.url.protocol() == u.protocol()))
3642  u.setRef(m_request.url.ref());
3643 
3644  m_bRedirect = true;
3645  m_redirectLocation = u;
3646 
3647  if (!m_request.id.isEmpty())
3648  {
3649  sendMetaData();
3650  }
3651 
3652  kdDebug(7113) << "(" << m_pid << ") request.url: " << m_request.url.prettyURL()
3653  << endl << "LocationStr: " << locationStr.data() << endl;
3654 
3655  kdDebug(7113) << "(" << m_pid << ") Requesting redirection to: " << u.prettyURL()
3656  << endl;
3657 
3658  // If we're redirected to a http:// url, remember that we're doing webdav...
3659  if (m_protocol == "webdav" || m_protocol == "webdavs")
3660  u.setProtocol(m_protocol);
3661 
3662  redirection(u);
3663  m_request.bCachedWrite = false; // Turn off caching on re-direction (DA)
3664  mayCache = false;
3665  }
3666 
3667  // Inform the job that we can indeed resume...
3668  if ( bCanResume && m_request.offset )
3669  canResume();
3670  else
3671  m_request.offset = 0;
3672 
3673  // We don't cache certain text objects
3674  if (m_strMimeType.startsWith("text/") &&
3675  (m_strMimeType != "text/css") &&
3676  (m_strMimeType != "text/x-javascript") &&
3677  !hasCacheDirective)
3678  {
3679  // Do not cache secure pages or pages
3680  // originating from password protected sites
3681  // unless the webserver explicitly allows it.
3682  if ( m_bIsSSL || (Authentication != AUTH_None) )
3683  {
3684  m_request.bCachedWrite = false;
3685  mayCache = false;
3686  }
3687  }
3688 
3689  // WABA: Correct for tgz files with a gzip-encoding.
3690  // They really shouldn't put gzip in the Content-Encoding field!
3691  // Web-servers really shouldn't do this: They let Content-Size refer
3692  // to the size of the tgz file, not to the size of the tar file,
3693  // while the Content-Type refers to "tar" instead of "tgz".
3694  if (m_qContentEncodings.last() == "gzip")
3695  {
3696  if (m_strMimeType == "application/x-tar")
3697  {
3698  m_qContentEncodings.remove(m_qContentEncodings.fromLast());
3699  m_strMimeType = TQString::fromLatin1("application/x-tgz");
3700  }
3701  else if (m_strMimeType == "application/postscript")
3702  {
3703  // LEONB: Adding another exception for psgz files.
3704  // Could we use the mimelnk files instead of hardcoding all this?
3705  m_qContentEncodings.remove(m_qContentEncodings.fromLast());
3706  m_strMimeType = TQString::fromLatin1("application/x-gzpostscript");
3707  }
3708  else if ( m_request.allowCompressedPage &&
3709  m_strMimeType != "application/x-tgz" &&
3710  m_strMimeType != "application/x-targz" &&
3711  m_strMimeType != "application/x-gzip" &&
3712  m_request.url.path().right(6) == ".ps.gz" )
3713  {
3714  m_qContentEncodings.remove(m_qContentEncodings.fromLast());
3715  m_strMimeType = TQString::fromLatin1("application/x-gzpostscript");
3716  }
3717  else if ( (m_request.allowCompressedPage &&
3718  m_strMimeType == "text/html")
3719  ||
3720  (m_request.allowCompressedPage &&
3721  m_strMimeType != "application/x-tgz" &&
3722  m_strMimeType != "application/x-targz" &&
3723  m_strMimeType != "application/x-gzip" &&
3724  m_request.url.path().right(3) != ".gz")
3725  )
3726  {
3727  // Unzip!
3728  }
3729  else
3730  {
3731  m_qContentEncodings.remove(m_qContentEncodings.fromLast());
3732  m_strMimeType = TQString::fromLatin1("application/x-gzip");
3733  }
3734  }
3735 
3736  // We can't handle "bzip2" encoding (yet). So if we get something with
3737  // bzip2 encoding, we change the mimetype to "application/x-bzip2".
3738  // Note for future changes: some web-servers send both "bzip2" as
3739  // encoding and "application/x-bzip2" as mimetype. That is wrong.
3740  // currently that doesn't bother us, because we remove the encoding
3741  // and set the mimetype to x-bzip2 anyway.
3742  if (m_qContentEncodings.last() == "bzip2")
3743  {
3744  m_qContentEncodings.remove(m_qContentEncodings.fromLast());
3745  m_strMimeType = TQString::fromLatin1("application/x-bzip2");
3746  }
3747 
3748  // Convert some common mimetypes to standard KDE mimetypes
3749  if (m_strMimeType == "application/x-targz")
3750  m_strMimeType = TQString::fromLatin1("application/x-tgz");
3751  else if (m_strMimeType == "application/zip")
3752  m_strMimeType = TQString::fromLatin1("application/x-zip");
3753  else if (m_strMimeType == "image/x-png")
3754  m_strMimeType = TQString::fromLatin1("image/png");
3755  else if (m_strMimeType == "image/bmp")
3756  m_strMimeType = TQString::fromLatin1("image/x-bmp");
3757  else if (m_strMimeType == "audio/mpeg" || m_strMimeType == "audio/x-mpeg" || m_strMimeType == "audio/mp3")
3758  m_strMimeType = TQString::fromLatin1("audio/x-mp3");
3759  else if (m_strMimeType == "audio/microsoft-wave")
3760  m_strMimeType = TQString::fromLatin1("audio/x-wav");
3761  else if (m_strMimeType == "audio/midi")
3762  m_strMimeType = TQString::fromLatin1("audio/x-midi");
3763  else if (m_strMimeType == "image/x-xpixmap")
3764  m_strMimeType = TQString::fromLatin1("image/x-xpm");
3765  else if (m_strMimeType == "application/rtf")
3766  m_strMimeType = TQString::fromLatin1("text/rtf");
3767 
3768  // Crypto ones....
3769  else if (m_strMimeType == "application/pkix-cert" ||
3770  m_strMimeType == "application/binary-certificate")
3771  {
3772  m_strMimeType = TQString::fromLatin1("application/x-x509-ca-cert");
3773  }
3774 
3775  // Prefer application/x-tgz or x-gzpostscript over application/x-gzip.
3776  else if (m_strMimeType == "application/x-gzip")
3777  {
3778  if ((m_request.url.path().right(7) == ".tar.gz") ||
3779  (m_request.url.path().right(4) == ".tar"))
3780  m_strMimeType = TQString::fromLatin1("application/x-tgz");
3781  if ((m_request.url.path().right(6) == ".ps.gz"))
3782  m_strMimeType = TQString::fromLatin1("application/x-gzpostscript");
3783  }
3784 
3785  // Some webservers say "text/plain" when they mean "application/x-bzip2"
3786  else if ((m_strMimeType == "text/plain") || (m_strMimeType == "application/octet-stream"))
3787  {
3788  TQString ext = m_request.url.path().right(4).upper();
3789  if (ext == ".BZ2")
3790  m_strMimeType = TQString::fromLatin1("application/x-bzip2");
3791  else if (ext == ".PEM")
3792  m_strMimeType = TQString::fromLatin1("application/x-x509-ca-cert");
3793  else if (ext == ".SWF")
3794  m_strMimeType = TQString::fromLatin1("application/x-shockwave-flash");
3795  else if (ext == ".PLS")
3796  m_strMimeType = TQString::fromLatin1("audio/x-scpls");
3797  else if (ext == ".WMV")
3798  m_strMimeType = TQString::fromLatin1("video/x-ms-wmv");
3799  }
3800 
3801 #if 0
3802  // Even if we can't rely on content-length, it seems that we should
3803  // never get more data than content-length. Maybe less, if the
3804  // content-length refers to the unzipped data.
3805  if (!m_qContentEncodings.isEmpty())
3806  {
3807  // If we still have content encoding we can't rely on the Content-Length.
3808  m_iSize = NO_SIZE;
3809  }
3810 #endif
3811 
3812  if( !dispositionType.isEmpty() )
3813  {
3814  kdDebug(7113) << "(" << m_pid << ") Setting Content-Disposition type to: "
3815  << dispositionType << endl;
3816  setMetaData("content-disposition-type", dispositionType);
3817  }
3818  if( !dispositionFilename.isEmpty() )
3819  {
3820  kdDebug(7113) << "(" << m_pid << ") Setting Content-Disposition filename to: "
3821  << dispositionFilename << endl;
3822  // ### KDE4: setting content-disposition to filename for pre 3.5.2 compatability
3823  setMetaData("content-disposition", dispositionFilename);
3824  setMetaData("content-disposition-filename", dispositionFilename);
3825  }
3826 
3827  if (!m_request.lastModified.isEmpty())
3828  setMetaData("modified", m_request.lastModified);
3829 
3830  if (!mayCache)
3831  {
3832  setMetaData("no-cache", "true");
3833  setMetaData("expire-date", "1"); // Expired
3834  }
3835  else
3836  {
3837  TQString tmp;
3838  tmp.setNum(expireDate);
3839  setMetaData("expire-date", tmp);
3840  tmp.setNum(time(0)); // Cache entry will be created shortly.
3841  setMetaData("cache-creation-date", tmp);
3842  }
3843 
3844  // Let the app know about the mime-type iff this is not
3845  // a redirection and the mime-type string is not empty.
3846  if (locationStr.isEmpty() && (!m_strMimeType.isEmpty() ||
3847  m_request.method == HTTP_HEAD))
3848  {
3849  kdDebug(7113) << "(" << m_pid << ") Emitting mimetype " << m_strMimeType << endl;
3850  mimeType( m_strMimeType );
3851  }
3852 
3853  // Do not move send response header before any redirection as it seems
3854  // to screw up some sites. See BR# 150904.
3855  forwardHttpResponseHeader();
3856 
3857  if (m_request.method == HTTP_HEAD)
3858  return true;
3859 
3860  // Do we want to cache this request?
3861  if (m_request.bUseCache)
3862  {
3863  ::unlink( TQFile::encodeName(m_request.cef));
3864  if ( m_request.bCachedWrite && !m_strMimeType.isEmpty() )
3865  {
3866  // Check...
3867  createCacheEntry(m_strMimeType, expireDate); // Create a cache entry
3868  if (!m_request.fcache)
3869  {
3870  m_request.bCachedWrite = false; // Error creating cache entry.
3871  kdDebug(7113) << "(" << m_pid << ") Error creating cache entry for " << m_request.url.prettyURL()<<"!\n";
3872  }
3873  m_request.expireDate = expireDate;
3874  m_maxCacheSize = config()->readNumEntry("MaxCacheSize", DEFAULT_MAX_CACHE_SIZE) / 2;
3875  }
3876  }
3877 
3878  if (m_request.bCachedWrite && !m_strMimeType.isEmpty())
3879  kdDebug(7113) << "(" << m_pid << ") Cache, adding \"" << m_request.url.prettyURL() << "\"" << endl;
3880  else if (m_request.bCachedWrite && m_strMimeType.isEmpty())
3881  kdDebug(7113) << "(" << m_pid << ") Cache, pending \"" << m_request.url.prettyURL() << "\"" << endl;
3882  else
3883  kdDebug(7113) << "(" << m_pid << ") Cache, not adding \"" << m_request.url.prettyURL() << "\"" << endl;
3884  return true;
3885 }
3886 
3887 
3888 void HTTPProtocol::addEncoding(TQString encoding, TQStringList &encs)
3889 {
3890  encoding = encoding.stripWhiteSpace().lower();
3891  // Identity is the same as no encoding
3892  if (encoding == "identity") {
3893  return;
3894  } else if (encoding == "8bit") {
3895  // Strange encoding returned by http://linac.ikp.physik.tu-darmstadt.de
3896  return;
3897  } else if (encoding == "chunked") {
3898  m_bChunked = true;
3899  // Anyone know of a better way to handle unknown sizes possibly/ideally with unsigned ints?
3900  //if ( m_cmd != CMD_COPY )
3901  m_iSize = NO_SIZE;
3902  } else if ((encoding == "x-gzip") || (encoding == "gzip")) {
3903  encs.append(TQString::fromLatin1("gzip"));
3904  } else if ((encoding == "x-bzip2") || (encoding == "bzip2")) {
3905  encs.append(TQString::fromLatin1("bzip2")); // Not yet supported!
3906  } else if ((encoding == "x-deflate") || (encoding == "deflate")) {
3907  encs.append(TQString::fromLatin1("deflate"));
3908  } else {
3909  kdDebug(7113) << "(" << m_pid << ") Unknown encoding encountered. "
3910  << "Please write code. Encoding = \"" << encoding
3911  << "\"" << endl;
3912  }
3913 }
3914 
3915 bool HTTPProtocol::sendBody()
3916 {
3917  int result=-1;
3918  int length=0;
3919 
3920  infoMessage( i18n( "Requesting data to send" ) );
3921 
3922  // m_bufPOST will NOT be empty iff authentication was required before posting
3923  // the data OR a re-connect is requested from ::readHeader because the
3924  // connection was lost for some reason.
3925  if ( !m_bufPOST.isNull() )
3926  {
3927  kdDebug(7113) << "(" << m_pid << ") POST'ing saved data..." << endl;
3928 
3929  result = 0;
3930  length = m_bufPOST.size();
3931  }
3932  else
3933  {
3934  kdDebug(7113) << "(" << m_pid << ") POST'ing live data..." << endl;
3935 
3936  TQByteArray buffer;
3937  int old_size;
3938 
3939  m_bufPOST.resize(0);
3940  do
3941  {
3942  dataReq(); // Request for data
3943  result = readData( buffer );
3944  if ( result > 0 )
3945  {
3946  length += result;
3947  old_size = m_bufPOST.size();
3948  m_bufPOST.resize( old_size+result );
3949  memcpy( m_bufPOST.data()+ old_size, buffer.data(), buffer.size() );
3950  buffer.resize(0);
3951  }
3952  } while ( result > 0 );
3953  }
3954 
3955  if ( result < 0 )
3956  {
3957  error( ERR_ABORTED, m_request.hostname );
3958  return false;
3959  }
3960 
3961  infoMessage( i18n( "Sending data to %1" ).arg( m_request.hostname ) );
3962 
3963  TQString size = TQString ("Content-Length: %1\r\n\r\n").arg(length);
3964  kdDebug( 7113 ) << "(" << m_pid << ")" << size << endl;
3965 
3966  // Send the content length...
3967  bool sendOk = (write(size.latin1(), size.length()) == (ssize_t) size.length());
3968  if (!sendOk)
3969  {
3970  kdDebug( 7113 ) << "(" << m_pid << ") Connection broken when sending "
3971  << "content length: (" << m_state.hostname << ")" << endl;
3972  error( ERR_CONNECTION_BROKEN, m_state.hostname );
3973  return false;
3974  }
3975 
3976  // Send the data...
3977  // kdDebug( 7113 ) << "(" << m_pid << ") POST DATA: " << TQCString(m_bufPOST) << endl;
3978  sendOk = (write(m_bufPOST.data(), m_bufPOST.size()) == (ssize_t) m_bufPOST.size());
3979  if (!sendOk)
3980  {
3981  kdDebug(7113) << "(" << m_pid << ") Connection broken when sending message body: ("
3982  << m_state.hostname << ")" << endl;
3983  error( ERR_CONNECTION_BROKEN, m_state.hostname );
3984  return false;
3985  }
3986 
3987  return true;
3988 }
3989 
3990 void HTTPProtocol::httpClose( bool keepAlive )
3991 {
3992  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpClose" << endl;
3993 
3994  if (m_request.fcache)
3995  {
3996  fclose(m_request.fcache);
3997  m_request.fcache = 0;
3998  if (m_request.bCachedWrite)
3999  {
4000  TQString filename = m_request.cef + ".new";
4001  ::unlink( TQFile::encodeName(filename) );
4002  }
4003  }
4004 
4005  // Only allow persistent connections for GET requests.
4006  // NOTE: we might even want to narrow this down to non-form
4007  // based submit requests which will require a meta-data from
4008  // tdehtml.
4009  if (keepAlive && (!m_bUseProxy ||
4010  m_bPersistentProxyConnection || m_bIsTunneled))
4011  {
4012  if (!m_keepAliveTimeout)
4013  m_keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
4014  else if (m_keepAliveTimeout > 2*DEFAULT_KEEP_ALIVE_TIMEOUT)
4015  m_keepAliveTimeout = 2*DEFAULT_KEEP_ALIVE_TIMEOUT;
4016 
4017  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpClose: keep alive (" << m_keepAliveTimeout << ")" << endl;
4018  TQByteArray data;
4019  TQDataStream stream( data, IO_WriteOnly );
4020  stream << int(99); // special: Close connection
4021  setTimeoutSpecialCommand(m_keepAliveTimeout, data);
4022  return;
4023  }
4024 
4025  httpCloseConnection();
4026 }
4027 
4028 void HTTPProtocol::closeConnection()
4029 {
4030  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::closeConnection" << endl;
4031  httpCloseConnection ();
4032 }
4033 
4034 void HTTPProtocol::httpCloseConnection ()
4035 {
4036  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::httpCloseConnection" << endl;
4037  m_bIsTunneled = false;
4038  m_bKeepAlive = false;
4039  closeDescriptor();
4040  setTimeoutSpecialCommand(-1); // Cancel any connection timeout
4041 }
4042 
4043 void HTTPProtocol::slave_status()
4044 {
4045  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::slave_status" << endl;
4046 
4047  if ( m_iSock != -1 && !isConnectionValid() )
4048  httpCloseConnection();
4049 
4050  slaveStatus( m_state.hostname, (m_iSock != -1) );
4051 }
4052 
4053 void HTTPProtocol::mimetype( const KURL& url )
4054 {
4055  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::mimetype: "
4056  << url.prettyURL() << endl;
4057 
4058  if ( !checkRequestURL( url ) )
4059  return;
4060 
4061  m_request.method = HTTP_HEAD;
4062  m_request.path = url.path();
4063  m_request.query = url.query();
4064  m_request.cache = CC_Cache;
4065  m_request.doProxy = m_bUseProxy;
4066 
4067  retrieveHeader();
4068 
4069  kdDebug(7113) << "(" << m_pid << ") http: mimetype = " << m_strMimeType
4070  << endl;
4071 }
4072 
4073 void HTTPProtocol::special( const TQByteArray &data )
4074 {
4075  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::special" << endl;
4076 
4077  int tmp;
4078  TQDataStream stream(data, IO_ReadOnly);
4079 
4080  stream >> tmp;
4081  switch (tmp) {
4082  case 1: // HTTP POST
4083  {
4084  KURL url;
4085  stream >> url;
4086  post( url );
4087  break;
4088  }
4089  case 2: // cache_update
4090  {
4091  KURL url;
4092  bool no_cache;
4093  time_t expireDate;
4094  stream >> url >> no_cache >> expireDate;
4095  cacheUpdate( url, no_cache, expireDate );
4096  break;
4097  }
4098  case 5: // WebDAV lock
4099  {
4100  KURL url;
4101  TQString scope, type, owner;
4102  stream >> url >> scope >> type >> owner;
4103  davLock( url, scope, type, owner );
4104  break;
4105  }
4106  case 6: // WebDAV unlock
4107  {
4108  KURL url;
4109  stream >> url;
4110  davUnlock( url );
4111  break;
4112  }
4113  case 7: // Generic WebDAV
4114  {
4115  KURL url;
4116  int method;
4117  stream >> url >> method;
4118  davGeneric( url, (TDEIO::HTTP_METHOD) method );
4119  break;
4120  }
4121  case 99: // Close Connection
4122  {
4123  httpCloseConnection();
4124  break;
4125  }
4126  default:
4127  // Some command we don't understand.
4128  // Just ignore it, it may come from some future version of KDE.
4129  break;
4130  }
4131 }
4132 
4136 int HTTPProtocol::readChunked()
4137 {
4138  if ((m_iBytesLeft == 0) || (m_iBytesLeft == NO_SIZE))
4139  {
4140  setRewindMarker();
4141 
4142  m_bufReceive.resize(4096);
4143 
4144  if (!gets(m_bufReceive.data(), m_bufReceive.size()-1))
4145  {
4146  kdDebug(7113) << "(" << m_pid << ") gets() failure on Chunk header" << endl;
4147  return -1;
4148  }
4149  // We could have got the CRLF of the previous chunk.
4150  // If so, try again.
4151  if (m_bufReceive[0] == '\0')
4152  {
4153  if (!gets(m_bufReceive.data(), m_bufReceive.size()-1))
4154  {
4155  kdDebug(7113) << "(" << m_pid << ") gets() failure on Chunk header" << endl;
4156  return -1;
4157  }
4158  }
4159 
4160  // m_bEOF is set to true when read called from gets returns 0. For chunked reading 0
4161  // means end of chunked transfer and not error. See RFC 2615 section 3.6.1
4162  #if 0
4163  if (m_bEOF)
4164  {
4165  kdDebug(7113) << "(" << m_pid << ") EOF on Chunk header" << endl;
4166  return -1;
4167  }
4168  #endif
4169 
4170  long long trunkSize = STRTOLL(m_bufReceive.data(), 0, 16);
4171  if (trunkSize < 0)
4172  {
4173  kdDebug(7113) << "(" << m_pid << ") Negative chunk size" << endl;
4174  return -1;
4175  }
4176  m_iBytesLeft = trunkSize;
4177 
4178  // kdDebug(7113) << "(" << m_pid << ") Chunk size = " << m_iBytesLeft << " bytes" << endl;
4179 
4180  if (m_iBytesLeft == 0)
4181  {
4182  // Last chunk.
4183  // Skip trailers.
4184  do {
4185  // Skip trailer of last chunk.
4186  if (!gets(m_bufReceive.data(), m_bufReceive.size()-1))
4187  {
4188  kdDebug(7113) << "(" << m_pid << ") gets() failure on Chunk trailer" << endl;
4189  return -1;
4190  }
4191  // kdDebug(7113) << "(" << m_pid << ") Chunk trailer = \"" << m_bufReceive.data() << "\"" << endl;
4192  }
4193  while (strlen(m_bufReceive.data()) != 0);
4194 
4195  return 0;
4196  }
4197  }
4198 
4199  int bytesReceived = readLimited();
4200  if (!m_iBytesLeft)
4201  m_iBytesLeft = NO_SIZE; // Don't stop, continue with next chunk
4202 
4203  // kdDebug(7113) << "(" << m_pid << ") readChunked: BytesReceived=" << bytesReceived << endl;
4204  return bytesReceived;
4205 }
4206 
4207 int HTTPProtocol::readLimited()
4208 {
4209  if (!m_iBytesLeft)
4210  return 0;
4211 
4212  m_bufReceive.resize(4096);
4213 
4214  int bytesReceived;
4215  int bytesToReceive;
4216 
4217  if (m_iBytesLeft > m_bufReceive.size())
4218  bytesToReceive = m_bufReceive.size();
4219  else
4220  bytesToReceive = m_iBytesLeft;
4221 
4222  bytesReceived = read(m_bufReceive.data(), bytesToReceive);
4223 
4224  if (bytesReceived <= 0)
4225  return -1; // Error: connection lost
4226 
4227  m_iBytesLeft -= bytesReceived;
4228  return bytesReceived;
4229 }
4230 
4231 int HTTPProtocol::readUnlimited()
4232 {
4233  if (m_bKeepAlive)
4234  {
4235  kdDebug(7113) << "(" << m_pid << ") Unbounded datastream on a Keep "
4236  << "alive connection!" << endl;
4237  m_bKeepAlive = false;
4238  }
4239 
4240  m_bufReceive.resize(4096);
4241 
4242  int result = read(m_bufReceive.data(), m_bufReceive.size());
4243  if (result > 0)
4244  return result;
4245 
4246  m_bEOF = true;
4247  m_iBytesLeft = 0;
4248  return 0;
4249 }
4250 
4251 void HTTPProtocol::slotData(const TQByteArray &_d)
4252 {
4253  if (!_d.size())
4254  {
4255  m_bEOD = true;
4256  return;
4257  }
4258 
4259  if (m_iContentLeft != NO_SIZE)
4260  {
4261  if (m_iContentLeft >= _d.size())
4262  m_iContentLeft -= _d.size();
4263  else
4264  m_iContentLeft = NO_SIZE;
4265  }
4266 
4267  TQByteArray d = _d;
4268  if ( !m_dataInternal )
4269  {
4270  // If a broken server does not send the mime-type,
4271  // we try to id it from the content before dealing
4272  // with the content itself.
4273  if ( m_strMimeType.isEmpty() && !m_bRedirect &&
4274  !( m_responseCode >= 300 && m_responseCode <=399) )
4275  {
4276  kdDebug(7113) << "(" << m_pid << ") Determining mime-type from content..." << endl;
4277  int old_size = m_mimeTypeBuffer.size();
4278  m_mimeTypeBuffer.resize( old_size + d.size() );
4279  memcpy( m_mimeTypeBuffer.data() + old_size, d.data(), d.size() );
4280  if ( (m_iBytesLeft != NO_SIZE) && (m_iBytesLeft > 0)
4281  && (m_mimeTypeBuffer.size() < 1024) )
4282  {
4283  m_cpMimeBuffer = true;
4284  return; // Do not send up the data since we do not yet know its mimetype!
4285  }
4286 
4287  kdDebug(7113) << "(" << m_pid << ") Mimetype buffer size: " << m_mimeTypeBuffer.size()
4288  << endl;
4289 
4290  KMimeMagicResult *result;
4291  result = KMimeMagic::self()->findBufferFileType( m_mimeTypeBuffer,
4292  m_request.url.fileName() );
4293  if( result )
4294  {
4295  m_strMimeType = result->mimeType();
4296  kdDebug(7113) << "(" << m_pid << ") Mimetype from content: "
4297  << m_strMimeType << endl;
4298  }
4299 
4300  if ( m_strMimeType.isEmpty() )
4301  {
4302  m_strMimeType = TQString::fromLatin1( DEFAULT_MIME_TYPE );
4303  kdDebug(7113) << "(" << m_pid << ") Using default mimetype: "
4304  << m_strMimeType << endl;
4305  }
4306 
4307  if ( m_request.bCachedWrite )
4308  {
4309  createCacheEntry( m_strMimeType, m_request.expireDate );
4310  if (!m_request.fcache)
4311  m_request.bCachedWrite = false;
4312  }
4313 
4314  if ( m_cpMimeBuffer )
4315  {
4316  // Do not make any assumption about the state of the TQByteArray we received.
4317  // Fix the crash described by BR# 130104.
4318  d.detach();
4319  d.resize(0);
4320  d.resize(m_mimeTypeBuffer.size());
4321  memcpy( d.data(), m_mimeTypeBuffer.data(),
4322  d.size() );
4323  }
4324  mimeType(m_strMimeType);
4325  m_mimeTypeBuffer.resize(0);
4326  }
4327 
4328  data( d );
4329  if (m_request.bCachedWrite && m_request.fcache)
4330  writeCacheEntry(d.data(), d.size());
4331  }
4332  else
4333  {
4334  uint old_size = m_bufWebDavData.size();
4335  m_bufWebDavData.resize (old_size + d.size());
4336  memcpy (m_bufWebDavData.data() + old_size, d.data(), d.size());
4337  }
4338 }
4339 
4349 bool HTTPProtocol::readBody( bool dataInternal /* = false */ )
4350 {
4351  if (m_responseCode == 204)
4352  return true;
4353 
4354  m_bEOD = false;
4355  // Note that when dataInternal is true, we are going to:
4356  // 1) save the body data to a member variable, m_bufWebDavData
4357  // 2) _not_ advertise the data, speed, size, etc., through the
4358  // corresponding functions.
4359  // This is used for returning data to WebDAV.
4360  m_dataInternal = dataInternal;
4361  if ( dataInternal )
4362  m_bufWebDavData.resize (0);
4363 
4364  // Check if we need to decode the data.
4365  // If we are in copy mode, then use only transfer decoding.
4366  bool useMD5 = !m_sContentMD5.isEmpty();
4367 
4368  // Deal with the size of the file.
4369  TDEIO::filesize_t sz = m_request.offset;
4370  if ( sz )
4371  m_iSize += sz;
4372 
4373  // Update the application with total size except when
4374  // it is compressed, or when the data is to be handled
4375  // internally (webDAV). If compressed we have to wait
4376  // until we uncompress to find out the actual data size
4377  if ( !dataInternal ) {
4378  if ( (m_iSize > 0) && (m_iSize != NO_SIZE)) {
4379  totalSize(m_iSize);
4380  infoMessage( i18n( "Retrieving %1 from %2...").arg(TDEIO::convertSize(m_iSize))
4381  .arg( m_request.hostname ) );
4382  }
4383  else
4384  {
4385  totalSize ( 0 );
4386  }
4387  }
4388  else
4389  infoMessage( i18n( "Retrieving from %1..." ).arg( m_request.hostname ) );
4390 
4391  if (m_request.bCachedRead)
4392  {
4393  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readBody: read data from cache!" << endl;
4394  m_request.bCachedWrite = false;
4395 
4396  char buffer[ MAX_IPC_SIZE ];
4397 
4398  m_iContentLeft = NO_SIZE;
4399 
4400  // Jippie! It's already in the cache :-)
4401  while (!feof(m_request.fcache) && !ferror(m_request.fcache))
4402  {
4403  int nbytes = fread( buffer, 1, MAX_IPC_SIZE, m_request.fcache);
4404 
4405  if (nbytes > 0)
4406  {
4407  m_bufReceive.setRawData( buffer, nbytes);
4408  slotData( m_bufReceive );
4409  m_bufReceive.resetRawData( buffer, nbytes );
4410  sz += nbytes;
4411  }
4412  }
4413 
4414  m_bufReceive.resize( 0 );
4415 
4416  if ( !dataInternal )
4417  {
4418  processedSize( sz );
4419  data( TQByteArray() );
4420  }
4421 
4422  return true;
4423  }
4424 
4425 
4426  if (m_iSize != NO_SIZE)
4427  m_iBytesLeft = m_iSize - sz;
4428  else
4429  m_iBytesLeft = NO_SIZE;
4430 
4431  m_iContentLeft = m_iBytesLeft;
4432 
4433  if (m_bChunked)
4434  m_iBytesLeft = NO_SIZE;
4435 
4436  kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::readBody: retrieve data. "
4437  << TDEIO::number(m_iBytesLeft) << " left." << endl;
4438 
4439  // Main incoming loop... Gather everything while we can...
4440  m_cpMimeBuffer = false;
4441  m_mimeTypeBuffer.resize(0);
4442  struct timeval last_tv;
4443  gettimeofday( &last_tv, 0L );
4444 
4445  HTTPFilterChain chain;
4446 
4447  TQObject::connect(&chain, TQT_SIGNAL(output(const TQByteArray &)),
4448  this, TQT_SLOT(slotData(const TQByteArray &)));
4449  TQObject::connect(&chain, TQT_SIGNAL(error(int, const TQString &)),
4450  this, TQT_SLOT(error(int, const TQString &)));
4451 
4452  // decode all of the transfer encodings
4453  while (!m_qTransferEncodings.isEmpty())
4454  {
4455  TQString enc = m_qTransferEncodings.last();
4456  m_qTransferEncodings.remove(m_qTransferEncodings.fromLast());
4457  if ( enc == "gzip" )
4458  chain.addFilter(new HTTPFilterGZip);
4459  else if ( enc == "deflate" )
4460  chain.addFilter(new HTTPFilterDeflate);
4461  }
4462 
4463  // From HTTP 1.1 Draft 6:
4464  // The MD5 digest is computed based on the content of the entity-body,
4465  // including any content-coding that has been applied, but not including
4466  // any transfer-encoding applied to the message-body. If the message is
4467  // received with a transfer-encoding, that encoding MUST be removed
4468  // prior to checking the Content-MD5 value against the received entity.
4469  HTTPFilterMD5 *md5Filter = 0;
4470  if ( useMD5 )
4471  {
4472  md5Filter = new HTTPFilterMD5;
4473  chain.addFilter(md5Filter);
4474  }
4475 
4476  // now decode all of the content encodings
4477  // -- Why ?? We are not
4478  // -- a proxy server, be a client side implementation!! The applications
4479  // -- are capable of determinig how to extract the encoded implementation.
4480  // WB: That's a misunderstanding. We are free to remove the encoding.
4481  // WB: Some braindead www-servers however, give .tgz files an encoding
4482  // WB: of "gzip" (or even "x-gzip") and a content-type of "applications/tar"
4483  // WB: They shouldn't do that. We can work around that though...
4484  while (!m_qContentEncodings.isEmpty())
4485  {
4486  TQString enc = m_qContentEncodings.last();
4487  m_qContentEncodings.remove(m_qContentEncodings.fromLast());
4488  if ( enc == "gzip" )
4489  chain.addFilter(new HTTPFilterGZip);
4490  else if ( enc == "deflate" )
4491  chain.addFilter(new HTTPFilterDeflate);
4492  }
4493 
4494  while (!m_bEOF)
4495  {
4496  int bytesReceived;
4497 
4498  if (m_bChunked)
4499  bytesReceived = readChunked();
4500  else if (m_iSize != NO_SIZE)
4501  bytesReceived = readLimited();
4502  else
4503  bytesReceived = readUnlimited();
4504 
4505  // make sure that this wasn't an error, first
4506  // kdDebug(7113) << "(" << (int) m_pid << ") readBody: bytesReceived: "
4507  // << (int) bytesReceived << " m_iSize: " << (int) m_iSize << " Chunked: "
4508  // << (int) m_bChunked << " BytesLeft: "<< (int) m_iBytesLeft << endl;
4509  if (bytesReceived == -1)
4510  {
4511  if (m_iContentLeft == 0)
4512  {
4513  // gzip'ed data sometimes reports a too long content-length.
4514  // (The length of the unzipped data)
4515  m_iBytesLeft = 0;
4516  break;
4517  }
4518  // Oh well... log an error and bug out
4519  kdDebug(7113) << "(" << m_pid << ") readBody: bytesReceived==-1 sz=" << (int)sz
4520  << " Connnection broken !" << endl;
4521  error(ERR_CONNECTION_BROKEN, m_state.hostname);
4522  return false;
4523  }
4524 
4525  // I guess that nbytes == 0 isn't an error.. but we certainly
4526  // won't work with it!
4527  if (bytesReceived > 0)
4528  {
4529  // Important: truncate the buffer to the actual size received!
4530  // Otherwise garbage will be passed to the app
4531  m_bufReceive.truncate( bytesReceived );
4532 
4533  chain.slotInput(m_bufReceive);
4534 
4535  if (m_bError)
4536  return false;
4537 
4538  sz += bytesReceived;
4539  if (!dataInternal)
4540  processedSize( sz );
4541  }
4542  m_bufReceive.resize(0); // res
4543 
4544  if (m_iBytesLeft && m_bEOD && !m_bChunked)
4545  {
4546  // gzip'ed data sometimes reports a too long content-length.
4547  // (The length of the unzipped data)
4548  m_iBytesLeft = 0;
4549  }
4550 
4551  if (m_iBytesLeft == 0)
4552  {
4553  kdDebug(7113) << "("<<m_pid<<") EOD received! Left = "<< TDEIO::number(m_iBytesLeft) << endl;
4554  break;
4555  }
4556  }
4557  chain.slotInput(TQByteArray()); // Flush chain.
4558 
4559  if ( useMD5 )
4560  {
4561  TQString calculatedMD5 = md5Filter->md5();
4562 
4563  if ( m_sContentMD5 == calculatedMD5 )
4564  kdDebug(7113) << "(" << m_pid << ") MD5 checksum MATCHED!!" << endl;
4565  else
4566  kdDebug(7113) << "(" << m_pid << ") MD5 checksum MISMATCH! Expected: "
4567  << calculatedMD5 << ", Got: " << m_sContentMD5 << endl;
4568  }
4569 
4570  // Close cache entry
4571  if (m_iBytesLeft == 0)
4572  {
4573  if (m_request.bCachedWrite && m_request.fcache)
4574  closeCacheEntry();
4575  else if (m_request.bCachedWrite)
4576  kdDebug(7113) << "(" << m_pid << ") no cache file!\n";
4577  }
4578  else
4579  {
4580  kdDebug(7113) << "(" << m_pid << ") still "<< TDEIO::number(m_iBytesLeft)
4581  << " bytes left! can't close cache entry!\n";
4582  }
4583 
4584  if (sz <= 1)
4585  {
4586  /* kdDebug(7113) << "(" << m_pid << ") readBody: sz = " << TDEIO::number(sz)
4587  << ", responseCode =" << m_responseCode << endl; */
4588  if (m_responseCode >= 500 && m_responseCode <= 599)
4589  error(ERR_INTERNAL_SERVER, m_state.hostname);
4590  else if (m_responseCode >= 400 && m_responseCode <= 499)
4591  error(ERR_DOES_NOT_EXIST, m_state.hostname);
4592  }
4593 
4594  if (!dataInternal)
4595  data( TQByteArray() );
4596 
4597  return true;
4598 }
4599 
4600 
4601 void HTTPProtocol::error( int _err, const TQString &_text )
4602 {
4603  httpClose(false);
4604 
4605  if (!m_request.id.isEmpty())
4606  {
4607  forwardHttpResponseHeader();
4608  sendMetaData();
4609  }
4610 
4611  // Clear of the temporary POST buffer if it is not empty...
4612  if (!m_bufPOST.isEmpty())
4613  {
4614  m_bufPOST.resize(0);
4615  kdDebug(7113) << "(" << m_pid << ") HTTP::retreiveHeader: Cleared POST "
4616  "buffer..." << endl;
4617  }
4618 
4619  SlaveBase::error( _err, _text );
4620  m_bError = true;
4621 }
4622 
4623 
4624 void HTTPProtocol::addCookies( const TQString &url, const TQCString &cookieHeader )
4625 {
4626  long windowId = m_request.window.toLong();
4627  TQByteArray params;
4628  TQDataStream stream(params, IO_WriteOnly);
4629  stream << url << cookieHeader << windowId;
4630 
4631  kdDebug(7113) << "(" << m_pid << ") " << cookieHeader << endl;
4632  kdDebug(7113) << "(" << m_pid << ") " << "Window ID: "
4633  << windowId << ", for host = " << url << endl;
4634 
4635  if ( !dcopClient()->send( "kded", "kcookiejar", "addCookies(TQString,TQCString,long int)", params ) )
4636  {
4637  kdWarning(7113) << "(" << m_pid << ") Can't communicate with kded_kcookiejar!" << endl;
4638  }
4639 }
4640 
4641 TQString HTTPProtocol::findCookies( const TQString &url)
4642 {
4643  TQCString replyType;
4644  TQByteArray params;
4645  TQByteArray reply;
4646  TQString result;
4647 
4648  long windowId = m_request.window.toLong();
4649  result = TQString::null;
4650  TQDataStream stream(params, IO_WriteOnly);
4651  stream << url << windowId;
4652 
4653  if ( !dcopClient()->call( "kded", "kcookiejar", "findCookies(TQString,long int)",
4654  params, replyType, reply ) )
4655  {
4656  kdWarning(7113) << "(" << m_pid << ") Can't communicate with kded_kcookiejar!" << endl;
4657  return result;
4658  }
4659  if ( replyType == "TQString" )
4660  {
4661  TQDataStream stream2( reply, IO_ReadOnly );
4662  stream2 >> result;
4663  }
4664  else
4665  {
4666  kdError(7113) << "(" << m_pid << ") DCOP function findCookies(...) returns "
4667  << replyType << ", expected TQString" << endl;
4668  }
4669  return result;
4670 }
4671 
4672 /******************************* CACHING CODE ****************************/
4673 
4674 
4675 void HTTPProtocol::cacheUpdate( const KURL& url, bool no_cache, time_t expireDate)
4676 {
4677  if ( !checkRequestURL( url ) )
4678  return;
4679 
4680  m_request.path = url.path();
4681  m_request.query = url.query();
4682  m_request.cache = CC_Reload;
4683  m_request.doProxy = m_bUseProxy;
4684 
4685  if (no_cache)
4686  {
4687  m_request.fcache = checkCacheEntry( );
4688  if (m_request.fcache)
4689  {
4690  fclose(m_request.fcache);
4691  m_request.fcache = 0;
4692  ::unlink( TQFile::encodeName(m_request.cef) );
4693  }
4694  }
4695  else
4696  {
4697  updateExpireDate( expireDate );
4698  }
4699  finished();
4700 }
4701 
4702 // !START SYNC!
4703 // The following code should be kept in sync
4704 // with the code in http_cache_cleaner.cpp
4705 
4706 FILE* HTTPProtocol::checkCacheEntry( bool readWrite)
4707 {
4708  const TQChar separator = '_';
4709 
4710  TQString CEF = m_request.path;
4711 
4712  int p = CEF.find('/');
4713 
4714  while(p != -1)
4715  {
4716  CEF[p] = separator;
4717  p = CEF.find('/', p);
4718  }
4719 
4720  TQString host = m_request.hostname.lower();
4721  CEF = host + CEF + '_';
4722 
4723  TQString dir = m_strCacheDir;
4724  if (dir[dir.length()-1] != '/')
4725  dir += "/";
4726 
4727  int l = host.length();
4728  for(int i = 0; i < l; i++)
4729  {
4730  if (host[i].isLetter() && (host[i] != 'w'))
4731  {
4732  dir += host[i];
4733  break;
4734  }
4735  }
4736  if (dir[dir.length()-1] == '/')
4737  dir += "0";
4738 
4739  unsigned long hash = 0x00000000;
4740  TQCString u = m_request.url.url().latin1();
4741  for(int i = u.length(); i--;)
4742  {
4743  hash = (hash * 12211 + static_cast<const char>(u.at(i))) % 2147483563;
4744  }
4745 
4746  TQString hashString;
4747  hashString.sprintf("%08lx", hash);
4748 
4749  CEF = CEF + hashString;
4750 
4751  CEF = dir + "/" + CEF;
4752 
4753  m_request.cef = CEF;
4754 
4755  const char *mode = (readWrite ? "r+" : "r");
4756 
4757  FILE *fs = fopen( TQFile::encodeName(CEF), mode); // Open for reading and writing
4758  if (!fs)
4759  return 0;
4760 
4761  char buffer[401];
4762  bool ok = true;
4763 
4764  // CacheRevision
4765  if (ok && (!fgets(buffer, 400, fs)))
4766  ok = false;
4767  if (ok && (strcmp(buffer, CACHE_REVISION) != 0))
4768  ok = false;
4769 
4770  time_t date;
4771  time_t currentDate = time(0);
4772 
4773  // URL
4774  if (ok && (!fgets(buffer, 400, fs)))
4775  ok = false;
4776  if (ok)
4777  {
4778  int l = strlen(buffer);
4779  if (l>0)
4780  buffer[l-1] = 0; // Strip newline
4781  if (m_request.url.url() != buffer)
4782  {
4783  ok = false; // Hash collision
4784  }
4785  }
4786 
4787  // Creation Date
4788  if (ok && (!fgets(buffer, 400, fs)))
4789  ok = false;
4790  if (ok)
4791  {
4792  date = (time_t) strtoul(buffer, 0, 10);
4793  m_request.creationDate = date;
4794  if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge))
4795  {
4796  m_request.bMustRevalidate = true;
4797  m_request.expireDate = currentDate;
4798  }
4799  }
4800 
4801  // Expiration Date
4802  m_request.cacheExpireDateOffset = ftell(fs);
4803  if (ok && (!fgets(buffer, 400, fs)))
4804  ok = false;
4805  if (ok)
4806  {
4807  if (m_request.cache == CC_Verify)
4808  {
4809  date = (time_t) strtoul(buffer, 0, 10);
4810  // After the expire date we need to revalidate.
4811  if (!date || difftime(currentDate, date) >= 0)
4812  m_request.bMustRevalidate = true;
4813  m_request.expireDate = date;
4814  }
4815  else if (m_request.cache == CC_Refresh)
4816  {
4817  m_request.bMustRevalidate = true;
4818  m_request.expireDate = currentDate;
4819  }
4820  }
4821 
4822  // ETag
4823  if (ok && (!fgets(buffer, 400, fs)))
4824  ok = false;
4825  if (ok)
4826  {
4827  m_request.etag = TQString(buffer).stripWhiteSpace();
4828  }
4829 
4830  // Last-Modified
4831  if (ok && (!fgets(buffer, 400, fs)))
4832  ok = false;
4833  if (ok)
4834  {
4835  m_request.lastModified = TQString(buffer).stripWhiteSpace();
4836  }
4837 
4838  if (ok)
4839  return fs;
4840 
4841  fclose(fs);
4842  unlink( TQFile::encodeName(CEF));
4843  return 0;
4844 }
4845 
4846 void HTTPProtocol::updateExpireDate(time_t expireDate, bool updateCreationDate)
4847 {
4848  bool ok = true;
4849 
4850  FILE *fs = checkCacheEntry(true);
4851  if (fs)
4852  {
4853  TQString date;
4854  char buffer[401];
4855  time_t creationDate;
4856 
4857  fseek(fs, 0, SEEK_SET);
4858  if (ok && !fgets(buffer, 400, fs))
4859  ok = false;
4860  if (ok && !fgets(buffer, 400, fs))
4861  ok = false;
4862  long cacheCreationDateOffset = ftell(fs);
4863  if (ok && !fgets(buffer, 400, fs))
4864  ok = false;
4865  creationDate = strtoul(buffer, 0, 10);
4866  if (!creationDate)
4867  ok = false;
4868 
4869  if (updateCreationDate)
4870  {
4871  if (!ok || fseek(fs, cacheCreationDateOffset, SEEK_SET))
4872  return;
4873  TQString date;
4874  date.setNum( time(0) );
4875  date = date.leftJustify(16);
4876  fputs(date.latin1(), fs); // Creation date
4877  fputc('\n', fs);
4878  }
4879 
4880  if (expireDate>(30*365*24*60*60))
4881  {
4882  // expire date is a really a big number, it can't be
4883  // a relative date.
4884  date.setNum( expireDate );
4885  }
4886  else
4887  {
4888  // expireDate before 2000. those values must be
4889  // interpreted as relative expiration dates from
4890  // <META http-equiv="Expires"> tags.
4891  // so we have to scan the creation time and add
4892  // it to the expiryDate
4893  date.setNum( creationDate + expireDate );
4894  }
4895  date = date.leftJustify(16);
4896  if (!ok || fseek(fs, m_request.cacheExpireDateOffset, SEEK_SET))
4897  return;
4898  fputs(date.latin1(), fs); // Expire date
4899  fseek(fs, 0, SEEK_END);
4900  fclose(fs);
4901  }
4902 }
4903 
4904 void HTTPProtocol::createCacheEntry( const TQString &mimetype, time_t expireDate)
4905 {
4906  TQString dir = m_request.cef;
4907  int p = dir.findRev('/');
4908  if (p == -1) return; // Error.
4909  dir.truncate(p);
4910 
4911  // Create file
4912  (void) ::mkdir( TQFile::encodeName(dir), 0700 );
4913 
4914  TQString filename = m_request.cef + ".new"; // Create a new cache entryexpireDate
4915 
4916 // kdDebug( 7103 ) << "creating new cache entry: " << filename << endl;
4917 
4918  m_request.fcache = fopen( TQFile::encodeName(filename), "w");
4919  if (!m_request.fcache)
4920  {
4921  kdWarning(7113) << "(" << m_pid << ")createCacheEntry: opening " << filename << " failed." << endl;
4922  return; // Error.
4923  }
4924 
4925  fputs(CACHE_REVISION, m_request.fcache); // Revision
4926 
4927  fputs(m_request.url.url().latin1(), m_request.fcache); // Url
4928  fputc('\n', m_request.fcache);
4929 
4930  TQString date;
4931  m_request.creationDate = time(0);
4932  date.setNum( m_request.creationDate );
4933  date = date.leftJustify(16);
4934  fputs(date.latin1(), m_request.fcache); // Creation date
4935  fputc('\n', m_request.fcache);
4936 
4937  date.setNum( expireDate );
4938  date = date.leftJustify(16);
4939  fputs(date.latin1(), m_request.fcache); // Expire date
4940  fputc('\n', m_request.fcache);
4941 
4942  if (!m_request.etag.isEmpty())
4943  fputs(m_request.etag.latin1(), m_request.fcache); //ETag
4944  fputc('\n', m_request.fcache);
4945 
4946  if (!m_request.lastModified.isEmpty())
4947  fputs(m_request.lastModified.latin1(), m_request.fcache); // Last modified
4948  fputc('\n', m_request.fcache);
4949 
4950  fputs(mimetype.latin1(), m_request.fcache); // Mimetype
4951  fputc('\n', m_request.fcache);
4952 
4953  if (!m_request.strCharset.isEmpty())
4954  fputs(m_request.strCharset.latin1(), m_request.fcache); // Charset
4955  fputc('\n', m_request.fcache);
4956 
4957  return;
4958 }
4959 // The above code should be kept in sync
4960 // with the code in http_cache_cleaner.cpp
4961 // !END SYNC!
4962 
4963 void HTTPProtocol::writeCacheEntry( const char *buffer, int nbytes)
4964 {
4965  if (fwrite( buffer, nbytes, 1, m_request.fcache) != 1)
4966  {
4967  kdWarning(7113) << "(" << m_pid << ") writeCacheEntry: writing " << nbytes << " bytes failed." << endl;
4968  fclose(m_request.fcache);
4969  m_request.fcache = 0;
4970  TQString filename = m_request.cef + ".new";
4971  ::unlink( TQFile::encodeName(filename) );
4972  return;
4973  }
4974  long file_pos = ftell( m_request.fcache ) / 1024;
4975  if ( file_pos > m_maxCacheSize )
4976  {
4977  kdDebug(7113) << "writeCacheEntry: File size reaches " << file_pos
4978  << "Kb, exceeds cache limits. (" << m_maxCacheSize << "Kb)" << endl;
4979  fclose(m_request.fcache);
4980  m_request.fcache = 0;
4981  TQString filename = m_request.cef + ".new";
4982  ::unlink( TQFile::encodeName(filename) );
4983  return;
4984  }
4985 }
4986 
4987 void HTTPProtocol::closeCacheEntry()
4988 {
4989  TQString filename = m_request.cef + ".new";
4990  int result = fclose( m_request.fcache);
4991  m_request.fcache = 0;
4992  if (result == 0)
4993  {
4994  if (::rename( TQFile::encodeName(filename), TQFile::encodeName(m_request.cef)) == 0)
4995  return; // Success
4996 
4997  kdWarning(7113) << "(" << m_pid << ") closeCacheEntry: error renaming "
4998  << "cache entry. (" << filename << " -> " << m_request.cef
4999  << ")" << endl;
5000  }
5001 
5002  kdWarning(7113) << "(" << m_pid << ") closeCacheEntry: error closing cache "
5003  << "entry. (" << filename<< ")" << endl;
5004 }
5005 
5006 void HTTPProtocol::cleanCache()
5007 {
5008  const time_t maxAge = DEFAULT_CLEAN_CACHE_INTERVAL; // 30 Minutes.
5009  bool doClean = false;
5010  TQString cleanFile = m_strCacheDir;
5011  if (cleanFile[cleanFile.length()-1] != '/')
5012  cleanFile += "/";
5013  cleanFile += "cleaned";
5014 
5015  struct stat stat_buf;
5016 
5017  int result = ::stat(TQFile::encodeName(cleanFile), &stat_buf);
5018  if (result == -1)
5019  {
5020  int fd = creat( TQFile::encodeName(cleanFile), 0600);
5021  if (fd != -1)
5022  {
5023  doClean = true;
5024  ::close(fd);
5025  }
5026  }
5027  else
5028  {
5029  time_t age = (time_t) difftime( time(0), stat_buf.st_mtime );
5030  if (age > maxAge) //
5031  doClean = true;
5032  }
5033  if (doClean)
5034  {
5035  // Touch file.
5036  utime(TQFile::encodeName(cleanFile), 0);
5037  TDEApplication::startServiceByDesktopPath("http_cache_cleaner.desktop");
5038  }
5039 }
5040 
5041 
5042 
5043 //************************** AUTHENTICATION CODE ********************/
5044 
5045 
5046 void HTTPProtocol::configAuth( char *p, bool isForProxy )
5047 {
5048  HTTP_AUTH f = AUTH_None;
5049  const char *strAuth = p;
5050 
5051  if ( strncasecmp( p, "Basic", 5 ) == 0 )
5052  {
5053  f = AUTH_Basic;
5054  p += 5;
5055  strAuth = "Basic"; // Correct for upper-case variations.
5056  }
5057  else if ( strncasecmp (p, "Digest", 6) == 0 )
5058  {
5059  f = AUTH_Digest;
5060  memcpy((void *)p, "Digest", 6); // Correct for upper-case variations.
5061  p += 6;
5062  }
5063  else if (strncasecmp( p, "MBS_PWD_COOKIE", 14 ) == 0)
5064  {
5065  // Found on http://www.webscription.net/baen/default.asp
5066  f = AUTH_Basic;
5067  p += 14;
5068  strAuth = "Basic";
5069  }
5070 #ifdef HAVE_LIBGSSAPI
5071  else if ( strncasecmp( p, "Negotiate", 9 ) == 0 )
5072  {
5073  // if we get two 401 in a row let's assume for now that
5074  // Negotiate isn't working and ignore it
5075  if ( !isForProxy && !(m_responseCode == 401 && m_prevResponseCode == 401) )
5076  {
5077  f = AUTH_Negotiate;
5078  memcpy((void *)p, "Negotiate", 9); // Correct for upper-case variations.
5079  p += 9;
5080  };
5081  }
5082 #endif
5083  else if ( strncasecmp( p, "NTLM", 4 ) == 0 )
5084  {
5085  f = AUTH_NTLM;
5086  memcpy((void *)p, "NTLM", 4); // Correct for upper-case variations.
5087  p += 4;
5088  m_strRealm = "NTLM"; // set a dummy realm
5089  }
5090  else
5091  {
5092  kdWarning(7113) << "(" << m_pid << ") Unsupported or invalid authorization "
5093  << "type requested" << endl;
5094  if (isForProxy)
5095  kdWarning(7113) << "(" << m_pid << ") Proxy URL: " << m_proxyURL << endl;
5096  else
5097  kdWarning(7113) << "(" << m_pid << ") URL: " << m_request.url << endl;
5098  kdWarning(7113) << "(" << m_pid << ") Request Authorization: " << p << endl;
5099  }
5100 
5101  /*
5102  This check ensures the following:
5103  1.) Rejection of any unknown/unsupported authentication schemes
5104  2.) Usage of the strongest possible authentication schemes if
5105  and when multiple Proxy-Authenticate or WWW-Authenticate
5106  header field is sent.
5107  */
5108  if (isForProxy)
5109  {
5110  if ((f == AUTH_None) ||
5111  ((m_iProxyAuthCount > 0) && (f < ProxyAuthentication)))
5112  {
5113  // Since I purposefully made the Proxy-Authentication settings
5114  // persistent to reduce the number of round-trips to tdesud we
5115  // have to take special care when an unknown/unsupported auth-
5116  // scheme is received. This check accomplishes just that...
5117  if ( m_iProxyAuthCount == 0)
5118  ProxyAuthentication = f;
5119  kdDebug(7113) << "(" << m_pid << ") Rejected proxy auth method: " << f << endl;
5120  return;
5121  }
5122  m_iProxyAuthCount++;
5123  kdDebug(7113) << "(" << m_pid << ") Accepted proxy auth method: " << f << endl;
5124  }
5125  else
5126  {
5127  if ((f == AUTH_None) ||
5128  ((m_iWWWAuthCount > 0) && (f < Authentication)))
5129  {
5130  kdDebug(7113) << "(" << m_pid << ") Rejected auth method: " << f << endl;
5131  return;
5132  }
5133  m_iWWWAuthCount++;
5134  kdDebug(7113) << "(" << m_pid << ") Accepted auth method: " << f << endl;
5135  }
5136 
5137 
5138  while (*p)
5139  {
5140  int i = 0;
5141  while( (*p == ' ') || (*p == ',') || (*p == '\t') ) { p++; }
5142  if ( strncasecmp( p, "realm=", 6 ) == 0 )
5143  {
5144  //for sites like lib.homelinux.org
5145  TQTextCodec* oldCodec=TQTextCodec::codecForCStrings();
5146  if (TDEGlobal::locale()->language().contains("ru"))
5147  TQTextCodec::setCodecForCStrings(TQTextCodec::codecForName("CP1251"));
5148 
5149  p += 6;
5150  if (*p == '"') p++;
5151  while( p[i] && p[i] != '"' ) i++;
5152  if( isForProxy )
5153  m_strProxyRealm = TQString::fromAscii( p, i );
5154  else
5155  m_strRealm = TQString::fromAscii( p, i );
5156 
5157  TQTextCodec::setCodecForCStrings(oldCodec);
5158 
5159  if (!p[i]) break;
5160  }
5161  p+=(i+1);
5162  }
5163 
5164  if( isForProxy )
5165  {
5166  ProxyAuthentication = f;
5167  m_strProxyAuthorization = TQString::fromLatin1( strAuth );
5168  }
5169  else
5170  {
5171  Authentication = f;
5172  m_strAuthorization = TQString::fromLatin1( strAuth );
5173  }
5174 }
5175 
5176 
5177 bool HTTPProtocol::retryPrompt()
5178 {
5179  TQString prompt;
5180  switch ( m_responseCode )
5181  {
5182  case 401:
5183  prompt = i18n("Authentication Failed.");
5184  break;
5185  case 407:
5186  prompt = i18n("Proxy Authentication Failed.");
5187  break;
5188  default:
5189  break;
5190  }
5191  prompt += i18n(" Do you want to retry?");
5192  return (messageBox(QuestionYesNo, prompt, i18n("Authentication")) == 3);
5193 }
5194 
5195 void HTTPProtocol::promptInfo( AuthInfo& info )
5196 {
5197  if ( m_responseCode == 401 )
5198  {
5199  info.url = m_request.url;
5200  if ( !m_state.user.isEmpty() )
5201  info.username = m_state.user;
5202  info.readOnly = !m_request.url.user().isEmpty();
5203  info.prompt = i18n( "You need to supply a username and a "
5204  "password to access this site." );
5205  info.keepPassword = true; // Prompt the user for persistence as well.
5206  if ( !m_strRealm.isEmpty() )
5207  {
5208  info.realmValue = m_strRealm;
5209  info.verifyPath = false;
5210  info.digestInfo = m_strAuthorization;
5211  info.commentLabel = i18n( "Site:" );
5212  info.comment = i18n("<b>%1</b> at <b>%2</b>").arg( htmlEscape(m_strRealm) ).arg( m_request.hostname );
5213  }
5214  }
5215  else if ( m_responseCode == 407 )
5216  {
5217  info.url = m_proxyURL;
5218  info.username = m_proxyURL.user();
5219  info.prompt = i18n( "You need to supply a username and a password for "
5220  "the proxy server listed below before you are allowed "
5221  "to access any sites." );
5222  info.keepPassword = true;
5223  if ( !m_strProxyRealm.isEmpty() )
5224  {
5225  info.realmValue = m_strProxyRealm;
5226  info.verifyPath = false;
5227  info.digestInfo = m_strProxyAuthorization;
5228  info.commentLabel = i18n( "Proxy:" );
5229  info.comment = i18n("<b>%1</b> at <b>%2</b>").arg( htmlEscape(m_strProxyRealm) ).arg( m_proxyURL.host() );
5230  }
5231  }
5232 }
5233 
5234 bool HTTPProtocol::getAuthorization()
5235 {
5236  AuthInfo info;
5237  bool result = false;
5238 
5239  kdDebug (7113) << "(" << m_pid << ") HTTPProtocol::getAuthorization: "
5240  << "Current Response: " << m_responseCode << ", "
5241  << "Previous Response: " << m_prevResponseCode << ", "
5242  << "Authentication: " << Authentication << ", "
5243  << "ProxyAuthentication: " << ProxyAuthentication << endl;
5244 
5245  if (m_request.bNoAuth)
5246  {
5247  if (m_request.bErrorPage)
5248  errorPage();
5249  else
5250  error( ERR_COULD_NOT_LOGIN, i18n("Authentication needed for %1 but authentication is disabled.").arg(m_request.hostname));
5251  return false;
5252  }
5253 
5254  bool repeatFailure = (m_prevResponseCode == m_responseCode);
5255 
5256  TQString errorMsg;
5257 
5258  if (repeatFailure)
5259  {
5260  bool prompt = true;
5261  if ( Authentication == AUTH_Digest || ProxyAuthentication == AUTH_Digest )
5262  {
5263  bool isStaleNonce = false;
5264  TQString auth = ( m_responseCode == 401 ) ? m_strAuthorization : m_strProxyAuthorization;
5265  int pos = auth.find("stale", 0, false);
5266  if ( pos != -1 )
5267  {
5268  pos += 5;
5269  int len = auth.length();
5270  while( pos < len && (auth[pos] == ' ' || auth[pos] == '=') ) pos++;
5271  if ( pos < len && auth.find("true", pos, false) != -1 )
5272  {
5273  isStaleNonce = true;
5274  kdDebug(7113) << "(" << m_pid << ") Stale nonce value. "
5275  << "Will retry using same info..." << endl;
5276  }
5277  }
5278  if ( isStaleNonce )
5279  {
5280  prompt = false;
5281  result = true;
5282  if ( m_responseCode == 401 )
5283  {
5284  info.username = m_request.user;
5285  info.password = m_request.passwd;
5286  info.realmValue = m_strRealm;
5287  info.digestInfo = m_strAuthorization;
5288  }
5289  else if ( m_responseCode == 407 )
5290  {
5291  info.username = m_proxyURL.user();
5292  info.password = m_proxyURL.pass();
5293  info.realmValue = m_strProxyRealm;
5294  info.digestInfo = m_strProxyAuthorization;
5295  }
5296  }
5297  }
5298 
5299  if ( Authentication == AUTH_NTLM || ProxyAuthentication == AUTH_NTLM )
5300  {
5301  TQString auth = ( m_responseCode == 401 ) ? m_strAuthorization : m_strProxyAuthorization;
5302  kdDebug(7113) << "auth: " << auth << endl;
5303  if ( auth.length() > 4 )
5304  {
5305  prompt = false;
5306  result = true;
5307  kdDebug(7113) << "(" << m_pid << ") NTLM auth second phase, "
5308  << "sending response..." << endl;
5309  if ( m_responseCode == 401 )
5310  {
5311  info.username = m_request.user;
5312  info.password = m_request.passwd;
5313  info.realmValue = m_strRealm;
5314  info.digestInfo = m_strAuthorization;
5315  }
5316  else if ( m_responseCode == 407 )
5317  {
5318  info.username = m_proxyURL.user();
5319  info.password = m_proxyURL.pass();
5320  info.realmValue = m_strProxyRealm;
5321  info.digestInfo = m_strProxyAuthorization;
5322  }
5323  }
5324  }
5325 
5326  if ( prompt )
5327  {
5328  switch ( m_responseCode )
5329  {
5330  case 401:
5331  errorMsg = i18n("Authentication Failed.");
5332  break;
5333  case 407:
5334  errorMsg = i18n("Proxy Authentication Failed.");
5335  break;
5336  default:
5337  break;
5338  }
5339  }
5340  }
5341  else
5342  {
5343  // At this point we know more details, so use it to find
5344  // out if we have a cached version and avoid a re-prompt!
5345  // We also do not use verify path unlike the pre-emptive
5346  // requests because we already know the realm value...
5347 
5348  if (m_bProxyAuthValid)
5349  {
5350  // Reset cached proxy auth
5351  m_bProxyAuthValid = false;
5352  KURL proxy ( config()->readEntry("UseProxy") );
5353  m_proxyURL.setUser(proxy.user());
5354  m_proxyURL.setPass(proxy.pass());
5355  }
5356 
5357  info.verifyPath = false;
5358  if ( m_responseCode == 407 )
5359  {
5360  info.url = m_proxyURL;
5361  info.username = m_proxyURL.user();
5362  info.password = m_proxyURL.pass();
5363  info.realmValue = m_strProxyRealm;
5364  info.digestInfo = m_strProxyAuthorization;
5365  }
5366  else
5367  {
5368  info.url = m_request.url;
5369  info.username = m_request.user;
5370  info.password = m_request.passwd;
5371  info.realmValue = m_strRealm;
5372  info.digestInfo = m_strAuthorization;
5373  }
5374 
5375  // If either username or password is not supplied
5376  // with the request, check the password cache.
5377  if ( info.username.isNull() ||
5378  info.password.isNull() )
5379  result = checkCachedAuthentication( info );
5380 
5381  if ( Authentication == AUTH_Digest )
5382  {
5383  TQString auth;
5384 
5385  if (m_responseCode == 401)
5386  auth = m_strAuthorization;
5387  else
5388  auth = m_strProxyAuthorization;
5389 
5390  int pos = auth.find("stale", 0, false);
5391  if ( pos != -1 )
5392  {
5393  pos += 5;
5394  int len = auth.length();
5395  while( pos < len && (auth[pos] == ' ' || auth[pos] == '=') ) pos++;
5396  if ( pos < len && auth.find("true", pos, false) != -1 )
5397  {
5398  info.digestInfo = (m_responseCode == 401) ? m_strAuthorization : m_strProxyAuthorization;
5399  kdDebug(7113) << "(" << m_pid << ") Just a stale nonce value! "
5400  << "Retrying using the new nonce sent..." << endl;
5401  }
5402  }
5403  }
5404  }
5405 
5406  if (!result )
5407  {
5408  // Do not prompt if the username & password
5409  // is already supplied and the login attempt
5410  // did not fail before.
5411  if ( !repeatFailure &&
5412  !info.username.isNull() &&
5413  !info.password.isNull() )
5414  result = true;
5415  else
5416  {
5417  if (Authentication == AUTH_Negotiate)
5418  {
5419  if (!repeatFailure)
5420  result = true;
5421  }
5422  else if ( m_request.disablePassDlg == false )
5423  {
5424  kdDebug( 7113 ) << "(" << m_pid << ") Prompting the user for authorization..." << endl;
5425  promptInfo( info );
5426  result = openPassDlg( info, errorMsg );
5427  }
5428  }
5429  }
5430 
5431  if ( result )
5432  {
5433  switch (m_responseCode)
5434  {
5435  case 401: // Request-Authentication
5436  m_request.user = info.username;
5437  m_request.passwd = info.password;
5438  m_strRealm = info.realmValue;
5439  m_strAuthorization = info.digestInfo;
5440  break;
5441  case 407: // Proxy-Authentication
5442  m_proxyURL.setUser( info.username );
5443  m_proxyURL.setPass( info.password );
5444  m_strProxyRealm = info.realmValue;
5445  m_strProxyAuthorization = info.digestInfo;
5446  break;
5447  default:
5448  break;
5449  }
5450  return true;
5451  }
5452 
5453  if (m_request.bErrorPage)
5454  errorPage();
5455  else
5456  error( ERR_USER_CANCELED, TQString::null );
5457  return false;
5458 }
5459 
5460 void HTTPProtocol::saveAuthorization()
5461 {
5462  AuthInfo info;
5463  if ( m_prevResponseCode == 407 )
5464  {
5465  if (!m_bUseProxy)
5466  return;
5467  m_bProxyAuthValid = true;
5468  info.url = m_proxyURL;
5469  info.username = m_proxyURL.user();
5470  info.password = m_proxyURL.pass();
5471  info.realmValue = m_strProxyRealm;
5472  info.digestInfo = m_strProxyAuthorization;
5473  cacheAuthentication( info );
5474  }
5475  else
5476  {
5477  info.url = m_request.url;
5478  info.username = m_request.user;
5479  info.password = m_request.passwd;
5480  info.realmValue = m_strRealm;
5481  info.digestInfo = m_strAuthorization;
5482  cacheAuthentication( info );
5483  }
5484 }
5485 
5486 #ifdef HAVE_LIBGSSAPI
5487 TQCString HTTPProtocol::gssError( int major_status, int minor_status )
5488 {
5489  OM_uint32 new_status;
5490  OM_uint32 msg_ctx = 0;
5491  gss_buffer_desc major_string;
5492  gss_buffer_desc minor_string;
5493  OM_uint32 ret;
5494  TQCString errorstr;
5495 
5496  errorstr = "";
5497 
5498  do {
5499  ret = gss_display_status(&new_status, major_status, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &major_string);
5500  errorstr += (const char *)major_string.value;
5501  errorstr += " ";
5502  ret = gss_display_status(&new_status, minor_status, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &minor_string);
5503  errorstr += (const char *)minor_string.value;
5504  errorstr += " ";
5505  } while (!GSS_ERROR(ret) && msg_ctx != 0);
5506 
5507  return errorstr;
5508 }
5509 
5510 TQString HTTPProtocol::createNegotiateAuth()
5511 {
5512  TQString auth;
5513  TQCString servicename;
5514  TQByteArray input;
5515  OM_uint32 major_status, minor_status;
5516  OM_uint32 req_flags = 0;
5517  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
5518  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
5519  gss_name_t server;
5520  gss_ctx_id_t ctx;
5521  gss_OID mech_oid;
5522  static gss_OID_desc krb5_oid_desc = {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
5523  static gss_OID_desc spnego_oid_desc = {6, (void *) "\x2b\x06\x01\x05\x05\x02"};
5524  int found = 0;
5525  unsigned int i;
5526  gss_OID_set mech_set;
5527  gss_OID tmp_oid;
5528 
5529  ctx = GSS_C_NO_CONTEXT;
5530  mech_oid = &krb5_oid_desc;
5531 
5532  // see whether we can use the SPNEGO mechanism
5533  major_status = gss_indicate_mechs(&minor_status, &mech_set);
5534  if (GSS_ERROR(major_status)) {
5535  kdDebug(7113) << "(" << m_pid << ") gss_indicate_mechs failed: " << gssError(major_status, minor_status) << endl;
5536  } else {
5537  for (i=0; i<mech_set->count && !found; i++) {
5538  tmp_oid = &mech_set->elements[i];
5539  if (tmp_oid->length == spnego_oid_desc.length &&
5540  !memcmp(tmp_oid->elements, spnego_oid_desc.elements, tmp_oid->length)) {
5541  kdDebug(7113) << "(" << m_pid << ") createNegotiateAuth: found SPNEGO mech" << endl;
5542  found = 1;
5543  mech_oid = &spnego_oid_desc;
5544  break;
5545  }
5546  }
5547  gss_release_oid_set(&minor_status, &mech_set);
5548  }
5549 
5550  // the service name is "HTTP/f.q.d.n"
5551  servicename = "HTTP@";
5552  servicename += m_state.hostname.ascii();
5553 
5554  input_token.value = (void *)servicename.data();
5555  input_token.length = servicename.length() + 1;
5556 
5557  major_status = gss_import_name(&minor_status, &input_token,
5558  GSS_C_NT_HOSTBASED_SERVICE, &server);
5559 
5560  input_token.value = NULL;
5561  input_token.length = 0;
5562 
5563  if (GSS_ERROR(major_status)) {
5564  kdDebug(7113) << "(" << m_pid << ") gss_import_name failed: " << gssError(major_status, minor_status) << endl;
5565  // reset the auth string so that subsequent methods aren't confused
5566  m_strAuthorization = TQString::null;
5567  return TQString::null;
5568  }
5569 
5570  major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
5571  &ctx, server, mech_oid,
5572  req_flags, GSS_C_INDEFINITE,
5573  GSS_C_NO_CHANNEL_BINDINGS,
5574  GSS_C_NO_BUFFER, NULL, &output_token,
5575  NULL, NULL);
5576 
5577 
5578  if (GSS_ERROR(major_status) || (output_token.length == 0)) {
5579  kdDebug(7113) << "(" << m_pid << ") gss_init_sec_context failed: " << gssError(major_status, minor_status) << endl;
5580  gss_release_name(&minor_status, &server);
5581  if (ctx != GSS_C_NO_CONTEXT) {
5582  gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
5583  ctx = GSS_C_NO_CONTEXT;
5584  }
5585  // reset the auth string so that subsequent methods aren't confused
5586  m_strAuthorization = TQString::null;
5587  return TQString::null;
5588  }
5589 
5590  input.duplicate((const char *)output_token.value, output_token.length);
5591  auth = "Authorization: Negotiate ";
5592  auth += KCodecs::base64Encode( input );
5593  auth += "\r\n";
5594 
5595  // free everything
5596  gss_release_name(&minor_status, &server);
5597  if (ctx != GSS_C_NO_CONTEXT) {
5598  gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
5599  ctx = GSS_C_NO_CONTEXT;
5600  }
5601  gss_release_buffer(&minor_status, &output_token);
5602 
5603  return auth;
5604 }
5605 #else
5606 
5607 // Dummy
5608 TQCString HTTPProtocol::gssError( int, int )
5609 {
5610  return "";
5611 }
5612 
5613 // Dummy
5614 TQString HTTPProtocol::createNegotiateAuth()
5615 {
5616  return TQString::null;
5617 }
5618 #endif
5619 
5620 TQString HTTPProtocol::createNTLMAuth( bool isForProxy )
5621 {
5622  uint len;
5623  TQString auth, user, domain, passwd;
5624  TQCString strauth;
5625  TQByteArray buf;
5626 
5627  if ( isForProxy )
5628  {
5629  auth = "Proxy-Connection: Keep-Alive\r\n";
5630  auth += "Proxy-Authorization: NTLM ";
5631  user = m_proxyURL.user();
5632  passwd = m_proxyURL.pass();
5633  strauth = m_strProxyAuthorization.latin1();
5634  len = m_strProxyAuthorization.length();
5635  }
5636  else
5637  {
5638  auth = "Authorization: NTLM ";
5639  user = m_state.user;
5640  passwd = m_state.passwd;
5641  strauth = m_strAuthorization.latin1();
5642  len = m_strAuthorization.length();
5643  }
5644  if ( user.contains('\\') ) {
5645  domain = user.section( '\\', 0, 0);
5646  user = user.section( '\\', 1 );
5647  }
5648 
5649  kdDebug(7113) << "(" << m_pid << ") NTLM length: " << len << endl;
5650  if ( user.isEmpty() || passwd.isEmpty() || len < 4 )
5651  return TQString::null;
5652 
5653  if ( len > 4 )
5654  {
5655  // create a response
5656  TQByteArray challenge;
5657  KCodecs::base64Decode( strauth.right( len - 5 ), challenge );
5658  KNTLM::getAuth( buf, challenge, user, passwd, domain,
5659  KNetwork::KResolver::localHostName(), false, false );
5660  }
5661  else
5662  {
5663  KNTLM::getNegotiate( buf );
5664  }
5665 
5666  // remove the challenge to prevent reuse
5667  if ( isForProxy )
5668  m_strProxyAuthorization = "NTLM";
5669  else
5670  m_strAuthorization = "NTLM";
5671 
5672  auth += KCodecs::base64Encode( buf );
5673  auth += "\r\n";
5674 
5675  return auth;
5676 }
5677 
5678 TQString HTTPProtocol::createBasicAuth( bool isForProxy )
5679 {
5680  TQString auth;
5681  TQCString user, passwd;
5682  if ( isForProxy )
5683  {
5684  auth = "Proxy-Authorization: Basic ";
5685  user = m_proxyURL.user().latin1();
5686  passwd = m_proxyURL.pass().latin1();
5687  }
5688  else
5689  {
5690  auth = "Authorization: Basic ";
5691  user = m_state.user.latin1();
5692  passwd = m_state.passwd.latin1();
5693  }
5694 
5695  if ( user.isEmpty() )
5696  user = "";
5697  if ( passwd.isEmpty() )
5698  passwd = "";
5699 
5700  user += ':';
5701  user += passwd;
5702  auth += KCodecs::base64Encode( user );
5703  auth += "\r\n";
5704 
5705  return auth;
5706 }
5707 
5708 void HTTPProtocol::calculateResponse( DigestAuthInfo& info, TQCString& Response )
5709 {
5710  KMD5 md;
5711  TQCString HA1;
5712  TQCString HA2;
5713 
5714  // Calculate H(A1)
5715  TQCString authStr = info.username;
5716  authStr += ':';
5717  authStr += info.realm;
5718  authStr += ':';
5719  authStr += info.password;
5720  md.update( authStr );
5721 
5722  if ( info.algorithm.lower() == "md5-sess" )
5723  {
5724  authStr = md.hexDigest();
5725  authStr += ':';
5726  authStr += info.nonce;
5727  authStr += ':';
5728  authStr += info.cnonce;
5729  md.reset();
5730  md.update( authStr );
5731  }
5732  HA1 = md.hexDigest();
5733 
5734  kdDebug(7113) << "(" << m_pid << ") calculateResponse(): A1 => " << HA1 << endl;
5735 
5736  // Calcualte H(A2)
5737  authStr = info.method;
5738  authStr += ':';
5739  authStr += m_request.url.encodedPathAndQuery(0, true).latin1();
5740  if ( info.qop == "auth-int" )
5741  {
5742  authStr += ':';
5743  authStr += info.entityBody;
5744  }
5745  md.reset();
5746  md.update( authStr );
5747  HA2 = md.hexDigest();
5748 
5749  kdDebug(7113) << "(" << m_pid << ") calculateResponse(): A2 => "
5750  << HA2 << endl;
5751 
5752  // Calcualte the response.
5753  authStr = HA1;
5754  authStr += ':';
5755  authStr += info.nonce;
5756  authStr += ':';
5757  if ( !info.qop.isEmpty() )
5758  {
5759  authStr += info.nc;
5760  authStr += ':';
5761  authStr += info.cnonce;
5762  authStr += ':';
5763  authStr += info.qop;
5764  authStr += ':';
5765  }
5766  authStr += HA2;
5767  md.reset();
5768  md.update( authStr );
5769  Response = md.hexDigest();
5770 
5771  kdDebug(7113) << "(" << m_pid << ") calculateResponse(): Response => "
5772  << Response << endl;
5773 }
5774 
5775 TQString HTTPProtocol::createDigestAuth ( bool isForProxy )
5776 {
5777  const char *p;
5778 
5779  TQString auth;
5780  TQCString opaque;
5781  TQCString Response;
5782 
5783  DigestAuthInfo info;
5784 
5785  opaque = "";
5786  if ( isForProxy )
5787  {
5788  auth = "Proxy-Authorization: Digest ";
5789  info.username = m_proxyURL.user().latin1();
5790  info.password = m_proxyURL.pass().latin1();
5791  p = m_strProxyAuthorization.latin1();
5792  }
5793  else
5794  {
5795  auth = "Authorization: Digest ";
5796  info.username = m_state.user.latin1();
5797  info.password = m_state.passwd.latin1();
5798  p = m_strAuthorization.latin1();
5799  }
5800  if (!p || !*p)
5801  return TQString::null;
5802 
5803  p += 6; // Skip "Digest"
5804 
5805  if ( info.username.isEmpty() || info.password.isEmpty() || !p )
5806  return TQString::null;
5807 
5808  // info.entityBody = p; // FIXME: send digest of data for POST action ??
5809  info.realm = "";
5810  info.algorithm = "MD5";
5811  info.nonce = "";
5812  info.qop = "";
5813 
5814  // cnonce is recommended to contain about 64 bits of entropy
5815  info.cnonce = TDEApplication::randomString(16).latin1();
5816 
5817  // HACK: Should be fixed according to RFC 2617 section 3.2.2
5818  info.nc = "00000001";
5819 
5820  // Set the method used...
5821  switch ( m_request.method )
5822  {
5823  case HTTP_GET:
5824  info.method = "GET";
5825  break;
5826  case HTTP_PUT:
5827  info.method = "PUT";
5828  break;
5829  case HTTP_POST:
5830  info.method = "POST";
5831  break;
5832  case HTTP_HEAD:
5833  info.method = "HEAD";
5834  break;
5835  case HTTP_DELETE:
5836  info.method = "DELETE";
5837  break;
5838  case DAV_PROPFIND:
5839  info.method = "PROPFIND";
5840  break;
5841  case DAV_PROPPATCH:
5842  info.method = "PROPPATCH";
5843  break;
5844  case DAV_MKCOL:
5845  info.method = "MKCOL";
5846  break;
5847  case DAV_COPY:
5848  info.method = "COPY";
5849  break;
5850  case DAV_MOVE:
5851  info.method = "MOVE";
5852  break;
5853  case DAV_LOCK:
5854  info.method = "LOCK";
5855  break;
5856  case DAV_UNLOCK:
5857  info.method = "UNLOCK";
5858  break;
5859  case DAV_SEARCH:
5860  info.method = "SEARCH";
5861  break;
5862  case DAV_SUBSCRIBE:
5863  info.method = "SUBSCRIBE";
5864  break;
5865  case DAV_UNSUBSCRIBE:
5866  info.method = "UNSUBSCRIBE";
5867  break;
5868  case DAV_POLL:
5869  info.method = "POLL";
5870  break;
5871  default:
5872  error( ERR_UNSUPPORTED_ACTION, i18n("Unsupported method: authentication will fail. Please submit a bug report."));
5873  break;
5874  }
5875 
5876  // Parse the Digest response....
5877  while (*p)
5878  {
5879  int i = 0;
5880  while ( (*p == ' ') || (*p == ',') || (*p == '\t')) { p++; }
5881  if (strncasecmp(p, "realm=", 6 )==0)
5882  {
5883  p+=6;
5884  while ( *p == '"' ) p++; // Go past any number of " mark(s) first
5885  while ( p[i] != '"' ) i++; // Read everything until the last " mark
5886  info.realm = TQCString( p, i+1 );
5887  }
5888  else if (strncasecmp(p, "algorith=", 9)==0)
5889  {
5890  p+=9;
5891  while ( *p == '"' ) p++; // Go past any number of " mark(s) first
5892  while ( ( p[i] != '"' ) && ( p[i] != ',' ) && ( p[i] != '\0' ) ) i++;
5893  info.algorithm = TQCString(p, i+1);
5894  }
5895  else if (strncasecmp(p, "algorithm=", 10)==0)
5896  {
5897  p+=10;
5898  while ( *p == '"' ) p++; // Go past any " mark(s) first
5899  while ( ( p[i] != '"' ) && ( p[i] != ',' ) && ( p[i] != '\0' ) ) i++;
5900  info.algorithm = TQCString(p,i+1);
5901  }
5902  else if (strncasecmp(p, "domain=", 7)==0)
5903  {
5904  p+=7;
5905  while ( *p == '"' ) p++; // Go past any " mark(s) first
5906  while ( p[i] != '"' ) i++; // Read everything until the last " mark
5907  int pos;
5908  int idx = 0;
5909  TQCString uri = TQCString(p,i+1);
5910  do
5911  {
5912  pos = uri.find( ' ', idx );
5913  if ( pos != -1 )
5914  {
5915  KURL u (m_request.url, uri.mid(idx, pos-idx));
5916  if (u.isValid ())
5917  info.digestURI.append( u.url().latin1() );
5918  }
5919  else
5920  {
5921  KURL u (m_request.url, uri.mid(idx, uri.length()-idx));
5922  if (u.isValid ())
5923  info.digestURI.append( u.url().latin1() );
5924  }
5925  idx = pos+1;
5926  } while ( pos != -1 );
5927  }
5928  else if (strncasecmp(p, "nonce=", 6)==0)
5929  {
5930  p+=6;
5931  while ( *p == '"' ) p++; // Go past any " mark(s) first
5932  while ( p[i] != '"' ) i++; // Read everything until the last " mark
5933  info.nonce = TQCString(p,i+1);
5934  }
5935  else if (strncasecmp(p, "opaque=", 7)==0)
5936  {
5937  p+=7;
5938  while ( *p == '"' ) p++; // Go past any " mark(s) first
5939  while ( p[i] != '"' ) i++; // Read everything until the last " mark
5940  opaque = TQCString(p,i+1);
5941  }
5942  else if (strncasecmp(p, "qop=", 4)==0)
5943  {
5944  p+=4;
5945  while ( *p == '"' ) p++; // Go past any " mark(s) first
5946  while ( p[i] != '"' ) i++; // Read everything until the last " mark
5947  info.qop = TQCString(p,i+1);
5948  }
5949  p+=(i+1);
5950  }
5951 
5952  if (info.realm.isEmpty() || info.nonce.isEmpty())
5953  return TQString::null;
5954 
5955  // If the "domain" attribute was not specified and the current response code
5956  // is authentication needed, add the current request url to the list over which
5957  // this credential can be automatically applied.
5958  if (info.digestURI.isEmpty() && (m_responseCode == 401 || m_responseCode == 407))
5959  info.digestURI.append (m_request.url.url().latin1());
5960  else
5961  {
5962  // Verify whether or not we should send a cached credential to the
5963  // server based on the stored "domain" attribute...
5964  bool send = true;
5965 
5966  // Determine the path of the request url...
5967  TQString requestPath = m_request.url.directory(false, false);
5968  if (requestPath.isEmpty())
5969  requestPath = "/";
5970 
5971  int count = info.digestURI.count();
5972 
5973  for (int i = 0; i < count; i++ )
5974  {
5975  KURL u ( info.digestURI.at(i) );
5976 
5977  send &= (m_request.url.protocol().lower() == u.protocol().lower());
5978  send &= (m_request.hostname.lower() == u.host().lower());
5979 
5980  if (m_request.port > 0 && u.port() > 0)
5981  send &= (m_request.port == u.port());
5982 
5983  TQString digestPath = u.directory (false, false);
5984  if (digestPath.isEmpty())
5985  digestPath = "/";
5986 
5987  send &= (requestPath.startsWith(digestPath));
5988 
5989  if (send)
5990  break;
5991  }
5992 
5993  kdDebug(7113) << "(" << m_pid << ") createDigestAuth(): passed digest "
5994  "authentication credential test: " << send << endl;
5995 
5996  if (!send)
5997  return TQString::null;
5998  }
5999 
6000  kdDebug(7113) << "(" << m_pid << ") RESULT OF PARSING:" << endl;
6001  kdDebug(7113) << "(" << m_pid << ") algorithm: " << info.algorithm << endl;
6002  kdDebug(7113) << "(" << m_pid << ") realm: " << info.realm << endl;
6003  kdDebug(7113) << "(" << m_pid << ") nonce: " << info.nonce << endl;
6004  kdDebug(7113) << "(" << m_pid << ") opaque: " << opaque << endl;
6005  kdDebug(7113) << "(" << m_pid << ") qop: " << info.qop << endl;
6006 
6007  // Calculate the response...
6008  calculateResponse( info, Response );
6009 
6010  auth += "username=\"";
6011  auth += info.username;
6012 
6013  auth += "\", realm=\"";
6014  auth += info.realm;
6015  auth += "\"";
6016 
6017  auth += ", nonce=\"";
6018  auth += info.nonce;
6019 
6020  auth += "\", uri=\"";
6021  auth += m_request.url.encodedPathAndQuery(0, true);
6022 
6023  auth += "\", algorithm=\"";
6024  auth += info.algorithm;
6025  auth +="\"";
6026 
6027  if ( !info.qop.isEmpty() )
6028  {
6029  auth += ", qop=\"";
6030  auth += info.qop;
6031  auth += "\", cnonce=\"";
6032  auth += info.cnonce;
6033  auth += "\", nc=";
6034  auth += info.nc;
6035  }
6036 
6037  auth += ", response=\"";
6038  auth += Response;
6039  if ( !opaque.isEmpty() )
6040  {
6041  auth += "\", opaque=\"";
6042  auth += opaque;
6043  }
6044  auth += "\"\r\n";
6045 
6046  return auth;
6047 }
6048 
6049 TQString HTTPProtocol::proxyAuthenticationHeader()
6050 {
6051  TQString header;
6052 
6053  // We keep proxy authentication locally until they are changed.
6054  // Thus, no need to check with the password manager for every
6055  // connection.
6056  if ( m_strProxyRealm.isEmpty() )
6057  {
6058  AuthInfo info;
6059  info.url = m_proxyURL;
6060  info.username = m_proxyURL.user();
6061  info.password = m_proxyURL.pass();
6062  info.verifyPath = true;
6063 
6064  // If the proxy URL already contains username
6065  // and password simply attempt to retrieve it
6066  // without prompting the user...
6067  if ( !info.username.isNull() && !info.password.isNull() )
6068  {
6069  if( m_strProxyAuthorization.isEmpty() )
6070  ProxyAuthentication = AUTH_None;
6071  else if( m_strProxyAuthorization.startsWith("Basic") )
6072  ProxyAuthentication = AUTH_Basic;
6073  else if( m_strProxyAuthorization.startsWith("NTLM") )
6074  ProxyAuthentication = AUTH_NTLM;
6075  else
6076  ProxyAuthentication = AUTH_Digest;
6077  }
6078  else
6079  {
6080  if ( checkCachedAuthentication(info) && !info.digestInfo.isEmpty() )
6081  {
6082  m_proxyURL.setUser( info.username );
6083  m_proxyURL.setPass( info.password );
6084  m_strProxyRealm = info.realmValue;
6085  m_strProxyAuthorization = info.digestInfo;
6086  if( m_strProxyAuthorization.startsWith("Basic") )
6087  ProxyAuthentication = AUTH_Basic;
6088  else if( m_strProxyAuthorization.startsWith("NTLM") )
6089  ProxyAuthentication = AUTH_NTLM;
6090  else
6091  ProxyAuthentication = AUTH_Digest;
6092  }
6093  else
6094  {
6095  ProxyAuthentication = AUTH_None;
6096  }
6097  }
6098  }
6099 
6100  /********* Only for debugging purpose... *********/
6101  if ( ProxyAuthentication != AUTH_None )
6102  {
6103  kdDebug(7113) << "(" << m_pid << ") Using Proxy Authentication: " << endl;
6104  kdDebug(7113) << "(" << m_pid << ") HOST= " << m_proxyURL.host() << endl;
6105  kdDebug(7113) << "(" << m_pid << ") PORT= " << m_proxyURL.port() << endl;
6106  kdDebug(7113) << "(" << m_pid << ") USER= " << m_proxyURL.user() << endl;
6107  kdDebug(7113) << "(" << m_pid << ") PASSWORD= [protected]" << endl;
6108  kdDebug(7113) << "(" << m_pid << ") REALM= " << m_strProxyRealm << endl;
6109  kdDebug(7113) << "(" << m_pid << ") EXTRA= " << m_strProxyAuthorization << endl;
6110  }
6111 
6112  switch ( ProxyAuthentication )
6113  {
6114  case AUTH_Basic:
6115  header += createBasicAuth( true );
6116  break;
6117  case AUTH_Digest:
6118  header += createDigestAuth( true );
6119  break;
6120  case AUTH_NTLM:
6121  if ( m_bFirstRequest ) header += createNTLMAuth( true );
6122  break;
6123  case AUTH_None:
6124  default:
6125  break;
6126  }
6127 
6128  return header;
6129 }
6130 
6131 #include "http.moc"
TDEIO
Definition: http.h:45

tdeioslave/http

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

tdeioslave/http

Skip menu "tdeioslave/http"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  •     tdecore
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  • tdeioslave
  •   http
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdeioslave/http by doxygen 1.8.8
This website is maintained by Timothy Pearson.