diff options
Diffstat (limited to 'ucb/source/ucp/webdav/SerfSession.cxx')
-rw-r--r-- | ucb/source/ucp/webdav/SerfSession.cxx | 1489 |
1 files changed, 1489 insertions, 0 deletions
diff --git a/ucb/source/ucp/webdav/SerfSession.cxx b/ucb/source/ucp/webdav/SerfSession.cxx new file mode 100644 index 000000000..8f7a7e38c --- /dev/null +++ b/ucb/source/ucp/webdav/SerfSession.cxx @@ -0,0 +1,1489 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vector> +#include <string.h> +#include <sal/log.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <ucbhelper/simplecertificatevalidationrequest.hxx> + +#include "AprEnv.hxx" +#include <apr_strings.h> + +#include "DAVAuthListener.hxx" +#include "SerfSession.hxx" +#include "SerfUri.hxx" +#include "SerfRequestProcessor.hxx" +#include "SerfCallbacks.hxx" +#include "SerfInputStream.hxx" + +#include <com/sun/star/xml/crypto/SEInitializer.hpp> +#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp> +#include <com/sun/star/security/XCertificate.hpp> +#include <com/sun/star/security/CertificateValidity.hpp> +#include <com/sun/star/security/CertificateContainerStatus.hpp> +#include <com/sun/star/security/CertificateContainer.hpp> +#include <com/sun/star/security/XCertificateContainer.hpp> +#include <com/sun/star/security/CertAltNameEntry.hpp> +#include <com/sun/star/security/XSanExtension.hpp> +#include <com/sun/star/io/NotConnectedException.hpp> +#include <com/sun/star/io/BufferSizeExceededException.hpp> +#include <com/sun/star/io/IOException.hpp> +#define OID_SUBJECT_ALTERNATIVE_NAME "2.5.29.17" + +#include <com/sun/star/ucb/Lock.hpp> +#include <com/sun/star/xml/crypto/XSEInitializer.hpp> + +using namespace com::sun::star; +using namespace http_dav_ucp; + +// Constructor + +SerfSession::SerfSession( + const rtl::Reference< DAVSessionFactory > & rSessionFactory, + const OUString& inUri, + const ucbhelper::InternetProxyDecider & rProxyDecider ) + : DAVSession( rSessionFactory ) + , m_aMutex() + , m_aUri( inUri ) + , m_aProxyName() + , m_nProxyPort( 0 ) + , m_pSerfConnection( nullptr ) + , m_pSerfContext( nullptr ) + , m_bIsHeadRequestInProgress( false ) + , m_bUseChunkedEncoding( false ) + , m_bNoOfTransferEncodingSwitches( 0 ) + , m_rProxyDecider( rProxyDecider ) + , m_aEnv() +{ + m_pSerfContext = serf_context_create( getAprPool() ); + + m_pSerfBucket_Alloc = serf_bucket_allocator_create( getAprPool(), nullptr, nullptr ); +} + + +// Destructor + +SerfSession::~SerfSession( ) +{ + if ( m_pSerfConnection ) + { + serf_connection_close( m_pSerfConnection ); + m_pSerfConnection = nullptr; + } +} + + +void SerfSession::Init( const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + m_aEnv = rEnv; + Init(); +} + + +void SerfSession::Init() +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + bool bCreateNewSession = false; + + if ( m_pSerfConnection == nullptr ) + { + const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings(); + + m_aProxyName = rProxyCfg.aName; + m_nProxyPort = rProxyCfg.nPort; + + // Not yet initialized. Create new session. + bCreateNewSession = true; + } + else + { + const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings(); + + if ( ( rProxyCfg.aName != m_aProxyName ) + || ( rProxyCfg.nPort != m_nProxyPort ) ) + { + m_aProxyName = rProxyCfg.aName; + m_nProxyPort = rProxyCfg.nPort; + + // new session needed, destroy old first + serf_connection_close( m_pSerfConnection ); + m_pSerfConnection = nullptr; + bCreateNewSession = true; + } + } + + if ( bCreateNewSession ) + { + // TODO - close_connection callback + apr_status_t status = serf_connection_create2( &m_pSerfConnection, + m_pSerfContext, + m_aUri.getAprUri(), + Serf_ConnectSetup, this, + nullptr /* close connection callback */, nullptr /* close connection baton */, + getAprPool() ); + + if ( m_pSerfConnection == nullptr ||status != APR_SUCCESS ) + { + throw DAVException( DAVException::DAV_SESSION_CREATE, + SerfUri::makeConnectionEndPointString( m_aUri.GetHost(), m_aUri.GetPort() ) ); + } + + // Register the session with the lock store +// m_aSerfLockStore.registerSession( m_pSerfConnection ); + + if ( m_aProxyName.getLength() ) + { + apr_sockaddr_t *proxy_address = nullptr; + status = apr_sockaddr_info_get( &proxy_address, + OUStringToOString( m_aProxyName, RTL_TEXTENCODING_UTF8 ).getStr(), + APR_UNSPEC, + static_cast<apr_port_t>(m_nProxyPort), + 0, getAprPool() ); + + if ( status != APR_SUCCESS ) + { + throw DAVException( DAVException::DAV_SESSION_CREATE, + SerfUri::makeConnectionEndPointString( m_aUri.GetHost(), m_aUri.GetPort() ) ); + } + + serf_config_proxy( m_pSerfContext, proxy_address ); + } + + + serf_config_credentials_callback( m_pSerfContext, Serf_Credentials ); + + m_bUseChunkedEncoding = isSSLNeeded(); + } +} + +apr_pool_t* SerfSession::getAprPool() +{ + return apr_environment::AprEnv::getAprEnv()->getAprPool(); +} + +serf_bucket_alloc_t* SerfSession::getSerfBktAlloc() +{ + return m_pSerfBucket_Alloc; +} + +serf_context_t* SerfSession::getSerfContext() +{ + return m_pSerfContext; +} + +serf_connection_t* SerfSession::getSerfConnection() +{ + return m_pSerfConnection; +} + +bool SerfSession::isHeadRequestInProgress() +{ + return m_bIsHeadRequestInProgress; +} + +bool SerfSession::isSSLNeeded() +{ + return m_aUri.GetScheme().equalsIgnoreAsciiCase( "https" ); +} + +char* SerfSession::getHostinfo() +{ + return m_aUri.getAprUri().hostinfo; +} + + +// virtual +bool SerfSession::CanUse( const OUString & inUri ) +{ + try + { + SerfUri theUri( inUri ); + if ( ( theUri.GetPort() == m_aUri.GetPort() ) && + ( theUri.GetHost() == m_aUri.GetHost() ) && + ( theUri.GetScheme() == m_aUri.GetScheme() ) ) + { + return true; + } + } + catch ( DAVException const & ) + { + return false; + } + return false; +} + + +// virtual +bool SerfSession::UsesProxy() +{ + Init(); + return ( m_aProxyName.getLength() > 0 ); +} + +apr_status_t SerfSession::setupSerfConnection( apr_socket_t * inAprSocket, + serf_bucket_t **outSerfInputBucket, + serf_bucket_t **outSerfOutputBucket, + apr_pool_t* /*inAprPool*/ ) +{ + serf_bucket_t *tmpInputBkt; + tmpInputBkt = serf_context_bucket_socket_create( getSerfContext(), + inAprSocket, + getSerfBktAlloc() ); + + if ( isSSLNeeded() ) + { + tmpInputBkt = serf_bucket_ssl_decrypt_create( tmpInputBkt, + nullptr, + getSerfBktAlloc() ); + /** Set the callback that is called to authenticate the + certificate (chain). + */ + serf_ssl_server_cert_chain_callback_set( + serf_bucket_ssl_decrypt_context_get(tmpInputBkt), + nullptr, + Serf_CertificateChainValidation, + this); + serf_ssl_set_hostname( serf_bucket_ssl_decrypt_context_get( tmpInputBkt ), + getHostinfo() ); + + *outSerfOutputBucket = serf_bucket_ssl_encrypt_create( *outSerfOutputBucket, + serf_bucket_ssl_decrypt_context_get( tmpInputBkt ), + getSerfBktAlloc() ); + } + + *outSerfInputBucket = tmpInputBkt; + + return APR_SUCCESS; +} + +apr_status_t SerfSession::provideSerfCredentials( bool bGiveProvidedCredentialsASecondTry, + char ** outUsername, + char ** outPassword, + serf_request_t * /*inRequest*/, + int /*inCode*/, + const char *inAuthProtocol, + const char *inRealm, + apr_pool_t *inAprPool ) +{ + DAVAuthListener * pListener = getRequestEnvironment().m_xAuthListener.get(); + if ( !pListener ) + { + // abort + return SERF_ERROR_AUTHN_FAILED; + } + + OUString theUserName; + OUString thePassWord; + try + { + SerfUri uri( getRequestEnvironment().m_aRequestURI ); + OUString aUserInfo( uri.GetUserInfo() ); + if ( aUserInfo.getLength() ) + { + sal_Int32 nPos = aUserInfo.indexOf( '@' ); + if ( nPos == -1 ) + { + theUserName = aUserInfo; + } + else + { + theUserName = aUserInfo.copy( 0, nPos ); + thePassWord = aUserInfo.copy( nPos + 1 ); + } + } + } + catch ( DAVException const & ) + { + // abort + return SERF_ERROR_AUTHN_FAILED; + } + + const bool bCanUseSystemCreds = ( ( strcasecmp( inAuthProtocol, "NTLM" ) == 0 ) || + ( strcasecmp( inAuthProtocol, "Negotiate" ) == 0 ) ); + + int theRetVal = pListener->authenticate( OUString::createFromAscii( inRealm ), + getHostName(), + theUserName, + thePassWord, + bCanUseSystemCreds, + bGiveProvidedCredentialsASecondTry ); + + if ( theRetVal == 0 ) + { + *outUsername = apr_pstrdup( inAprPool, OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ).getStr() ); + *outPassword = apr_pstrdup( inAprPool, OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + + return theRetVal != 0 ? SERF_ERROR_AUTHN_FAILED : APR_SUCCESS; +} + +apr_status_t SerfSession::verifySerfCertificateChain ( + int, + const serf_ssl_certificate_t * const * pCertificateChainBase64Encoded, + int nCertificateChainLength) +{ + // Check arguments. + if (pCertificateChainBase64Encoded == nullptr || nCertificateChainLength<=0) + { + assert(pCertificateChainBase64Encoded != nullptr); + assert(nCertificateChainLength>0); + return SERF_SSL_CERT_UNKNOWN_FAILURE; + } + + // When called from SerfLockStore::~SerfLockStore(), + // css::xml::crypto::SEInitializer::create() will fail + // but we want to send unlock commands anyway, + // so just ignore certificates and return here. + if (apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->finishing()) + return APR_SUCCESS; + + // Create some crypto objects to decode and handle the base64 + // encoded certificate chain. + uno::Reference< security::XCertificateContainer > xCertificateContainer; + uno::Reference< xml::crypto::XXMLSecurityContext > xSecurityContext; + uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnv; + try + { + css::uno::Reference< css::uno::XComponentContext > xContext = + ::comphelper::getProcessComponentContext(); + // Create a certificate container. + xCertificateContainer = security::CertificateContainer::create( xContext ); + + css::uno::Reference< css::xml::crypto::XSEInitializer > xSEInitializer = + css::xml::crypto::SEInitializer::create( xContext ); + + xSecurityContext = xSEInitializer->createSecurityContext( OUString() ); + if (xSecurityContext.is()) + xSecurityEnv = xSecurityContext->getSecurityEnvironment(); + + if ( ! xSecurityContext.is() || ! xSecurityEnv.is()) + { + // Do we have to dispose xSEInitializer or xCertificateContainer? + return SERF_SSL_CERT_UNKNOWN_FAILURE; + } + } + catch ( uno::Exception const &) + { + return SERF_SSL_CERT_UNKNOWN_FAILURE; + } + + // Decode the server certificate. + const char* sBase64EncodedServerCertificate ( + serf_ssl_cert_export( + pCertificateChainBase64Encoded[0], + getAprPool())); + uno::Reference< security::XCertificate > xServerCertificate( + xSecurityEnv->createCertificateFromAscii( + OUString::createFromAscii(sBase64EncodedServerCertificate))); + if ( ! xServerCertificate.is()) + return SERF_SSL_CERT_UNKNOWN_FAILURE; + + // Get the subject from the server certificate. + OUString sServerCertificateSubject (xServerCertificate->getSubjectName()); + sal_Int32 nIndex = 0; + while (nIndex >= 0) + { + const OUString sToken (sServerCertificateSubject.getToken(0, ',', nIndex)); + if (sToken.startsWith("CN=")) + { + sServerCertificateSubject = sToken.copy(3); + break; + } + else if (sToken.startsWith(" CN=")) + { + sServerCertificateSubject = sToken.copy(4); + break; + } + } + + // When the certificate container already contains a (trusted) + // entry for the server then we do not have to authenticate any + // certificate. + const security::CertificateContainerStatus eStatus ( + xCertificateContainer->hasCertificate( + getHostName(), sServerCertificateSubject ) ); + if (eStatus != security::CertificateContainerStatus_NOCERT) + { + return eStatus == security::CertificateContainerStatus_TRUSTED + ? APR_SUCCESS + : SERF_SSL_CERT_UNKNOWN_FAILURE; + } + + // The shortcut failed, so try to verify the whole chain. This is + // done outside the isDomainMatch() block because the result is + // used by the interaction handler. + std::vector< uno::Reference< security::XCertificate > > aChain; + for (nIndex = 1; nIndex < nCertificateChainLength; ++nIndex) + { + const char* sBase64EncodedCertificate ( + serf_ssl_cert_export( + pCertificateChainBase64Encoded[nIndex], + getAprPool())); + uno::Reference< security::XCertificate > xCertificate( + xSecurityEnv->createCertificateFromAscii( + OUString::createFromAscii(sBase64EncodedCertificate))); + if ( ! xCertificate.is()) + return SERF_SSL_CERT_UNKNOWN_FAILURE; + aChain.push_back(xCertificate); + } + const sal_Int64 nVerificationResult (xSecurityEnv->verifyCertificate( + xServerCertificate, + ::comphelper::containerToSequence(aChain))); + + // When the certificate matches the host name then we can use the + // result of the verification. + bool bHostnameMatchesCertHostnames = false; + { + uno::Sequence< uno::Reference< security::XCertificateExtension > > extensions = xServerCertificate->getExtensions(); + uno::Sequence< security::CertAltNameEntry > altNames; + for (sal_Int32 i = 0 ; i < extensions.getLength(); ++i) + { + uno::Reference< security::XCertificateExtension >element = extensions[i]; + + const OString aId ( reinterpret_cast<const char *>(const_cast<const signed char *>(element->getExtensionId().getArray())), element->getExtensionId().getLength()); + if ( aId.equals( OID_SUBJECT_ALTERNATIVE_NAME ) ) + { + uno::Reference< security::XSanExtension > sanExtension ( element, uno::UNO_QUERY ); + altNames = sanExtension->getAlternativeNames(); + break; + } + } + + uno::Sequence< OUString > certHostNames(altNames.getLength() + 1); + certHostNames[0] = sServerCertificateSubject; + for( int n = 0; n < altNames.getLength(); ++n ) + { + if (altNames[n].Type == security::ExtAltNameType_DNS_NAME) + { + altNames[n].Value >>= certHostNames[n+1]; + } + } + + for ( int i = 0; i < certHostNames.getLength() && !bHostnameMatchesCertHostnames; ++i ) + { + bHostnameMatchesCertHostnames = isDomainMatch( certHostNames[i] ); + } + + } + if ( bHostnameMatchesCertHostnames ) + { + + if (nVerificationResult == 0) + { + // Certificate (chain) is valid. + xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, true); + return APR_SUCCESS; + } + else if ((nVerificationResult & security::CertificateValidity::CHAIN_INCOMPLETE) != 0) + { + // We do not have enough information for verification, + // neither automatically (as we just discovered) nor + // manually (so there is no point in showing any dialog.) + return SERF_SSL_CERT_UNKNOWN_FAILURE; + } + else if ((nVerificationResult & + (security::CertificateValidity::INVALID | security::CertificateValidity::REVOKED)) != 0) + { + // Certificate (chain) is invalid. + xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, false); + return SERF_SSL_CERT_UNKNOWN_FAILURE; + } + else + { + // For all other we have to ask the user. + } + } + + // We have not been able to automatically verify (or falsify) the + // certificate chain. To resolve this we have to ask the user. + const uno::Reference< ucb::XCommandEnvironment > xEnv( getRequestEnvironment().m_xEnv ); + if ( xEnv.is() ) + { + uno::Reference< task::XInteractionHandler > xIH( xEnv->getInteractionHandler() ); + if ( xIH.is() ) + { + rtl::Reference< ucbhelper::SimpleCertificateValidationRequest > + xRequest( new ucbhelper::SimpleCertificateValidationRequest( + static_cast<sal_Int32>(nVerificationResult), xServerCertificate, getHostName() ) ); + xIH->handle( xRequest.get() ); + + rtl::Reference< ucbhelper::InteractionContinuation > xSelection + = xRequest->getSelection(); + + if ( xSelection.is() ) + { + uno::Reference< task::XInteractionApprove > xApprove( xSelection.get(), uno::UNO_QUERY ); + if ( xApprove.is() ) + { + xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, true ); + return APR_SUCCESS; + } + else + { + // Don't trust cert + xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, false ); + return SERF_SSL_CERT_UNKNOWN_FAILURE; + } + } + } + else + { + // Don't trust cert + xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, false ); + return SERF_SSL_CERT_UNKNOWN_FAILURE; + } + } + + return SERF_SSL_CERT_UNKNOWN_FAILURE; +} + +serf_bucket_t* SerfSession::acceptSerfResponse( serf_request_t * inSerfRequest, + serf_bucket_t * inSerfStreamBucket, + apr_pool_t* /*inAprPool*/ ) +{ + // get the per-request bucket allocator + serf_bucket_alloc_t* SerfBktAlloc = serf_request_get_alloc( inSerfRequest ); + + // create a barrier bucket so the response doesn't eat us! + serf_bucket_t *responseBkt = serf_bucket_barrier_create( inSerfStreamBucket, + SerfBktAlloc ); + + // create response bucket + responseBkt = serf_bucket_response_create( responseBkt, + SerfBktAlloc ); + + if ( isHeadRequestInProgress() ) + { + // advise the response bucket that this was from a HEAD request and that it should not expect to see a response body. + serf_bucket_response_set_head( responseBkt ); + } + + return responseBkt; +} + +SerfRequestProcessor* SerfSession::createReqProc( const OUString & inPath ) +{ + return new SerfRequestProcessor( *this, + inPath, + m_bUseChunkedEncoding ); +} + + +// PROPFIND - allprop & named + +void SerfSession::PROPFIND( const OUString & inPath, + const Depth inDepth, + const std::vector< OUString > & inPropNames, + std::vector< DAVResource > & ioResources, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + apr_status_t status = APR_SUCCESS; + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + aReqProc->processPropFind( inDepth, + inPropNames, + ioResources, + status ); + + if ( status == APR_SUCCESS && + aReqProc->mpDAVException == nullptr && + ioResources.empty() ) + { + m_aEnv = DAVRequestEnvironment(); + throw DAVException( DAVException::DAV_HTTP_ERROR, inPath, APR_EGENERAL ); + } + HandleError( aReqProc ); +} + + +// PROPFIND - propnames + +void SerfSession::PROPFIND( const OUString & inPath, + const Depth inDepth, + std::vector< DAVResourceInfo > & ioResInfo, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + apr_status_t status = APR_SUCCESS; + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + aReqProc->processPropFind( inDepth, + ioResInfo, + status ); + + if ( status == APR_SUCCESS && + aReqProc->mpDAVException == nullptr && + ioResInfo.empty() ) + { + m_aEnv = DAVRequestEnvironment(); + throw DAVException( DAVException::DAV_HTTP_ERROR, inPath, APR_EGENERAL ); + } + HandleError( aReqProc ); +} + + +// PROPPATCH + +void SerfSession::PROPPATCH( const OUString & inPath, + const std::vector< ProppatchValue > & inValues, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + apr_status_t status = APR_SUCCESS; + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + aReqProc->processPropPatch( inValues, + status ); + + HandleError( aReqProc ); +} + + +// HEAD + +void SerfSession::HEAD( const OUString & inPath, + const std::vector< OUString > & inHeaderNames, + DAVResource & ioResource, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + m_bIsHeadRequestInProgress = true; + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + ioResource.uri = inPath; + ioResource.properties.clear(); + apr_status_t status = APR_SUCCESS; + aReqProc->processHead( inHeaderNames, + ioResource, + status ); + + m_bIsHeadRequestInProgress = false; + + HandleError( aReqProc ); +} + + +// GET + +uno::Reference< io::XInputStream > +SerfSession::GET( const OUString & inPath, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + rtl::Reference< SerfInputStream > xInputStream( new SerfInputStream ); + apr_status_t status = APR_SUCCESS; + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + aReqProc->processGet( xInputStream, + status ); + + HandleError( aReqProc ); + + return uno::Reference< io::XInputStream >( xInputStream.get() ); +} + + +// GET + +void SerfSession::GET( const OUString & inPath, + uno::Reference< io::XOutputStream > & ioOutputStream, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + apr_status_t status = APR_SUCCESS; + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + aReqProc->processGet( ioOutputStream, + status ); + + HandleError( aReqProc ); +} + + +// GET + +uno::Reference< io::XInputStream > +SerfSession::GET( const OUString & inPath, + const std::vector< OUString > & inHeaderNames, + DAVResource & ioResource, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + rtl::Reference< SerfInputStream > xInputStream( new SerfInputStream ); + ioResource.uri = inPath; + ioResource.properties.clear(); + apr_status_t status = APR_SUCCESS; + aReqProc->processGet( xInputStream, + inHeaderNames, + ioResource, + status ); + + HandleError( aReqProc ); + + return uno::Reference< io::XInputStream >( xInputStream.get() ); +} + + +// GET + +void SerfSession::GET( const OUString & inPath, + uno::Reference< io::XOutputStream > & ioOutputStream, + const std::vector< OUString > & inHeaderNames, + DAVResource & ioResource, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + ioResource.uri = inPath; + ioResource.properties.clear(); + apr_status_t status = APR_SUCCESS; + aReqProc->processGet( ioOutputStream, + inHeaderNames, + ioResource, + status ); + + HandleError( aReqProc ); +} + + +// PUT + +void SerfSession::PUT( const OUString & inPath, + const uno::Reference< io::XInputStream > & inInputStream, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + uno::Sequence< sal_Int8 > aDataToSend; + if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) ) + throw DAVException( DAVException::DAV_INVALID_ARG ); + apr_status_t status = APR_SUCCESS; + aReqProc->processPut( reinterpret_cast< const char * >( aDataToSend.getConstArray() ), + aDataToSend.getLength(), + status ); + + HandleError( aReqProc ); +} + + +// POST + +uno::Reference< io::XInputStream > +SerfSession::POST( const OUString & inPath, + const OUString & rContentType, + const OUString & rReferer, + const uno::Reference< io::XInputStream > & inInputStream, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + uno::Sequence< sal_Int8 > aDataToSend; + if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) ) + { + throw DAVException( DAVException::DAV_INVALID_ARG ); + } + + Init( rEnv ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + rtl::Reference< SerfInputStream > xInputStream( new SerfInputStream ); + apr_status_t status = APR_SUCCESS; + aReqProc->processPost( reinterpret_cast< const char * >( aDataToSend.getConstArray() ), + aDataToSend.getLength(), + rContentType, + rReferer, + xInputStream, + status ); + + HandleError( aReqProc ); + return uno::Reference< io::XInputStream >( xInputStream.get() ); +} + + +// POST + +void SerfSession::POST( const OUString & inPath, + const OUString & rContentType, + const OUString & rReferer, + const uno::Reference< io::XInputStream > & inInputStream, + uno::Reference< io::XOutputStream > & oOutputStream, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + uno::Sequence< sal_Int8 > aDataToSend; + if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) ) + { + throw DAVException( DAVException::DAV_INVALID_ARG ); + } + + Init( rEnv ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + apr_status_t status = APR_SUCCESS; + aReqProc->processPost( reinterpret_cast< const char * >( aDataToSend.getConstArray() ), + aDataToSend.getLength(), + rContentType, + rReferer, + oOutputStream, + status ); + + HandleError( aReqProc ); +} + + +// MKCOL + +void SerfSession::MKCOL( const OUString & inPath, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + apr_status_t status = APR_SUCCESS; + aReqProc->processMkCol( status ); + + HandleError( aReqProc ); +} + + +// COPY + +void SerfSession::COPY( const OUString & inSourceURL, + const OUString & inDestinationURL, + const DAVRequestEnvironment & rEnv, + bool inOverWrite ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + SerfUri theSourceUri( inSourceURL ); + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( theSourceUri.GetPath() ) ); + apr_status_t status = APR_SUCCESS; + aReqProc->processCopy( inDestinationURL, inOverWrite, status ); + + HandleError( aReqProc ); +} + + +// MOVE + +void SerfSession::MOVE( const OUString & inSourceURL, + const OUString & inDestinationURL, + const DAVRequestEnvironment & rEnv, + bool inOverWrite ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + SerfUri theSourceUri( inSourceURL ); + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( theSourceUri.GetPath() ) ); + apr_status_t status = APR_SUCCESS; + aReqProc->processMove( inDestinationURL, inOverWrite, status ); + + HandleError( aReqProc ); +} + + +// DESTROY + +void SerfSession::DESTROY( const OUString & inPath, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + apr_status_t status = APR_SUCCESS; + aReqProc->processDelete( status ); + + HandleError( aReqProc ); +} + + +/* +namespace +{ + sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart, + int timeout ) + { + TimeValue aEnd; + osl_getSystemTime( &aEnd ); + + // Try to estimate a safe absolute time for sending the + // lock refresh request. + sal_Int32 lastChanceToSendRefreshRequest = -1; + if ( timeout != NE_TIMEOUT_INFINITE ) + { + sal_Int32 calltime = aEnd.Seconds - rStart.Seconds; + if ( calltime <= timeout ) + { + lastChanceToSendRefreshRequest + = aEnd.Seconds + timeout - calltime; + } + else + { + SAL_INFO("ucb.ucp.webdav", "No chance to refresh lock before timeout!" ); + } + } + return lastChanceToSendRefreshRequest; + } + +} // namespace +*/ + +// LOCK (set new lock) + +void SerfSession::LOCK( const OUString & inPath, + ucb::Lock & rLock, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + aReqProc->processLock( rLock ); + + HandleError( aReqProc ); +} + + +// LOCK (refresh existing lock) + +sal_Int64 SerfSession::LOCK( const OUString & /*inPath*/, + sal_Int64 nTimeout, + const DAVRequestEnvironment & /*rEnv*/ ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + return nTimeout; + /* + // Try to get the neon lock from lock store + SerfLock * theLock + = m_aSerfLockStore.findByUri( makeAbsoluteURL( inPath ) ); + if ( !theLock ) + throw DAVException( DAVException::DAV_NOT_LOCKED ); + + Init( rEnv ); + + // refresh existing lock. + theLock->timeout = static_cast< long >( nTimeout ); + + TimeValue startCall; + osl_getSystemTime( &startCall ); + + int theRetVal = ne_lock_refresh( m_pHttpSession, theLock ); + + if ( theRetVal == NE_OK ) + { + m_aSerfLockStore.updateLock( theLock, + lastChanceToSendRefreshRequest( + startCall, theLock->timeout ) ); + } + + HandleError( theRetVal, inPath, rEnv ); + + return theLock->timeout; + */ +} + + +// LOCK (refresh existing lock) + +bool SerfSession::LOCK( const OUString& rLock, + sal_Int32 *plastChanceToSendRefreshRequest ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( rLock ) ); + aReqProc->processLock( ucb::Lock(), plastChanceToSendRefreshRequest ); + + try + { + HandleError( aReqProc ); + SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock << " succeeded." ); + return true; + } + catch(...) + { + SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock << " failed!" ); + return false; + } +} + + +// UNLOCK + +void SerfSession::UNLOCK( const OUString & inPath, + const DAVRequestEnvironment & rEnv ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + Init( rEnv ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) ); + aReqProc->processUnlock(); + + try + { + HandleError( aReqProc ); + SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath << " succeeded." ); + apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->removeLock( inPath ); + } + catch(...) + { + SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath << " failed!" ); + } +} + + +// UNLOCK + +void SerfSession::UNLOCK( const OUString& rLock ) +{ + osl::Guard< osl::Mutex > theGuard( m_aMutex ); + + std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( rLock ) ); + aReqProc->processUnlock(); + + try + { + HandleError( aReqProc ); + SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << rLock << " succeeded." ); + } + catch(...) + { + SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << rLock << " failed!" ); + } +} + + +void SerfSession::abort() +{ + // 11.11.09 (tkr): The following code lines causing crashes if + // closing a ongoing connection. It turned out that this existing + // solution doesn't work in multi-threading environments. + // So I disabled them in 3.2. . Issue #73893# should fix it in OOo 3.3. + //if ( m_pHttpSession ) + // ne_close_connection( m_pHttpSession ); +} + + +ucbhelper::InternetProxyServer SerfSession::getProxySettings() const +{ + if ( m_aUri.GetScheme() == "http" || m_aUri.GetScheme() == "https" ) + { + return m_rProxyDecider.getProxy( m_aUri.GetScheme(), + m_aUri.GetHost(), + m_aUri.GetPort() ); + } + else + { + // TODO: figure out, if this case can occur + return m_rProxyDecider.getProxy( m_aUri.GetScheme(), + OUString() /* not used */, + -1 /* not used */ ); + } +} + +/* + +namespace { + +bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks, + const char * token ) +{ + for ( sal_Int32 n = 0; n < rLocks.getLength(); ++n ) + { + const uno::Sequence< OUString > & rTokens + = rLocks[ n ].LockTokens; + for ( sal_Int32 m = 0; m < rTokens.getLength(); ++m ) + { + if ( rTokens[ m ].equalsAscii( token ) ) + return true; + } + } + return false; +} + +} // namespace +*/ + + +bool SerfSession::removeExpiredLocktoken( const OUString & /*inURL*/, + const DAVRequestEnvironment & /*rEnv*/ ) +{ + return true; + /* + SerfLock * theLock = m_aSerfLockStore.findByUri( inURL ); + if ( !theLock ) + return false; + + // do a lockdiscovery to check whether this lock is still valid. + try + { + // @@@ Alternative: use ne_lock_discover() => less overhead + + std::vector< DAVResource > aResources; + std::vector< OUString > aPropNames; + aPropNames.push_back( DAVProperties::LOCKDISCOVERY ); + + PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv ); + + if ( aResources.empty() ) + return false; + + std::vector< DAVPropertyValue >::const_iterator it + = aResources[ 0 ].properties.begin(); + std::vector< DAVPropertyValue >::const_iterator end + = aResources[ 0 ].properties.end(); + + while ( it != end ) + { + if ( (*it).Name.equals( DAVProperties::LOCKDISCOVERY ) ) + { + uno::Sequence< ucb::Lock > aLocks; + if ( !( (*it).Value >>= aLocks ) ) + return false; + + if ( !containsLocktoken( aLocks, theLock->token ) ) + { + // expired! + break; + } + + // still valid. + return false; + } + ++it; + } + + // No lockdiscovery prop in propfind result / locktoken not found + // in propfind result -> not locked + SAL_INFO("ucb.ucp.webdav", "SerfSession::removeExpiredLocktoken: Removing " + " expired lock token for " << inURL << ". token: " << theLock->token ); + + m_aSerfLockStore.removeLock( theLock ); + ne_lock_destroy( theLock ); + return true; + } + catch ( DAVException const & ) + { + } + return false; + */ +} + + +// HandleError +// Common Error Handler + +void SerfSession::HandleError( std::shared_ptr<SerfRequestProcessor> rReqProc ) +{ + m_aEnv = DAVRequestEnvironment(); + + if ( rReqProc->mpDAVException ) + { + DAVException* mpDAVExp( rReqProc->mpDAVException ); + + serf_connection_reset( getSerfConnection() ); + + if ( mpDAVExp->getStatus() == 413 && + m_bNoOfTransferEncodingSwitches < 2 ) + { + m_bUseChunkedEncoding = !m_bUseChunkedEncoding; + ++m_bNoOfTransferEncodingSwitches; + } + + throw DAVException( mpDAVExp->getError(), + mpDAVExp->getData(), + mpDAVExp->getStatus() ); + } + + /* + // Map error code to DAVException. + switch ( nError ) + { + case NE_OK: + return; + + case NE_ERROR: // Generic error + { + OUString aText = OUString::createFromAscii( + ne_get_error( m_pHttpSession ) ); + + sal_uInt16 code = makeStatusCode( aText ); + + if ( code == SC_LOCKED ) + { + if ( m_aSerfLockStore.findByUri( + makeAbsoluteURL( inPath ) ) == 0 ) + { + // locked by 3rd party + throw DAVException( DAVException::DAV_LOCKED ); + } + else + { + // locked by ourself + throw DAVException( DAVException::DAV_LOCKED_SELF ); + } + } + + // Special handling for 400 and 412 status codes, which may indicate + // that a lock previously obtained by us has been released meanwhile + // by the server. Unfortunately, RFC is not clear at this point, + // thus server implementations behave different... + else if ( code == SC_BAD_REQUEST || code == SC_PRECONDITION_FAILED ) + { + if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) ) + throw DAVException( DAVException::DAV_LOCK_EXPIRED ); + } + + throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code ); + } + case NE_LOOKUP: // Name lookup failed. + throw DAVException( DAVException::DAV_HTTP_LOOKUP, + SerfUri::makeConnectionEndPointString( + m_aHostName, m_nPort ) ); + + case NE_AUTH: // User authentication failed on server + throw DAVException( DAVException::DAV_HTTP_AUTH, + SerfUri::makeConnectionEndPointString( + m_aHostName, m_nPort ) ); + + case NE_PROXYAUTH: // User authentication failed on proxy + throw DAVException( DAVException::DAV_HTTP_AUTHPROXY, + SerfUri::makeConnectionEndPointString( + m_aProxyName, m_nProxyPort ) ); + + case NE_CONNECT: // Could not connect to server + throw DAVException( DAVException::DAV_HTTP_CONNECT, + SerfUri::makeConnectionEndPointString( + m_aHostName, m_nPort ) ); + + case NE_TIMEOUT: // Connection timed out + throw DAVException( DAVException::DAV_HTTP_TIMEOUT, + SerfUri::makeConnectionEndPointString( + m_aHostName, m_nPort ) ); + + case NE_FAILED: // The precondition failed + throw DAVException( DAVException::DAV_HTTP_FAILED, + SerfUri::makeConnectionEndPointString( + m_aHostName, m_nPort ) ); + + case NE_RETRY: // Retry request (ne_end_request ONLY) + throw DAVException( DAVException::DAV_HTTP_RETRY, + SerfUri::makeConnectionEndPointString( + m_aHostName, m_nPort ) ); + + case NE_REDIRECT: + { + SerfUri aUri( ne_redirect_location( m_pHttpSession ) ); + throw DAVException( + DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() ); + } + default: + { + SAL_INFO("ucb.ucp.webdav", "SerfSession::HandleError : Unknown Serf error code!" ); + throw DAVException( DAVException::DAV_HTTP_ERROR, + OUString::createFromAscii( + ne_get_error( m_pHttpSession ) ) ); + } + } + */ +} + + +// static +bool +SerfSession::getDataFromInputStream( + const uno::Reference< io::XInputStream > & xStream, + uno::Sequence< sal_Int8 > & rData, + bool bAppendTrailingZeroByte ) +{ + if ( xStream.is() ) + { + uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY ); + if ( xSeekable.is() ) + { + try + { + sal_Int32 nSize + = sal::static_int_cast<sal_Int32>(xSeekable->getLength()); + sal_Int32 nRead + = xStream->readBytes( rData, nSize ); + + if ( nRead == nSize ) + { + if ( bAppendTrailingZeroByte ) + { + rData.realloc( nSize + 1 ); + rData[ nSize ] = sal_Int8( 0 ); + } + return true; + } + } + catch ( io::NotConnectedException const & ) + { + // readBytes + } + catch ( io::BufferSizeExceededException const & ) + { + // readBytes + } + catch ( io::IOException const & ) + { + // getLength, readBytes + } + } + else + { + try + { + uno::Sequence< sal_Int8 > aBuffer; + sal_Int32 nPos = 0; + + sal_Int32 nRead = xStream->readSomeBytes( aBuffer, 65536 ); + while ( nRead > 0 ) + { + if ( rData.getLength() < ( nPos + nRead ) ) + rData.realloc( nPos + nRead ); + + aBuffer.realloc( nRead ); + memcpy( rData.getArray() + nPos, aBuffer.getConstArray(), nRead ); + nPos += nRead; + + aBuffer.realloc( 0 ); + nRead = xStream->readSomeBytes( aBuffer, 65536 ); + } + + if ( bAppendTrailingZeroByte ) + { + rData.realloc( nPos + 1 ); + rData[ nPos ] = sal_Int8( 0 ); + } + return true; + } + catch ( io::NotConnectedException const & ) + { + // readBytes + } + catch ( io::BufferSizeExceededException const & ) + { + // readBytes + } + catch ( io::IOException const & ) + { + // readBytes + } + } + } + return false; +} + + +bool +SerfSession::isDomainMatch( const OUString & certHostName ) +{ + OUString hostName = getHostName(); + + if (hostName.equalsIgnoreAsciiCase( certHostName ) ) + return true; + + if ( certHostName.startsWith( "*" ) && + hostName.getLength() >= certHostName.getLength() ) + { + OUString cmpStr = certHostName.copy( 1 ); + + if ( hostName.matchIgnoreAsciiCase( + cmpStr, hostName.getLength() - cmpStr.getLength() ) ) + return true; + } + return false; +} + +/* + +OUString SerfSession::makeAbsoluteURL( OUString const & rURL ) const +{ + try + { + // Is URL relative or already absolute? + if ( rURL[ 0 ] != '/' ) + { + // absolute. + return OUString( rURL ); + } + else + { + ne_uri aUri; + memset( &aUri, 0, sizeof( aUri ) ); + + ne_fill_server_uri( m_pHttpSession, &aUri ); + aUri.path + = ne_strdup( OUStringToOString( + rURL, RTL_TEXTENCODING_UTF8 ).getStr() ); + SerfUri aSerfUri( &aUri ); + ne_uri_free( &aUri ); + return aSerfUri.GetURI(); + } + } + catch ( DAVException const & ) + { + } + // error. + return OUString(); +} +*/ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |