summaryrefslogtreecommitdiffstats
path: root/ucb/source/ucp/webdav-neon/NeonSession.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'ucb/source/ucp/webdav-neon/NeonSession.cxx')
-rw-r--r--ucb/source/ucp/webdav-neon/NeonSession.cxx2361
1 files changed, 2361 insertions, 0 deletions
diff --git a/ucb/source/ucp/webdav-neon/NeonSession.cxx b/ucb/source/ucp/webdav-neon/NeonSession.cxx
new file mode 100644
index 000000000..7fac08345
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonSession.cxx
@@ -0,0 +1,2361 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+#include <unordered_map>
+#include <vector>
+#include <string.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/time.h>
+#include <ne_socket.h>
+#include <ne_auth.h>
+#include <ne_redirect.h>
+#include <ne_ssl.h>
+
+// old neon versions forgot to set this
+extern "C" {
+#include <ne_compress.h>
+}
+
+#include <libxml/parser.h>
+#include <comphelper/sequence.hxx>
+#include <ucbhelper/simplecertificatevalidationrequest.hxx>
+
+#include "DAVAuthListener.hxx"
+#include "NeonTypes.hxx"
+#include "NeonSession.hxx"
+#include "NeonInputStream.hxx"
+#include "NeonPropFindRequest.hxx"
+#include "NeonHeadRequest.hxx"
+#include "NeonUri.hxx"
+#include "LinkSequence.hxx"
+#include "UCBDeadPropertyValue.hxx"
+
+#include <officecfg/Inet.hxx>
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/NotConnectedException.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/ucb/Lock.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/xml/crypto/SEInitializer.hpp>
+
+
+using namespace com::sun::star;
+using namespace webdav_ucp;
+
+#ifndef EOL
+# define EOL "\r\n"
+#endif
+
+namespace {
+
+struct RequestData
+{
+ // POST
+ OUString aContentType;
+ OUString aReferer;
+
+ RequestData() {}
+ RequestData( const OUString & rContentType,
+ const OUString & rReferer )
+ : aContentType( rContentType ), aReferer( rReferer ) {}
+};
+
+struct equalPtr
+{
+ bool operator()( const ne_request* p1, const ne_request* p2 ) const
+ {
+ return p1 == p2;
+ }
+};
+
+struct hashPtr
+{
+ size_t operator()( const ne_request* p ) const
+ {
+ return reinterpret_cast<size_t>(p);
+ }
+};
+
+}
+
+typedef std::unordered_map
+<
+ ne_request*,
+ RequestData,
+ hashPtr,
+ equalPtr
+>
+RequestDataMap;
+
+static sal_uInt16 makeStatusCode( const OUString & rStatusText )
+{
+ // Extract status code from session error string. Unfortunately
+ // neon provides no direct access to the status code...
+
+ if ( rStatusText.getLength() < 3 )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "makeStatusCode - status text string to short!" );
+ return 0;
+ }
+
+ sal_Int32 nPos = rStatusText.indexOf( ' ' );
+ if ( nPos == -1 )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "makeStatusCode - wrong status text format!" );
+ return 0;
+ }
+
+ return sal_uInt16( rStatusText.copy( 0, nPos ).toInt32() );
+}
+
+static bool noKeepAlive( const uno::Sequence< beans::NamedValue >& rFlags )
+{
+ if ( !rFlags.hasElements() )
+ return false;
+
+ // find "KeepAlive" property
+ const beans::NamedValue* pValue(
+ std::find_if(rFlags.begin(), rFlags.end(),
+ [] (beans::NamedValue const& rNV) { return rNV.Name == "KeepAlive"; } ));
+ return pValue != rFlags.end() && !pValue->Value.get<bool>();
+}
+
+namespace {
+
+struct NeonRequestContext
+{
+ uno::Reference< io::XOutputStream > xOutputStream;
+ rtl::Reference< NeonInputStream > xInputStream;
+ const std::vector< OUString > * pHeaderNames;
+ DAVResource * pResource;
+
+ explicit NeonRequestContext( uno::Reference< io::XOutputStream > const & xOutStrm )
+ : xOutputStream( xOutStrm ),
+ pHeaderNames( nullptr ), pResource( nullptr ) {}
+
+ explicit NeonRequestContext( const rtl::Reference< NeonInputStream > & xInStrm )
+ : xInputStream( xInStrm ),
+ pHeaderNames( nullptr ), pResource( nullptr ) {}
+
+ NeonRequestContext( uno::Reference< io::XOutputStream > const & xOutStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource )
+ : xOutputStream( xOutStrm ),
+ pHeaderNames( &inHeaderNames ), pResource( &ioResource ) {}
+
+ NeonRequestContext( const rtl::Reference< NeonInputStream > & xInStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource )
+ : xInputStream( xInStrm ),
+ pHeaderNames( &inHeaderNames ), pResource( &ioResource ) {}
+
+ void ResponseBlockReader(const char * inBuf, size_t inLen)
+ {
+ if (xInputStream.is())
+ xInputStream->AddToStream( inBuf, inLen );
+ }
+
+ void ResponseBlockWriter(const char * inBuf, size_t inLen)
+ {
+ if (xOutputStream.is())
+ {
+ const uno::Sequence< sal_Int8 > aSeq( reinterpret_cast<sal_Int8 const *>(inBuf), inLen );
+ xOutputStream->writeBytes( aSeq );
+ }
+ }
+
+};
+
+}
+
+// A simple Neon response_block_reader for use with an XInputStream
+extern "C" {
+
+static int NeonSession_ResponseBlockReader(void * inUserData,
+ const char * inBuf,
+ size_t inLen )
+{
+ // neon sometimes calls this function with (inLen == 0)...
+ if ( inLen > 0 )
+ {
+ NeonRequestContext * pCtx = static_cast<NeonRequestContext*>(inUserData);
+ pCtx->ResponseBlockReader(inBuf, inLen);
+ }
+ return 0;
+}
+
+// A simple Neon response_block_reader for use with an XOutputStream
+static int NeonSession_ResponseBlockWriter( void * inUserData,
+ const char * inBuf,
+ size_t inLen )
+{
+ // neon calls this function with (inLen == 0)...
+ if ( inLen > 0 )
+ {
+ NeonRequestContext * pCtx = static_cast<NeonRequestContext*>(inUserData);
+ pCtx->ResponseBlockWriter(inBuf, inLen);
+ }
+ return 0;
+}
+
+static int NeonSession_NeonAuth( void * inUserData,
+#if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
+ const char * inAuthProtocol,
+#endif
+ const char * inRealm,
+ int attempt,
+ char * inoutUserName,
+ char * inoutPassWord )
+{
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be copied into the buffers
+ * which are both of size NE_ABUFSIZ. The 'attempt' parameter is zero
+ * on the first call to the callback, and increases by one each time
+ * an attempt to authenticate fails.
+ *
+ * The callback must return zero to indicate that authentication
+ * should be attempted with the username/password, or non-zero to
+ * cancel the request. (if non-zero, username and password are
+ * ignored.) */
+
+ NeonSession * theSession = static_cast<NeonSession*>(inUserData);
+ const char * pAuthProtocol;
+#if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
+ pAuthProtocol = inAuthProtocol;
+#else
+ pAuthProtocol = nullptr;
+#endif
+ return theSession->NeonAuth(pAuthProtocol, inRealm, attempt, inoutUserName, inoutPassWord);
+}
+
+}
+
+int NeonSession::NeonAuth(const char* inAuthProtocol, const char* inRealm,
+ int attempt, char* inoutUserName, char * inoutPassWord)
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be copied into the buffers
+ * which are both of size NE_ABUFSIZ. The 'attempt' parameter is zero
+ * on the first call to the callback, and increases by one each time
+ * an attempt to authenticate fails.
+ *
+ * The callback must return zero to indicate that authentication
+ * should be attempted with the username/password, or non-zero to
+ * cancel the request. (if non-zero, username and password are
+ * ignored.) */
+
+ DAVAuthListener * pListener
+ = getRequestEnvironment().m_xAuthListener.get();
+ if ( !pListener )
+ {
+ // abort
+ return -1;
+ }
+ OUString theUserName;
+ OUString thePassWord;
+
+ if ( attempt == 0 )
+ {
+ // neon does not handle username supplied with request URI (for
+ // instance when doing FTP over proxy - last checked: 0.23.5 )
+
+ try
+ {
+ NeonUri uri( getRequestEnvironment().m_aRequestURI );
+ const OUString& aUserInfo( uri.GetUserInfo() );
+ if ( !aUserInfo.isEmpty() )
+ {
+ 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 -1;
+ }
+ }
+ else
+ {
+ // username buffer is prefilled with user name from last attempt.
+ theUserName = OUString::createFromAscii( inoutUserName );
+ // @@@ Neon does not initialize password buffer (last checked: 0.22.0).
+ //thePassWord = OUString::createFromAscii( inoutPassWord );
+ }
+
+#if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
+ const bool bCanUseSystemCreds
+ = (attempt == 0) && // avoid endless loops
+ ne_has_support( NE_FEATURE_SSPI ) && // Windows-only feature.
+ ( ( ne_strcasecmp( inAuthProtocol, "NTLM" ) == 0 ) ||
+ ( ne_strcasecmp( inAuthProtocol, "Negotiate" ) == 0 ) );
+#else
+ (void)inAuthProtocol;
+ const bool bCanUseSystemCreds = false;
+#endif
+
+ int theRetVal = pListener->authenticate(
+ OUString::createFromAscii( inRealm ),
+ getHostName(),
+ theUserName,
+ thePassWord,
+ bCanUseSystemCreds);
+
+ OString aUser( OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ) );
+ if ( aUser.getLength() > ( NE_ABUFSIZ - 1 ) )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "NeonSession_NeonAuth - username too long!" );
+ return -1;
+ }
+
+ OString aPass( OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ) );
+ if ( aPass.getLength() > ( NE_ABUFSIZ - 1 ) )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "NeonSession_NeonAuth - password too long!" );
+ return -1;
+ }
+
+ // #100211# - checked
+ strcpy( inoutUserName, aUser.getStr() );
+ strcpy( inoutPassWord, aPass.getStr() );
+
+ return theRetVal;
+}
+
+namespace {
+ OUString GetHostnamePart( const OUString& _rRawString )
+ {
+ OUString sPart;
+ OUString sPartId("CN=");
+ sal_Int32 nContStart = _rRawString.indexOf( sPartId );
+ if ( nContStart != -1 )
+ {
+ nContStart += sPartId.getLength();
+ sal_Int32 nContEnd = _rRawString.indexOf( ',', nContStart );
+ sPart = nContEnd != -1 ?
+ _rRawString.copy(nContStart, nContEnd - nContStart) :
+ _rRawString.copy(nContStart);
+ }
+ return sPart;
+ }
+} // namespace
+
+extern "C" {
+
+static int NeonSession_CertificationNotify( void *userdata,
+ int,
+ const ne_ssl_certificate *cert )
+{
+ NeonSession * pSession = static_cast< NeonSession * >( userdata );
+ return pSession->CertificationNotify(cert);
+}
+
+}
+
+int NeonSession::CertificationNotify(const ne_ssl_certificate *cert)
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ OSL_ASSERT( cert );
+
+ uno::Reference< security::XCertificateContainer > xCertificateContainer;
+ try
+ {
+ xCertificateContainer = security::CertificateContainer::create( getComponentContext() );
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+
+ if ( !xCertificateContainer.is() )
+ return 1;
+
+ char * dn = ne_ssl_readable_dname( ne_ssl_cert_subject( cert ) );
+ OUString cert_subject( dn, strlen( dn ), RTL_TEXTENCODING_UTF8, 0 );
+
+ ne_free( dn );
+
+ security::CertificateContainerStatus certificateContainer(
+ xCertificateContainer->hasCertificate(
+ getHostName(), cert_subject ) );
+
+ if ( certificateContainer != security::CertificateContainerStatus_NOCERT )
+ return
+ certificateContainer == security::CertificateContainerStatus_TRUSTED
+ ? 0
+ : 1;
+
+ uno::Reference< xml::crypto::XSEInitializer > xSEInitializer;
+ try
+ {
+ xSEInitializer = xml::crypto::SEInitializer::create( getComponentContext() );
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+
+ if ( !xSEInitializer.is() )
+ return 1;
+
+ uno::Reference< xml::crypto::XXMLSecurityContext > xSecurityContext(
+ xSEInitializer->createSecurityContext( OUString() ) );
+
+ uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnv(
+ xSecurityContext->getSecurityEnvironment() );
+
+ //The end entity certificate
+ char * eeCertB64 = ne_ssl_cert_export( cert );
+
+ OString sEECertB64( eeCertB64 );
+
+ uno::Reference< security::XCertificate > xEECert(
+ xSecurityEnv->createCertificateFromAscii(
+ OStringToOUString( sEECertB64, RTL_TEXTENCODING_ASCII_US ) ) );
+
+ ne_free( eeCertB64 );
+ eeCertB64 = nullptr;
+
+ std::vector< uno::Reference< security::XCertificate > > vecCerts;
+ const ne_ssl_certificate * issuerCert = cert;
+ do
+ {
+ //get the intermediate certificate
+ //the returned value is const ! Therefore it does not need to be freed
+ //with ne_ssl_cert_free, which takes a non-const argument
+ issuerCert = ne_ssl_cert_signedby( issuerCert );
+ if ( nullptr == issuerCert )
+ break;
+
+ char * imCertB64 = ne_ssl_cert_export( issuerCert );
+ OString sInterMediateCertB64( imCertB64 );
+ ne_free( imCertB64 );
+
+ uno::Reference< security::XCertificate> xImCert(
+ xSecurityEnv->createCertificateFromAscii(
+ OStringToOUString( sInterMediateCertB64, RTL_TEXTENCODING_ASCII_US ) ) );
+ if ( xImCert.is() )
+ vecCerts.push_back( xImCert );
+ }
+ while ( true );
+
+ sal_Int64 certValidity = xSecurityEnv->verifyCertificate( xEECert,
+ ::comphelper::containerToSequence( vecCerts ) );
+
+ if ( isDomainMatch(
+ GetHostnamePart( xEECert->getSubjectName() ) ) )
+ {
+ // if host name matched with certificate then look if the
+ // certificate was ok
+ if( certValidity == security::CertificateValidity::VALID )
+ return 0;
+ }
+
+ 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>(certValidity), xEECert, 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(), cert_subject, true );
+ return 0;
+ }
+ else
+ {
+ // Don't trust cert
+ xCertificateContainer->addCertificate(
+ getHostName(), cert_subject, false );
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ // Don't trust cert
+ xCertificateContainer->addCertificate(
+ getHostName(), cert_subject, false );
+ return 1;
+ }
+ }
+ return 1;
+}
+
+extern "C" {
+
+static void NeonSession_PreSendRequest( ne_request * req,
+ void * userdata,
+ ne_buffer * headers )
+{
+ // userdata -> value returned by 'create'
+ NeonSession * pSession = static_cast< NeonSession * >( userdata );
+ if (!pSession)
+ return;
+ pSession->PreSendRequest(req, headers);
+}
+
+}
+
+void NeonSession::PreSendRequest(ne_request* req, ne_buffer* headers)
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ // If there is a proxy server in between, it shall never use
+ // cached data. We always want 'up-to-date' data.
+ ne_buffer_concat( headers, "Pragma: no-cache", EOL, nullptr );
+ // alternative, but understood by HTTP 1.1 servers only:
+ // ne_buffer_concat( headers, "Cache-Control: max-age=0", EOL, NULL );
+
+ const RequestDataMap * pRequestData
+ = static_cast< const RequestDataMap* >(
+ getRequestData() );
+
+ RequestDataMap::const_iterator it = pRequestData->find( req );
+ if ( it != pRequestData->end() )
+ {
+ if ( !(*it).second.aContentType.isEmpty() )
+ {
+ char * pData = headers->data;
+ if ( strstr( pData, "Content-Type:" ) == nullptr )
+ {
+ OString aType
+ = OUStringToOString( (*it).second.aContentType,
+ RTL_TEXTENCODING_UTF8 );
+ ne_buffer_concat( headers, "Content-Type: ",
+ aType.getStr(), EOL, nullptr );
+ }
+ }
+
+ if ( !(*it).second.aReferer.isEmpty() )
+ {
+ char * pData = headers->data;
+ if ( strstr( pData, "Referer:" ) == nullptr )
+ {
+ OString aReferer
+ = OUStringToOString( (*it).second.aReferer,
+ RTL_TEXTENCODING_UTF8 );
+ ne_buffer_concat( headers, "Referer: ",
+ aReferer.getStr(), EOL, nullptr );
+ }
+ }
+ }
+
+ const DAVRequestHeaders & rHeaders
+ = getRequestEnvironment().m_aRequestHeaders;
+
+ for ( const auto& rHeader : rHeaders )
+ {
+ OString aHeader
+ = OUStringToOString( rHeader.first,
+ RTL_TEXTENCODING_UTF8 );
+ OString aValue
+ = OUStringToOString( rHeader.second,
+ RTL_TEXTENCODING_UTF8 );
+ ne_buffer_concat( headers, aHeader.getStr(), ": ",
+ aValue.getStr(), EOL, nullptr );
+ }
+}
+
+//See https://bugzilla.redhat.com/show_bug.cgi?id=544619#c4
+//neon is threadsafe, but uses gnutls which is only thread-safe
+//if initialized to be thread-safe. cups, unfortunately, generally
+//initializes it first, and as non-thread-safe, leaving the entire
+//stack unsafe
+namespace webdav_ucp
+{
+ osl::Mutex& getGlobalNeonMutex()
+ {
+ static osl::Mutex aMutex;
+ return aMutex;
+ }
+}
+
+// static members
+bool NeonSession::m_bGlobalsInited = false;
+NeonLockStore NeonSession::m_aNeonLockStore;
+
+NeonSession::NeonSession( const rtl::Reference< DAVSessionFactory > & rSessionFactory,
+ const OUString& inUri,
+ const uno::Sequence< beans::NamedValue >& rFlags,
+ const ucbhelper::InternetProxyDecider & rProxyDecider )
+ : DAVSession( rSessionFactory )
+ , m_nProxyPort( 0 )
+ , m_aFlags( rFlags )
+ , m_pHttpSession( nullptr )
+ , m_pRequestData( new RequestDataMap )
+ , m_rProxyDecider( rProxyDecider )
+{
+ NeonUri theUri( inUri );
+ m_aScheme = theUri.GetScheme();
+ m_aHostName = theUri.GetHost();
+ m_nPort = theUri.GetPort();
+ SAL_INFO( "ucb.ucp.webdav", "NeonSession ctor - URL <" << inUri << ">" );
+}
+
+NeonSession::~NeonSession( )
+{
+ if ( m_pHttpSession )
+ {
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ne_session_destroy( m_pHttpSession );
+ }
+ m_pHttpSession = nullptr;
+ }
+ delete static_cast< RequestDataMap * >( m_pRequestData );
+}
+
+void NeonSession::Init( const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ m_aEnv = rEnv;
+ Init();
+}
+
+void NeonSession::Init()
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ bool bCreateNewSession = m_bNeedNewSession;
+ m_bNeedNewSession = false;
+
+ if ( m_pHttpSession == nullptr )
+ {
+ // Ensure that Neon sockets are initialized
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ if (!m_bGlobalsInited )
+ {
+ if ( ne_sock_init() != 0 )
+ throw DAVException( DAVException::DAV_SESSION_CREATE,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ // #122205# - libxml2 needs to be initialized once if used by
+ // multithreaded programs like OOo.
+ xmlInitParser();
+#if OSL_DEBUG_LEVEL > 0
+ // for more debug flags see ne_utils.h; NE_DEBUGGING must be defined
+ // while compiling neon in order to actually activate neon debug
+ // output.
+ ne_debug_init( stderr, NE_DBG_FLUSH
+ | NE_DBG_HTTP
+ // | NE_DBG_HTTPBODY
+ // | NE_DBG_HTTPAUTH
+ // | NE_DBG_XML
+ // | NE_DBG_XMLPARSE
+ | NE_DBG_LOCKS
+ | NE_DBG_SSL
+ );
+#endif
+ m_bGlobalsInited = true;
+ }
+
+ const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
+
+ m_aProxyName = rProxyCfg.aName;
+ m_nProxyPort = rProxyCfg.nPort;
+
+ // Not yet initialized. Create new session.
+ bCreateNewSession = true;
+ }
+ else
+ {
+ // #112271# Check whether proxy settings are still valid (They may
+ // change at any time). If not, create new Neon session.
+
+ const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
+
+ if ( ( rProxyCfg.aName != m_aProxyName )
+ || ( rProxyCfg.nPort != m_nProxyPort ) )
+ {
+ m_aProxyName = rProxyCfg.aName;
+ m_nProxyPort = rProxyCfg.nPort;
+
+ bCreateNewSession = true;
+ }
+
+ if (bCreateNewSession)
+ {
+ // new session needed, destroy old first
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ne_session_destroy( m_pHttpSession );
+ }
+ m_pHttpSession = nullptr;
+ }
+ }
+
+ if ( !bCreateNewSession )
+ return;
+
+ const sal_Int32 nConnectTimeoutMax = 180;
+ const sal_Int32 nConnectTimeoutMin = 2;
+ const sal_Int32 nReadTimeoutMax = 180;
+ const sal_Int32 nReadTimeoutMin = 20;
+
+ // @@@ For FTP over HTTP proxy inUserInfo is needed to be able to
+ // build the complete request URI (including user:pass), but
+ // currently (0.22.0) neon does not allow to pass the user info
+ // to the session
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ m_pHttpSession = ne_session_create(
+ OUStringToOString( m_aScheme, RTL_TEXTENCODING_UTF8 ).getStr(),
+ /* theUri.GetUserInfo(),
+ @@@ for FTP via HTTP proxy, but not supported by Neon */
+ OUStringToOString( m_aHostName, RTL_TEXTENCODING_UTF8 ).getStr(),
+ m_nPort );
+ }
+
+ if ( m_pHttpSession == nullptr )
+ throw DAVException( DAVException::DAV_SESSION_CREATE,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ // Register the session with the lock store
+ m_aNeonLockStore.registerSession( m_pHttpSession );
+
+ if ( m_aScheme.equalsIgnoreAsciiCase("https") )
+ {
+ // Set a failure callback for certificate check
+ ne_ssl_set_verify(
+ m_pHttpSession, NeonSession_CertificationNotify, this);
+
+ // Tell Neon to tell the SSL library used (OpenSSL or
+ // GnuTLS, I guess) to use a default set of root
+ // certificates.
+ ne_ssl_trust_default_ca(m_pHttpSession);
+ }
+
+ // Add hooks (i.e. for adding additional headers to the request)
+ ne_hook_pre_send( m_pHttpSession, NeonSession_PreSendRequest, this );
+
+ if ( !m_aProxyName.isEmpty() )
+ {
+ ne_session_proxy( m_pHttpSession,
+ OUStringToOString(
+ m_aProxyName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ m_nProxyPort );
+ }
+
+ // avoid KeepAlive?
+ if ( noKeepAlive(m_aFlags) )
+ ne_set_session_flag( m_pHttpSession, NE_SESSFLAG_PERSIST, 0 );
+
+ // Register for redirects.
+ ne_redirect_register( m_pHttpSession );
+
+ // authentication callbacks.
+ ne_add_server_auth( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
+ ne_add_proxy_auth ( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
+
+ // set timeout to connect
+ // if connect_timeout is not set, neon returns NE_CONNECT when the TCP socket default
+ // timeout elapses
+ // with connect_timeout set neon returns NE_TIMEOUT if elapsed when the connection
+ // didn't succeed
+ // grab it from configuration
+ uno::Reference< uno::XComponentContext > rContext = m_xFactory->getComponentContext();
+
+ // set the timeout (in seconds) used when making a connection
+ sal_Int32 nConnectTimeout = officecfg::Inet::Settings::ConnectTimeout::get( rContext );
+ ne_set_connect_timeout( m_pHttpSession,
+ std::max( nConnectTimeoutMin,
+ std::min( nConnectTimeout, nConnectTimeoutMax ) ) );
+
+ // provides a read time out facility as well
+ // set the timeout (in seconds) used when reading from a socket.
+ sal_Int32 nReadTimeout = officecfg::Inet::Settings::ReadTimeout::get( rContext );
+ ne_set_read_timeout( m_pHttpSession,
+ std::max( nReadTimeoutMin,
+ std::min( nReadTimeout, nReadTimeoutMax ) ) );
+
+}
+
+bool NeonSession::CanUse( const OUString & inUri,
+ const uno::Sequence< beans::NamedValue >& rFlags )
+{
+ try
+ {
+ NeonUri theUri( inUri );
+ if ( ( theUri.GetPort() == m_nPort ) &&
+ ( theUri.GetHost() == m_aHostName ) &&
+ ( theUri.GetScheme() == m_aScheme ) &&
+ ( rFlags == m_aFlags ) )
+ return true;
+ }
+ catch ( DAVException const & )
+ {
+ return false;
+ }
+ return false;
+}
+
+bool NeonSession::UsesProxy()
+{
+ Init();
+ return !m_aProxyName.isEmpty() ;
+}
+
+void NeonSession::OPTIONS( const OUString & inPath,
+ DAVOptions & rOptions, // contains the name+values of every header
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ SAL_INFO( "ucb.ucp.webdav", "OPTIONS - relative URL <" << inPath << ">" );
+
+ rOptions.init();
+
+ Init( rEnv );
+ int theRetVal;
+
+ ne_request *req = ne_request_create(m_pHttpSession, "OPTIONS", OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr());
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ theRetVal = ne_request_dispatch(req);
+ }
+
+ //check if http error is in the 200 class (no error)
+ if (theRetVal == NE_OK && ne_get_status(req)->klass != 2) {
+ theRetVal = NE_ERROR;
+ }
+
+ if ( theRetVal == NE_OK )
+ {
+ void *cursor = nullptr;
+ const char *name, *value;
+ while ( ( cursor = ne_response_header_iterate(
+ req, cursor, &name, &value ) ) != nullptr )
+ {
+ OUString aHeaderName(OUString(name, strlen(name), RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase());
+ OUString aHeaderValue(value, strlen(value), RTL_TEXTENCODING_ASCII_US);
+
+ // display the single header
+ SAL_INFO( "ucb.ucp.webdav", "OPTIONS - received header: " << aHeaderName << ":" << aHeaderValue );
+
+ if ( aHeaderName == "allow" )
+ {
+ rOptions.setAllowedMethods( aHeaderValue );
+ }
+ else if ( aHeaderName == "dav" )
+ {
+ // check type of dav capability
+ // need to parse the value, token separator: ","
+ // see <http://tools.ietf.org/html/rfc4918#section-10.1>,
+ // <http://tools.ietf.org/html/rfc4918#section-18>,
+ // and <http://tools.ietf.org/html/rfc7230#section-3.2>
+ // we detect the class (1, 2 and 3), other elements (token, URL)
+ // are not used for now
+ // silly parser written using OUString, not very efficient
+ // but quick and easy to write...
+ sal_Int32 nFromIndex = 0;
+ sal_Int32 nNextIndex = 0;
+ while( ( nNextIndex = aHeaderValue.indexOf( ",", nFromIndex ) ) != -1 )
+ { // found a comma
+ // try to convert from nFromIndex to nNextIndex -1 in a number
+ // if this is 1 or 2 or 3, use for class setting
+ sal_Int32 nClass =
+ aHeaderValue.copy( nFromIndex, nNextIndex - nFromIndex ).toInt32();
+ switch( nClass )
+ {
+ case 1:
+ rOptions.setClass1();
+ break;
+ case 2:
+ rOptions.setClass2();
+ break;
+ case 3:
+ rOptions.setClass3();
+ break;
+ default:
+ ;
+ }
+ // next starting point
+ nFromIndex = nNextIndex + 1;
+ }
+ // check for last fragment
+ if ( nFromIndex < aHeaderValue.getLength() )
+ {
+ sal_Int32 nClass = aHeaderValue.copy( nFromIndex ).toInt32();
+ switch( nClass )
+ {
+ case 1:
+ rOptions.setClass1();
+ break;
+ case 2:
+ rOptions.setClass2();
+ break;
+ case 3:
+ rOptions.setClass3();
+ break;
+ default:
+ ;
+ }
+ }
+ }
+ }
+ // if applicable, check for lock state:
+ if( rOptions.isClass2() || rOptions.isClass3() )
+ {
+ //dav with lock possible, check for locked state
+ if ( m_aNeonLockStore.findByUri(
+ makeAbsoluteURL( inPath ) ) != nullptr )
+ {
+ // we own a lock for this URL,
+ // set locked state
+ rOptions.setLocked();
+ }
+ }
+ }
+
+ ne_request_destroy(req);
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::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 );
+
+#if defined SAL_LOG_INFO
+ { //debug
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - relative URL: <" << inPath << "> Depth: " << inDepth );
+ for(const auto& rPropName : inPropNames)
+ {
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - property requested: " << rPropName );
+ }
+ } //debug
+#endif
+
+ Init( rEnv );
+
+ int theRetVal = NE_OK;
+ NeonPropFindRequest theRequest( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ inDepth,
+ inPropNames,
+ ioResources,
+ theRetVal );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - relative URL: <" << inPath << "> Depth: " << inDepth );
+
+ Init( rEnv );
+
+ int theRetVal = NE_OK;
+ NeonPropFindRequest theRequest( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ inDepth,
+ ioResInfo,
+ theRetVal );
+
+#if defined SAL_LOG_INFO
+ { //debug
+ for ( const auto& rResInfo : ioResInfo )
+ {
+ for ( const auto& rProp : rResInfo.properties )
+ {
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - returned property (name only): " << rProp );
+ }
+ }
+ } //debug
+#endif
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::PROPPATCH( const OUString & inPath,
+ const std::vector< ProppatchValue > & inValues,
+ const DAVRequestEnvironment & rEnv )
+{
+ SAL_INFO( "ucb.ucp.webdav", "PROPPATCH - relative URL <" << inPath << ">" );
+
+ /* @@@ Which standard live properties can be set by the client?
+ This is a known WebDAV RFC issue ( verified: 04/10/2001 )
+ --> http://www.ics.uci.edu/pub/ietf/webdav/protocol/issues.html
+
+ mod_dav implementation:
+
+ creationdate r ( File System prop )
+ displayname w
+ getcontentlanguage r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
+ getcontentlength r ( File System prop )
+ getcontenttype r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
+ getetag r ( File System prop )
+ getlastmodified r ( File System prop )
+ lockdiscovery r
+ resourcetype r
+ source w
+ supportedlock r
+ executable w ( #ifndef WIN32 )
+
+ All dead properties are of course writable.
+ */
+
+ int theRetVal = NE_OK;
+
+ int n; // for the "for" loop
+
+ // Generate the list of properties we want to set.
+ int nPropCount = inValues.size();
+ std::unique_ptr<ne_proppatch_operation[]> pItems(
+ new ne_proppatch_operation[ nPropCount + 1 ]);
+ for ( n = 0; n < nPropCount; ++n )
+ {
+ const ProppatchValue & rValue = inValues[ n ];
+
+ // Split fullname into namespace and name!
+ ne_propname * pName = new ne_propname;
+ DAVProperties::createNeonPropName( rValue.name, *pName );
+ pItems[ n ].name = pName;
+
+ if ( rValue.operation == PROPSET )
+ {
+ pItems[ n ].type = ne_propset;
+
+ OUString aStringValue;
+ if ( DAVProperties::isUCBDeadProperty( *pName ) )
+ {
+ // DAV dead property added by WebDAV UCP?
+ if ( !UCBDeadPropertyValue::toXML( rValue.value,
+ aStringValue ) )
+ {
+ // Error!
+ pItems[ n ].value = nullptr;
+ theRetVal = NE_ERROR;
+ nPropCount = n + 1;
+ break;
+ }
+ }
+ else if ( !( rValue.value >>= aStringValue ) )
+ {
+ // complex properties...
+ if ( rValue.name == DAVProperties::SOURCE )
+ {
+ uno::Sequence< ucb::Link > aLinks;
+ if ( rValue.value >>= aLinks )
+ {
+ LinkSequence::toXML( aLinks, aStringValue );
+ }
+ else
+ {
+ // Error!
+ pItems[ n ].value = nullptr;
+ theRetVal = NE_ERROR;
+ nPropCount = n + 1;
+ break;
+ }
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "PROPPATCH - Unsupported type!" );
+ // Error!
+ pItems[ n ].value = nullptr;
+ theRetVal = NE_ERROR;
+ nPropCount = n + 1;
+ break;
+ }
+ }
+ pItems[ n ].value
+ = strdup( OUStringToOString( aStringValue,
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ else
+ {
+ pItems[ n ].type = ne_propremove;
+ pItems[ n ].value = nullptr;
+ }
+ }
+
+ if ( theRetVal == NE_OK )
+ {
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ pItems[ n ].name = nullptr;
+
+ theRetVal = ne_proppatch( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ pItems.get() );
+ }
+
+ for ( n = 0; n < nPropCount; ++n )
+ {
+ free( const_cast<char *>(pItems[ n ].name->name) );
+ delete pItems[ n ].name;
+ free( const_cast<char *>(pItems[ n ].value) );
+ }
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::HEAD( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "HEAD - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ int theRetVal = NE_OK;
+ NeonHeadRequest theRequest( m_pHttpSession,
+ inPath,
+ inHeaderNames,
+ ioResource,
+ theRetVal );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+uno::Reference< io::XInputStream >
+NeonSession::GET( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
+ NeonRequestContext aCtx( xInputStream );
+ int theRetVal = GET( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ NeonSession_ResponseBlockReader,
+ false,
+ &aCtx );
+
+ HandleError( theRetVal, inPath, rEnv );
+
+ return uno::Reference< io::XInputStream >( xInputStream.get() );
+}
+
+void NeonSession::GET( const OUString & inPath,
+ uno::Reference< io::XOutputStream > & ioOutputStream,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ NeonRequestContext aCtx( ioOutputStream );
+ int theRetVal = GET( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ NeonSession_ResponseBlockWriter,
+ false,
+ &aCtx );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+uno::Reference< io::XInputStream >
+NeonSession::GET( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ ioResource.uri = inPath;
+ ioResource.properties.clear();
+
+ rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
+ NeonRequestContext aCtx( xInputStream, inHeaderNames, ioResource );
+ int theRetVal = GET( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ NeonSession_ResponseBlockReader,
+ true,
+ &aCtx );
+
+ HandleError( theRetVal, inPath, rEnv );
+
+ return uno::Reference< io::XInputStream >( xInputStream.get() );
+}
+
+void NeonSession::GET0( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ ioResource.uri = inPath;
+ ioResource.properties.clear();
+
+ rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
+ NeonRequestContext aCtx( xInputStream, inHeaderNames, ioResource );
+ int theRetVal = GET0( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ true,
+ &aCtx );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::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 );
+ SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ ioResource.uri = inPath;
+ ioResource.properties.clear();
+
+ NeonRequestContext aCtx( ioOutputStream, inHeaderNames, ioResource );
+ int theRetVal = GET( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ NeonSession_ResponseBlockWriter,
+ true,
+ &aCtx );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::PUT( const OUString & inPath,
+ const uno::Reference< io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "PUT - relative URL <" << inPath << ">" );
+
+ uno::Sequence< sal_Int8 > aDataToSend;
+ if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ Init( rEnv );
+
+ int theRetVal = PUT( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ reinterpret_cast< const char * >(
+ aDataToSend.getConstArray() ),
+ aDataToSend.getLength() );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+uno::Reference< io::XInputStream >
+NeonSession::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 );
+ SAL_INFO( "ucb.ucp.webdav", "POST - relative URL <" << inPath << ">" );
+
+ uno::Sequence< sal_Int8 > aDataToSend;
+ if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ Init( rEnv );
+
+ rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
+ NeonRequestContext aCtx( xInputStream );
+ int theRetVal = POST( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ reinterpret_cast< const char * >(
+ aDataToSend.getConstArray() ),
+ NeonSession_ResponseBlockReader,
+ &aCtx,
+ rContentType,
+ rReferer );
+
+ HandleError( theRetVal, inPath, rEnv );
+
+ return uno::Reference< io::XInputStream >( xInputStream.get() );
+}
+
+void NeonSession::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 );
+ SAL_INFO( "ucb.ucp.webdav", "POST - relative URL <" << inPath << ">" );
+
+ uno::Sequence< sal_Int8 > aDataToSend;
+ if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ Init( rEnv );
+
+ NeonRequestContext aCtx( oOutputStream );
+ int theRetVal = POST( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ reinterpret_cast< const char * >(
+ aDataToSend.getConstArray() ),
+ NeonSession_ResponseBlockWriter,
+ &aCtx,
+ rContentType,
+ rReferer );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::MKCOL( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "MKCOL - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ int theRetVal = ne_mkcol( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::COPY( const OUString & inSourceURL,
+ const OUString & inDestinationURL,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverWrite )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "COPY - inSourceURL: "<<inSourceURL<<" inDestinationURL: "<<inDestinationURL);
+
+ Init( rEnv );
+
+ NeonUri theSourceUri( inSourceURL );
+ NeonUri theDestinationUri( inDestinationURL );
+
+ int theRetVal = ne_copy( m_pHttpSession,
+ inOverWrite ? 1 : 0,
+ NE_DEPTH_INFINITE,
+ OUStringToOString(
+ theSourceUri.GetPath(),
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ OUStringToOString(
+ theDestinationUri.GetPath(),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ HandleError( theRetVal, inSourceURL, rEnv );
+}
+
+void NeonSession::MOVE( const OUString & inSourceURL,
+ const OUString & inDestinationURL,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverWrite )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "MOVE - inSourceURL: "<<inSourceURL<<" inDestinationURL: "<<inDestinationURL);
+
+ Init( rEnv );
+
+ NeonUri theSourceUri( inSourceURL );
+ NeonUri theDestinationUri( inDestinationURL );
+ int theRetVal = ne_move( m_pHttpSession,
+ inOverWrite ? 1 : 0,
+ OUStringToOString(
+ theSourceUri.GetPath(),
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ OUStringToOString(
+ theDestinationUri.GetPath(),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ HandleError( theRetVal, inSourceURL, rEnv );
+}
+
+void NeonSession::DESTROY( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "DESTROY - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ int theRetVal = ne_delete( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+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 = rStart.Seconds + timeout;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "LOCK - no chance to refresh lock before timeout!" );
+ }
+ }
+ return lastChanceToSendRefreshRequest;
+ }
+
+} // namespace
+
+// Set new lock
+void NeonSession::LOCK( const OUString & inPath,
+ ucb::Lock & rLock,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "LOCK (create) - relative URL: <" << inPath << ">" );
+
+ // before issuing the lock command,
+ // better check first if we already have one on this href
+ if ( m_aNeonLockStore.findByUri(
+ makeAbsoluteURL( inPath ) ) != nullptr )
+ {
+ // we already own a lock for this href
+ // no need to ask for another
+ // TODO: add a lockdiscovery request for confirmation
+ // checking the locktoken, the only item that's unique
+ return;
+ }
+
+ Init( rEnv );
+
+ /* Create a depth zero, exclusive write lock, with default timeout
+ * (allowing a server to pick a default). token, owner and uri are
+ * unset. */
+ NeonLock * theLock = ne_lock_create();
+
+ // Set the lock uri
+ ne_uri aUri;
+ ne_uri_parse( OUStringToOString( makeAbsoluteURL( inPath ),
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ &aUri );
+ theLock->uri = aUri;
+
+ // Set the lock depth
+ switch( rLock.Depth )
+ {
+ case ucb::LockDepth_ZERO:
+ theLock->depth = NE_DEPTH_ZERO;
+ break;
+ case ucb::LockDepth_ONE:
+ theLock->depth = NE_DEPTH_ONE;
+ break;
+ case ucb::LockDepth_INFINITY:
+ theLock->depth = NE_DEPTH_INFINITE;
+ break;
+ default:
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+
+ // Set the lock scope
+ switch ( rLock.Scope )
+ {
+ case ucb::LockScope_EXCLUSIVE:
+ theLock->scope = ne_lockscope_exclusive;
+ break;
+ case ucb::LockScope_SHARED:
+ theLock->scope = ne_lockscope_shared;
+ break;
+ default:
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+
+ // Set the lock timeout
+ theLock->timeout = static_cast<long>(rLock.Timeout);
+
+ // Set the lock owner
+ OUString aValue;
+ rLock.Owner >>= aValue;
+ theLock->owner =
+ ne_strdup( OUStringToOString( aValue,
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ TimeValue startCall;
+ osl_getSystemTime( &startCall );
+
+ int theRetVal = ne_lock( m_pHttpSession, theLock );
+
+ if ( theRetVal == NE_OK )
+ {
+ m_aNeonLockStore.addLock( theLock,
+ this,
+ lastChanceToSendRefreshRequest(
+ startCall, theLock->timeout ) );
+
+ uno::Sequence< OUString > aTokens( 1 );
+ aTokens[ 0 ] = OUString::createFromAscii( theLock->token );
+ rLock.LockTokens = aTokens;
+
+ SAL_INFO( "ucb.ucp.webdav", "LOCK (create) - Created lock for <" << makeAbsoluteURL( inPath )
+ << "> token: <" << theLock->token << "> timeout: " << theLock->timeout << " sec.");
+ }
+ else
+ {
+ ne_lock_destroy( theLock );
+
+ SAL_INFO( "ucb.ucp.webdav", "LOCK (create) - Obtaining lock for <"
+ << makeAbsoluteURL( inPath ) << " failed!" );
+ }
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+// Refresh existing lock
+bool NeonSession::LOCK( NeonLock * pLock,
+ sal_Int32 & rlastChanceToSendRefreshRequest )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+#if defined SAL_LOG_INFO
+ {
+ char * p = ne_uri_unparse( &(pLock->uri) );
+ SAL_INFO( "ucb.ucp.webdav", "LOCK (refresh) - relative URL: <" << p << "> token: <" << pLock->token << ">" );
+ ne_free( p );
+ }
+#endif
+
+ // refresh existing lock.
+
+ TimeValue startCall;
+ osl_getSystemTime( &startCall );
+
+ // save the current requested timeout, because ne_lock_refresh uses
+ // pLock->timeout as an out parameter. This prevents a feedback-loop,
+ // where we would request a shorter timeout on each refresh.
+ long timeout = pLock->timeout;
+ const int theRetVal = ne_lock_refresh(m_pHttpSession, pLock);
+ if (theRetVal == NE_OK)
+ {
+ rlastChanceToSendRefreshRequest
+ = lastChanceToSendRefreshRequest( startCall, pLock->timeout );
+
+ SAL_INFO( "ucb.ucp.webdav", "LOCK (refresh) - Lock successfully refreshed." );
+ pLock->timeout = timeout;
+ return true;
+ }
+ else
+ {
+#if defined SAL_LOG_WARN
+ char * p = ne_uri_unparse( &(pLock->uri) );
+ SAL_WARN( "ucb.ucp.webdav", "LOCK (refresh) - not refreshed! Relative URL: <" << p << "> token: <" << pLock->token << ">" );
+ ne_free( p );
+#endif
+ if (theRetVal == NE_AUTH)
+ {
+ // tdf#126279: see handling of NE_AUTH in HandleError
+ m_bNeedNewSession = true;
+ m_aNeonLockStore.removeLockDeferred(pLock);
+ }
+ return false;
+ }
+}
+
+void NeonSession::UNLOCK( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ // get the neon lock from lock store
+ NeonLock * theLock
+ = m_aNeonLockStore.findByUri( makeAbsoluteURL( inPath ) );
+ if ( !theLock )
+ throw DAVException( DAVException::DAV_NOT_LOCKED );
+
+ SAL_INFO( "ucb.ucp.webdav", "UNLOCK - relative URL: <" << inPath << "> token: <" << theLock->token << ">" );
+ Init( rEnv );
+
+ int theRetVal = ne_unlock( m_pHttpSession, theLock );
+
+ if ( theRetVal == NE_OK )
+ {
+ m_aNeonLockStore.removeLock( theLock );
+ ne_lock_destroy( theLock );
+ }
+ else
+ {
+ SAL_INFO( "ucb.ucp.webdav", "UNLOCK - Unlocking of <"
+ << makeAbsoluteURL( inPath ) << "> failed." );
+ }
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+bool NeonSession::UNLOCK( NeonLock * pLock )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+#if defined SAL_LOG_INFO
+ {
+ char * p = ne_uri_unparse( &(pLock->uri) );
+ SAL_INFO( "ucb.ucp.webdav", "UNLOCK (from store) - relative URL: <" << p << "> token: <" << pLock->token << ">" );
+ ne_free( p );
+ }
+#endif
+
+ const int theRetVal = ne_unlock(m_pHttpSession, pLock);
+ if (theRetVal == NE_OK)
+ {
+#if defined SAL_LOG_INFO
+ {
+ char * p = ne_uri_unparse( &(pLock->uri) );
+ SAL_INFO( "ucb.ucp.webdav", "UNLOCK (from store) - relative URL: <" << p << "> token: <" << pLock->token << "> succeeded." );
+ ne_free( p );
+ }
+#endif
+ return true;
+ }
+ else
+ {
+#if defined SAL_LOG_INFO
+ {
+ char * p = ne_uri_unparse( &(pLock->uri) );
+ SAL_INFO( "ucb.ucp.webdav", "UNLOCK (from store) - relative URL: <" << p << "> token: <" << pLock->token << "> failed!" );
+ ne_free( p );
+ }
+#endif
+ if (theRetVal == NE_AUTH)
+ {
+ // tdf#126279: see handling of NE_AUTH in HandleError
+ m_bNeedNewSession = true;
+ }
+ return false;
+ }
+}
+
+void NeonSession::abort()
+{
+ SAL_INFO( "ucb.ucp.webdav", "neon commands cannot be aborted" );
+}
+
+ucbhelper::InternetProxyServer NeonSession::getProxySettings() const
+{
+ if ( m_aScheme == "http" || m_aScheme == "https" )
+ {
+ return m_rProxyDecider.getProxy( m_aScheme,
+ m_aHostName,
+ m_nPort );
+ }
+ else
+ {
+ return m_rProxyDecider.getProxy( m_aScheme,
+ OUString() /* not used */,
+ -1 /* not used */ );
+ }
+}
+
+namespace {
+
+bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
+ const char * token )
+{
+ return std::any_of(rLocks.begin(), rLocks.end(), [&token](const ucb::Lock& rLock) {
+ const uno::Sequence< OUString > & rTokens = rLock.LockTokens;
+ return std::any_of(rTokens.begin(), rTokens.end(),
+ [&token](const OUString& rToken) { return rToken.equalsAscii( token ); });
+ });
+}
+
+} // namespace
+
+bool NeonSession::removeExpiredLocktoken( const OUString & inURL,
+ const DAVRequestEnvironment & rEnv )
+{
+ NeonLock * theLock = m_aNeonLockStore.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;
+
+ for ( const auto& rProp : aResources[ 0 ].properties )
+ {
+ if ( rProp.Name == DAVProperties::LOCKDISCOVERY )
+ {
+ uno::Sequence< ucb::Lock > aLocks;
+ if ( !( rProp.Value >>= aLocks ) )
+ return false;
+
+ if ( !containsLocktoken( aLocks, theLock->token ) )
+ {
+ // expired!
+ break;
+ }
+
+ // still valid.
+ return false;
+ }
+ }
+
+ // No lockdiscovery prop in propfind result / locktoken not found
+ // in propfind result -> not locked
+ SAL_WARN( "ucb.ucp.webdav", "Removing expired lock token for <" << inURL
+ << "> token: " << theLock->token );
+
+ m_aNeonLockStore.removeLock( theLock );
+ ne_lock_destroy( theLock );
+ return true;
+ }
+ catch ( DAVException const & )
+ {
+ }
+ return false;
+}
+
+// Common error handler
+void NeonSession::HandleError( int nError,
+ const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ // Map error code to DAVException.
+ switch ( nError )
+ {
+ case NE_OK:
+ return;
+
+ case NE_ERROR: // Generic error
+ {
+ const char* sErr = ne_get_error(m_pHttpSession);
+ OUString aText(sErr, strlen(sErr), osl_getThreadTextEncoding());
+
+ sal_uInt16 code = makeStatusCode( aText );
+
+ SAL_WARN( "ucb.ucp.webdav", "Neon returned NE_ERROR, http response status code was: " << code << " '" << aText << "'" );
+ if ( SC_BAD_REQUEST <= code && code < SC_INTERNAL_SERVER_ERROR )
+ {
+ // error codes in the range 4xx
+ switch ( code )
+ {
+ case SC_LOCKED:
+ {
+ if ( m_aNeonLockStore.findByUri(
+ makeAbsoluteURL( inPath ) ) == nullptr )
+ {
+ // locked by 3rd party
+ throw DAVException( DAVException::DAV_LOCKED );
+ }
+ else
+ {
+ // locked by ourself
+ throw DAVException( DAVException::DAV_LOCKED_SELF );
+ }
+ }
+ break;
+ case SC_PRECONDITION_FAILED:
+ case SC_BAD_REQUEST:
+ {
+ // 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...
+ if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) )
+ throw DAVException( DAVException::DAV_LOCK_EXPIRED );
+ }
+ break;
+ case SC_REQUEST_TIMEOUT:
+ {
+ throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+ }
+ break;
+ case SC_UNAUTHORIZED: // User authentication failed on server
+ {
+ throw DAVException( DAVException::DAV_HTTP_AUTH,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+ }
+ break;
+ case SC_GONE:
+ case SC_LENGTH_REQUIRED:
+ case SC_REQUEST_ENTITY_TOO_LARGE:
+ case SC_REQUEST_URI_TOO_LONG:
+ case SC_UNSUPPORTED_MEDIA_TYPE:
+ case SC_REQUESTED_RANGE_NOT_SATISFIABLE:
+ case SC_EXPECTATION_FAILED:
+ case SC_UNPROCESSABLE_ENTITY:
+ case SC_FAILED_DEPENDENCY:
+ case SC_CONFLICT:
+ case SC_NOT_ACCEPTABLE:
+ case SC_PAYMENT_REQUIRED:
+ case SC_PROXY_AUTHENTICATION_REQUIRED:
+ default:
+ // set 400 error, if not one of others
+ code = SC_BAD_REQUEST;
+ [[fallthrough]];
+ case SC_FORBIDDEN:
+ case SC_NOT_FOUND:
+ case SC_METHOD_NOT_ALLOWED:
+ throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
+ break;
+ }
+ }
+ else if ( SC_INTERNAL_SERVER_ERROR <= code )
+ {
+ // deal with HTTP response status codes higher then 500
+ // error codes in the range 5xx, server errors
+ // but there exists unofficial code in the range 1000 and up
+ // for example see:
+ // <https://support.cloudflare.com/hc/en-us/sections/200820298-Error-Pages> (retrieved 2016-10-05)
+ switch ( code )
+ {
+ // the error codes case before the default case are not actively
+ // managed by LO
+ case SC_BAD_GATEWAY:
+ case SC_SERVICE_UNAVAILABLE:
+ case SC_GATEWAY_TIMEOUT:
+ case SC_HTTP_VERSION_NOT_SUPPORTED:
+ case SC_INSUFFICIENT_STORAGE:
+ default:
+ // set 500 error, if not one of others
+ // expand the error code
+ code = SC_INTERNAL_SERVER_ERROR;
+ [[fallthrough]];
+ case SC_INTERNAL_SERVER_ERROR:
+ case SC_NOT_IMPLEMENTED:
+ throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
+ break;
+ }
+ }
+ else
+ throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
+ }
+ break;
+ case NE_LOOKUP: // Name lookup failed.
+ SAL_WARN( "ucb.ucp.webdav", "Name lookup failed" );
+ throw DAVException( DAVException::DAV_HTTP_LOOKUP,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_AUTH: // User authentication failed on server
+ // m_pHttpSession could get invalidated, e.g., as result of clean_session called in
+ // ah_post_send in case when auth_challenge failed, which invalidates the auth_session
+ // which we established in Init(): the auth_session's sspi_host gets disposed, and
+ // next attempt to authenticate would crash in continue_sspi trying to dereference it
+ m_bNeedNewSession = true;
+ throw DAVException( DAVException::DAV_HTTP_AUTH,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_PROXYAUTH: // User authentication failed on proxy
+ SAL_WARN( "ucb.ucp.webdav", "DAVException::DAV_HTTP_AUTHPROXY" );
+ throw DAVException( DAVException::DAV_HTTP_AUTHPROXY,
+ NeonUri::makeConnectionEndPointString(
+ m_aProxyName, m_nProxyPort ) );
+
+ case NE_CONNECT: // Could not connect to server
+ SAL_WARN( "ucb.ucp.webdav", "DAVException::DAV_HTTP_CONNECT" );
+ throw DAVException( DAVException::DAV_HTTP_CONNECT,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_TIMEOUT: // Connection timed out
+ SAL_WARN( "ucb.ucp.webdav", "DAVException::DAV_HTTP_TIMEOUT" );
+ throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_FAILED: // The precondition failed
+ SAL_WARN( "ucb.ucp.webdav", "The precondition failed" );
+ throw DAVException( DAVException::DAV_HTTP_FAILED,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_RETRY: // Retry request (ne_end_request ONLY)
+ throw DAVException( DAVException::DAV_HTTP_RETRY,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_REDIRECT:
+ {
+ NeonUri aUri( ne_redirect_location( m_pHttpSession ) );
+ SAL_INFO( "ucb.ucp.webdav", "DAVException::DAV_HTTP_REDIRECT: new URI: " << aUri.GetURI() );
+ throw DAVException(
+ DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() );
+ }
+ default:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Unknown Neon error code!" );
+ const char* sErr = ne_get_error(m_pHttpSession);
+ throw DAVException( DAVException::DAV_HTTP_ERROR,
+ OUString(sErr, strlen(sErr), osl_getThreadTextEncoding()) );
+ }
+ }
+}
+
+namespace {
+
+void runResponseHeaderHandler( void * userdata,
+ const char * value )
+{
+ OUString aHeader(value, strlen(value), RTL_TEXTENCODING_ASCII_US);
+ sal_Int32 nPos = aHeader.indexOf( ':' );
+
+ if ( nPos != -1 )
+ {
+ OUString aHeaderName( aHeader.copy( 0, nPos ) );
+
+ NeonRequestContext * pCtx
+ = static_cast< NeonRequestContext * >( userdata );
+
+ // Note: Empty vector means that all headers are requested.
+ bool bIncludeIt = pCtx->pHeaderNames->empty();
+
+ if ( !bIncludeIt )
+ {
+ // Check whether this header was requested.
+ auto it = std::find_if(pCtx->pHeaderNames->cbegin(), pCtx->pHeaderNames->cend(),
+ [&aHeaderName](const OUString& rName) {
+ // header names are case insensitive
+ return rName.equalsIgnoreAsciiCase( aHeaderName ); });
+
+ if ( it != pCtx->pHeaderNames->end() )
+ {
+ aHeaderName = *it;
+ bIncludeIt = true;
+ }
+ }
+
+ if ( bIncludeIt )
+ {
+ // Create & set the PropertyValue
+ DAVPropertyValue thePropertyValue;
+ // header names are case insensitive, so are the
+ // corresponding property names.
+ thePropertyValue.Name = aHeaderName.toAsciiLowerCase();
+ thePropertyValue.IsCaseSensitive = false;
+
+ if ( nPos < aHeader.getLength() )
+ thePropertyValue.Value <<= aHeader.copy( nPos + 1 ).trim();
+
+ // Add the newly created PropertyValue
+ pCtx->pResource->properties.push_back( thePropertyValue );
+ }
+ }
+}
+
+} // namespace
+
+int NeonSession::GET( ne_session * sess,
+ const char * uri,
+ ne_block_reader reader,
+ bool getheaders,
+ void * userdata )
+{
+ //struct get_context ctx;
+ ne_request * req = ne_request_create( sess, "GET", uri );
+ int ret;
+
+ ne_decompress * dc
+ = ne_decompress_reader( req, ne_accept_2xx, reader, userdata );
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ret = ne_request_dispatch( req );
+ }
+
+ if ( getheaders )
+ {
+ void *cursor = nullptr;
+ const char *name, *value;
+ while ( ( cursor = ne_response_header_iterate(
+ req, cursor, &name, &value ) ) != nullptr )
+ {
+ char buffer[8192];
+
+ SAL_INFO( "ucb.ucp.webdav", "GET - received header: " << name << ": " << value );
+ ne_snprintf(buffer, sizeof buffer, "%s: %s", name, value);
+ runResponseHeaderHandler(userdata, buffer);
+ }
+ }
+
+ if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
+ ret = NE_ERROR;
+
+ if ( dc != nullptr )
+ ne_decompress_destroy(dc);
+
+ ne_request_destroy( req );
+ return ret;
+}
+
+int NeonSession::GET0( ne_session * sess,
+ const char * uri,
+ bool getheaders,
+ void * userdata )
+{
+ //struct get_context ctx;
+ ne_request * req = ne_request_create( sess, "GET", uri );
+ int ret;
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ret = ne_request_dispatch( req );
+ }
+
+ if ( getheaders )
+ {
+ void *cursor = nullptr;
+ const char *name, *value;
+ while ( ( cursor = ne_response_header_iterate(
+ req, cursor, &name, &value ) ) != nullptr )
+ {
+ char buffer[8192];
+
+ SAL_INFO( "ucb.ucp.webdav", "GET - received header: " << name << ": " << value );
+ ne_snprintf(buffer, sizeof buffer, "%s: %s", name, value);
+ runResponseHeaderHandler(userdata, buffer);
+ }
+ }
+
+ if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
+ ret = NE_ERROR;
+
+ ne_request_destroy( req );
+ return ret;
+}
+
+int NeonSession::PUT( ne_session * sess,
+ const char * uri,
+ const char * buffer,
+ size_t size)
+{
+ ne_request * req = ne_request_create( sess, "PUT", uri );
+ int ret;
+
+ // tdf#99246
+ // extract the path of uri
+ // ne_lock_using_resource below compares path, ignores all the rest.
+ // in case of Web proxy active, this function uri parameter is instead absolute
+ ne_uri aUri;
+ ne_uri_parse( uri, &aUri );
+ ne_lock_using_resource( req, aUri.path, 0 );
+ ne_lock_using_parent( req, uri );
+
+ ne_set_request_body_buffer( req, buffer, size );
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ret = ne_request_dispatch( req );
+ }
+
+ if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
+ ret = NE_ERROR;
+
+ ne_request_destroy( req );
+ return ret;
+}
+
+int NeonSession::POST( ne_session * sess,
+ const char * uri,
+ const char * buffer,
+ ne_block_reader reader,
+ void * userdata,
+ const OUString & rContentType,
+ const OUString & rReferer )
+{
+ ne_request * req = ne_request_create( sess, "POST", uri );
+ //struct get_context ctx;
+ int ret;
+
+ RequestDataMap * pData = nullptr;
+
+ if ( !rContentType.isEmpty() || !rReferer.isEmpty() )
+ {
+ // Remember contenttype and referer. Data will be added to HTTP request
+ // header in 'PreSendRequest' callback.
+ pData = static_cast< RequestDataMap* >( m_pRequestData );
+ (*pData)[ req ] = RequestData( rContentType, rReferer );
+ }
+
+ //ctx.total = -1;
+ //ctx.fd = fd;
+ //ctx.error = 0;
+ //ctx.session = sess;
+
+ ///* Read the value of the Content-Length header into ctx.total */
+ //ne_add_response_header_handler( req, "Content-Length",
+ // ne_handle_numeric_header, &ctx.total );
+
+ ne_add_response_body_reader( req, ne_accept_2xx, reader, userdata );
+
+ ne_set_request_body_buffer( req, buffer, strlen( buffer ) );
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ret = ne_request_dispatch( req );
+ }
+
+ //if ( ctx.error )
+ // ret = NE_ERROR;
+ //else
+ if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
+ ret = NE_ERROR;
+
+ ne_request_destroy( req );
+
+ if ( pData )
+ {
+ // Remove request data from session's list.
+ RequestDataMap::iterator it = pData->find( req );
+ if ( it != pData->end() )
+ pData->erase( it );
+ }
+
+ return ret;
+}
+
+bool
+NeonSession::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( static_cast<void*>( rData.getArray() + nPos ),
+ static_cast<const void*>(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
+NeonSession::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 NeonSession::makeAbsoluteURL( OUString const & rURL ) const
+{
+ try
+ {
+ // Is URL relative or already absolute?
+ if ( !rURL.isEmpty() && rURL[ 0 ] != '/' )
+ {
+ // absolute.
+ return rURL;
+ }
+ else
+ {
+ ne_uri aUri = {};
+
+ ne_fill_server_uri( m_pHttpSession, &aUri );
+ aUri.path
+ = ne_strdup( OUStringToOString(
+ rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
+ NeonUri aNeonUri( &aUri );
+ ne_uri_free( &aUri );
+ return aNeonUri.GetURI();
+ }
+ }
+ catch ( DAVException const & )
+ {
+ }
+ // error.
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */