• 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 <tdestandarddirs.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
93using namespace TDEIO;
94
95extern "C" {
96 TDE_EXPORT int kdemain(int argc, char **argv);
97}
98
99int 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
118static char * trimLead (char *orig_string)
119{
120 while (*orig_string == ' ')
121 orig_string++;
122 return orig_string;
123}
124
125static 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*/
165static 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
187static 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
220HTTPProtocol::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
248HTTPProtocol::~HTTPProtocol()
249{
250 httpClose(false);
251}
252
253void 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
270void 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
281void 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
297void 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
448void 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
481bool 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
516void 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
544bool 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
622void 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
663void 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
679void 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
688void 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
835void 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
856int 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
863void 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
1059void 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
1104long 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
1123TQString 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
1172bool 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
1227void HTTPProtocol::davFinished()
1228{
1229 // TODO: Check with the DAV extension developers
1230 httpClose(m_bKeepAlive);
1231 finished();
1232}
1233
1234void 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
1256void 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
1281void 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
1345void 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
1377void 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
1433void 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
1457void 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
1474void 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
1540void 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
1562TQString 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
1765void 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
1827bool 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
1853void 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
1910ssize_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
1938void HTTPProtocol::setRewindMarker()
1939{
1940 m_rewindCount = 0;
1941}
1942
1943void HTTPProtocol::rewind()
1944{
1945 m_linePtrUnget = m_rewindBuf,
1946 m_lineCountUnget = m_rewindCount;
1947 m_rewindCount = 0;
1948}
1949
1950
1951char *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
1980ssize_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
2027void 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
2074bool 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
2189bool 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
2681void 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
2698bool HTTPProtocol::readHeader()
2699{
2700try_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
3888void 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
3915bool 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
3990void 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
4028void HTTPProtocol::closeConnection()
4029{
4030 kdDebug(7113) << "(" << m_pid << ") HTTPProtocol::closeConnection" << endl;
4031 httpCloseConnection ();
4032}
4033
4034void 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
4043void 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
4053void 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
4073void 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
4136int 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
4207int 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
4231int 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
4251void 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
4349bool 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, TQ_SIGNAL(output(const TQByteArray &)),
4448 this, TQ_SLOT(slotData(const TQByteArray &)));
4449 TQObject::connect(&chain, TQ_SIGNAL(error(int, const TQString &)),
4450 this, TQ_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
4601void 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
4624void 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
4641TQString 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
4675void 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
4706FILE* 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
4846void 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
4904void 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
4963void 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
4987void 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
5006void 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
5046void 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
5177bool 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
5195void 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
5234bool 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
5460void 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
5487TQCString 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
5510TQString 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
5608TQCString HTTPProtocol::gssError( int, int )
5609{
5610 return "";
5611}
5612
5613// Dummy
5614TQString HTTPProtocol::createNegotiateAuth()
5615{
5616 return TQString::null;
5617}
5618#endif
5619
5620TQString 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
5678TQString 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
5708void 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
5775TQString 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
6049TQString 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"

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
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdeioslave/http by doxygen 1.9.4
This website is maintained by Timothy Pearson.