Bump for 3.6-28
[LibreOffice.git] / ucb / source / ucp / webdav / NeonSession.cxx
blobb21dab928b6c4094a71d95a3372843713367bc5e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include <boost/unordered_map.hpp>
31 #include <vector>
32 #include <string.h>
33 #include "osl/diagnose.h"
34 #include "osl/time.h"
35 #include <rtl/string.h>
36 #include <ne_socket.h>
37 #include <ne_auth.h>
38 #include <ne_redirect.h>
39 #include <ne_ssl.h>
41 #if NEON_VERSION < 0x0260
42 // old neon versions forgot to set this
43 extern "C" {
44 #endif
45 #include <ne_compress.h>
46 #if NEON_VERSION < 0x0260
48 #endif
50 #include "libxml/parser.h"
51 #include "rtl/ustrbuf.hxx"
52 #include "comphelper/sequence.hxx"
53 #include <comphelper/stl_types.hxx>
54 #include "ucbhelper/simplecertificatevalidationrequest.hxx"
56 #include "DAVAuthListener.hxx"
57 #include "NeonTypes.hxx"
58 #include "NeonSession.hxx"
59 #include "NeonInputStream.hxx"
60 #include "NeonPropFindRequest.hxx"
61 #include "NeonHeadRequest.hxx"
62 #include "NeonUri.hxx"
63 #include "LinkSequence.hxx"
64 #include "UCBDeadPropertyValue.hxx"
66 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
67 #include <com/sun/star/security/XCertificate.hpp>
68 #include <com/sun/star/security/CertificateValidity.hpp>
69 #include <com/sun/star/security/CertificateContainerStatus.hpp>
70 #include <com/sun/star/security/CertificateContainer.hpp>
71 #include <com/sun/star/security/XCertificateContainer.hpp>
72 #include <com/sun/star/ucb/Lock.hpp>
73 #include <com/sun/star/beans/NamedValue.hpp>
74 #include <com/sun/star/xml/crypto/XSEInitializer.hpp>
76 #include <boost/bind.hpp>
78 using namespace com::sun::star;
79 using namespace webdav_ucp;
81 #define SEINITIALIZER_COMPONENT "com.sun.star.xml.crypto.SEInitializer"
83 #ifndef EOL
84 # define EOL "\r\n"
85 #endif
87 // -------------------------------------------------------------------
88 // RequestData
89 // -------------------------------------------------------------------
91 struct RequestData
93 // POST
94 rtl::OUString aContentType;
95 rtl::OUString aReferer;
97 RequestData() {}
98 RequestData( const rtl::OUString & rContentType,
99 const rtl::OUString & rReferer )
100 : aContentType( rContentType ), aReferer( rReferer ) {}
103 // -------------------------------------------------------------------
104 // RequestDataMap
105 // -------------------------------------------------------------------
107 struct equalPtr
109 bool operator()( const ne_request* p1, const ne_request* p2 ) const
111 return p1 == p2;
115 struct hashPtr
117 size_t operator()( const ne_request* p ) const
119 return (size_t)p;
123 typedef boost::unordered_map
125 ne_request*,
126 RequestData,
127 hashPtr,
128 equalPtr
130 RequestDataMap;
132 // -------------------------------------------------------------------
133 // Helper fuction
134 // -------------------------------------------------------------------
135 static sal_uInt16 makeStatusCode( const rtl::OUString & rStatusText )
137 // Extract status code from session error string. Unfortunately
138 // neon provides no direct access to the status code...
140 if ( rStatusText.getLength() < 3 )
142 OSL_FAIL(
143 "makeStatusCode - status text string to short!" );
144 return 0;
147 sal_Int32 nPos = rStatusText.indexOf( ' ' );
148 if ( nPos == -1 )
150 OSL_FAIL( "makeStatusCode - wrong status text format!" );
151 return 0;
154 return sal_uInt16( rStatusText.copy( 0, nPos ).toInt32() );
157 // -------------------------------------------------------------------
158 static bool noKeepAlive( const uno::Sequence< beans::NamedValue >& rFlags )
160 if ( !rFlags.hasElements() )
161 return false;
163 // find "KeepAlive" property
164 const beans::NamedValue* pAry(rFlags.getConstArray());
165 const sal_Int32 nLen(rFlags.getLength());
166 const beans::NamedValue* pValue(
167 std::find_if(pAry,pAry+nLen,
168 boost::bind(comphelper::TNamedValueEqualFunctor(),
170 rtl::OUString("KeepAlive"))));
171 if ( pValue != pAry+nLen && !pValue->Value.get<sal_Bool>() )
172 return true;
174 return false;
177 // -------------------------------------------------------------------
178 struct NeonRequestContext
180 uno::Reference< io::XOutputStream > xOutputStream;
181 rtl::Reference< NeonInputStream > xInputStream;
182 const std::vector< ::rtl::OUString > * pHeaderNames;
183 DAVResource * pResource;
185 NeonRequestContext( uno::Reference< io::XOutputStream > & xOutStrm )
186 : xOutputStream( xOutStrm ), xInputStream( 0 ),
187 pHeaderNames( 0 ), pResource( 0 ) {}
189 NeonRequestContext( const rtl::Reference< NeonInputStream > & xInStrm )
190 : xOutputStream( 0 ), xInputStream( xInStrm ),
191 pHeaderNames( 0 ), pResource( 0 ) {}
193 NeonRequestContext( uno::Reference< io::XOutputStream > & xOutStrm,
194 const std::vector< ::rtl::OUString > & inHeaderNames,
195 DAVResource & ioResource )
196 : xOutputStream( xOutStrm ), xInputStream( 0 ),
197 pHeaderNames( &inHeaderNames ), pResource( &ioResource ) {}
199 NeonRequestContext( const rtl::Reference< NeonInputStream > & xInStrm,
200 const std::vector< ::rtl::OUString > & inHeaderNames,
201 DAVResource & ioResource )
202 : xOutputStream( 0 ), xInputStream( xInStrm ),
203 pHeaderNames( &inHeaderNames ), pResource( &ioResource ) {}
206 //--------------------------------------------------------------------
207 //--------------------------------------------------------------------
209 // Callback functions
211 //--------------------------------------------------------------------
212 //--------------------------------------------------------------------
214 // -------------------------------------------------------------------
215 // ResponseBlockReader
216 // A simple Neon response_block_reader for use with an XInputStream
217 // -------------------------------------------------------------------
219 extern "C" int NeonSession_ResponseBlockReader(void * inUserData,
220 const char * inBuf,
221 size_t inLen )
223 // neon sometimes calls this function with (inLen == 0)...
224 if ( inLen > 0 )
226 NeonRequestContext * pCtx
227 = static_cast< NeonRequestContext * >( inUserData );
229 rtl::Reference< NeonInputStream > xInputStream(
230 pCtx->xInputStream );
232 if ( xInputStream.is() )
233 xInputStream->AddToStream( inBuf, inLen );
235 return 0;
238 // -------------------------------------------------------------------
239 // ResponseBlockWriter
240 // A simple Neon response_block_reader for use with an XOutputStream
241 // -------------------------------------------------------------------
243 extern "C" int NeonSession_ResponseBlockWriter( void * inUserData,
244 const char * inBuf,
245 size_t inLen )
247 // neon calls this function with (inLen == 0)...
248 if ( inLen > 0 )
250 NeonRequestContext * pCtx
251 = static_cast< NeonRequestContext * >( inUserData );
252 uno::Reference< io::XOutputStream > xOutputStream
253 = pCtx->xOutputStream;
255 if ( xOutputStream.is() )
257 const uno::Sequence< sal_Int8 > aSeq( (sal_Int8 *)inBuf, inLen );
258 xOutputStream->writeBytes( aSeq );
261 return 0;
264 // -------------------------------------------------------------------
265 extern "C" int NeonSession_NeonAuth( void * inUserData,
266 #ifdef NE_FEATURE_SSPI
267 const char * inAuthProtocol,
268 #endif
269 const char * inRealm,
270 int attempt,
271 char * inoutUserName,
272 char * inoutPassWord )
274 /* The callback used to request the username and password in the given
275 * realm. The username and password must be copied into the buffers
276 * which are both of size NE_ABUFSIZ. The 'attempt' parameter is zero
277 * on the first call to the callback, and increases by one each time
278 * an attempt to authenticate fails.
280 * The callback must return zero to indicate that authentication
281 * should be attempted with the username/password, or non-zero to
282 * cancel the request. (if non-zero, username and password are
283 * ignored.) */
285 NeonSession * theSession = static_cast< NeonSession * >( inUserData );
286 DAVAuthListener * pListener
287 = theSession->getRequestEnvironment().m_xAuthListener.get();
288 if ( !pListener )
290 // abort
291 return -1;
293 rtl::OUString theUserName;
294 rtl::OUString thePassWord;
296 if ( attempt == 0 )
298 // neon does not handle username supplied with request URI (for
299 // instance when doing FTP over proxy - last checked: 0.23.5 )
303 NeonUri uri( theSession->getRequestEnvironment().m_aRequestURI );
304 rtl::OUString aUserInfo( uri.GetUserInfo() );
305 if ( !aUserInfo.isEmpty() )
307 sal_Int32 nPos = aUserInfo.indexOf( '@' );
308 if ( nPos == -1 )
310 theUserName = aUserInfo;
312 else
314 theUserName = aUserInfo.copy( 0, nPos );
315 thePassWord = aUserInfo.copy( nPos + 1 );
319 catch ( DAVException const & )
321 // abort
322 return -1;
325 else
327 // username buffer is prefilled with user name from last attempt.
328 theUserName = rtl::OUString::createFromAscii( inoutUserName );
329 // @@@ Neon does not initialize password buffer (last checked: 0.22.0).
330 //thePassWord = rtl::OUString::createFromAscii( inoutPassWord );
333 bool bCanUseSystemCreds = false;
335 #ifdef NE_FEATURE_SSPI
336 bCanUseSystemCreds
337 = (attempt == 0) && // avoid endless loops
338 ne_has_support( NE_FEATURE_SSPI ) && // Windows-only feature.
339 ( ( ne_strcasecmp( inAuthProtocol, "NTLM" ) == 0 ) ||
340 ( ne_strcasecmp( inAuthProtocol, "Negotiate" ) == 0 ) );
341 #endif
343 int theRetVal = pListener->authenticate(
344 rtl::OUString::createFromAscii( inRealm ),
345 theSession->getHostName(),
346 theUserName,
347 thePassWord,
348 bCanUseSystemCreds);
350 rtl::OString aUser(
351 rtl::OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ) );
352 if ( aUser.getLength() > ( NE_ABUFSIZ - 1 ) )
354 OSL_FAIL(
355 "NeonSession_NeonAuth - username to long!" );
356 return -1;
359 rtl::OString aPass(
360 rtl::OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ) );
361 if ( aPass.getLength() > ( NE_ABUFSIZ - 1 ) )
363 OSL_FAIL(
364 "NeonSession_NeonAuth - password to long!" );
365 return -1;
368 strcpy( inoutUserName, // #100211# - checked
369 rtl::OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ).getStr() );
371 strcpy( inoutPassWord, // #100211# - checked
372 rtl::OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ).getStr() );
374 return theRetVal;
377 // -------------------------------------------------------------------
379 namespace {
380 // -------------------------------------------------------------------
381 // Helper function
382 ::rtl::OUString GetHostnamePart( const ::rtl::OUString& _rRawString )
384 ::rtl::OUString sPart;
385 ::rtl::OUString sPartId("CN=");
386 sal_Int32 nContStart = _rRawString.indexOf( sPartId );
387 if ( nContStart != -1 )
389 nContStart = nContStart + sPartId.getLength();
390 sal_Int32 nContEnd
391 = _rRawString.indexOf( sal_Unicode( ',' ), nContStart );
392 sPart = _rRawString.copy( nContStart, nContEnd - nContStart );
394 return sPart;
396 } // namespace
398 // -------------------------------------------------------------------
399 extern "C" int NeonSession_CertificationNotify( void *userdata,
400 int failures,
401 const ne_ssl_certificate *cert )
403 OSL_ASSERT( cert );
405 NeonSession * pSession = static_cast< NeonSession * >( userdata );
406 uno::Reference< security::XCertificateContainer > xCertificateContainer;
409 xCertificateContainer
410 = uno::Reference< security::XCertificateContainer >(
411 pSession->getMSF()->createInstance(
412 rtl::OUString( "com.sun.star.security.CertificateContainer" ) ),
413 uno::UNO_QUERY );
415 catch ( uno::Exception const & )
419 if ( !xCertificateContainer.is() )
420 return 1;
422 failures = 0;
424 char * dn = ne_ssl_readable_dname( ne_ssl_cert_subject( cert ) );
425 rtl::OUString cert_subject( dn, strlen( dn ), RTL_TEXTENCODING_UTF8, 0 );
427 ne_free( dn );
429 security::CertificateContainerStatus certificateContainer(
430 xCertificateContainer->hasCertificate(
431 pSession->getHostName(), cert_subject ) );
433 if ( certificateContainer != security::CertificateContainerStatus_NOCERT )
434 return
435 certificateContainer == security::CertificateContainerStatus_TRUSTED
437 : 1;
439 uno::Reference< xml::crypto::XSEInitializer > xSEInitializer;
442 xSEInitializer = uno::Reference< xml::crypto::XSEInitializer >(
443 pSession->getMSF()->createInstance(
444 rtl::OUString( SEINITIALIZER_COMPONENT ) ),
445 uno::UNO_QUERY );
447 catch ( uno::Exception const & )
451 if ( !xSEInitializer.is() )
452 return 1;
454 uno::Reference< xml::crypto::XXMLSecurityContext > xSecurityContext(
455 xSEInitializer->createSecurityContext( rtl::OUString() ) );
457 uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnv(
458 xSecurityContext->getSecurityEnvironment() );
460 //The end entity certificate
461 char * eeCertB64 = ne_ssl_cert_export( cert );
463 rtl::OString sEECertB64( eeCertB64 );
465 uno::Reference< security::XCertificate > xEECert(
466 xSecurityEnv->createCertificateFromAscii(
467 rtl::OStringToOUString( sEECertB64, RTL_TEXTENCODING_ASCII_US ) ) );
469 ne_free( eeCertB64 );
470 eeCertB64 = 0;
472 std::vector< uno::Reference< security::XCertificate > > vecCerts;
473 const ne_ssl_certificate * issuerCert = cert;
476 //get the intermediate certificate
477 //the returned value is const ! Therfore it does not need to be freed
478 //with ne_ssl_cert_free, which takes a non-const argument
479 issuerCert = ne_ssl_cert_signedby( issuerCert );
480 if ( NULL == issuerCert )
481 break;
483 char * imCertB64 = ne_ssl_cert_export( issuerCert );
484 rtl::OString sInterMediateCertB64( imCertB64 );
485 ne_free( imCertB64 );
487 uno::Reference< security::XCertificate> xImCert(
488 xSecurityEnv->createCertificateFromAscii(
489 rtl::OStringToOUString( sInterMediateCertB64, RTL_TEXTENCODING_ASCII_US ) ) );
490 if ( xImCert.is() )
491 vecCerts.push_back( xImCert );
493 while ( 1 );
495 sal_Int64 certValidity = xSecurityEnv->verifyCertificate( xEECert,
496 ::comphelper::containerToSequence( vecCerts ) );
498 if ( pSession->isDomainMatch(
499 GetHostnamePart( xEECert.get()->getSubjectName() ) ) )
501 // if host name matched with certificate then look if the
502 // certificate was ok
503 if( certValidity == security::CertificateValidity::VALID )
504 return 0;
507 const uno::Reference< ucb::XCommandEnvironment > xEnv(
508 pSession->getRequestEnvironment().m_xEnv );
509 if ( xEnv.is() )
511 failures = static_cast< int >( certValidity );
513 uno::Reference< task::XInteractionHandler > xIH(
514 xEnv->getInteractionHandler() );
515 if ( xIH.is() )
517 rtl::Reference< ucbhelper::SimpleCertificateValidationRequest >
518 xRequest( new ucbhelper::SimpleCertificateValidationRequest(
519 (sal_Int32)failures, xEECert, pSession->getHostName() ) );
520 xIH->handle( xRequest.get() );
522 rtl::Reference< ucbhelper::InteractionContinuation > xSelection
523 = xRequest->getSelection();
525 if ( xSelection.is() )
527 uno::Reference< task::XInteractionApprove > xApprove(
528 xSelection.get(), uno::UNO_QUERY );
529 if ( xApprove.is() )
531 xCertificateContainer->addCertificate(
532 pSession->getHostName(), cert_subject, sal_True );
533 return 0;
535 else
537 // Don't trust cert
538 xCertificateContainer->addCertificate(
539 pSession->getHostName(), cert_subject, sal_False );
540 return 1;
544 else
546 // Don't trust cert
547 xCertificateContainer->addCertificate(
548 pSession->getHostName(), cert_subject, sal_False );
549 return 1;
552 return 1;
555 // -------------------------------------------------------------------
556 extern "C" void NeonSession_PreSendRequest( ne_request * req,
557 void * userdata,
558 ne_buffer * headers )
560 // userdata -> value returned by 'create'
562 NeonSession * pSession = static_cast< NeonSession * >( userdata );
563 if ( pSession )
565 // If there is a proxy server in between, it shall never use
566 // cached data. We always want 'up-to-date' data.
567 ne_buffer_concat( headers, "Pragma: no-cache", EOL, NULL );
568 // alternative, but understoud by HTTP 1.1 servers only:
569 // ne_buffer_concat( headers, "Cache-Control: max-age=0", EOL, NULL );
571 const RequestDataMap * pRequestData
572 = static_cast< const RequestDataMap* >(
573 pSession->getRequestData() );
575 RequestDataMap::const_iterator it = pRequestData->find( req );
576 if ( it != pRequestData->end() )
578 if ( !(*it).second.aContentType.isEmpty() )
580 char * pData = headers->data;
581 if ( strstr( pData, "Content-Type:" ) == NULL )
583 rtl::OString aType
584 = rtl::OUStringToOString( (*it).second.aContentType,
585 RTL_TEXTENCODING_UTF8 );
586 ne_buffer_concat( headers, "Content-Type: ",
587 aType.getStr(), EOL, NULL );
591 if ( !(*it).second.aReferer.isEmpty() )
593 char * pData = headers->data;
594 if ( strstr( pData, "Referer:" ) == NULL )
596 rtl::OString aReferer
597 = rtl::OUStringToOString( (*it).second.aReferer,
598 RTL_TEXTENCODING_UTF8 );
599 ne_buffer_concat( headers, "Referer: ",
600 aReferer.getStr(), EOL, NULL );
605 const DAVRequestHeaders & rHeaders
606 = pSession->getRequestEnvironment().m_aRequestHeaders;
608 DAVRequestHeaders::const_iterator it1( rHeaders.begin() );
609 const DAVRequestHeaders::const_iterator end1( rHeaders.end() );
611 while ( it1 != end1 )
613 rtl::OString aHeader
614 = rtl::OUStringToOString( (*it1).first,
615 RTL_TEXTENCODING_UTF8 );
616 rtl::OString aValue
617 = rtl::OUStringToOString( (*it1).second,
618 RTL_TEXTENCODING_UTF8 );
619 ne_buffer_concat( headers, aHeader.getStr(), ": ",
620 aValue.getStr(), EOL, NULL );
622 ++it1;
627 // -------------------------------------------------------------------
628 // static members!
629 bool NeonSession::m_bGlobalsInited = false;
630 //See https://bugzilla.redhat.com/show_bug.cgi?id=544619#c4
631 //neon is threadsafe, but uses gnutls which is only thread-safe
632 //if initialized to be thread-safe. cups, unfortunately, generally
633 //initializes it first, and as non-thread-safe, leaving the entire
634 //stack unsafe
635 osl::Mutex aGlobalNeonMutex;
636 NeonLockStore NeonSession::m_aNeonLockStore;
638 // -------------------------------------------------------------------
639 // Constructor
640 // -------------------------------------------------------------------
641 NeonSession::NeonSession(
642 const rtl::Reference< DAVSessionFactory > & rSessionFactory,
643 const rtl::OUString& inUri,
644 const uno::Sequence< beans::NamedValue >& rFlags,
645 const ucbhelper::InternetProxyDecider & rProxyDecider )
646 throw ( DAVException )
647 : DAVSession( rSessionFactory ),
648 m_aFlags( rFlags ),
649 m_pHttpSession( 0 ),
650 m_pRequestData( new RequestDataMap ),
651 m_rProxyDecider( rProxyDecider )
653 NeonUri theUri( inUri );
654 m_aScheme = theUri.GetScheme();
655 m_aHostName = theUri.GetHost();
656 m_nPort = theUri.GetPort();
659 // -------------------------------------------------------------------
660 // Destructor
661 // -------------------------------------------------------------------
662 NeonSession::~NeonSession( )
664 if ( m_pHttpSession )
667 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
668 ne_session_destroy( m_pHttpSession );
670 m_pHttpSession = 0;
672 delete static_cast< RequestDataMap * >( m_pRequestData );
675 // -------------------------------------------------------------------
676 void NeonSession::Init( const DAVRequestEnvironment & rEnv )
677 throw ( DAVException )
679 osl::Guard< osl::Mutex > theGuard( m_aMutex );
680 m_aEnv = rEnv;
681 Init();
684 // -------------------------------------------------------------------
685 void NeonSession::Init()
686 throw ( DAVException )
688 osl::Guard< osl::Mutex > theGuard( m_aMutex );
690 bool bCreateNewSession = false;
692 if ( m_pHttpSession == 0 )
694 // Ensure that Neon sockets are initialized
695 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
696 if ( !m_bGlobalsInited )
698 if ( ne_sock_init() != 0 )
699 throw DAVException( DAVException::DAV_SESSION_CREATE,
700 NeonUri::makeConnectionEndPointString(
701 m_aHostName, m_nPort ) );
703 // #122205# - libxml2 needs to be initialized once if used by
704 // multithreaded programs like OOo.
705 xmlInitParser();
706 #if OSL_DEBUG_LEVEL > 0
707 // for more debug flags see ne_utils.h; NE_DEBUGGING must be defined
708 // while compiling neon in order to actually activate neon debug
709 // output.
710 ne_debug_init( stderr, NE_DBG_FLUSH
711 | NE_DBG_HTTP
712 // | NE_DBG_HTTPBODY
713 // | NE_DBG_HTTPAUTH
714 // | NE_DBG_XML
715 // | NE_DBG_XMLPARSE
716 | NE_DBG_LOCKS
717 | NE_DBG_SSL
719 #endif
720 m_bGlobalsInited = true;
723 const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
725 m_aProxyName = rProxyCfg.aName;
726 m_nProxyPort = rProxyCfg.nPort;
728 // Not yet initialized. Create new session.
729 bCreateNewSession = true;
731 else
733 // #112271# Check whether proxy settings are still valid (They may
734 // change at any time). If not, create new Neon session.
736 const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
738 if ( ( rProxyCfg.aName != m_aProxyName )
739 || ( rProxyCfg.nPort != m_nProxyPort ) )
741 m_aProxyName = rProxyCfg.aName;
742 m_nProxyPort = rProxyCfg.nPort;
744 // new session needed, destroy old first
746 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
747 ne_session_destroy( m_pHttpSession );
749 m_pHttpSession = 0;
750 bCreateNewSession = true;
754 if ( bCreateNewSession )
756 // @@@ For FTP over HTTP proxy inUserInfo is needed to be able to
757 // build the complete request URI (including user:pass), but
758 // currently (0.22.0) neon does not allow to pass the user info
759 // to the session
762 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
763 m_pHttpSession = ne_session_create(
764 rtl::OUStringToOString( m_aScheme, RTL_TEXTENCODING_UTF8 ).getStr(),
765 /* theUri.GetUserInfo(),
766 @@@ for FTP via HTTP proxy, but not supported by Neon */
767 rtl::OUStringToOString( m_aHostName, RTL_TEXTENCODING_UTF8 ).getStr(),
768 m_nPort );
771 if ( m_pHttpSession == 0 )
772 throw DAVException( DAVException::DAV_SESSION_CREATE,
773 NeonUri::makeConnectionEndPointString(
774 m_aHostName, m_nPort ) );
776 // Register the session with the lock store
777 m_aNeonLockStore.registerSession( m_pHttpSession );
779 if ( m_aScheme.equalsIgnoreAsciiCase(
780 rtl::OUString( "https" ) ) )
782 // Set a failure callback for certificate check
783 ne_ssl_set_verify(
784 m_pHttpSession, NeonSession_CertificationNotify, this);
786 ne_ssl_trust_default_ca(m_pHttpSession);
789 // Add hooks (i.e. for adding additional headers to the request)
791 #if 0
792 /* Hook called when a request is created. */
793 //typedef void (*ne_create_request_fn)(ne_request *req, void *userdata,
794 // const char *method, const char *path);
796 ne_hook_create_request( m_pHttpSession, create_req_hook_fn, this );
797 #endif
799 /* Hook called before the request is sent. 'header' is the raw HTTP
800 * header before the trailing CRLF is added: add in more here. */
801 //typedef void (*ne_pre_send_fn)(ne_request *req, void *userdata,
802 // ne_buffer *header);
804 ne_hook_pre_send( m_pHttpSession, NeonSession_PreSendRequest, this );
805 #if 0
806 /* Hook called after the request is sent. May return:
807 * NE_OK everything is okay
808 * NE_RETRY try sending the request again.
809 * anything else signifies an error, and the request is failed. The
810 * return code is passed back the _dispatch caller, so the session error
811 * must also be set appropriately (ne_set_error).
813 //typedef int (*ne_post_send_fn)(ne_request *req, void *userdata,
814 // const ne_status *status);
816 ne_hook_post_send( m_pHttpSession, post_send_req_hook_fn, this );
818 /* Hook called when the request is destroyed. */
819 //typedef void (*ne_destroy_req_fn)(ne_request *req, void *userdata);
821 ne_hook_destroy_request( m_pHttpSession, destroy_req_hook_fn, this );
823 /* Hook called when the session is destroyed. */
824 //typedef void (*ne_destroy_sess_fn)(void *userdata);
826 ne_hook_destroy_session( m_pHttpSession, destroy_sess_hook_fn, this );
827 #endif
829 if ( !m_aProxyName.isEmpty() )
831 ne_session_proxy( m_pHttpSession,
832 rtl::OUStringToOString(
833 m_aProxyName,
834 RTL_TEXTENCODING_UTF8 ).getStr(),
835 m_nProxyPort );
838 // avoid KeepAlive?
839 if ( noKeepAlive(m_aFlags) )
840 ne_set_session_flag( m_pHttpSession, NE_SESSFLAG_PERSIST, 0 );
842 // Register for redirects.
843 ne_redirect_register( m_pHttpSession );
845 // authentication callbacks.
846 #if NEON_VERSION >= 0x0260
847 ne_add_server_auth( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
848 ne_add_proxy_auth ( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
849 #else
850 ne_set_server_auth( m_pHttpSession, NeonSession_NeonAuth, this );
851 ne_set_proxy_auth ( m_pHttpSession, NeonSession_NeonAuth, this );
852 #endif
856 // -------------------------------------------------------------------
857 // virtual
858 sal_Bool NeonSession::CanUse( const rtl::OUString & inUri,
859 const uno::Sequence< beans::NamedValue >& rFlags )
863 NeonUri theUri( inUri );
864 if ( ( theUri.GetPort() == m_nPort ) &&
865 ( theUri.GetHost() == m_aHostName ) &&
866 ( theUri.GetScheme() == m_aScheme ) &&
867 ( rFlags == m_aFlags ) )
868 return sal_True;
870 catch ( DAVException const & )
872 return sal_False;
874 return sal_False;
877 // -------------------------------------------------------------------
878 // virtual
879 sal_Bool NeonSession::UsesProxy()
881 Init();
882 return !m_aProxyName.isEmpty() ;
885 // -------------------------------------------------------------------
886 // OPTIONS
887 // -------------------------------------------------------------------
888 void NeonSession::OPTIONS( const rtl::OUString & inPath,
889 DAVCapabilities & outCapabilities,
890 const DAVRequestEnvironment & rEnv )
891 throw( DAVException )
893 osl::Guard< osl::Mutex > theGuard( m_aMutex );
895 Init( rEnv );
897 HttpServerCapabilities servercaps;
898 memset( &servercaps, 0, sizeof( servercaps ) );
900 int theRetVal = ne_options( m_pHttpSession,
901 rtl::OUStringToOString(
902 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
903 &servercaps );
905 HandleError( theRetVal, inPath, rEnv );
907 outCapabilities.class1 = !!servercaps.dav_class1;
908 outCapabilities.class2 = !!servercaps.dav_class2;
909 outCapabilities.executable = !!servercaps.dav_executable;
912 // -------------------------------------------------------------------
913 // PROPFIND - allprop & named
914 // -------------------------------------------------------------------
915 void NeonSession::PROPFIND( const rtl::OUString & inPath,
916 const Depth inDepth,
917 const std::vector< rtl::OUString > & inPropNames,
918 std::vector< DAVResource > & ioResources,
919 const DAVRequestEnvironment & rEnv )
920 throw ( DAVException )
922 osl::Guard< osl::Mutex > theGuard( m_aMutex );
924 Init( rEnv );
926 int theRetVal = NE_OK;
927 NeonPropFindRequest theRequest( m_pHttpSession,
928 rtl::OUStringToOString(
929 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
930 inDepth,
931 inPropNames,
932 ioResources,
933 theRetVal );
935 HandleError( theRetVal, inPath, rEnv );
938 // -------------------------------------------------------------------
939 // PROPFIND - propnames
940 // -------------------------------------------------------------------
941 void NeonSession::PROPFIND( const rtl::OUString & inPath,
942 const Depth inDepth,
943 std::vector< DAVResourceInfo > & ioResInfo,
944 const DAVRequestEnvironment & rEnv )
945 throw( DAVException )
947 osl::Guard< osl::Mutex > theGuard( m_aMutex );
949 Init( rEnv );
951 int theRetVal = NE_OK;
952 NeonPropFindRequest theRequest( m_pHttpSession,
953 rtl::OUStringToOString(
954 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
955 inDepth,
956 ioResInfo,
957 theRetVal );
959 HandleError( theRetVal, inPath, rEnv );
962 // -------------------------------------------------------------------
963 // PROPPATCH
964 // -------------------------------------------------------------------
965 void NeonSession::PROPPATCH( const rtl::OUString & inPath,
966 const std::vector< ProppatchValue > & inValues,
967 const DAVRequestEnvironment & rEnv )
968 throw( DAVException )
970 /* @@@ Which standard live properties can be set by the client?
971 This is a known WebDAV RFC issue ( verified: 04/10/2001 )
972 --> http://www.ics.uci.edu/pub/ietf/webdav/protocol/issues.html
974 mod_dav implementation:
976 creationdate r ( File System prop )
977 displayname w
978 getcontentlanguage r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
979 getcontentlength r ( File System prop )
980 getcontenttype r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
981 getetag r ( File System prop )
982 getlastmodified r ( File System prop )
983 lockdiscovery r
984 resourcetype r
985 source w
986 supportedlock r
987 executable w ( #ifndef WIN32 )
989 All dead properties are of course writable.
992 int theRetVal = NE_OK;
994 int n; // for the "for" loop
996 // Generate the list of properties we want to set.
997 int nPropCount = inValues.size();
998 ne_proppatch_operation* pItems
999 = new ne_proppatch_operation[ nPropCount + 1 ];
1000 for ( n = 0; n < nPropCount; ++n )
1002 const ProppatchValue & rValue = inValues[ n ];
1004 // Split fullname into namespace and name!
1005 ne_propname * pName = new ne_propname;
1006 DAVProperties::createNeonPropName( rValue.name, *pName );
1007 pItems[ n ].name = pName;
1009 if ( rValue.operation == PROPSET )
1011 pItems[ n ].type = ne_propset;
1013 rtl::OUString aStringValue;
1014 if ( DAVProperties::isUCBDeadProperty( *pName ) )
1016 // DAV dead property added by WebDAV UCP?
1017 if ( !UCBDeadPropertyValue::toXML( rValue.value,
1018 aStringValue ) )
1020 // Error!
1021 pItems[ n ].value = 0;
1022 theRetVal = NE_ERROR;
1023 nPropCount = n + 1;
1024 break;
1027 else if ( !( rValue.value >>= aStringValue ) )
1029 // complex properties...
1030 if ( rValue.name == DAVProperties::SOURCE )
1032 uno::Sequence< ucb::Link > aLinks;
1033 if ( rValue.value >>= aLinks )
1035 LinkSequence::toXML( aLinks, aStringValue );
1037 else
1039 // Error!
1040 pItems[ n ].value = 0;
1041 theRetVal = NE_ERROR;
1042 nPropCount = n + 1;
1043 break;
1046 else
1048 OSL_FAIL( "NeonSession::PROPPATCH - unsupported type!" );
1049 // Error!
1050 pItems[ n ].value = 0;
1051 theRetVal = NE_ERROR;
1052 nPropCount = n + 1;
1053 break;
1056 pItems[ n ].value
1057 = strdup( rtl::OUStringToOString( aStringValue,
1058 RTL_TEXTENCODING_UTF8 ).getStr() );
1060 else
1062 pItems[ n ].type = ne_propremove;
1063 pItems[ n ].value = 0;
1067 if ( theRetVal == NE_OK )
1069 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1071 Init( rEnv );
1073 pItems[ n ].name = 0;
1075 theRetVal = ne_proppatch( m_pHttpSession,
1076 rtl::OUStringToOString(
1077 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1078 pItems );
1081 for ( n = 0; n < nPropCount; ++n )
1083 free( (void *)pItems[ n ].name->name );
1084 delete pItems[ n ].name;
1085 free( (void *)pItems[ n ].value );
1088 delete [] pItems;
1090 HandleError( theRetVal, inPath, rEnv );
1093 // -------------------------------------------------------------------
1094 // HEAD
1095 // -------------------------------------------------------------------
1096 void NeonSession::HEAD( const ::rtl::OUString & inPath,
1097 const std::vector< ::rtl::OUString > & inHeaderNames,
1098 DAVResource & ioResource,
1099 const DAVRequestEnvironment & rEnv )
1100 throw( DAVException )
1102 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1104 Init( rEnv );
1106 int theRetVal = NE_OK;
1107 NeonHeadRequest theRequest( m_pHttpSession,
1108 inPath,
1109 inHeaderNames,
1110 ioResource,
1111 theRetVal );
1113 HandleError( theRetVal, inPath, rEnv );
1116 // -------------------------------------------------------------------
1117 // GET
1118 // -------------------------------------------------------------------
1119 uno::Reference< io::XInputStream >
1120 NeonSession::GET( const rtl::OUString & inPath,
1121 const DAVRequestEnvironment & rEnv )
1122 throw ( DAVException )
1124 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1126 Init( rEnv );
1128 rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
1129 NeonRequestContext aCtx( xInputStream );
1130 int theRetVal = GET( m_pHttpSession,
1131 rtl::OUStringToOString(
1132 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1133 NeonSession_ResponseBlockReader,
1134 false,
1135 &aCtx );
1137 HandleError( theRetVal, inPath, rEnv );
1139 return uno::Reference< io::XInputStream >( xInputStream.get() );
1142 // -------------------------------------------------------------------
1143 // GET
1144 // -------------------------------------------------------------------
1145 void NeonSession::GET( const rtl::OUString & inPath,
1146 uno::Reference< io::XOutputStream > & ioOutputStream,
1147 const DAVRequestEnvironment & rEnv )
1148 throw ( DAVException )
1150 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1152 Init( rEnv );
1154 NeonRequestContext aCtx( ioOutputStream );
1155 int theRetVal = GET( m_pHttpSession,
1156 rtl::OUStringToOString(
1157 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1158 NeonSession_ResponseBlockWriter,
1159 false,
1160 &aCtx );
1162 HandleError( theRetVal, inPath, rEnv );
1165 // -------------------------------------------------------------------
1166 // GET
1167 // -------------------------------------------------------------------
1168 uno::Reference< io::XInputStream >
1169 NeonSession::GET( const rtl::OUString & inPath,
1170 const std::vector< ::rtl::OUString > & inHeaderNames,
1171 DAVResource & ioResource,
1172 const DAVRequestEnvironment & rEnv )
1173 throw ( DAVException )
1175 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1177 Init( rEnv );
1179 ioResource.uri = inPath;
1180 ioResource.properties.clear();
1182 rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
1183 NeonRequestContext aCtx( xInputStream, inHeaderNames, ioResource );
1184 int theRetVal = GET( m_pHttpSession,
1185 rtl::OUStringToOString(
1186 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1187 NeonSession_ResponseBlockReader,
1188 true,
1189 &aCtx );
1191 HandleError( theRetVal, inPath, rEnv );
1193 return uno::Reference< io::XInputStream >( xInputStream.get() );
1196 // -------------------------------------------------------------------
1197 // GET
1198 // -------------------------------------------------------------------
1199 void NeonSession::GET( const rtl::OUString & inPath,
1200 uno::Reference< io::XOutputStream > & ioOutputStream,
1201 const std::vector< ::rtl::OUString > & inHeaderNames,
1202 DAVResource & ioResource,
1203 const DAVRequestEnvironment & rEnv )
1204 throw ( DAVException )
1206 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1208 Init( rEnv );
1210 ioResource.uri = inPath;
1211 ioResource.properties.clear();
1213 NeonRequestContext aCtx( ioOutputStream, inHeaderNames, ioResource );
1214 int theRetVal = GET( m_pHttpSession,
1215 rtl::OUStringToOString(
1216 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1217 NeonSession_ResponseBlockWriter,
1218 true,
1219 &aCtx );
1221 HandleError( theRetVal, inPath, rEnv );
1224 // -------------------------------------------------------------------
1225 // PUT
1226 // -------------------------------------------------------------------
1227 void NeonSession::PUT( const rtl::OUString & inPath,
1228 const uno::Reference< io::XInputStream > & inInputStream,
1229 const DAVRequestEnvironment & rEnv )
1230 throw ( DAVException )
1232 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1234 uno::Sequence< sal_Int8 > aDataToSend;
1235 if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
1236 throw DAVException( DAVException::DAV_INVALID_ARG );
1238 Init( rEnv );
1240 int theRetVal = PUT( m_pHttpSession,
1241 rtl::OUStringToOString(
1242 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1243 reinterpret_cast< const char * >(
1244 aDataToSend.getConstArray() ),
1245 aDataToSend.getLength() );
1247 HandleError( theRetVal, inPath, rEnv );
1250 // -------------------------------------------------------------------
1251 // POST
1252 // -------------------------------------------------------------------
1253 uno::Reference< io::XInputStream >
1254 NeonSession::POST( const rtl::OUString & inPath,
1255 const rtl::OUString & rContentType,
1256 const rtl::OUString & rReferer,
1257 const uno::Reference< io::XInputStream > & inInputStream,
1258 const DAVRequestEnvironment & rEnv )
1259 throw ( DAVException )
1261 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1263 uno::Sequence< sal_Int8 > aDataToSend;
1264 if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
1265 throw DAVException( DAVException::DAV_INVALID_ARG );
1267 Init( rEnv );
1269 rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
1270 NeonRequestContext aCtx( xInputStream );
1271 int theRetVal = POST( m_pHttpSession,
1272 rtl::OUStringToOString(
1273 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1274 reinterpret_cast< const char * >(
1275 aDataToSend.getConstArray() ),
1276 NeonSession_ResponseBlockReader,
1277 &aCtx,
1278 rContentType,
1279 rReferer );
1281 HandleError( theRetVal, inPath, rEnv );
1283 return uno::Reference< io::XInputStream >( xInputStream.get() );
1286 // -------------------------------------------------------------------
1287 // POST
1288 // -------------------------------------------------------------------
1289 void NeonSession::POST( const rtl::OUString & inPath,
1290 const rtl::OUString & rContentType,
1291 const rtl::OUString & rReferer,
1292 const uno::Reference< io::XInputStream > & inInputStream,
1293 uno::Reference< io::XOutputStream > & oOutputStream,
1294 const DAVRequestEnvironment & rEnv )
1295 throw ( DAVException )
1297 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1299 uno::Sequence< sal_Int8 > aDataToSend;
1300 if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
1301 throw DAVException( DAVException::DAV_INVALID_ARG );
1303 Init( rEnv );
1305 NeonRequestContext aCtx( oOutputStream );
1306 int theRetVal = POST( m_pHttpSession,
1307 rtl::OUStringToOString(
1308 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1309 reinterpret_cast< const char * >(
1310 aDataToSend.getConstArray() ),
1311 NeonSession_ResponseBlockWriter,
1312 &aCtx,
1313 rContentType,
1314 rReferer );
1316 HandleError( theRetVal, inPath, rEnv );
1319 // -------------------------------------------------------------------
1320 // MKCOL
1321 // -------------------------------------------------------------------
1322 void NeonSession::MKCOL( const rtl::OUString & inPath,
1323 const DAVRequestEnvironment & rEnv )
1324 throw ( DAVException )
1326 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1328 Init( rEnv );
1330 int theRetVal = ne_mkcol( m_pHttpSession,
1331 rtl::OUStringToOString(
1332 inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
1334 HandleError( theRetVal, inPath, rEnv );
1337 // -------------------------------------------------------------------
1338 // COPY
1339 // -------------------------------------------------------------------
1340 void NeonSession::COPY( const rtl::OUString & inSourceURL,
1341 const rtl::OUString & inDestinationURL,
1342 const DAVRequestEnvironment & rEnv,
1343 sal_Bool inOverWrite )
1344 throw ( DAVException )
1346 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1348 Init( rEnv );
1350 NeonUri theSourceUri( inSourceURL );
1351 NeonUri theDestinationUri( inDestinationURL );
1353 int theRetVal = ne_copy( m_pHttpSession,
1354 inOverWrite ? 1 : 0,
1355 NE_DEPTH_INFINITE,
1356 rtl::OUStringToOString(
1357 theSourceUri.GetPath(),
1358 RTL_TEXTENCODING_UTF8 ).getStr(),
1359 rtl::OUStringToOString(
1360 theDestinationUri.GetPath(),
1361 RTL_TEXTENCODING_UTF8 ).getStr() );
1363 HandleError( theRetVal, inSourceURL, rEnv );
1366 // -------------------------------------------------------------------
1367 // MOVE
1368 // -------------------------------------------------------------------
1369 void NeonSession::MOVE( const rtl::OUString & inSourceURL,
1370 const rtl::OUString & inDestinationURL,
1371 const DAVRequestEnvironment & rEnv,
1372 sal_Bool inOverWrite )
1373 throw ( DAVException )
1375 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1377 Init( rEnv );
1379 NeonUri theSourceUri( inSourceURL );
1380 NeonUri theDestinationUri( inDestinationURL );
1381 int theRetVal = ne_move( m_pHttpSession,
1382 inOverWrite ? 1 : 0,
1383 rtl::OUStringToOString(
1384 theSourceUri.GetPath(),
1385 RTL_TEXTENCODING_UTF8 ).getStr(),
1386 rtl::OUStringToOString(
1387 theDestinationUri.GetPath(),
1388 RTL_TEXTENCODING_UTF8 ).getStr() );
1390 HandleError( theRetVal, inSourceURL, rEnv );
1393 // -------------------------------------------------------------------
1394 // DESTROY
1395 // -------------------------------------------------------------------
1396 void NeonSession::DESTROY( const rtl::OUString & inPath,
1397 const DAVRequestEnvironment & rEnv )
1398 throw ( DAVException )
1400 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1402 Init( rEnv );
1404 int theRetVal = ne_delete( m_pHttpSession,
1405 rtl::OUStringToOString(
1406 inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
1408 HandleError( theRetVal, inPath, rEnv );
1411 // -------------------------------------------------------------------
1412 namespace
1414 sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
1415 int timeout )
1417 TimeValue aEnd;
1418 osl_getSystemTime( &aEnd );
1420 // Try to estimate a safe absolute time for sending the
1421 // lock refresh request.
1422 sal_Int32 lastChanceToSendRefreshRequest = -1;
1423 if ( timeout != NE_TIMEOUT_INFINITE )
1425 sal_Int32 calltime = aEnd.Seconds - rStart.Seconds;
1426 if ( calltime <= timeout )
1428 lastChanceToSendRefreshRequest
1429 = aEnd.Seconds + timeout - calltime;
1431 else
1433 OSL_TRACE( "No chance to refresh lock before timeout!" );
1436 return lastChanceToSendRefreshRequest;
1439 } // namespace
1441 // -------------------------------------------------------------------
1442 // LOCK (set new lock)
1443 // -------------------------------------------------------------------
1444 void NeonSession::LOCK( const ::rtl::OUString & inPath,
1445 ucb::Lock & rLock,
1446 const DAVRequestEnvironment & rEnv )
1447 throw ( DAVException )
1449 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1451 Init( rEnv );
1453 /* Create a depth zero, exclusive write lock, with default timeout
1454 * (allowing a server to pick a default). token, owner and uri are
1455 * unset. */
1456 NeonLock * theLock = ne_lock_create();
1458 // Set the lock uri
1459 ne_uri aUri;
1460 ne_uri_parse( rtl::OUStringToOString( makeAbsoluteURL( inPath ),
1461 RTL_TEXTENCODING_UTF8 ).getStr(),
1462 &aUri );
1463 theLock->uri = aUri;
1465 // Set the lock depth
1466 switch( rLock.Depth )
1468 case ucb::LockDepth_ZERO:
1469 theLock->depth = NE_DEPTH_ZERO;
1470 break;
1471 case ucb::LockDepth_ONE:
1472 theLock->depth = NE_DEPTH_ONE;
1473 break;
1474 case ucb::LockDepth_INFINITY:
1475 theLock->depth = NE_DEPTH_INFINITE;
1476 break;
1477 default:
1478 throw DAVException( DAVException::DAV_INVALID_ARG );
1481 // Set the lock scope
1482 switch ( rLock.Scope )
1484 case ucb::LockScope_EXCLUSIVE:
1485 theLock->scope = ne_lockscope_exclusive;
1486 break;
1487 case ucb::LockScope_SHARED:
1488 theLock->scope = ne_lockscope_shared;
1489 break;
1490 default:
1491 throw DAVException( DAVException::DAV_INVALID_ARG );
1494 // Set the lock timeout
1495 theLock->timeout = (long)rLock.Timeout;
1497 // Set the lock owner
1498 rtl::OUString aValue;
1499 rLock.Owner >>= aValue;
1500 theLock->owner =
1501 ne_strdup( rtl::OUStringToOString( aValue,
1502 RTL_TEXTENCODING_UTF8 ).getStr() );
1503 TimeValue startCall;
1504 osl_getSystemTime( &startCall );
1506 int theRetVal = ne_lock( m_pHttpSession, theLock );
1508 if ( theRetVal == NE_OK )
1510 m_aNeonLockStore.addLock( theLock,
1511 this,
1512 lastChanceToSendRefreshRequest(
1513 startCall, theLock->timeout ) );
1515 uno::Sequence< rtl::OUString > aTokens( 1 );
1516 aTokens[ 0 ] = rtl::OUString::createFromAscii( theLock->token );
1517 rLock.LockTokens = aTokens;
1519 OSL_TRACE( "NeonSession::LOCK: created lock for %s. token: %s",
1520 rtl::OUStringToOString( makeAbsoluteURL( inPath ),
1521 RTL_TEXTENCODING_UTF8 ).getStr(),
1522 theLock->token );
1524 else
1526 ne_lock_destroy( theLock );
1528 OSL_TRACE( "NeonSession::LOCK: obtaining lock for %s failed!",
1529 rtl::OUStringToOString( makeAbsoluteURL( inPath ),
1530 RTL_TEXTENCODING_UTF8 ).getStr() );
1533 HandleError( theRetVal, inPath, rEnv );
1536 // -------------------------------------------------------------------
1537 // LOCK (refresh existing lock)
1538 // -------------------------------------------------------------------
1539 sal_Int64 NeonSession::LOCK( const ::rtl::OUString & inPath,
1540 sal_Int64 nTimeout,
1541 const DAVRequestEnvironment & rEnv )
1542 throw ( DAVException )
1544 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1546 // Try to get the neon lock from lock store
1547 NeonLock * theLock
1548 = m_aNeonLockStore.findByUri( makeAbsoluteURL( inPath ) );
1549 if ( !theLock )
1550 throw DAVException( DAVException::DAV_NOT_LOCKED );
1552 Init( rEnv );
1554 // refresh existing lock.
1555 theLock->timeout = static_cast< long >( nTimeout );
1557 TimeValue startCall;
1558 osl_getSystemTime( &startCall );
1560 int theRetVal = ne_lock_refresh( m_pHttpSession, theLock );
1562 if ( theRetVal == NE_OK )
1564 m_aNeonLockStore.updateLock( theLock,
1565 lastChanceToSendRefreshRequest(
1566 startCall, theLock->timeout ) );
1569 HandleError( theRetVal, inPath, rEnv );
1571 return theLock->timeout;
1574 // -------------------------------------------------------------------
1575 // LOCK (refresh existing lock)
1576 // -------------------------------------------------------------------
1577 bool NeonSession::LOCK( NeonLock * pLock,
1578 sal_Int32 & rlastChanceToSendRefreshRequest )
1580 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1582 #if OSL_DEBUG_LEVEL > 0
1583 char * p = ne_uri_unparse( &(pLock->uri) );
1584 OSL_TRACE( "NeonSession::LOCK: Refreshing lock for %s.", p );
1585 ne_free( p );
1586 #endif
1588 // refresh existing lock.
1590 TimeValue startCall;
1591 osl_getSystemTime( &startCall );
1593 if ( ne_lock_refresh( m_pHttpSession, pLock ) == NE_OK )
1595 rlastChanceToSendRefreshRequest
1596 = lastChanceToSendRefreshRequest( startCall, pLock->timeout );
1598 OSL_TRACE( "Lock successfully refreshed." );
1599 return true;
1601 else
1603 OSL_TRACE( "Lock not refreshed!" );
1604 return false;
1608 // -------------------------------------------------------------------
1609 // UNLOCK
1610 // -------------------------------------------------------------------
1611 void NeonSession::UNLOCK( const ::rtl::OUString & inPath,
1612 const DAVRequestEnvironment & rEnv )
1613 throw ( DAVException )
1615 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1617 // get the neon lock from lock store
1618 NeonLock * theLock
1619 = m_aNeonLockStore.findByUri( makeAbsoluteURL( inPath ) );
1620 if ( !theLock )
1621 throw DAVException( DAVException::DAV_NOT_LOCKED );
1623 Init( rEnv );
1625 int theRetVal = ne_unlock( m_pHttpSession, theLock );
1627 if ( theRetVal == NE_OK )
1629 m_aNeonLockStore.removeLock( theLock );
1630 ne_lock_destroy( theLock );
1632 else
1634 OSL_TRACE( "NeonSession::UNLOCK: unlocking of %s failed.",
1635 rtl::OUStringToOString( makeAbsoluteURL( inPath ),
1636 RTL_TEXTENCODING_UTF8 ).getStr() );
1639 HandleError( theRetVal, inPath, rEnv );
1642 // -------------------------------------------------------------------
1643 // UNLOCK
1644 // -------------------------------------------------------------------
1645 bool NeonSession::UNLOCK( NeonLock * pLock )
1647 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1649 #if OSL_DEBUG_LEVEL > 0
1650 char * p = ne_uri_unparse( &(pLock->uri) );
1651 OSL_TRACE( "NeonSession::UNLOCK: Unlocking %s.", p );
1652 ne_free( p );
1653 #endif
1655 if ( ne_unlock( m_pHttpSession, pLock ) == NE_OK )
1657 OSL_TRACE( "UNLOCK succeeded." );
1658 return true;
1660 else
1662 OSL_TRACE( "UNLOCK failed!" );
1663 return false;
1667 // -------------------------------------------------------------------
1668 void NeonSession::abort()
1669 throw ( DAVException )
1671 if ( m_pHttpSession )
1673 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
1674 ne_close_connection( m_pHttpSession );
1678 // -------------------------------------------------------------------
1679 const ucbhelper::InternetProxyServer & NeonSession::getProxySettings() const
1681 if ( m_aScheme == "http" || m_aScheme == "https" )
1683 return m_rProxyDecider.getProxy( m_aScheme,
1684 m_aHostName,
1685 m_nPort );
1687 else
1689 return m_rProxyDecider.getProxy( m_aScheme,
1690 rtl::OUString() /* not used */,
1691 -1 /* not used */ );
1695 // -------------------------------------------------------------------
1696 namespace {
1698 bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
1699 const char * token )
1701 for ( sal_Int32 n = 0; n < rLocks.getLength(); ++n )
1703 const uno::Sequence< rtl::OUString > & rTokens
1704 = rLocks[ n ].LockTokens;
1705 for ( sal_Int32 m = 0; m < rTokens.getLength(); ++m )
1707 if ( rTokens[ m ].equalsAscii( token ) )
1708 return true;
1711 return false;
1714 } // namespace
1716 // -------------------------------------------------------------------
1717 bool NeonSession::removeExpiredLocktoken( const rtl::OUString & inURL,
1718 const DAVRequestEnvironment & rEnv )
1720 NeonLock * theLock = m_aNeonLockStore.findByUri( inURL );
1721 if ( !theLock )
1722 return false;
1724 // do a lockdiscovery to check whether this lock is still valid.
1727 // @@@ Alternative: use ne_lock_discover() => less overhead
1729 std::vector< DAVResource > aResources;
1730 std::vector< rtl::OUString > aPropNames;
1731 aPropNames.push_back( DAVProperties::LOCKDISCOVERY );
1733 PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv );
1735 if ( aResources.empty() )
1736 return false;
1738 std::vector< DAVPropertyValue >::const_iterator it
1739 = aResources[ 0 ].properties.begin();
1740 std::vector< DAVPropertyValue >::const_iterator end
1741 = aResources[ 0 ].properties.end();
1743 while ( it != end )
1745 if ( (*it).Name.equals( DAVProperties::LOCKDISCOVERY ) )
1747 uno::Sequence< ucb::Lock > aLocks;
1748 if ( !( (*it).Value >>= aLocks ) )
1749 return false;
1751 if ( !containsLocktoken( aLocks, theLock->token ) )
1753 // expired!
1754 break;
1757 // still valid.
1758 return false;
1760 ++it;
1763 // No lockdiscovery prop in propfind result / locktoken not found
1764 // in propfind result -> not locked
1765 OSL_TRACE( "NeonSession::removeExpiredLocktoken: Removing "
1766 " expired lock token for %s. token: %s",
1767 rtl::OUStringToOString( inURL,
1768 RTL_TEXTENCODING_UTF8 ).getStr(),
1769 theLock->token );
1771 m_aNeonLockStore.removeLock( theLock );
1772 ne_lock_destroy( theLock );
1773 return true;
1775 catch ( DAVException const & )
1778 return false;
1781 // -------------------------------------------------------------------
1782 // HandleError
1783 // Common Error Handler
1784 // -------------------------------------------------------------------
1785 void NeonSession::HandleError( int nError,
1786 const rtl::OUString & inPath,
1787 const DAVRequestEnvironment & rEnv )
1788 throw ( DAVException )
1790 m_aEnv = DAVRequestEnvironment();
1792 // Map error code to DAVException.
1793 switch ( nError )
1795 case NE_OK:
1796 return;
1798 case NE_ERROR: // Generic error
1800 rtl::OUString aText = rtl::OUString::createFromAscii(
1801 ne_get_error( m_pHttpSession ) );
1803 sal_uInt16 code = makeStatusCode( aText );
1805 if ( code == SC_LOCKED )
1807 if ( m_aNeonLockStore.findByUri(
1808 makeAbsoluteURL( inPath ) ) == 0 )
1810 // locked by 3rd party
1811 throw DAVException( DAVException::DAV_LOCKED );
1813 else
1815 // locked by ourself
1816 throw DAVException( DAVException::DAV_LOCKED_SELF );
1820 // Special handling for 400 and 412 status codes, which may indicate
1821 // that a lock previously obtained by us has been released meanwhile
1822 // by the server. Unfortunately, RFC is not clear at this point,
1823 // thus server implementations behave different...
1824 else if ( code == SC_BAD_REQUEST || code == SC_PRECONDITION_FAILED )
1826 if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) )
1827 throw DAVException( DAVException::DAV_LOCK_EXPIRED );
1830 throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
1832 case NE_LOOKUP: // Name lookup failed.
1833 throw DAVException( DAVException::DAV_HTTP_LOOKUP,
1834 NeonUri::makeConnectionEndPointString(
1835 m_aHostName, m_nPort ) );
1837 case NE_AUTH: // User authentication failed on server
1838 throw DAVException( DAVException::DAV_HTTP_AUTH,
1839 NeonUri::makeConnectionEndPointString(
1840 m_aHostName, m_nPort ) );
1842 case NE_PROXYAUTH: // User authentication failed on proxy
1843 throw DAVException( DAVException::DAV_HTTP_AUTHPROXY,
1844 NeonUri::makeConnectionEndPointString(
1845 m_aProxyName, m_nProxyPort ) );
1847 case NE_CONNECT: // Could not connect to server
1848 throw DAVException( DAVException::DAV_HTTP_CONNECT,
1849 NeonUri::makeConnectionEndPointString(
1850 m_aHostName, m_nPort ) );
1852 case NE_TIMEOUT: // Connection timed out
1853 throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
1854 NeonUri::makeConnectionEndPointString(
1855 m_aHostName, m_nPort ) );
1857 case NE_FAILED: // The precondition failed
1858 throw DAVException( DAVException::DAV_HTTP_FAILED,
1859 NeonUri::makeConnectionEndPointString(
1860 m_aHostName, m_nPort ) );
1862 case NE_RETRY: // Retry request (ne_end_request ONLY)
1863 throw DAVException( DAVException::DAV_HTTP_RETRY,
1864 NeonUri::makeConnectionEndPointString(
1865 m_aHostName, m_nPort ) );
1867 case NE_REDIRECT:
1869 NeonUri aUri( ne_redirect_location( m_pHttpSession ) );
1870 throw DAVException(
1871 DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() );
1873 default:
1875 OSL_TRACE( "NeonSession::HandleError : Unknown Neon error code!" );
1876 throw DAVException( DAVException::DAV_HTTP_ERROR,
1877 rtl::OUString::createFromAscii(
1878 ne_get_error( m_pHttpSession ) ) );
1883 // -------------------------------------------------------------------
1884 namespace {
1886 void runResponseHeaderHandler( void * userdata,
1887 const char * value )
1889 rtl::OUString aHeader( rtl::OUString::createFromAscii( value ) );
1890 sal_Int32 nPos = aHeader.indexOf( ':' );
1892 if ( nPos != -1 )
1894 rtl::OUString aHeaderName( aHeader.copy( 0, nPos ) );
1896 NeonRequestContext * pCtx
1897 = static_cast< NeonRequestContext * >( userdata );
1899 // Note: Empty vector means that all headers are requested.
1900 bool bIncludeIt = ( pCtx->pHeaderNames->empty() );
1902 if ( !bIncludeIt )
1904 // Check whether this header was requested.
1905 std::vector< ::rtl::OUString >::const_iterator it(
1906 pCtx->pHeaderNames->begin() );
1907 const std::vector< ::rtl::OUString >::const_iterator end(
1908 pCtx->pHeaderNames->end() );
1910 while ( it != end )
1912 // header names are case insensitive
1913 if ( (*it).equalsIgnoreAsciiCase( aHeaderName ) )
1915 aHeaderName = (*it);
1916 break;
1918 ++it;
1921 if ( it != end )
1922 bIncludeIt = true;
1925 if ( bIncludeIt )
1927 // Create & set the PropertyValue
1928 DAVPropertyValue thePropertyValue;
1929 thePropertyValue.IsCaseSensitive = false;
1930 thePropertyValue.Name = aHeaderName;
1932 if ( nPos < aHeader.getLength() )
1933 thePropertyValue.Value <<= aHeader.copy( nPos + 1 ).trim();
1935 // Add the newly created PropertyValue
1936 pCtx->pResource->properties.push_back( thePropertyValue );
1941 } // namespace
1943 // -------------------------------------------------------------------
1944 // static
1945 int NeonSession::GET( ne_session * sess,
1946 const char * uri,
1947 ne_block_reader reader,
1948 bool getheaders,
1949 void * userdata )
1951 //struct get_context ctx;
1952 ne_request * req = ne_request_create( sess, "GET", uri );
1953 int ret;
1955 ne_decompress * dc
1956 = ne_decompress_reader( req, ne_accept_2xx, reader, userdata );
1959 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
1960 ret = ne_request_dispatch( req );
1963 if ( getheaders )
1965 void *cursor = NULL;
1966 const char *name, *value;
1967 while ( ( cursor = ne_response_header_iterate(
1968 req, cursor, &name, &value ) ) != NULL )
1970 char buffer[8192];
1972 ne_snprintf(buffer, sizeof buffer, "%s: %s", name, value);
1973 runResponseHeaderHandler(userdata, buffer);
1977 if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
1978 ret = NE_ERROR;
1980 if ( dc != 0 )
1981 ne_decompress_destroy(dc);
1983 ne_request_destroy( req );
1984 return ret;
1987 // -------------------------------------------------------------------
1988 // static
1989 int NeonSession::PUT( ne_session * sess,
1990 const char * uri,
1991 const char * buffer,
1992 size_t size)
1994 ne_request * req = ne_request_create( sess, "PUT", uri );
1995 int ret;
1997 ne_lock_using_resource( req, uri, 0 );
1998 ne_lock_using_parent( req, uri );
2000 ne_set_request_body_buffer( req, buffer, size );
2003 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
2004 ret = ne_request_dispatch( req );
2007 if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
2008 ret = NE_ERROR;
2010 ne_request_destroy( req );
2011 return ret;
2014 // -------------------------------------------------------------------
2015 int NeonSession::POST( ne_session * sess,
2016 const char * uri,
2017 const char * buffer,
2018 ne_block_reader reader,
2019 void * userdata,
2020 const rtl::OUString & rContentType,
2021 const rtl::OUString & rReferer )
2023 ne_request * req = ne_request_create( sess, "POST", uri );
2024 //struct get_context ctx;
2025 int ret;
2027 RequestDataMap * pData = 0;
2029 if ( !rContentType.isEmpty() || !rReferer.isEmpty() )
2031 // Remember contenttype and referer. Data will be added to HTTP request
2032 // header in in 'PreSendRequest' callback.
2033 pData = static_cast< RequestDataMap* >( m_pRequestData );
2034 (*pData)[ req ] = RequestData( rContentType, rReferer );
2037 //ctx.total = -1;
2038 //ctx.fd = fd;
2039 //ctx.error = 0;
2040 //ctx.session = sess;
2042 ///* Read the value of the Content-Length header into ctx.total */
2043 //ne_add_response_header_handler( req, "Content-Length",
2044 // ne_handle_numeric_header, &ctx.total );
2046 ne_add_response_body_reader( req, ne_accept_2xx, reader, userdata );
2048 ne_set_request_body_buffer( req, buffer, strlen( buffer ) );
2051 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
2052 ret = ne_request_dispatch( req );
2055 //if ( ctx.error )
2056 // ret = NE_ERROR;
2057 //else
2058 if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
2059 ret = NE_ERROR;
2061 ne_request_destroy( req );
2063 if ( pData )
2065 // Remove request data from session's list.
2066 RequestDataMap::iterator it = pData->find( req );
2067 if ( it != pData->end() )
2068 pData->erase( it );
2071 return ret;
2074 // -------------------------------------------------------------------
2075 // static
2076 bool
2077 NeonSession::getDataFromInputStream(
2078 const uno::Reference< io::XInputStream > & xStream,
2079 uno::Sequence< sal_Int8 > & rData,
2080 bool bAppendTrailingZeroByte )
2082 if ( xStream.is() )
2084 uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY );
2085 if ( xSeekable.is() )
2089 sal_Int32 nSize
2090 = sal::static_int_cast<sal_Int32>(xSeekable->getLength());
2091 sal_Int32 nRead
2092 = xStream->readBytes( rData, nSize );
2094 if ( nRead == nSize )
2096 if ( bAppendTrailingZeroByte )
2098 rData.realloc( nSize + 1 );
2099 rData[ nSize ] = sal_Int8( 0 );
2101 return true;
2104 catch ( io::NotConnectedException const & )
2106 // readBytes
2108 catch ( io::BufferSizeExceededException const & )
2110 // readBytes
2112 catch ( io::IOException const & )
2114 // getLength, readBytes
2117 else
2121 uno::Sequence< sal_Int8 > aBuffer;
2122 sal_Int32 nPos = 0;
2124 sal_Int32 nRead = xStream->readSomeBytes( aBuffer, 65536 );
2125 while ( nRead > 0 )
2127 if ( rData.getLength() < ( nPos + nRead ) )
2128 rData.realloc( nPos + nRead );
2130 aBuffer.realloc( nRead );
2131 rtl_copyMemory( (void*)( rData.getArray() + nPos ),
2132 (const void*)aBuffer.getConstArray(),
2133 nRead );
2134 nPos += nRead;
2136 aBuffer.realloc( 0 );
2137 nRead = xStream->readSomeBytes( aBuffer, 65536 );
2140 if ( bAppendTrailingZeroByte )
2142 rData.realloc( nPos + 1 );
2143 rData[ nPos ] = sal_Int8( 0 );
2145 return true;
2147 catch ( io::NotConnectedException const & )
2149 // readBytes
2151 catch ( io::BufferSizeExceededException const & )
2153 // readBytes
2155 catch ( io::IOException const & )
2157 // readBytes
2161 return false;
2164 // ---------------------------------------------------------------------
2165 sal_Bool
2166 NeonSession::isDomainMatch( rtl::OUString certHostName )
2168 rtl::OUString hostName = getHostName();
2170 if (hostName.equalsIgnoreAsciiCase( certHostName ) )
2171 return sal_True;
2173 if ( 0 == certHostName.indexOf( rtl::OUString("*") ) &&
2174 hostName.getLength() >= certHostName.getLength() )
2176 rtl::OUString cmpStr = certHostName.copy( 1 );
2178 if ( hostName.matchIgnoreAsciiCase(
2179 cmpStr, hostName.getLength() - cmpStr.getLength() ) )
2180 return sal_True;
2182 return sal_False;
2185 // ---------------------------------------------------------------------
2186 rtl::OUString NeonSession::makeAbsoluteURL( rtl::OUString const & rURL ) const
2190 // Is URL relative or already absolute?
2191 if ( rURL[ 0 ] != sal_Unicode( '/' ) )
2193 // absolute.
2194 return rtl::OUString( rURL );
2196 else
2198 ne_uri aUri;
2199 memset( &aUri, 0, sizeof( aUri ) );
2201 ne_fill_server_uri( m_pHttpSession, &aUri );
2202 aUri.path
2203 = ne_strdup( rtl::OUStringToOString(
2204 rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
2205 NeonUri aNeonUri( &aUri );
2206 ne_uri_free( &aUri );
2207 return aNeonUri.GetURI();
2210 catch ( DAVException const & )
2213 // error.
2214 return rtl::OUString();
2217 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */