bump product version to 7.2.5.1
[LibreOffice.git] / ucb / source / ucp / webdav / SerfSession.cxx
blob8f7a7e38c754d85b956aaa53b7ad3bfc82776887
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <vector>
21 #include <string.h>
22 #include <sal/log.hxx>
23 #include <comphelper/processfactory.hxx>
24 #include <comphelper/sequence.hxx>
25 #include <ucbhelper/simplecertificatevalidationrequest.hxx>
27 #include "AprEnv.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;
57 // Constructor
59 SerfSession::SerfSession(
60 const rtl::Reference< DAVSessionFactory > & rSessionFactory,
61 const OUString& inUri,
62 const ucbhelper::InternetProxyDecider & rProxyDecider )
63 : DAVSession( rSessionFactory )
64 , m_aMutex()
65 , m_aUri( inUri )
66 , m_aProxyName()
67 , m_nProxyPort( 0 )
68 , m_pSerfConnection( nullptr )
69 , m_pSerfContext( nullptr )
70 , m_bIsHeadRequestInProgress( false )
71 , m_bUseChunkedEncoding( false )
72 , m_bNoOfTransferEncodingSwitches( 0 )
73 , m_rProxyDecider( rProxyDecider )
74 , m_aEnv()
76 m_pSerfContext = serf_context_create( getAprPool() );
78 m_pSerfBucket_Alloc = serf_bucket_allocator_create( getAprPool(), nullptr, nullptr );
82 // Destructor
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 );
97 m_aEnv = rEnv;
98 Init();
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;
118 else
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,
139 m_pSerfContext,
140 m_aUri.getAprUri(),
141 Serf_ConnectSetup, this,
142 nullptr /* close connection callback */, nullptr /* close connection baton */,
143 getAprPool() );
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(),
159 APR_UNSPEC,
160 static_cast<apr_port_t>(m_nProxyPort),
161 0, getAprPool() );
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;
215 // virtual
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() ) )
225 return true;
228 catch ( DAVException const & )
230 return false;
232 return false;
236 // virtual
237 bool SerfSession::UsesProxy()
239 Init();
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(),
250 inAprSocket,
251 getSerfBktAlloc() );
253 if ( isSSLNeeded() )
255 tmpInputBkt = serf_bucket_ssl_decrypt_create( tmpInputBkt,
256 nullptr,
257 getSerfBktAlloc() );
258 /** Set the callback that is called to authenticate the
259 certificate (chain).
261 serf_ssl_server_cert_chain_callback_set(
262 serf_bucket_ssl_decrypt_context_get(tmpInputBkt),
263 nullptr,
264 Serf_CertificateChainValidation,
265 this);
266 serf_ssl_set_hostname( serf_bucket_ssl_decrypt_context_get( tmpInputBkt ),
267 getHostinfo() );
269 *outSerfOutputBucket = serf_bucket_ssl_encrypt_create( *outSerfOutputBucket,
270 serf_bucket_ssl_decrypt_context_get( tmpInputBkt ),
271 getSerfBktAlloc() );
274 *outSerfInputBucket = tmpInputBkt;
276 return APR_SUCCESS;
279 apr_status_t SerfSession::provideSerfCredentials( bool bGiveProvidedCredentialsASecondTry,
280 char ** outUsername,
281 char ** outPassword,
282 serf_request_t * /*inRequest*/,
283 int /*inCode*/,
284 const char *inAuthProtocol,
285 const char *inRealm,
286 apr_pool_t *inAprPool )
288 DAVAuthListener * pListener = getRequestEnvironment().m_xAuthListener.get();
289 if ( !pListener )
291 // abort
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( '@' );
304 if ( nPos == -1 )
306 theUserName = aUserInfo;
308 else
310 theUserName = aUserInfo.copy( 0, nPos );
311 thePassWord = aUserInfo.copy( nPos + 1 );
315 catch ( DAVException const & )
317 // abort
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 ),
325 getHostName(),
326 theUserName,
327 thePassWord,
328 bCanUseSystemCreds,
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 (
341 int,
342 const serf_ssl_certificate_t * const * pCertificateChainBase64Encoded,
343 int nCertificateChainLength)
345 // Check arguments.
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())
358 return APR_SUCCESS;
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],
394 getAprPool()));
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;
404 while (nIndex >= 0)
406 const OUString sToken (sServerCertificateSubject.getToken(0, ',', nIndex));
407 if (sToken.startsWith("CN="))
409 sServerCertificateSubject = sToken.copy(3);
410 break;
412 else if (sToken.startsWith(" CN="))
414 sServerCertificateSubject = sToken.copy(4);
415 break;
419 // When the certificate container already contains a (trusted)
420 // entry for the server then we do not have to authenticate any
421 // certificate.
422 const security::CertificateContainerStatus eStatus (
423 xCertificateContainer->hasCertificate(
424 getHostName(), sServerCertificateSubject ) );
425 if (eStatus != security::CertificateContainerStatus_NOCERT)
427 return eStatus == security::CertificateContainerStatus_TRUSTED
428 ? APR_SUCCESS
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],
441 getAprPool()));
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(
450 xServerCertificate,
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();
468 break;
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);
495 return APR_SUCCESS;
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;
511 else
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 );
520 if ( xEnv.is() )
522 uno::Reference< task::XInteractionHandler > xIH( xEnv->getInteractionHandler() );
523 if ( xIH.is() )
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 );
536 if ( xApprove.is() )
538 xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, true );
539 return APR_SUCCESS;
541 else
543 // Don't trust cert
544 xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, false );
545 return SERF_SSL_CERT_UNKNOWN_FAILURE;
549 else
551 // Don't trust cert
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,
569 SerfBktAlloc );
571 // create response bucket
572 responseBkt = serf_bucket_response_create( responseBkt,
573 SerfBktAlloc );
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 );
581 return responseBkt;
584 SerfRequestProcessor* SerfSession::createReqProc( const OUString & inPath )
586 return new SerfRequestProcessor( *this,
587 inPath,
588 m_bUseChunkedEncoding );
592 // PROPFIND - allprop & named
594 void SerfSession::PROPFIND( const OUString & inPath,
595 const Depth inDepth,
596 const std::vector< OUString > & inPropNames,
597 std::vector< DAVResource > & ioResources,
598 const DAVRequestEnvironment & rEnv )
600 osl::Guard< osl::Mutex > theGuard( m_aMutex );
602 Init( rEnv );
604 apr_status_t status = APR_SUCCESS;
605 std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
606 aReqProc->processPropFind( inDepth,
607 inPropNames,
608 ioResources,
609 status );
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,
625 const Depth inDepth,
626 std::vector< DAVResourceInfo > & ioResInfo,
627 const DAVRequestEnvironment & rEnv )
629 osl::Guard< osl::Mutex > theGuard( m_aMutex );
631 Init( rEnv );
633 apr_status_t status = APR_SUCCESS;
634 std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
635 aReqProc->processPropFind( inDepth,
636 ioResInfo,
637 status );
639 if ( status == APR_SUCCESS &&
640 aReqProc->mpDAVException == nullptr &&
641 ioResInfo.empty() )
643 m_aEnv = DAVRequestEnvironment();
644 throw DAVException( DAVException::DAV_HTTP_ERROR, inPath, APR_EGENERAL );
646 HandleError( aReqProc );
650 // PROPPATCH
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 );
658 Init( rEnv );
660 apr_status_t status = APR_SUCCESS;
661 std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
662 aReqProc->processPropPatch( inValues,
663 status );
665 HandleError( aReqProc );
669 // HEAD
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 );
678 Init( rEnv );
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,
687 ioResource,
688 status );
690 m_bIsHeadRequestInProgress = false;
692 HandleError( aReqProc );
696 // GET
698 uno::Reference< io::XInputStream >
699 SerfSession::GET( const OUString & inPath,
700 const DAVRequestEnvironment & rEnv )
702 osl::Guard< osl::Mutex > theGuard( m_aMutex );
704 Init( rEnv );
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,
710 status );
712 HandleError( aReqProc );
714 return uno::Reference< io::XInputStream >( xInputStream.get() );
718 // 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 );
726 Init( rEnv );
728 apr_status_t status = APR_SUCCESS;
729 std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
730 aReqProc->processGet( ioOutputStream,
731 status );
733 HandleError( aReqProc );
737 // GET
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 );
747 Init( rEnv );
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,
755 inHeaderNames,
756 ioResource,
757 status );
759 HandleError( aReqProc );
761 return uno::Reference< io::XInputStream >( xInputStream.get() );
765 // 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 );
775 Init( rEnv );
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,
782 inHeaderNames,
783 ioResource,
784 status );
786 HandleError( aReqProc );
790 // PUT
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 );
798 Init( rEnv );
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(),
807 status );
809 HandleError( aReqProc );
813 // POST
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 );
830 Init( rEnv );
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(),
837 rContentType,
838 rReferer,
839 xInputStream,
840 status );
842 HandleError( aReqProc );
843 return uno::Reference< io::XInputStream >( xInputStream.get() );
847 // POST
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 );
864 Init( rEnv );
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(),
870 rContentType,
871 rReferer,
872 oOutputStream,
873 status );
875 HandleError( aReqProc );
879 // MKCOL
881 void SerfSession::MKCOL( const OUString & inPath,
882 const DAVRequestEnvironment & rEnv )
884 osl::Guard< osl::Mutex > theGuard( m_aMutex );
886 Init( rEnv );
888 std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
889 apr_status_t status = APR_SUCCESS;
890 aReqProc->processMkCol( status );
892 HandleError( aReqProc );
896 // COPY
898 void SerfSession::COPY( const OUString & inSourceURL,
899 const OUString & inDestinationURL,
900 const DAVRequestEnvironment & rEnv,
901 bool inOverWrite )
903 osl::Guard< osl::Mutex > theGuard( m_aMutex );
905 Init( rEnv );
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 );
916 // MOVE
918 void SerfSession::MOVE( const OUString & inSourceURL,
919 const OUString & inDestinationURL,
920 const DAVRequestEnvironment & rEnv,
921 bool inOverWrite )
923 osl::Guard< osl::Mutex > theGuard( m_aMutex );
925 Init( rEnv );
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 );
936 // DESTROY
938 void SerfSession::DESTROY( const OUString & inPath,
939 const DAVRequestEnvironment & rEnv )
941 osl::Guard< osl::Mutex > theGuard( m_aMutex );
943 Init( rEnv );
945 std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
946 apr_status_t status = APR_SUCCESS;
947 aReqProc->processDelete( status );
949 HandleError( aReqProc );
954 namespace
956 sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
957 int timeout )
959 TimeValue aEnd;
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;
973 else
975 SAL_INFO("ucb.ucp.webdav", "No chance to refresh lock before timeout!" );
978 return lastChanceToSendRefreshRequest;
981 } // namespace
984 // LOCK (set new lock)
986 void SerfSession::LOCK( const OUString & inPath,
987 ucb::Lock & rLock,
988 const DAVRequestEnvironment & rEnv )
990 osl::Guard< osl::Mutex > theGuard( m_aMutex );
992 Init( rEnv );
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*/,
1004 sal_Int64 nTimeout,
1005 const DAVRequestEnvironment & /*rEnv*/ )
1007 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1009 return nTimeout;
1011 // Try to get the neon lock from lock store
1012 SerfLock * theLock
1013 = m_aSerfLockStore.findByUri( makeAbsoluteURL( inPath ) );
1014 if ( !theLock )
1015 throw DAVException( DAVException::DAV_NOT_LOCKED );
1017 Init( rEnv );
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." );
1055 return true;
1057 catch(...)
1059 SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock << " failed!" );
1060 return false;
1065 // UNLOCK
1067 void SerfSession::UNLOCK( const OUString & inPath,
1068 const DAVRequestEnvironment & rEnv )
1070 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1072 Init( rEnv );
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 );
1083 catch(...)
1085 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath << " failed!" );
1090 // UNLOCK
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." );
1104 catch(...)
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(),
1127 m_aUri.GetHost(),
1128 m_aUri.GetPort() );
1130 else
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 */ );
1141 namespace {
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 ) )
1153 return true;
1156 return false;
1159 } // namespace
1163 bool SerfSession::removeExpiredLocktoken( const OUString & /*inURL*/,
1164 const DAVRequestEnvironment & /*rEnv*/ )
1166 return true;
1168 SerfLock * theLock = m_aSerfLockStore.findByUri( inURL );
1169 if ( !theLock )
1170 return false;
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() )
1184 return false;
1186 std::vector< DAVPropertyValue >::const_iterator it
1187 = aResources[ 0 ].properties.begin();
1188 std::vector< DAVPropertyValue >::const_iterator end
1189 = aResources[ 0 ].properties.end();
1191 while ( it != end )
1193 if ( (*it).Name.equals( DAVProperties::LOCKDISCOVERY ) )
1195 uno::Sequence< ucb::Lock > aLocks;
1196 if ( !( (*it).Value >>= aLocks ) )
1197 return false;
1199 if ( !containsLocktoken( aLocks, theLock->token ) )
1201 // expired!
1202 break;
1205 // still valid.
1206 return false;
1208 ++it;
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 );
1218 return true;
1220 catch ( DAVException const & )
1223 return false;
1228 // HandleError
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.
1255 switch ( nError )
1257 case NE_OK:
1258 return;
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 );
1275 else
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 ) );
1329 case NE_REDIRECT:
1331 SerfUri aUri( ne_redirect_location( m_pHttpSession ) );
1332 throw DAVException(
1333 DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() );
1335 default:
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 ) ) );
1347 // static
1348 bool
1349 SerfSession::getDataFromInputStream(
1350 const uno::Reference< io::XInputStream > & xStream,
1351 uno::Sequence< sal_Int8 > & rData,
1352 bool bAppendTrailingZeroByte )
1354 if ( xStream.is() )
1356 uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY );
1357 if ( xSeekable.is() )
1361 sal_Int32 nSize
1362 = sal::static_int_cast<sal_Int32>(xSeekable->getLength());
1363 sal_Int32 nRead
1364 = xStream->readBytes( rData, nSize );
1366 if ( nRead == nSize )
1368 if ( bAppendTrailingZeroByte )
1370 rData.realloc( nSize + 1 );
1371 rData[ nSize ] = sal_Int8( 0 );
1373 return true;
1376 catch ( io::NotConnectedException const & )
1378 // readBytes
1380 catch ( io::BufferSizeExceededException const & )
1382 // readBytes
1384 catch ( io::IOException const & )
1386 // getLength, readBytes
1389 else
1393 uno::Sequence< sal_Int8 > aBuffer;
1394 sal_Int32 nPos = 0;
1396 sal_Int32 nRead = xStream->readSomeBytes( aBuffer, 65536 );
1397 while ( nRead > 0 )
1399 if ( rData.getLength() < ( nPos + nRead ) )
1400 rData.realloc( nPos + nRead );
1402 aBuffer.realloc( nRead );
1403 memcpy( rData.getArray() + nPos, aBuffer.getConstArray(), nRead );
1404 nPos += 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 );
1415 return true;
1417 catch ( io::NotConnectedException const & )
1419 // readBytes
1421 catch ( io::BufferSizeExceededException const & )
1423 // readBytes
1425 catch ( io::IOException const & )
1427 // readBytes
1431 return false;
1435 bool
1436 SerfSession::isDomainMatch( const OUString & certHostName )
1438 OUString hostName = getHostName();
1440 if (hostName.equalsIgnoreAsciiCase( certHostName ) )
1441 return true;
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() ) )
1450 return true;
1452 return false;
1457 OUString SerfSession::makeAbsoluteURL( OUString const & rURL ) const
1461 // Is URL relative or already absolute?
1462 if ( rURL[ 0 ] != '/' )
1464 // absolute.
1465 return OUString( rURL );
1467 else
1469 ne_uri aUri;
1470 memset( &aUri, 0, sizeof( aUri ) );
1472 ne_fill_server_uri( m_pHttpSession, &aUri );
1473 aUri.path
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 & )
1484 // error.
1485 return OUString();
1489 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */