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 <rtl/string.h>
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"
36 #include "UCBDeadPropertyValue.hxx"
38 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
39 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
40 #include <com/sun/star/security/XCertificate.hpp>
41 #include <com/sun/star/security/CertificateValidity.hpp>
42 #include <com/sun/star/security/CertificateContainerStatus.hpp>
43 #include <com/sun/star/security/CertificateContainer.hpp>
44 #include <com/sun/star/security/XCertificateContainer.hpp>
45 #include <com/sun/star/security/CertAltNameEntry.hpp>
46 #include <com/sun/star/security/XSanExtension.hpp>
47 #define OID_SUBJECT_ALTERNATIVE_NAME "2.5.29.17"
49 #include <com/sun/star/ucb/Lock.hpp>
50 #include <com/sun/star/xml/crypto/XSEInitializer.hpp>
52 using namespace com::sun::star
;
53 using namespace http_dav_ucp
;
57 SerfSession::SerfSession(
58 const rtl::Reference
< DAVSessionFactory
> & rSessionFactory
,
59 const OUString
& inUri
,
60 const ucbhelper::InternetProxyDecider
& rProxyDecider
)
61 throw ( DAVException
)
62 : DAVSession( rSessionFactory
)
67 , m_pSerfConnection( 0 )
69 , m_bIsHeadRequestInProgress( false )
70 , m_bUseChunkedEncoding( false )
71 , m_bNoOfTransferEncodingSwitches( 0 )
72 , m_rProxyDecider( rProxyDecider
)
75 m_pSerfContext
= serf_context_create( getAprPool() );
77 m_pSerfBucket_Alloc
= serf_bucket_allocator_create( getAprPool(), NULL
, NULL
);
83 SerfSession::~SerfSession( )
85 if ( m_pSerfConnection
)
87 serf_connection_close( m_pSerfConnection
);
88 m_pSerfConnection
= 0;
93 void SerfSession::Init( const DAVRequestEnvironment
& rEnv
)
94 throw ( DAVException
)
96 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
102 void SerfSession::Init()
103 throw ( DAVException
)
105 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
107 bool bCreateNewSession
= false;
109 if ( m_pSerfConnection
== 0 )
111 const ucbhelper::InternetProxyServer
& rProxyCfg
= getProxySettings();
113 m_aProxyName
= rProxyCfg
.aName
;
114 m_nProxyPort
= rProxyCfg
.nPort
;
116 // Not yet initialized. Create new session.
117 bCreateNewSession
= true;
121 const ucbhelper::InternetProxyServer
& rProxyCfg
= getProxySettings();
123 if ( ( rProxyCfg
.aName
!= m_aProxyName
)
124 || ( rProxyCfg
.nPort
!= m_nProxyPort
) )
126 m_aProxyName
= rProxyCfg
.aName
;
127 m_nProxyPort
= rProxyCfg
.nPort
;
129 // new session needed, destroy old first
130 serf_connection_close( m_pSerfConnection
);
131 m_pSerfConnection
= 0;
132 bCreateNewSession
= true;
136 if ( bCreateNewSession
)
138 // TODO - close_connection callback
139 apr_status_t status
= serf_connection_create2( &m_pSerfConnection
,
142 Serf_ConnectSetup
, this,
143 0 /* close connection callback */, 0 /* close connection baton */,
146 if ( m_pSerfConnection
== 0 ||status
!= APR_SUCCESS
)
148 throw DAVException( DAVException::DAV_SESSION_CREATE
,
149 SerfUri::makeConnectionEndPointString( m_aUri
.GetHost(), m_aUri
.GetPort() ) );
152 // Register the session with the lock store
153 // m_aSerfLockStore.registerSession( m_pSerfConnection );
155 if ( m_aProxyName
.getLength() )
157 apr_sockaddr_t
*proxy_address
= NULL
;
158 status
= apr_sockaddr_info_get( &proxy_address
,
159 OUStringToOString( m_aProxyName
, RTL_TEXTENCODING_UTF8
).getStr(),
161 static_cast<apr_port_t
>(m_nProxyPort
),
164 if ( status
!= APR_SUCCESS
)
166 throw DAVException( DAVException::DAV_SESSION_CREATE
,
167 SerfUri::makeConnectionEndPointString( m_aUri
.GetHost(), m_aUri
.GetPort() ) );
170 serf_config_proxy( m_pSerfContext
, proxy_address
);
174 serf_config_credentials_callback( m_pSerfContext
, Serf_Credentials
);
176 m_bUseChunkedEncoding
= isSSLNeeded();
180 apr_pool_t
* SerfSession::getAprPool()
182 return apr_environment::AprEnv::getAprEnv()->getAprPool();
185 serf_bucket_alloc_t
* SerfSession::getSerfBktAlloc()
187 return m_pSerfBucket_Alloc
;
190 serf_context_t
* SerfSession::getSerfContext()
192 return m_pSerfContext
;
195 serf_connection_t
* SerfSession::getSerfConnection()
197 return m_pSerfConnection
;
200 bool SerfSession::isHeadRequestInProgress()
202 return m_bIsHeadRequestInProgress
;
205 bool SerfSession::isSSLNeeded()
207 return m_aUri
.GetScheme().equalsIgnoreAsciiCase( "https" );
210 char* SerfSession::getHostinfo()
212 return m_aUri
.getAprUri().hostinfo
;
218 bool SerfSession::CanUse( const OUString
& inUri
)
222 SerfUri
theUri( inUri
);
223 if ( ( theUri
.GetPort() == m_aUri
.GetPort() ) &&
224 ( theUri
.GetHost() == m_aUri
.GetHost() ) &&
225 ( theUri
.GetScheme() == m_aUri
.GetScheme() ) )
230 catch ( DAVException
const & )
239 bool SerfSession::UsesProxy()
242 return ( m_aProxyName
.getLength() > 0 );
245 apr_status_t
SerfSession::setupSerfConnection( apr_socket_t
* inAprSocket
,
246 serf_bucket_t
**outSerfInputBucket
,
247 serf_bucket_t
**outSerfOutputBucket
,
248 apr_pool_t
* /*inAprPool*/ )
250 serf_bucket_t
*tmpInputBkt
;
251 tmpInputBkt
= serf_context_bucket_socket_create( getSerfContext(),
257 tmpInputBkt
= serf_bucket_ssl_decrypt_create( tmpInputBkt
,
260 /** Set the callback that is called to authenticate the
263 serf_ssl_server_cert_chain_callback_set(
264 serf_bucket_ssl_decrypt_context_get(tmpInputBkt
),
266 Serf_CertificateChainValidation
,
268 serf_ssl_set_hostname( serf_bucket_ssl_decrypt_context_get( tmpInputBkt
),
271 *outSerfOutputBucket
= serf_bucket_ssl_encrypt_create( *outSerfOutputBucket
,
272 serf_bucket_ssl_decrypt_context_get( tmpInputBkt
),
276 *outSerfInputBucket
= tmpInputBkt
;
281 apr_status_t
SerfSession::provideSerfCredentials( bool bGiveProvidedCredentialsASecondTry
,
284 serf_request_t
* /*inRequest*/,
286 const char *inAuthProtocol
,
288 apr_pool_t
*inAprPool
)
290 DAVAuthListener
* pListener
= getRequestEnvironment().m_xAuthListener
.get();
294 return SERF_ERROR_AUTHN_FAILED
;
297 OUString theUserName
;
298 OUString thePassWord
;
301 SerfUri
uri( getRequestEnvironment().m_aRequestURI
);
302 OUString
aUserInfo( uri
.GetUserInfo() );
303 if ( aUserInfo
.getLength() )
305 sal_Int32 nPos
= aUserInfo
.indexOf( '@' );
308 theUserName
= aUserInfo
;
312 theUserName
= aUserInfo
.copy( 0, nPos
);
313 thePassWord
= aUserInfo
.copy( nPos
+ 1 );
317 catch ( DAVException
const & )
320 return SERF_ERROR_AUTHN_FAILED
;
323 const bool bCanUseSystemCreds
= ( ( strcasecmp( inAuthProtocol
, "NTLM" ) == 0 ) ||
324 ( strcasecmp( inAuthProtocol
, "Negotiate" ) == 0 ) );
326 int theRetVal
= pListener
->authenticate( OUString::createFromAscii( inRealm
),
331 bGiveProvidedCredentialsASecondTry
? sal_False
: sal_True
);
333 if ( theRetVal
== 0 )
335 *outUsername
= apr_pstrdup( inAprPool
, OUStringToOString( theUserName
, RTL_TEXTENCODING_UTF8
).getStr() );
336 *outPassword
= apr_pstrdup( inAprPool
, OUStringToOString( thePassWord
, RTL_TEXTENCODING_UTF8
).getStr() );
339 return theRetVal
!= 0 ? SERF_ERROR_AUTHN_FAILED
: APR_SUCCESS
;
342 apr_status_t
SerfSession::verifySerfCertificateChain (
344 const serf_ssl_certificate_t
* const * pCertificateChainBase64Encoded
,
345 int nCertificateChainLength
)
348 if (pCertificateChainBase64Encoded
== NULL
|| nCertificateChainLength
<=0)
350 assert(pCertificateChainBase64Encoded
!= NULL
);
351 assert(nCertificateChainLength
>0);
352 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
355 // When called from SerfLockStore::~SerfLockStore(),
356 // css::xml::crypto::SEInitializer::create() will fail
357 // but we want to send unlock commands anyway,
358 // so just ignore certificates and return here.
359 if (apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->finishing())
362 // Create some crypto objects to decode and handle the base64
363 // encoded certificate chain.
364 uno::Reference
< security::XCertificateContainer
> xCertificateContainer
;
365 uno::Reference
< xml::crypto::XXMLSecurityContext
> xSecurityContext
;
366 uno::Reference
< xml::crypto::XSecurityEnvironment
> xSecurityEnv
;
369 css::uno::Reference
< css::uno::XComponentContext
> xContext
=
370 ::comphelper::getProcessComponentContext();
371 // Create a certificate container.
372 xCertificateContainer
= security::CertificateContainer::create( xContext
);
374 css::uno::Reference
< css::xml::crypto::XSEInitializer
> xSEInitializer
=
375 css::xml::crypto::SEInitializer::create( xContext
);
377 xSecurityContext
= xSEInitializer
->createSecurityContext( OUString() );
378 if (xSecurityContext
.is())
379 xSecurityEnv
= xSecurityContext
->getSecurityEnvironment();
381 if ( ! xSecurityContext
.is() || ! xSecurityEnv
.is())
383 // Do we have to dispose xSEInitializer or xCertificateContainer?
384 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
387 catch ( uno::Exception
const &)
389 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
392 // Decode the server certificate.
393 const char* sBase64EncodedServerCertificate (
394 serf_ssl_cert_export(
395 pCertificateChainBase64Encoded
[0],
397 uno::Reference
< security::XCertificate
> xServerCertificate(
398 xSecurityEnv
->createCertificateFromAscii(
399 OUString::createFromAscii(sBase64EncodedServerCertificate
)));
400 if ( ! xServerCertificate
.is())
401 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
403 // Get the subject from the server certificate.
404 OUString
sServerCertificateSubject (xServerCertificate
->getSubjectName());
405 sal_Int32 nIndex
= 0;
408 const OUString
sToken (sServerCertificateSubject
.getToken(0, ',', nIndex
));
409 if (sToken
.startsWith("CN="))
411 sServerCertificateSubject
= sToken
.copy(3);
414 else if (sToken
.startsWith(" CN="))
416 sServerCertificateSubject
= sToken
.copy(4);
421 // When the certificate container already contains a (trusted)
422 // entry for the server then we do not have to authenticate any
424 const security::CertificateContainerStatus
eStatus (
425 xCertificateContainer
->hasCertificate(
426 getHostName(), sServerCertificateSubject
) );
427 if (eStatus
!= security::CertificateContainerStatus_NOCERT
)
429 return eStatus
== security::CertificateContainerStatus_TRUSTED
431 : SERF_SSL_CERT_UNKNOWN_FAILURE
;
434 // The shortcut failed, so try to verify the whole chain. This is
435 // done outside the isDomainMatch() block because the result is
436 // used by the interaction handler.
437 std::vector
< uno::Reference
< security::XCertificate
> > aChain
;
438 for (nIndex
= 1; nIndex
< nCertificateChainLength
; ++nIndex
)
440 const char* sBase64EncodedCertificate (
441 serf_ssl_cert_export(
442 pCertificateChainBase64Encoded
[nIndex
],
444 uno::Reference
< security::XCertificate
> xCertificate(
445 xSecurityEnv
->createCertificateFromAscii(
446 OUString::createFromAscii(sBase64EncodedCertificate
)));
447 if ( ! xCertificate
.is())
448 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
449 aChain
.push_back(xCertificate
);
451 const sal_Int64
nVerificationResult (xSecurityEnv
->verifyCertificate(
453 ::comphelper::containerToSequence(aChain
)));
455 // When the certificate matches the host name then we can use the
456 // result of the verification.
457 bool bHostnameMatchesCertHostnames
= false;
459 uno::Sequence
< uno::Reference
< security::XCertificateExtension
> > extensions
= xServerCertificate
->getExtensions();
460 uno::Sequence
< security::CertAltNameEntry
> altNames
;
461 for (sal_Int32 i
= 0 ; i
< extensions
.getLength(); ++i
)
463 uno::Reference
< security::XCertificateExtension
>element
= extensions
[i
];
465 const rtl::OString
aId ( (const sal_Char
*)element
->getExtensionId().getArray(), element
->getExtensionId().getLength());
466 if ( aId
.equals( OID_SUBJECT_ALTERNATIVE_NAME
) )
468 uno::Reference
< security::XSanExtension
> sanExtension ( element
, uno::UNO_QUERY
);
469 altNames
= sanExtension
->getAlternativeNames();
474 uno::Sequence
< ::rtl::OUString
> certHostNames(altNames
.getLength() + 1);
475 certHostNames
[0] = sServerCertificateSubject
;
476 for( int n
= 0; n
< altNames
.getLength(); ++n
)
478 if (altNames
[n
].Type
== security::ExtAltNameType_DNS_NAME
)
480 altNames
[n
].Value
>>= certHostNames
[n
+1];
484 for ( int i
= 0; i
< certHostNames
.getLength() && !bHostnameMatchesCertHostnames
; ++i
)
486 bHostnameMatchesCertHostnames
= isDomainMatch( certHostNames
[i
] );
490 if ( bHostnameMatchesCertHostnames
)
493 if (nVerificationResult
== 0)
495 // Certificate (chain) is valid.
496 xCertificateContainer
->addCertificate(getHostName(), sServerCertificateSubject
, sal_True
);
499 else if ((nVerificationResult
& security::CertificateValidity::CHAIN_INCOMPLETE
) != 0)
501 // We do not have enough information for verification,
502 // neither automatically (as we just discovered) nor
503 // manually (so there is no point in showing any dialog.)
504 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
506 else if ((nVerificationResult
&
507 (security::CertificateValidity::INVALID
| security::CertificateValidity::REVOKED
)) != 0)
509 // Certificate (chain) is invalid.
510 xCertificateContainer
->addCertificate(getHostName(), sServerCertificateSubject
, sal_False
);
511 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
515 // For all other we have to ask the user.
519 // We have not been able to automatically verify (or falsify) the
520 // certificate chain. To resolve this we have to ask the user.
521 const uno::Reference
< ucb::XCommandEnvironment
> xEnv( getRequestEnvironment().m_xEnv
);
524 uno::Reference
< task::XInteractionHandler
> xIH( xEnv
->getInteractionHandler() );
527 rtl::Reference
< ucbhelper::SimpleCertificateValidationRequest
>
528 xRequest( new ucbhelper::SimpleCertificateValidationRequest(
529 static_cast<sal_Int32
>(nVerificationResult
), xServerCertificate
, getHostName() ) );
530 xIH
->handle( xRequest
.get() );
532 rtl::Reference
< ucbhelper::InteractionContinuation
> xSelection
533 = xRequest
->getSelection();
535 if ( xSelection
.is() )
537 uno::Reference
< task::XInteractionApprove
> xApprove( xSelection
.get(), uno::UNO_QUERY
);
540 xCertificateContainer
->addCertificate( getHostName(), sServerCertificateSubject
, sal_True
);
546 xCertificateContainer
->addCertificate( getHostName(), sServerCertificateSubject
, sal_False
);
547 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
554 xCertificateContainer
->addCertificate( getHostName(), sServerCertificateSubject
, sal_False
);
555 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
559 return SERF_SSL_CERT_UNKNOWN_FAILURE
;
562 serf_bucket_t
* SerfSession::acceptSerfResponse( serf_request_t
* inSerfRequest
,
563 serf_bucket_t
* inSerfStreamBucket
,
564 apr_pool_t
* /*inAprPool*/ )
566 // get the per-request bucket allocator
567 serf_bucket_alloc_t
* SerfBktAlloc
= serf_request_get_alloc( inSerfRequest
);
569 // create a barrier bucket so the response doesn't eat us!
570 serf_bucket_t
*responseBkt
= serf_bucket_barrier_create( inSerfStreamBucket
,
573 // create response bucket
574 responseBkt
= serf_bucket_response_create( responseBkt
,
577 if ( isHeadRequestInProgress() )
579 // advise the response bucket that this was from a HEAD request and that it should not expect to see a response body.
580 serf_bucket_response_set_head( responseBkt
);
586 SerfRequestProcessor
* SerfSession::createReqProc( const OUString
& inPath
)
588 return new SerfRequestProcessor( *this,
590 m_bUseChunkedEncoding
);
594 // PROPFIND - allprop & named
596 void SerfSession::PROPFIND( const OUString
& inPath
,
598 const std::vector
< OUString
> & inPropNames
,
599 std::vector
< DAVResource
> & ioResources
,
600 const DAVRequestEnvironment
& rEnv
)
601 throw ( DAVException
)
603 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
607 apr_status_t status
= APR_SUCCESS
;
608 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
609 aReqProc
->processPropFind( inDepth
,
614 if ( status
== APR_SUCCESS
&&
615 aReqProc
->mpDAVException
== 0 &&
616 ioResources
.empty() )
618 m_aEnv
= DAVRequestEnvironment();
619 throw DAVException( DAVException::DAV_HTTP_ERROR
, inPath
, (sal_uInt16
)APR_EGENERAL
);
621 HandleError( aReqProc
);
625 // PROPFIND - propnames
627 void SerfSession::PROPFIND( const OUString
& inPath
,
629 std::vector
< DAVResourceInfo
> & ioResInfo
,
630 const DAVRequestEnvironment
& rEnv
)
631 throw( DAVException
)
633 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
637 apr_status_t status
= APR_SUCCESS
;
638 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
639 aReqProc
->processPropFind( inDepth
,
643 if ( status
== APR_SUCCESS
&&
644 aReqProc
->mpDAVException
== 0 &&
647 m_aEnv
= DAVRequestEnvironment();
648 throw DAVException( DAVException::DAV_HTTP_ERROR
, inPath
, (sal_uInt16
)APR_EGENERAL
);
650 HandleError( aReqProc
);
656 void SerfSession::PROPPATCH( const OUString
& inPath
,
657 const std::vector
< ProppatchValue
> & inValues
,
658 const DAVRequestEnvironment
& rEnv
)
659 throw( DAVException
)
661 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
665 apr_status_t status
= APR_SUCCESS
;
666 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
667 aReqProc
->processPropPatch( inValues
,
670 HandleError( aReqProc
);
676 void SerfSession::HEAD( const OUString
& inPath
,
677 const std::vector
< OUString
> & inHeaderNames
,
678 DAVResource
& ioResource
,
679 const DAVRequestEnvironment
& rEnv
)
680 throw( DAVException
)
682 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
686 m_bIsHeadRequestInProgress
= true;
688 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
689 ioResource
.uri
= inPath
;
690 ioResource
.properties
.clear();
691 apr_status_t status
= APR_SUCCESS
;
692 aReqProc
->processHead( inHeaderNames
,
696 m_bIsHeadRequestInProgress
= false;
698 HandleError( aReqProc
);
704 uno::Reference
< io::XInputStream
>
705 SerfSession::GET( const OUString
& inPath
,
706 const DAVRequestEnvironment
& rEnv
)
707 throw ( DAVException
)
709 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
713 uno::Reference
< SerfInputStream
> xInputStream( new SerfInputStream
);
714 apr_status_t status
= APR_SUCCESS
;
715 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
716 aReqProc
->processGet( xInputStream
,
719 HandleError( aReqProc
);
721 return uno::Reference
< io::XInputStream
>( xInputStream
.get() );
727 void SerfSession::GET( const OUString
& inPath
,
728 uno::Reference
< io::XOutputStream
> & ioOutputStream
,
729 const DAVRequestEnvironment
& rEnv
)
730 throw ( DAVException
)
732 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
736 apr_status_t status
= APR_SUCCESS
;
737 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
738 aReqProc
->processGet( ioOutputStream
,
741 HandleError( aReqProc
);
747 uno::Reference
< io::XInputStream
>
748 SerfSession::GET( const OUString
& inPath
,
749 const std::vector
< OUString
> & inHeaderNames
,
750 DAVResource
& ioResource
,
751 const DAVRequestEnvironment
& rEnv
)
752 throw ( DAVException
)
754 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
758 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
759 uno::Reference
< SerfInputStream
> xInputStream( new SerfInputStream
);
760 ioResource
.uri
= inPath
;
761 ioResource
.properties
.clear();
762 apr_status_t status
= APR_SUCCESS
;
763 aReqProc
->processGet( xInputStream
,
768 HandleError( aReqProc
);
770 return uno::Reference
< io::XInputStream
>( xInputStream
.get() );
777 void SerfSession::GET( const OUString
& inPath
,
778 uno::Reference
< io::XOutputStream
> & ioOutputStream
,
779 const std::vector
< OUString
> & inHeaderNames
,
780 DAVResource
& ioResource
,
781 const DAVRequestEnvironment
& rEnv
)
782 throw ( DAVException
)
784 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
788 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
789 ioResource
.uri
= inPath
;
790 ioResource
.properties
.clear();
791 apr_status_t status
= APR_SUCCESS
;
792 aReqProc
->processGet( ioOutputStream
,
797 HandleError( aReqProc
);
803 void SerfSession::PUT( const OUString
& inPath
,
804 const uno::Reference
< io::XInputStream
> & inInputStream
,
805 const DAVRequestEnvironment
& rEnv
)
806 throw ( DAVException
)
808 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
812 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
813 uno::Sequence
< sal_Int8
> aDataToSend
;
814 if ( !getDataFromInputStream( inInputStream
, aDataToSend
, false ) )
815 throw DAVException( DAVException::DAV_INVALID_ARG
);
816 apr_status_t status
= APR_SUCCESS
;
817 aReqProc
->processPut( reinterpret_cast< const char * >( aDataToSend
.getConstArray() ),
818 aDataToSend
.getLength(),
821 HandleError( aReqProc
);
827 uno::Reference
< io::XInputStream
>
828 SerfSession::POST( const OUString
& inPath
,
829 const OUString
& rContentType
,
830 const OUString
& rReferer
,
831 const uno::Reference
< io::XInputStream
> & inInputStream
,
832 const DAVRequestEnvironment
& rEnv
)
833 throw ( DAVException
)
835 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
837 uno::Sequence
< sal_Int8
> aDataToSend
;
838 if ( !getDataFromInputStream( inInputStream
, aDataToSend
, true ) )
840 throw DAVException( DAVException::DAV_INVALID_ARG
);
845 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
846 uno::Reference
< SerfInputStream
> xInputStream( new SerfInputStream
);
847 apr_status_t status
= APR_SUCCESS
;
848 aReqProc
->processPost( reinterpret_cast< const char * >( aDataToSend
.getConstArray() ),
849 aDataToSend
.getLength(),
855 HandleError( aReqProc
);
856 return uno::Reference
< io::XInputStream
>( xInputStream
.get() );
862 void SerfSession::POST( const OUString
& inPath
,
863 const OUString
& rContentType
,
864 const OUString
& rReferer
,
865 const uno::Reference
< io::XInputStream
> & inInputStream
,
866 uno::Reference
< io::XOutputStream
> & oOutputStream
,
867 const DAVRequestEnvironment
& rEnv
)
868 throw ( DAVException
)
870 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
872 uno::Sequence
< sal_Int8
> aDataToSend
;
873 if ( !getDataFromInputStream( inInputStream
, aDataToSend
, true ) )
875 throw DAVException( DAVException::DAV_INVALID_ARG
);
880 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
881 apr_status_t status
= APR_SUCCESS
;
882 aReqProc
->processPost( reinterpret_cast< const char * >( aDataToSend
.getConstArray() ),
883 aDataToSend
.getLength(),
889 HandleError( aReqProc
);
895 void SerfSession::MKCOL( const OUString
& inPath
,
896 const DAVRequestEnvironment
& rEnv
)
897 throw ( DAVException
)
899 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
903 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
904 apr_status_t status
= APR_SUCCESS
;
905 aReqProc
->processMkCol( status
);
907 HandleError( aReqProc
);
913 void SerfSession::COPY( const OUString
& inSourceURL
,
914 const OUString
& inDestinationURL
,
915 const DAVRequestEnvironment
& rEnv
,
917 throw ( DAVException
)
919 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
923 SerfUri
theSourceUri( inSourceURL
);
924 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( theSourceUri
.GetPath() ) );
925 apr_status_t status
= APR_SUCCESS
;
926 aReqProc
->processCopy( inDestinationURL
,
927 (inOverWrite
? true : false),
930 HandleError( aReqProc
);
936 void SerfSession::MOVE( const OUString
& inSourceURL
,
937 const OUString
& inDestinationURL
,
938 const DAVRequestEnvironment
& rEnv
,
940 throw ( DAVException
)
942 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
946 SerfUri
theSourceUri( inSourceURL
);
947 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( theSourceUri
.GetPath() ) );
948 apr_status_t status
= APR_SUCCESS
;
949 aReqProc
->processMove( inDestinationURL
,
950 (inOverWrite
? true : false),
953 HandleError( aReqProc
);
959 void SerfSession::DESTROY( const OUString
& inPath
,
960 const DAVRequestEnvironment
& rEnv
)
961 throw ( DAVException
)
963 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
967 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
968 apr_status_t status
= APR_SUCCESS
;
969 aReqProc
->processDelete( status
);
971 HandleError( aReqProc
);
978 sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
982 osl_getSystemTime( &aEnd );
984 // Try to estimate a safe absolute time for sending the
985 // lock refresh request.
986 sal_Int32 lastChanceToSendRefreshRequest = -1;
987 if ( timeout != NE_TIMEOUT_INFINITE )
989 sal_Int32 calltime = aEnd.Seconds - rStart.Seconds;
990 if ( calltime <= timeout )
992 lastChanceToSendRefreshRequest
993 = aEnd.Seconds + timeout - calltime;
997 SAL_INFO("ucb.ucp.webdav", "No chance to refresh lock before timeout!" );
1000 return lastChanceToSendRefreshRequest;
1006 // LOCK (set new lock)
1008 void SerfSession::LOCK( const OUString
& inPath
,
1010 const DAVRequestEnvironment
& rEnv
)
1011 throw ( DAVException
)
1013 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
1017 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
1018 aReqProc
->processLock( rLock
);
1020 HandleError( aReqProc
);
1024 // LOCK (refresh existing lock)
1026 sal_Int64
SerfSession::LOCK( const OUString
& /*inPath*/,
1028 const DAVRequestEnvironment
& /*rEnv*/ )
1029 throw ( DAVException
)
1031 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
1035 // Try to get the neon lock from lock store
1037 = m_aSerfLockStore.findByUri( makeAbsoluteURL( inPath ) );
1039 throw DAVException( DAVException::DAV_NOT_LOCKED );
1043 // refresh existing lock.
1044 theLock->timeout = static_cast< long >( nTimeout );
1046 TimeValue startCall;
1047 osl_getSystemTime( &startCall );
1049 int theRetVal = ne_lock_refresh( m_pHttpSession, theLock );
1051 if ( theRetVal == NE_OK )
1053 m_aSerfLockStore.updateLock( theLock,
1054 lastChanceToSendRefreshRequest(
1055 startCall, theLock->timeout ) );
1058 HandleError( theRetVal, inPath, rEnv );
1060 return theLock->timeout;
1065 // LOCK (refresh existing lock)
1067 bool SerfSession::LOCK( const OUString
& rLock
,
1068 sal_Int32
*plastChanceToSendRefreshRequest
)
1070 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
1072 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( rLock
) );
1073 aReqProc
->processLock( ucb::Lock(), plastChanceToSendRefreshRequest
);
1077 HandleError( aReqProc
);
1078 SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock
<< " succeeded." );
1083 SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock
<< " failed!" );
1091 void SerfSession::UNLOCK( const OUString
& inPath
,
1092 const DAVRequestEnvironment
& rEnv
)
1093 throw ( DAVException
)
1095 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
1099 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( inPath
) );
1100 aReqProc
->processUnlock();
1104 HandleError( aReqProc
);
1105 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath
<< " succeeded." );
1106 apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->removeLock( inPath
);
1110 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath
<< " failed!" );
1117 void SerfSession::UNLOCK( const OUString
& rLock
)
1119 osl::Guard
< osl::Mutex
> theGuard( m_aMutex
);
1121 boost::shared_ptr
<SerfRequestProcessor
> aReqProc( createReqProc( rLock
) );
1122 aReqProc
->processUnlock();
1126 HandleError( aReqProc
);
1127 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << rLock
<< " succeeded." );
1131 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << rLock
<< " failed!" );
1136 void SerfSession::abort()
1137 throw ( DAVException
)
1139 // 11.11.09 (tkr): The following code lines causing crashes if
1140 // closing a ongoing connection. It turned out that this existing
1141 // solution doesn't work in multi-threading environments.
1142 // So I disabled them in 3.2. . Issue #73893# should fix it in OOo 3.3.
1143 //if ( m_pHttpSession )
1144 // ne_close_connection( m_pHttpSession );
1148 const ucbhelper::InternetProxyServer
& SerfSession::getProxySettings() const
1150 if ( m_aUri
.GetScheme() == "http" || m_aUri
.GetScheme() == "https" )
1152 return m_rProxyDecider
.getProxy( m_aUri
.GetScheme(),
1158 // TODO: figure out, if this case can occur
1159 return m_rProxyDecider
.getProxy( m_aUri
.GetScheme(),
1160 OUString() /* not used */,
1161 -1 /* not used */ );
1169 bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
1170 const char * token )
1172 for ( sal_Int32 n = 0; n < rLocks.getLength(); ++n )
1174 const uno::Sequence< OUString > & rTokens
1175 = rLocks[ n ].LockTokens;
1176 for ( sal_Int32 m = 0; m < rTokens.getLength(); ++m )
1178 if ( rTokens[ m ].equalsAscii( token ) )
1189 bool SerfSession::removeExpiredLocktoken( const OUString
& /*inURL*/,
1190 const DAVRequestEnvironment
& /*rEnv*/ )
1194 SerfLock * theLock = m_aSerfLockStore.findByUri( inURL );
1198 // do a lockdiscovery to check whether this lock is still valid.
1201 // @@@ Alternative: use ne_lock_discover() => less overhead
1203 std::vector< DAVResource > aResources;
1204 std::vector< OUString > aPropNames;
1205 aPropNames.push_back( DAVProperties::LOCKDISCOVERY );
1207 PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv );
1209 if ( aResources.size() == 0 )
1212 std::vector< DAVPropertyValue >::const_iterator it
1213 = aResources[ 0 ].properties.begin();
1214 std::vector< DAVPropertyValue >::const_iterator end
1215 = aResources[ 0 ].properties.end();
1219 if ( (*it).Name.equals( DAVProperties::LOCKDISCOVERY ) )
1221 uno::Sequence< ucb::Lock > aLocks;
1222 if ( !( (*it).Value >>= aLocks ) )
1225 if ( !containsLocktoken( aLocks, theLock->token ) )
1237 // No lockdiscovery prop in propfind result / locktoken not found
1238 // in propfind result -> not locked
1239 SAL_INFO("ucb.ucp.webdav", "SerfSession::removeExpiredLocktoken: Removing "
1240 " expired lock token for " << inURL << ". token: " << theLock->token );
1242 m_aSerfLockStore.removeLock( theLock );
1243 ne_lock_destroy( theLock );
1246 catch ( DAVException const & )
1255 // Common Error Handler
1257 void SerfSession::HandleError( boost::shared_ptr
<SerfRequestProcessor
> rReqProc
)
1258 throw ( DAVException
)
1260 m_aEnv
= DAVRequestEnvironment();
1262 if ( rReqProc
->mpDAVException
)
1264 DAVException
* mpDAVExp( rReqProc
->mpDAVException
);
1266 serf_connection_reset( getSerfConnection() );
1268 if ( mpDAVExp
->getStatus() == 413 &&
1269 m_bNoOfTransferEncodingSwitches
< 2 )
1271 m_bUseChunkedEncoding
= !m_bUseChunkedEncoding
;
1272 ++m_bNoOfTransferEncodingSwitches
;
1275 throw DAVException( mpDAVExp
->getError(),
1276 mpDAVExp
->getData(),
1277 mpDAVExp
->getStatus() );
1281 // Map error code to DAVException.
1287 case NE_ERROR: // Generic error
1289 OUString aText = OUString::createFromAscii(
1290 ne_get_error( m_pHttpSession ) );
1292 sal_uInt16 code = makeStatusCode( aText );
1294 if ( code == SC_LOCKED )
1296 if ( m_aSerfLockStore.findByUri(
1297 makeAbsoluteURL( inPath ) ) == 0 )
1299 // locked by 3rd party
1300 throw DAVException( DAVException::DAV_LOCKED );
1304 // locked by ourself
1305 throw DAVException( DAVException::DAV_LOCKED_SELF );
1309 // Special handling for 400 and 412 status codes, which may indicate
1310 // that a lock previously obtained by us has been released meanwhile
1311 // by the server. Unfortunately, RFC is not clear at this point,
1312 // thus server implementations behave different...
1313 else if ( code == SC_BAD_REQUEST || code == SC_PRECONDITION_FAILED )
1315 if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) )
1316 throw DAVException( DAVException::DAV_LOCK_EXPIRED );
1319 throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
1321 case NE_LOOKUP: // Name lookup failed.
1322 throw DAVException( DAVException::DAV_HTTP_LOOKUP,
1323 SerfUri::makeConnectionEndPointString(
1324 m_aHostName, m_nPort ) );
1326 case NE_AUTH: // User authentication failed on server
1327 throw DAVException( DAVException::DAV_HTTP_AUTH,
1328 SerfUri::makeConnectionEndPointString(
1329 m_aHostName, m_nPort ) );
1331 case NE_PROXYAUTH: // User authentication failed on proxy
1332 throw DAVException( DAVException::DAV_HTTP_AUTHPROXY,
1333 SerfUri::makeConnectionEndPointString(
1334 m_aProxyName, m_nProxyPort ) );
1336 case NE_CONNECT: // Could not connect to server
1337 throw DAVException( DAVException::DAV_HTTP_CONNECT,
1338 SerfUri::makeConnectionEndPointString(
1339 m_aHostName, m_nPort ) );
1341 case NE_TIMEOUT: // Connection timed out
1342 throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
1343 SerfUri::makeConnectionEndPointString(
1344 m_aHostName, m_nPort ) );
1346 case NE_FAILED: // The precondition failed
1347 throw DAVException( DAVException::DAV_HTTP_FAILED,
1348 SerfUri::makeConnectionEndPointString(
1349 m_aHostName, m_nPort ) );
1351 case NE_RETRY: // Retry request (ne_end_request ONLY)
1352 throw DAVException( DAVException::DAV_HTTP_RETRY,
1353 SerfUri::makeConnectionEndPointString(
1354 m_aHostName, m_nPort ) );
1358 SerfUri aUri( ne_redirect_location( m_pHttpSession ) );
1360 DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() );
1364 SAL_INFO("ucb.ucp.webdav", "SerfSession::HandleError : Unknown Serf error code!" );
1365 throw DAVException( DAVException::DAV_HTTP_ERROR,
1366 OUString::createFromAscii(
1367 ne_get_error( m_pHttpSession ) ) );
1376 SerfSession::getDataFromInputStream(
1377 const uno::Reference
< io::XInputStream
> & xStream
,
1378 uno::Sequence
< sal_Int8
> & rData
,
1379 bool bAppendTrailingZeroByte
)
1383 uno::Reference
< io::XSeekable
> xSeekable( xStream
, uno::UNO_QUERY
);
1384 if ( xSeekable
.is() )
1389 = sal::static_int_cast
<sal_Int32
>(xSeekable
->getLength());
1391 = xStream
->readBytes( rData
, nSize
);
1393 if ( nRead
== nSize
)
1395 if ( bAppendTrailingZeroByte
)
1397 rData
.realloc( nSize
+ 1 );
1398 rData
[ nSize
] = sal_Int8( 0 );
1403 catch ( io::NotConnectedException
const & )
1407 catch ( io::BufferSizeExceededException
const & )
1411 catch ( io::IOException
const & )
1413 // getLength, readBytes
1420 uno::Sequence
< sal_Int8
> aBuffer
;
1423 sal_Int32 nRead
= xStream
->readSomeBytes( aBuffer
, 65536 );
1426 if ( rData
.getLength() < ( nPos
+ nRead
) )
1427 rData
.realloc( nPos
+ nRead
);
1429 aBuffer
.realloc( nRead
);
1430 memcpy( (void*)( rData
.getArray() + nPos
),
1431 (const void*)aBuffer
.getConstArray(),
1435 aBuffer
.realloc( 0 );
1436 nRead
= xStream
->readSomeBytes( aBuffer
, 65536 );
1439 if ( bAppendTrailingZeroByte
)
1441 rData
.realloc( nPos
+ 1 );
1442 rData
[ nPos
] = sal_Int8( 0 );
1446 catch ( io::NotConnectedException
const & )
1450 catch ( io::BufferSizeExceededException
const & )
1454 catch ( io::IOException
const & )
1465 SerfSession::isDomainMatch( const OUString
& certHostName
)
1467 OUString hostName
= getHostName();
1469 if (hostName
.equalsIgnoreAsciiCase( certHostName
) )
1472 if ( certHostName
.startsWith( "*" ) &&
1473 hostName
.getLength() >= certHostName
.getLength() )
1475 OUString cmpStr
= certHostName
.copy( 1 );
1477 if ( hostName
.matchIgnoreAsciiCase(
1478 cmpStr
, hostName
.getLength() - cmpStr
.getLength() ) )
1486 OUString SerfSession::makeAbsoluteURL( OUString const & rURL ) const
1490 // Is URL relative or already absolute?
1491 if ( rURL[ 0 ] != '/' )
1494 return OUString( rURL );
1499 memset( &aUri, 0, sizeof( aUri ) );
1501 ne_fill_server_uri( m_pHttpSession, &aUri );
1503 = ne_strdup( OUStringToOString(
1504 rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
1505 SerfUri aSerfUri( &aUri );
1506 ne_uri_free( &aUri );
1507 return aSerfUri.GetURI();
1510 catch ( DAVException const & )
1518 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */