1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <sal/log.hxx>
23 #include <comphelper/processfactory.hxx>
24 #include <comphelper/sequence.hxx>
25 #include <ucbhelper/simplecertificatevalidationrequest.hxx>
28 #include <apr_strings.h>
30 #include "DAVAuthListener.hxx"
31 #include "SerfSession.hxx"
32 #include "SerfUri.hxx"
33 #include "SerfRequestProcessor.hxx"
34 #include "SerfCallbacks.hxx"
35 #include "SerfInputStream.hxx"
37 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
38 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
39 #include <com/sun/star/security/XCertificate.hpp>
40 #include <com/sun/star/security/CertificateValidity.hpp>
41 #include <com/sun/star/security/CertificateContainerStatus.hpp>
42 #include <com/sun/star/security/CertificateContainer.hpp>
43 #include <com/sun/star/security/XCertificateContainer.hpp>
44 #include <com/sun/star/security/CertAltNameEntry.hpp>
45 #include <com/sun/star/security/XSanExtension.hpp>
46 #include <com/sun/star/io/NotConnectedException.hpp>
47 #include <com/sun/star/io/BufferSizeExceededException.hpp>
48 #include <com/sun/star/io/IOException.hpp>
49 #define OID_SUBJECT_ALTERNATIVE_NAME "2.5.29.17"
51 #include <com/sun/star/ucb/Lock.hpp>
52 #include <com/sun/star/xml/crypto/XSEInitializer.hpp>
54 using namespace com::sun::star
;
55 using namespace http_dav_ucp
;
59 SerfSession::SerfSession(
60 const rtl::Reference
< DAVSessionFactory
> & rSessionFactory
,
61 const OUString
& inUri
,
62 const ucbhelper::InternetProxyDecider
& rProxyDecider
)
63 : DAVSession( rSessionFactory
)
68 , m_pSerfConnection( nullptr )
69 , m_pSerfContext( nullptr )
70 , m_bIsHeadRequestInProgress( false )
71 , m_bUseChunkedEncoding( false )
72 , m_bNoOfTransferEncodingSwitches( 0 )
73 , m_rProxyDecider( rProxyDecider
)
76 m_pSerfContext
= serf_context_create( getAprPool() );
78 m_pSerfBucket_Alloc
= serf_bucket_allocator_create( getAprPool(), nullptr, nullptr );
84 SerfSession::~SerfSession( )
86 if ( m_pSerfConnection
)
88 serf_connection_close( m_pSerfConnection
);
89 m_pSerfConnection
= nullptr;
94 void SerfSession::Init( const DAVRequestEnvironment
& rEnv
)
96 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
102 void SerfSession::Init()
104 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
106 bool bCreateNewSession
= false;
108 if ( m_pSerfConnection
== nullptr )
110 const ucbhelper::InternetProxyServer
& rProxyCfg
= getProxySettings();
112 m_aProxyName
= rProxyCfg
.aName
;
113 m_nProxyPort
= rProxyCfg
.nPort
;
115 // Not yet initialized. Create new session.
116 bCreateNewSession
= true;
120 const ucbhelper::InternetProxyServer
& rProxyCfg
= getProxySettings();
122 if ( ( rProxyCfg
.aName
!= m_aProxyName
)
123 || ( rProxyCfg
.nPort
!= m_nProxyPort
) )
125 m_aProxyName
= rProxyCfg
.aName
;
126 m_nProxyPort
= rProxyCfg
.nPort
;
128 // new session needed, destroy old first
129 serf_connection_close( m_pSerfConnection
);
130 m_pSerfConnection
= nullptr;
131 bCreateNewSession
= true;
135 if ( bCreateNewSession
)
137 // TODO - close_connection callback
138 apr_status_t status
= serf_connection_create2( &m_pSerfConnection
,
141 Serf_ConnectSetup
, this,
142 nullptr /* close connection callback */, nullptr /* close connection baton */,
145 if ( m_pSerfConnection
== nullptr ||status
!= APR_SUCCESS
)
147 throw DAVException( DAVException::DAV_SESSION_CREATE
,
148 SerfUri::makeConnectionEndPointString( m_aUri
.GetHost(), m_aUri
.GetPort() ) );
151 // Register the session with the lock store
152 // m_aSerfLockStore.registerSession( m_pSerfConnection );
154 if ( m_aProxyName
.getLength() )
156 apr_sockaddr_t
*proxy_address
= nullptr;
157 status
= apr_sockaddr_info_get( &proxy_address
,
158 OUStringToOString( m_aProxyName
, RTL_TEXTENCODING_UTF8
).getStr(),
160 static_cast<apr_port_t
>(m_nProxyPort
),
163 if ( status
!= APR_SUCCESS
)
165 throw DAVException( DAVException::DAV_SESSION_CREATE
,
166 SerfUri::makeConnectionEndPointString( m_aUri
.GetHost(), m_aUri
.GetPort() ) );
169 serf_config_proxy( m_pSerfContext
, proxy_address
);
173 serf_config_credentials_callback( m_pSerfContext
, Serf_Credentials
);
175 m_bUseChunkedEncoding
= isSSLNeeded();
179 apr_pool_t
* SerfSession::getAprPool()
181 return apr_environment::AprEnv::getAprEnv()->getAprPool();
184 serf_bucket_alloc_t
* SerfSession::getSerfBktAlloc()
186 return m_pSerfBucket_Alloc
;
189 serf_context_t
* SerfSession::getSerfContext()
191 return m_pSerfContext
;
194 serf_connection_t
* SerfSession::getSerfConnection()
196 return m_pSerfConnection
;
199 bool SerfSession::isHeadRequestInProgress()
201 return m_bIsHeadRequestInProgress
;
204 bool SerfSession::isSSLNeeded()
206 return m_aUri
.GetScheme().equalsIgnoreAsciiCase( "https" );
209 char* SerfSession::getHostinfo()
211 return m_aUri
.getAprUri().hostinfo
;
216 bool SerfSession::CanUse( const OUString
& inUri
)
220 SerfUri
theUri( inUri
);
221 if ( ( theUri
.GetPort() == m_aUri
.GetPort() ) &&
222 ( theUri
.GetHost() == m_aUri
.GetHost() ) &&
223 ( theUri
.GetScheme() == m_aUri
.GetScheme() ) )
228 catch ( DAVException
const & )
237 bool SerfSession::UsesProxy()
240 return ( m_aProxyName
.getLength() > 0 );
243 apr_status_t
SerfSession::setupSerfConnection( apr_socket_t
* inAprSocket
,
244 serf_bucket_t
**outSerfInputBucket
,
245 serf_bucket_t
**outSerfOutputBucket
,
246 apr_pool_t
* /*inAprPool*/ )
248 serf_bucket_t
*tmpInputBkt
;
249 tmpInputBkt
= serf_context_bucket_socket_create( getSerfContext(),
255 tmpInputBkt
= serf_bucket_ssl_decrypt_create( tmpInputBkt
,
258 /** Set the callback that is called to authenticate the
261 serf_ssl_server_cert_chain_callback_set(
262 serf_bucket_ssl_decrypt_context_get(tmpInputBkt
),
264 Serf_CertificateChainValidation
,
266 serf_ssl_set_hostname( serf_bucket_ssl_decrypt_context_get( tmpInputBkt
),
269 *outSerfOutputBucket
= serf_bucket_ssl_encrypt_create( *outSerfOutputBucket
,
270 serf_bucket_ssl_decrypt_context_get( tmpInputBkt
),
274 *outSerfInputBucket
= tmpInputBkt
;
279 apr_status_t
SerfSession::provideSerfCredentials( bool bGiveProvidedCredentialsASecondTry
,
282 serf_request_t
* /*inRequest*/,
284 const char *inAuthProtocol
,
286 apr_pool_t
*inAprPool
)
288 DAVAuthListener
* pListener
= getRequestEnvironment().m_xAuthListener
.get();
292 return SERF_ERROR_AUTHN_FAILED
;
295 OUString theUserName
;
296 OUString thePassWord
;
299 SerfUri
uri( getRequestEnvironment().m_aRequestURI
);
300 OUString
aUserInfo( uri
.GetUserInfo() );
301 if ( aUserInfo
.getLength() )
303 sal_Int32 nPos
= aUserInfo
.indexOf( '@' );
306 theUserName
= aUserInfo
;
310 theUserName
= aUserInfo
.copy( 0, nPos
);
311 thePassWord
= aUserInfo
.copy( nPos
+ 1 );
315 catch ( DAVException
const & )
318 return SERF_ERROR_AUTHN_FAILED
;
321 const bool bCanUseSystemCreds
= ( ( strcasecmp( inAuthProtocol
, "NTLM" ) == 0 ) ||
322 ( strcasecmp( inAuthProtocol
, "Negotiate" ) == 0 ) );
324 int theRetVal
= pListener
->authenticate( OUString::createFromAscii( inRealm
),
329 bGiveProvidedCredentialsASecondTry
);
331 if ( theRetVal
== 0 )
333 *outUsername
= apr_pstrdup( inAprPool
, OUStringToOString( theUserName
, RTL_TEXTENCODING_UTF8
).getStr() );
334 *outPassword
= apr_pstrdup( inAprPool
, OUStringToOString( thePassWord
, RTL_TEXTENCODING_UTF8
).getStr() );
337 return theRetVal
!= 0 ? SERF_ERROR_AUTHN_FAILED
: APR_SUCCESS
;
340 apr_status_t
SerfSession::verifySerfCertificateChain (
342 const serf_ssl_certificate_t
* const * pCertificateChainBase64Encoded
,
343 int nCertificateChainLength
)
346 if (pCertificateChainBase64Encoded
== nullptr || nCertificateChainLength
<=0)
348 assert(pCertificateChainBase64Encoded
!= nullptr);
349 assert(nCertificateChainLength
>0);
350 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
353 // When called from SerfLockStore::~SerfLockStore(),
354 // css::xml::crypto::SEInitializer::create() will fail
355 // but we want to send unlock commands anyway,
356 // so just ignore certificates and return here.
357 if (apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->finishing())
360 // Create some crypto objects to decode and handle the base64
361 // encoded certificate chain.
362 uno::Reference
< security::XCertificateContainer
> xCertificateContainer
;
363 uno::Reference
< xml::crypto::XXMLSecurityContext
> xSecurityContext
;
364 uno::Reference
< xml::crypto::XSecurityEnvironment
> xSecurityEnv
;
367 css::uno::Reference
< css::uno::XComponentContext
> xContext
=
368 ::comphelper::getProcessComponentContext();
369 // Create a certificate container.
370 xCertificateContainer
= security::CertificateContainer::create( xContext
);
372 css::uno::Reference
< css::xml::crypto::XSEInitializer
> xSEInitializer
=
373 css::xml::crypto::SEInitializer::create( xContext
);
375 xSecurityContext
= xSEInitializer
->createSecurityContext( OUString() );
376 if (xSecurityContext
.is())
377 xSecurityEnv
= xSecurityContext
->getSecurityEnvironment();
379 if ( ! xSecurityContext
.is() || ! xSecurityEnv
.is())
381 // Do we have to dispose xSEInitializer or xCertificateContainer?
382 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
385 catch ( uno::Exception
const &)
387 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
390 // Decode the server certificate.
391 const char* sBase64EncodedServerCertificate (
392 serf_ssl_cert_export(
393 pCertificateChainBase64Encoded
[0],
395 uno::Reference
< security::XCertificate
> xServerCertificate(
396 xSecurityEnv
->createCertificateFromAscii(
397 OUString::createFromAscii(sBase64EncodedServerCertificate
)));
398 if ( ! xServerCertificate
.is())
399 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
401 // Get the subject from the server certificate.
402 OUString
sServerCertificateSubject (xServerCertificate
->getSubjectName());
403 sal_Int32 nIndex
= 0;
406 const OUString
sToken (sServerCertificateSubject
.getToken(0, ',', nIndex
));
407 if (sToken
.startsWith("CN="))
409 sServerCertificateSubject
= sToken
.copy(3);
412 else if (sToken
.startsWith(" CN="))
414 sServerCertificateSubject
= sToken
.copy(4);
419 // When the certificate container already contains a (trusted)
420 // entry for the server then we do not have to authenticate any
422 const security::CertificateContainerStatus
eStatus (
423 xCertificateContainer
->hasCertificate(
424 getHostName(), sServerCertificateSubject
) );
425 if (eStatus
!= security::CertificateContainerStatus_NOCERT
)
427 return eStatus
== security::CertificateContainerStatus_TRUSTED
429 : SERF_SSL_CERT_UNKNOWN_FAILURE
;
432 // The shortcut failed, so try to verify the whole chain. This is
433 // done outside the isDomainMatch() block because the result is
434 // used by the interaction handler.
435 std::vector
< uno::Reference
< security::XCertificate
> > aChain
;
436 for (nIndex
= 1; nIndex
< nCertificateChainLength
; ++nIndex
)
438 const char* sBase64EncodedCertificate (
439 serf_ssl_cert_export(
440 pCertificateChainBase64Encoded
[nIndex
],
442 uno::Reference
< security::XCertificate
> xCertificate(
443 xSecurityEnv
->createCertificateFromAscii(
444 OUString::createFromAscii(sBase64EncodedCertificate
)));
445 if ( ! xCertificate
.is())
446 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
447 aChain
.push_back(xCertificate
);
449 const sal_Int64
nVerificationResult (xSecurityEnv
->verifyCertificate(
451 ::comphelper::containerToSequence(aChain
)));
453 // When the certificate matches the host name then we can use the
454 // result of the verification.
455 bool bHostnameMatchesCertHostnames
= false;
457 uno::Sequence
< uno::Reference
< security::XCertificateExtension
> > extensions
= xServerCertificate
->getExtensions();
458 uno::Sequence
< security::CertAltNameEntry
> altNames
;
459 for (sal_Int32 i
= 0 ; i
< extensions
.getLength(); ++i
)
461 uno::Reference
< security::XCertificateExtension
>element
= extensions
[i
];
463 const OString
aId ( reinterpret_cast<const char *>(const_cast<const signed char *>(element
->getExtensionId().getArray())), element
->getExtensionId().getLength());
464 if ( aId
.equals( OID_SUBJECT_ALTERNATIVE_NAME
) )
466 uno::Reference
< security::XSanExtension
> sanExtension ( element
, uno::UNO_QUERY
);
467 altNames
= sanExtension
->getAlternativeNames();
472 uno::Sequence
< OUString
> certHostNames(altNames
.getLength() + 1);
473 certHostNames
[0] = sServerCertificateSubject
;
474 for( int n
= 0; n
< altNames
.getLength(); ++n
)
476 if (altNames
[n
].Type
== security::ExtAltNameType_DNS_NAME
)
478 altNames
[n
].Value
>>= certHostNames
[n
+1];
482 for ( int i
= 0; i
< certHostNames
.getLength() && !bHostnameMatchesCertHostnames
; ++i
)
484 bHostnameMatchesCertHostnames
= isDomainMatch( certHostNames
[i
] );
488 if ( bHostnameMatchesCertHostnames
)
491 if (nVerificationResult
== 0)
493 // Certificate (chain) is valid.
494 xCertificateContainer
->addCertificate(getHostName(), sServerCertificateSubject
, true);
497 else if ((nVerificationResult
& security::CertificateValidity::CHAIN_INCOMPLETE
) != 0)
499 // We do not have enough information for verification,
500 // neither automatically (as we just discovered) nor
501 // manually (so there is no point in showing any dialog.)
502 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
504 else if ((nVerificationResult
&
505 (security::CertificateValidity::INVALID
| security::CertificateValidity::REVOKED
)) != 0)
507 // Certificate (chain) is invalid.
508 xCertificateContainer
->addCertificate(getHostName(), sServerCertificateSubject
, false);
509 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
513 // For all other we have to ask the user.
517 // We have not been able to automatically verify (or falsify) the
518 // certificate chain. To resolve this we have to ask the user.
519 const uno::Reference
< ucb::XCommandEnvironment
> xEnv( getRequestEnvironment().m_xEnv
);
522 uno::Reference
< task::XInteractionHandler
> xIH( xEnv
->getInteractionHandler() );
525 rtl::Reference
< ucbhelper::SimpleCertificateValidationRequest
>
526 xRequest( new ucbhelper::SimpleCertificateValidationRequest(
527 static_cast<sal_Int32
>(nVerificationResult
), xServerCertificate
, getHostName() ) );
528 xIH
->handle( xRequest
.get() );
530 rtl::Reference
< ucbhelper::InteractionContinuation
> xSelection
531 = xRequest
->getSelection();
533 if ( xSelection
.is() )
535 uno::Reference
< task::XInteractionApprove
> xApprove( xSelection
.get(), uno::UNO_QUERY
);
538 xCertificateContainer
->addCertificate( getHostName(), sServerCertificateSubject
, true );
544 xCertificateContainer
->addCertificate( getHostName(), sServerCertificateSubject
, false );
545 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
552 xCertificateContainer
->addCertificate( getHostName(), sServerCertificateSubject
, false );
553 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
557 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
560 serf_bucket_t
* SerfSession::acceptSerfResponse( serf_request_t
* inSerfRequest
,
561 serf_bucket_t
* inSerfStreamBucket
,
562 apr_pool_t
* /*inAprPool*/ )
564 // get the per-request bucket allocator
565 serf_bucket_alloc_t
* SerfBktAlloc
= serf_request_get_alloc( inSerfRequest
);
567 // create a barrier bucket so the response doesn't eat us!
568 serf_bucket_t
*responseBkt
= serf_bucket_barrier_create( inSerfStreamBucket
,
571 // create response bucket
572 responseBkt
= serf_bucket_response_create( responseBkt
,
575 if ( isHeadRequestInProgress() )
577 // advise the response bucket that this was from a HEAD request and that it should not expect to see a response body.
578 serf_bucket_response_set_head( responseBkt
);
584 SerfRequestProcessor
* SerfSession::createReqProc( const OUString
& inPath
)
586 return new SerfRequestProcessor( *this,
588 m_bUseChunkedEncoding
);
592 // PROPFIND - allprop & named
594 void SerfSession::PROPFIND( const OUString
& inPath
,
596 const std::vector
< OUString
> & inPropNames
,
597 std::vector
< DAVResource
> & ioResources
,
598 const DAVRequestEnvironment
& rEnv
)
600 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
604 apr_status_t status
= APR_SUCCESS
;
605 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
606 aReqProc
->processPropFind( inDepth
,
611 if ( status
== APR_SUCCESS
&&
612 aReqProc
->mpDAVException
== nullptr &&
613 ioResources
.empty() )
615 m_aEnv
= DAVRequestEnvironment();
616 throw DAVException( DAVException::DAV_HTTP_ERROR
, inPath
, APR_EGENERAL
);
618 HandleError( aReqProc
);
622 // PROPFIND - propnames
624 void SerfSession::PROPFIND( const OUString
& inPath
,
626 std::vector
< DAVResourceInfo
> & ioResInfo
,
627 const DAVRequestEnvironment
& rEnv
)
629 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
633 apr_status_t status
= APR_SUCCESS
;
634 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
635 aReqProc
->processPropFind( inDepth
,
639 if ( status
== APR_SUCCESS
&&
640 aReqProc
->mpDAVException
== nullptr &&
643 m_aEnv
= DAVRequestEnvironment();
644 throw DAVException( DAVException::DAV_HTTP_ERROR
, inPath
, APR_EGENERAL
);
646 HandleError( aReqProc
);
652 void SerfSession::PROPPATCH( const OUString
& inPath
,
653 const std::vector
< ProppatchValue
> & inValues
,
654 const DAVRequestEnvironment
& rEnv
)
656 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
660 apr_status_t status
= APR_SUCCESS
;
661 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
662 aReqProc
->processPropPatch( inValues
,
665 HandleError( aReqProc
);
671 void SerfSession::HEAD( const OUString
& inPath
,
672 const std::vector
< OUString
> & inHeaderNames
,
673 DAVResource
& ioResource
,
674 const DAVRequestEnvironment
& rEnv
)
676 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
680 m_bIsHeadRequestInProgress
= true;
682 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
683 ioResource
.uri
= inPath
;
684 ioResource
.properties
.clear();
685 apr_status_t status
= APR_SUCCESS
;
686 aReqProc
->processHead( inHeaderNames
,
690 m_bIsHeadRequestInProgress
= false;
692 HandleError( aReqProc
);
698 uno::Reference
< io::XInputStream
>
699 SerfSession::GET( const OUString
& inPath
,
700 const DAVRequestEnvironment
& rEnv
)
702 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
706 rtl::Reference
< SerfInputStream
> xInputStream( new SerfInputStream
);
707 apr_status_t status
= APR_SUCCESS
;
708 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
709 aReqProc
->processGet( xInputStream
,
712 HandleError( aReqProc
);
714 return uno::Reference
< io::XInputStream
>( xInputStream
.get() );
720 void SerfSession::GET( const OUString
& inPath
,
721 uno::Reference
< io::XOutputStream
> & ioOutputStream
,
722 const DAVRequestEnvironment
& rEnv
)
724 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
728 apr_status_t status
= APR_SUCCESS
;
729 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
730 aReqProc
->processGet( ioOutputStream
,
733 HandleError( aReqProc
);
739 uno::Reference
< io::XInputStream
>
740 SerfSession::GET( const OUString
& inPath
,
741 const std::vector
< OUString
> & inHeaderNames
,
742 DAVResource
& ioResource
,
743 const DAVRequestEnvironment
& rEnv
)
745 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
749 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
750 rtl::Reference
< SerfInputStream
> xInputStream( new SerfInputStream
);
751 ioResource
.uri
= inPath
;
752 ioResource
.properties
.clear();
753 apr_status_t status
= APR_SUCCESS
;
754 aReqProc
->processGet( xInputStream
,
759 HandleError( aReqProc
);
761 return uno::Reference
< io::XInputStream
>( xInputStream
.get() );
767 void SerfSession::GET( const OUString
& inPath
,
768 uno::Reference
< io::XOutputStream
> & ioOutputStream
,
769 const std::vector
< OUString
> & inHeaderNames
,
770 DAVResource
& ioResource
,
771 const DAVRequestEnvironment
& rEnv
)
773 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
777 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
778 ioResource
.uri
= inPath
;
779 ioResource
.properties
.clear();
780 apr_status_t status
= APR_SUCCESS
;
781 aReqProc
->processGet( ioOutputStream
,
786 HandleError( aReqProc
);
792 void SerfSession::PUT( const OUString
& inPath
,
793 const uno::Reference
< io::XInputStream
> & inInputStream
,
794 const DAVRequestEnvironment
& rEnv
)
796 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
800 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
801 uno::Sequence
< sal_Int8
> aDataToSend
;
802 if ( !getDataFromInputStream( inInputStream
, aDataToSend
, false ) )
803 throw DAVException( DAVException::DAV_INVALID_ARG
);
804 apr_status_t status
= APR_SUCCESS
;
805 aReqProc
->processPut( reinterpret_cast< const char * >( aDataToSend
.getConstArray() ),
806 aDataToSend
.getLength(),
809 HandleError( aReqProc
);
815 uno::Reference
< io::XInputStream
>
816 SerfSession::POST( const OUString
& inPath
,
817 const OUString
& rContentType
,
818 const OUString
& rReferer
,
819 const uno::Reference
< io::XInputStream
> & inInputStream
,
820 const DAVRequestEnvironment
& rEnv
)
822 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
824 uno::Sequence
< sal_Int8
> aDataToSend
;
825 if ( !getDataFromInputStream( inInputStream
, aDataToSend
, true ) )
827 throw DAVException( DAVException::DAV_INVALID_ARG
);
832 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
833 rtl::Reference
< SerfInputStream
> xInputStream( new SerfInputStream
);
834 apr_status_t status
= APR_SUCCESS
;
835 aReqProc
->processPost( reinterpret_cast< const char * >( aDataToSend
.getConstArray() ),
836 aDataToSend
.getLength(),
842 HandleError( aReqProc
);
843 return uno::Reference
< io::XInputStream
>( xInputStream
.get() );
849 void SerfSession::POST( const OUString
& inPath
,
850 const OUString
& rContentType
,
851 const OUString
& rReferer
,
852 const uno::Reference
< io::XInputStream
> & inInputStream
,
853 uno::Reference
< io::XOutputStream
> & oOutputStream
,
854 const DAVRequestEnvironment
& rEnv
)
856 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
858 uno::Sequence
< sal_Int8
> aDataToSend
;
859 if ( !getDataFromInputStream( inInputStream
, aDataToSend
, true ) )
861 throw DAVException( DAVException::DAV_INVALID_ARG
);
866 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
867 apr_status_t status
= APR_SUCCESS
;
868 aReqProc
->processPost( reinterpret_cast< const char * >( aDataToSend
.getConstArray() ),
869 aDataToSend
.getLength(),
875 HandleError( aReqProc
);
881 void SerfSession::MKCOL( const OUString
& inPath
,
882 const DAVRequestEnvironment
& rEnv
)
884 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
888 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
889 apr_status_t status
= APR_SUCCESS
;
890 aReqProc
->processMkCol( status
);
892 HandleError( aReqProc
);
898 void SerfSession::COPY( const OUString
& inSourceURL
,
899 const OUString
& inDestinationURL
,
900 const DAVRequestEnvironment
& rEnv
,
903 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
907 SerfUri
theSourceUri( inSourceURL
);
908 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( theSourceUri
.GetPath() ) );
909 apr_status_t status
= APR_SUCCESS
;
910 aReqProc
->processCopy( inDestinationURL
, inOverWrite
, status
);
912 HandleError( aReqProc
);
918 void SerfSession::MOVE( const OUString
& inSourceURL
,
919 const OUString
& inDestinationURL
,
920 const DAVRequestEnvironment
& rEnv
,
923 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
927 SerfUri
theSourceUri( inSourceURL
);
928 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( theSourceUri
.GetPath() ) );
929 apr_status_t status
= APR_SUCCESS
;
930 aReqProc
->processMove( inDestinationURL
, inOverWrite
, status
);
932 HandleError( aReqProc
);
938 void SerfSession::DESTROY( const OUString
& inPath
,
939 const DAVRequestEnvironment
& rEnv
)
941 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
945 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
946 apr_status_t status
= APR_SUCCESS
;
947 aReqProc
->processDelete( status
);
949 HandleError( aReqProc
);
956 sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
960 osl_getSystemTime( &aEnd );
962 // Try to estimate a safe absolute time for sending the
963 // lock refresh request.
964 sal_Int32 lastChanceToSendRefreshRequest = -1;
965 if ( timeout != NE_TIMEOUT_INFINITE )
967 sal_Int32 calltime = aEnd.Seconds - rStart.Seconds;
968 if ( calltime <= timeout )
970 lastChanceToSendRefreshRequest
971 = aEnd.Seconds + timeout - calltime;
975 SAL_INFO("ucb.ucp.webdav", "No chance to refresh lock before timeout!" );
978 return lastChanceToSendRefreshRequest;
984 // LOCK (set new lock)
986 void SerfSession::LOCK( const OUString
& inPath
,
988 const DAVRequestEnvironment
& rEnv
)
990 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
994 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
995 aReqProc
->processLock( rLock
);
997 HandleError( aReqProc
);
1001 // LOCK (refresh existing lock)
1003 sal_Int64
SerfSession::LOCK( const OUString
& /*inPath*/,
1005 const DAVRequestEnvironment
& /*rEnv*/ )
1007 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
1011 // Try to get the neon lock from lock store
1013 = m_aSerfLockStore.findByUri( makeAbsoluteURL( inPath ) );
1015 throw DAVException( DAVException::DAV_NOT_LOCKED );
1019 // refresh existing lock.
1020 theLock->timeout = static_cast< long >( nTimeout );
1022 TimeValue startCall;
1023 osl_getSystemTime( &startCall );
1025 int theRetVal = ne_lock_refresh( m_pHttpSession, theLock );
1027 if ( theRetVal == NE_OK )
1029 m_aSerfLockStore.updateLock( theLock,
1030 lastChanceToSendRefreshRequest(
1031 startCall, theLock->timeout ) );
1034 HandleError( theRetVal, inPath, rEnv );
1036 return theLock->timeout;
1041 // LOCK (refresh existing lock)
1043 bool SerfSession::LOCK( const OUString
& rLock
,
1044 sal_Int32
*plastChanceToSendRefreshRequest
)
1046 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
1048 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( rLock
) );
1049 aReqProc
->processLock( ucb::Lock(), plastChanceToSendRefreshRequest
);
1053 HandleError( aReqProc
);
1054 SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock
<< " succeeded." );
1059 SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock
<< " failed!" );
1067 void SerfSession::UNLOCK( const OUString
& inPath
,
1068 const DAVRequestEnvironment
& rEnv
)
1070 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
1074 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
1075 aReqProc
->processUnlock();
1079 HandleError( aReqProc
);
1080 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath
<< " succeeded." );
1081 apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->removeLock( inPath
);
1085 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath
<< " failed!" );
1092 void SerfSession::UNLOCK( const OUString
& rLock
)
1094 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
1096 std::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( rLock
) );
1097 aReqProc
->processUnlock();
1101 HandleError( aReqProc
);
1102 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << rLock
<< " succeeded." );
1106 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << rLock
<< " failed!" );
1111 void SerfSession::abort()
1113 // 11.11.09 (tkr): The following code lines causing crashes if
1114 // closing a ongoing connection. It turned out that this existing
1115 // solution doesn't work in multi-threading environments.
1116 // So I disabled them in 3.2. . Issue #73893# should fix it in OOo 3.3.
1117 //if ( m_pHttpSession )
1118 // ne_close_connection( m_pHttpSession );
1122 ucbhelper::InternetProxyServer
SerfSession::getProxySettings() const
1124 if ( m_aUri
.GetScheme() == "http" || m_aUri
.GetScheme() == "https" )
1126 return m_rProxyDecider
.getProxy( m_aUri
.GetScheme(),
1132 // TODO: figure out, if this case can occur
1133 return m_rProxyDecider
.getProxy( m_aUri
.GetScheme(),
1134 OUString() /* not used */,
1135 -1 /* not used */ );
1143 bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
1144 const char * token )
1146 for ( sal_Int32 n = 0; n < rLocks.getLength(); ++n )
1148 const uno::Sequence< OUString > & rTokens
1149 = rLocks[ n ].LockTokens;
1150 for ( sal_Int32 m = 0; m < rTokens.getLength(); ++m )
1152 if ( rTokens[ m ].equalsAscii( token ) )
1163 bool SerfSession::removeExpiredLocktoken( const OUString
& /*inURL*/,
1164 const DAVRequestEnvironment
& /*rEnv*/ )
1168 SerfLock * theLock = m_aSerfLockStore.findByUri( inURL );
1172 // do a lockdiscovery to check whether this lock is still valid.
1175 // @@@ Alternative: use ne_lock_discover() => less overhead
1177 std::vector< DAVResource > aResources;
1178 std::vector< OUString > aPropNames;
1179 aPropNames.push_back( DAVProperties::LOCKDISCOVERY );
1181 PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv );
1183 if ( aResources.empty() )
1186 std::vector< DAVPropertyValue >::const_iterator it
1187 = aResources[ 0 ].properties.begin();
1188 std::vector< DAVPropertyValue >::const_iterator end
1189 = aResources[ 0 ].properties.end();
1193 if ( (*it).Name.equals( DAVProperties::LOCKDISCOVERY ) )
1195 uno::Sequence< ucb::Lock > aLocks;
1196 if ( !( (*it).Value >>= aLocks ) )
1199 if ( !containsLocktoken( aLocks, theLock->token ) )
1211 // No lockdiscovery prop in propfind result / locktoken not found
1212 // in propfind result -> not locked
1213 SAL_INFO("ucb.ucp.webdav", "SerfSession::removeExpiredLocktoken: Removing "
1214 " expired lock token for " << inURL << ". token: " << theLock->token );
1216 m_aSerfLockStore.removeLock( theLock );
1217 ne_lock_destroy( theLock );
1220 catch ( DAVException const & )
1229 // Common Error Handler
1231 void SerfSession::HandleError( std::shared_ptr
<SerfRequestProcessor
> rReqProc
)
1233 m_aEnv
= DAVRequestEnvironment();
1235 if ( rReqProc
->mpDAVException
)
1237 DAVException
* mpDAVExp( rReqProc
->mpDAVException
);
1239 serf_connection_reset( getSerfConnection() );
1241 if ( mpDAVExp
->getStatus() == 413 &&
1242 m_bNoOfTransferEncodingSwitches
< 2 )
1244 m_bUseChunkedEncoding
= !m_bUseChunkedEncoding
;
1245 ++m_bNoOfTransferEncodingSwitches
;
1248 throw DAVException( mpDAVExp
->getError(),
1249 mpDAVExp
->getData(),
1250 mpDAVExp
->getStatus() );
1254 // Map error code to DAVException.
1260 case NE_ERROR: // Generic error
1262 OUString aText = OUString::createFromAscii(
1263 ne_get_error( m_pHttpSession ) );
1265 sal_uInt16 code = makeStatusCode( aText );
1267 if ( code == SC_LOCKED )
1269 if ( m_aSerfLockStore.findByUri(
1270 makeAbsoluteURL( inPath ) ) == 0 )
1272 // locked by 3rd party
1273 throw DAVException( DAVException::DAV_LOCKED );
1277 // locked by ourself
1278 throw DAVException( DAVException::DAV_LOCKED_SELF );
1282 // Special handling for 400 and 412 status codes, which may indicate
1283 // that a lock previously obtained by us has been released meanwhile
1284 // by the server. Unfortunately, RFC is not clear at this point,
1285 // thus server implementations behave different...
1286 else if ( code == SC_BAD_REQUEST || code == SC_PRECONDITION_FAILED )
1288 if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) )
1289 throw DAVException( DAVException::DAV_LOCK_EXPIRED );
1292 throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
1294 case NE_LOOKUP: // Name lookup failed.
1295 throw DAVException( DAVException::DAV_HTTP_LOOKUP,
1296 SerfUri::makeConnectionEndPointString(
1297 m_aHostName, m_nPort ) );
1299 case NE_AUTH: // User authentication failed on server
1300 throw DAVException( DAVException::DAV_HTTP_AUTH,
1301 SerfUri::makeConnectionEndPointString(
1302 m_aHostName, m_nPort ) );
1304 case NE_PROXYAUTH: // User authentication failed on proxy
1305 throw DAVException( DAVException::DAV_HTTP_AUTHPROXY,
1306 SerfUri::makeConnectionEndPointString(
1307 m_aProxyName, m_nProxyPort ) );
1309 case NE_CONNECT: // Could not connect to server
1310 throw DAVException( DAVException::DAV_HTTP_CONNECT,
1311 SerfUri::makeConnectionEndPointString(
1312 m_aHostName, m_nPort ) );
1314 case NE_TIMEOUT: // Connection timed out
1315 throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
1316 SerfUri::makeConnectionEndPointString(
1317 m_aHostName, m_nPort ) );
1319 case NE_FAILED: // The precondition failed
1320 throw DAVException( DAVException::DAV_HTTP_FAILED,
1321 SerfUri::makeConnectionEndPointString(
1322 m_aHostName, m_nPort ) );
1324 case NE_RETRY: // Retry request (ne_end_request ONLY)
1325 throw DAVException( DAVException::DAV_HTTP_RETRY,
1326 SerfUri::makeConnectionEndPointString(
1327 m_aHostName, m_nPort ) );
1331 SerfUri aUri( ne_redirect_location( m_pHttpSession ) );
1333 DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() );
1337 SAL_INFO("ucb.ucp.webdav", "SerfSession::HandleError : Unknown Serf error code!" );
1338 throw DAVException( DAVException::DAV_HTTP_ERROR,
1339 OUString::createFromAscii(
1340 ne_get_error( m_pHttpSession ) ) );
1349 SerfSession::getDataFromInputStream(
1350 const uno::Reference
< io::XInputStream
> & xStream
,
1351 uno::Sequence
< sal_Int8
> & rData
,
1352 bool bAppendTrailingZeroByte
)
1356 uno::Reference
< io::XSeekable
> xSeekable( xStream
, uno::UNO_QUERY
);
1357 if ( xSeekable
.is() )
1362 = sal::static_int_cast
<sal_Int32
>(xSeekable
->getLength());
1364 = xStream
->readBytes( rData
, nSize
);
1366 if ( nRead
== nSize
)
1368 if ( bAppendTrailingZeroByte
)
1370 rData
.realloc( nSize
+ 1 );
1371 rData
[ nSize
] = sal_Int8( 0 );
1376 catch ( io::NotConnectedException
const & )
1380 catch ( io::BufferSizeExceededException
const & )
1384 catch ( io::IOException
const & )
1386 // getLength, readBytes
1393 uno::Sequence
< sal_Int8
> aBuffer
;
1396 sal_Int32 nRead
= xStream
->readSomeBytes( aBuffer
, 65536 );
1399 if ( rData
.getLength() < ( nPos
+ nRead
) )
1400 rData
.realloc( nPos
+ nRead
);
1402 aBuffer
.realloc( nRead
);
1403 memcpy( rData
.getArray() + nPos
, aBuffer
.getConstArray(), nRead
);
1406 aBuffer
.realloc( 0 );
1407 nRead
= xStream
->readSomeBytes( aBuffer
, 65536 );
1410 if ( bAppendTrailingZeroByte
)
1412 rData
.realloc( nPos
+ 1 );
1413 rData
[ nPos
] = sal_Int8( 0 );
1417 catch ( io::NotConnectedException
const & )
1421 catch ( io::BufferSizeExceededException
const & )
1425 catch ( io::IOException
const & )
1436 SerfSession::isDomainMatch( const OUString
& certHostName
)
1438 OUString hostName
= getHostName();
1440 if (hostName
.equalsIgnoreAsciiCase( certHostName
) )
1443 if ( certHostName
.startsWith( "*" ) &&
1444 hostName
.getLength() >= certHostName
.getLength() )
1446 OUString cmpStr
= certHostName
.copy( 1 );
1448 if ( hostName
.matchIgnoreAsciiCase(
1449 cmpStr
, hostName
.getLength() - cmpStr
.getLength() ) )
1457 OUString SerfSession::makeAbsoluteURL( OUString const & rURL ) const
1461 // Is URL relative or already absolute?
1462 if ( rURL[ 0 ] != '/' )
1465 return OUString( rURL );
1470 memset( &aUri, 0, sizeof( aUri ) );
1472 ne_fill_server_uri( m_pHttpSession, &aUri );
1474 = ne_strdup( OUStringToOString(
1475 rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
1476 SerfUri aSerfUri( &aUri );
1477 ne_uri_free( &aUri );
1478 return aSerfUri.GetURI();
1481 catch ( DAVException const & )
1489 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */