| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_webdav.hxx" |
| |
| #include <hash_map> |
| #include <vector> |
| #include <string.h> |
| #include <rtl/string.h> |
| #include <rtl/strbuf.hxx> |
| #include <rtl/ustrbuf.hxx> |
| #include <osl/time.h> |
| #include "comphelper/sequence.hxx" |
| #include "ucbhelper/simplecertificatevalidationrequest.hxx" |
| |
| #include "DAVAuthListener.hxx" |
| #include "CurlTypes.hxx" |
| #include "CurlSession.hxx" |
| #include "LockRequest.hxx" |
| #include "PropfindRequest.hxx" |
| #include "ProppatchRequest.hxx" |
| #include "CurlInputStream.hxx" |
| #include "UCBDeadPropertyValue.hxx" |
| #include "webdavuseragent.hxx" |
| #include "webdavresponseparser.hxx" |
| #include "webdavprovider.hxx" |
| |
| |
| #include <com/sun/star/logging/LogLevel.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/CertAltNameEntry.hpp> |
| #include <com/sun/star/security/XSanExtension.hpp> |
| #include <com/sun/star/ucb/Lock.hpp> |
| #include <com/sun/star/xml/crypto/XSEInitializer.hpp> |
| |
| using namespace com::sun::star; |
| using namespace com::sun::star::logging; |
| using namespace http_dav_ucp; |
| |
| #define OID_SUBJECT_ALTERNATIVE_NAME "2.5.29.17" |
| |
| struct CredentialsData |
| { |
| CredentialsData( CurlSession *curlSession, CurlRequest &curlRequest, const DAVRequestEnvironment &requestEnvironment ) |
| : session( curlSession) |
| , request( curlRequest ) |
| , env( requestEnvironment ) |
| {} |
| |
| CurlSession *session; |
| CurlRequest &request; |
| const DAVRequestEnvironment &env; |
| }; |
| |
| // ------------------------------------------------------------------- |
| // static members! |
| CurlLockStore CurlSession::m_aCurlLockStore; |
| |
| |
| // ------------------------------------------------------------------- |
| // Constructor |
| // ------------------------------------------------------------------- |
| CurlSession::CurlSession( |
| const rtl::Reference< DAVSessionFactory > & rSessionFactory, |
| const rtl::OUString& inUri, |
| const ucbhelper::InternetProxyDecider & rProxyDecider ) |
| throw ( DAVException ) |
| : DAVSession( rSessionFactory ) |
| , m_aMutex() |
| , m_aContext( m_xFactory->getServiceFactory() ) |
| , m_aLogger( m_aContext.getUNOContext(), WEBDAV_CONTENT_PROVIDER_SERVICE_NAME ) |
| , m_aUri( inUri ) |
| , m_aProxyName() |
| , m_nProxyPort( 0 ) |
| , m_aServerHeaderField() |
| , m_pCurl( 0 ) |
| , m_bUseChunkedEncoding( false ) |
| , m_bTransferEncodingSwitched( false ) |
| , m_rProxyDecider( rProxyDecider ) |
| , m_aEnv() |
| { |
| m_pCurl = curl_easy_init(); |
| |
| curl_easy_setopt( m_pCurl, CURLOPT_HTTPAUTH, CURLAUTH_ANY ); |
| curl_easy_setopt( m_pCurl, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); |
| |
| curl_easy_setopt( m_pCurl, CURLOPT_SSL_CTX_FUNCTION, Curl_SSLContextCallback ); |
| curl_easy_setopt( m_pCurl, CURLOPT_SSL_CTX_DATA, this ); |
| |
| // If a certificate's commmon name / alt name doesn't match the hostname we are |
| // connecting to, Curl will refuse to connect. Disable this, as we do that check |
| // ourselves, and give the user the option of connecting anyway. |
| // |
| // Note also, how "man CURLOPT_SSL_VERIFYHOST" tells us that setting 0 here |
| // disables SNI, which is bad news, some servers require SNI. However reading Curl |
| // 8.6.0's Curl_ssl_peer_init() in file lib/vtls/vtls.c shows that SNI is sent |
| // regardless, as long as we are connecting to a domain name, NOT an IP address. |
| // Tests confirm this. For OpenSSL anyway - other Curl crypto providers are stricter... |
| curl_easy_setopt( m_pCurl, CURLOPT_SSL_VERIFYHOST, 0 ); |
| |
| if ( m_aLogger.getLogLevel() == LogLevel::FINEST ) |
| { |
| curl_easy_setopt( m_pCurl, CURLOPT_DEBUGFUNCTION, Curl_DebugCallback ); |
| curl_easy_setopt( m_pCurl, CURLOPT_DEBUGDATA, this ); |
| curl_easy_setopt( m_pCurl, CURLOPT_VERBOSE, 1L); |
| } |
| |
| // Create a certificate container. |
| if( !m_aContext.createComponent( "com.sun.star.security.CertificateContainer", m_xCertificateContainer ) ) |
| throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Failed to create com.sun.star.security.CertificateContainer" ) ); |
| uno::Reference< xml::crypto::XSEInitializer > xSEInitializer; |
| if( !m_aContext.createComponent( "com.sun.star.xml.crypto.SEInitializer", xSEInitializer ) ) |
| throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Failed to create com.sun.star.xml.crypto.SEInitializer" ) ); |
| m_xSecurityContext = xSEInitializer->createSecurityContext( rtl::OUString() ); |
| if( m_xSecurityContext.is() ) |
| m_xSecurityEnv = m_xSecurityContext->getSecurityEnvironment(); |
| if ( ! m_xSecurityContext.is() || ! m_xSecurityEnv.is()) |
| throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Failure creating security services for certificate verification" ) ); |
| |
| // Populate one nonsense certificate, which we won't ever really use, just to get Curl to initialize: |
| struct curl_blob blob; |
| blob.data = (void*) |
| "-----BEGIN CERTIFICATE-----\n" |
| "MIIC/zCCAeegAwIBAgIUQYFHL3Bv7alQBtXQWy9SXGusm5YwDQYJKoZIhvcNAQEL\n" |
| "BQAwDzENMAsGA1UEAwwEVEVTVDAeFw0yNDA0MjExNzU3MzdaFw0yNDA0MjIxNzU3\n" |
| "MzdaMA8xDTALBgNVBAMMBFRFU1QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" |
| "AoIBAQCZSXla2TE7GU6xOfie5uilpRf7KQflWcQRgwTCFhk0yzbsSPJYdqbuUqfx\n" |
| "k0pV9Sx8GIkvc7jKQBwS79T15qn6dAZOF40x/k2jEMq150oc/80+dqeNP2jWvxv7\n" |
| "FjgBKSiuGUaHldy6XU3NhrA9G1Ys2/yHQRXER1NTeknEzPiPlobRUk1sNR2Prc5r\n" |
| "0u6cdUWGhbDOKDV9jjvA/14jmaAK+vUqrzzAdiOHVrkglA5oyBKX0BUokRCa8jID\n" |
| "34tH9zeuvozA3xXCi8l9to+HOgT/n7LAGeOSnNPeSHC/xkwumt/rJ05tL9DXg6Ud\n" |
| "3Pjf8KZM+FWJsjoJkcwBR0P2Qh3FAgMBAAGjUzBRMB0GA1UdDgQWBBR7pCl5msAz\n" |
| "rGApirAQ+/tFuHl5kDAfBgNVHSMEGDAWgBR7pCl5msAzrGApirAQ+/tFuHl5kDAP\n" |
| "BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBDJ1S51MKlafDAfFbU\n" |
| "DJcxw3JNHn+VxQuaQQpeeqLIn3rgKHRBV9eOTYHf8AMoCYdQfPs1z45vqBmcyrDw\n" |
| "LoXL6vlUbSLUuYFyfCaFup3bbh2lLozsLcD6bcvV07amX6V3u0ZOKpwqhg+k/IJd\n" |
| "cPVM8jYAnNZZYD6rMHWnW5ZgMFSzSj3Jyyaov/3zwixvFZdViBG+R2RmJZVgMiFP\n" |
| "PNxY3USKiHqdwZIszf3G63Ku0EYtFf3KN8YpoqSMDCDfjL0NhJOtkBUs5HL+4XfK\n" |
| "hToBqJojDMLFRdVIhPQX1LoPd92CUwhueIrYTikScAqY2TIwXpPH0kBjfrVDus8s\n" |
| "vPAk\n" |
| "-----END CERTIFICATE-----"; |
| blob.len = strlen( (char*) blob.data ) + 1; |
| blob.flags = CURL_BLOB_COPY; |
| CURLcode rc; |
| rc = curl_easy_setopt( m_pCurl, CURLOPT_CAINFO_BLOB, &blob ); |
| if( rc != CURLE_OK ) |
| throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii("Error initializing Curl certificate" ) ); |
| |
| m_aLogger.log( LogLevel::INFO, "CurlSession::CurlSession with URL $1$", |
| rtl::OUStringToOString( inUri, RTL_TEXTENCODING_UTF8 ).getStr() ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Destructor |
| // ------------------------------------------------------------------- |
| CurlSession::~CurlSession( ) |
| { |
| if ( m_pCurl ) |
| { |
| curl_easy_cleanup( m_pCurl ); |
| m_pCurl = 0; |
| m_aLogger.log( LogLevel::INFO, "CurlSession::~CurlSession: closed curl session"); |
| } |
| } |
| |
| // ------------------------------------------------------------------- |
| void CurlSession::Init( const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| m_aEnv = rEnv; |
| Init(); |
| } |
| |
| // ------------------------------------------------------------------- |
| void CurlSession::Init() |
| throw ( DAVException ) |
| { |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| const sal_Char *url = rtl::OUStringToOString( m_aUri.GetURI(), RTL_TEXTENCODING_UTF8 ).getStr(); |
| CURLcode rc; |
| rc = curl_easy_setopt( m_pCurl, CURLOPT_URL, url ); |
| if ( rc != CURLE_OK ) |
| throw DAVException( DAVException::DAV_SESSION_CREATE, |
| CurlUri::makeConnectionEndPointString( m_aUri.GetHost(), m_aUri.GetPort() ) ); |
| |
| const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings(); |
| if ( ( rProxyCfg.aName != m_aProxyName ) |
| || ( rProxyCfg.nPort != m_nProxyPort ) ) |
| { |
| m_aProxyName = rProxyCfg.aName; |
| m_nProxyPort = rProxyCfg.nPort; |
| if ( !m_aProxyName.isEmpty() ) |
| { |
| m_aLogger.log( LogLevel::INFO, "Using $1$ proxy server at $2$:$3$", |
| m_aUri.GetScheme(), m_aProxyName, m_nProxyPort ); |
| curl_easy_setopt( m_pCurl, CURLOPT_PROXY, rtl::OUStringToOString( m_aProxyName, RTL_TEXTENCODING_UTF8 ).getStr() ); |
| curl_easy_setopt( m_pCurl, CURLOPT_PROXYPORT, (long)m_nProxyPort ); |
| if ( m_aUri.GetScheme().equalsAscii( "https" ) ) |
| curl_easy_setopt( m_pCurl, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS ); |
| else |
| curl_easy_setopt( m_pCurl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP ); |
| // no other proxy types are implemented by AOO |
| } |
| else |
| { |
| // Empty string as opposed to NULL, means don't use the default curl proxy. |
| m_aLogger.log( LogLevel::INFO, "Not using a proxy server" ); |
| curl_easy_setopt( m_pCurl, CURLOPT_PROXY, "" ); |
| } |
| // if we change the proxy settings, clear the credentials for the previous proxy too |
| curl_easy_setopt( m_pCurl, CURLOPT_PROXYUSERNAME, "" ); |
| curl_easy_setopt( m_pCurl, CURLOPT_PROXYPASSWORD, "" ); |
| } |
| } |
| |
| bool CurlSession::isSSLNeeded() |
| { |
| return m_aUri.GetScheme().equalsIgnoreAsciiCase( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "https" ) ) ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // helper function |
| // it composes the uri for lockstore registration |
| rtl::OUString CurlSession::composeCurrentUri(const rtl::OUString & inPath) |
| { |
| rtl::OUString aScheme( m_aUri.GetScheme() ); |
| rtl::OUStringBuffer aBuf( aScheme ); |
| aBuf.appendAscii( "://" ); |
| if ( m_aUri.GetUserName().getLength() > 0 ) |
| { |
| aBuf.append( m_aUri.GetUserName() ); |
| if ( m_aUri.GetPassword().getLength() > 0 ) |
| { |
| aBuf.appendAscii( ":" ); |
| aBuf.append( m_aUri.GetPassword() ); |
| } |
| aBuf.appendAscii( "@" ); |
| } |
| // Is host a numeric IPv6 address? |
| if ( ( m_aUri.GetHost().indexOf( ':' ) != -1 ) && |
| ( m_aUri.GetHost()[ 0 ] != sal_Unicode( '[' ) ) ) |
| { |
| aBuf.appendAscii( "[" ); |
| aBuf.append( m_aUri.GetHost() ); |
| aBuf.appendAscii( "]" ); |
| } |
| else |
| { |
| aBuf.append( m_aUri.GetHost() ); |
| } |
| |
| // append port, but only, if not default port. |
| bool bAppendPort = true; |
| sal_Int32 aPort = m_aUri.GetPort(); |
| switch ( aPort ) |
| { |
| case DEFAULT_HTTP_PORT: |
| bAppendPort = aScheme.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "http" ) ); |
| break; |
| |
| case DEFAULT_HTTPS_PORT: |
| bAppendPort = !aScheme.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "https" ) ); |
| break; |
| } |
| if ( bAppendPort ) |
| { |
| aBuf.appendAscii( ":" ); |
| aBuf.append( rtl::OUString::valueOf( aPort ) ); |
| } |
| aBuf.append( inPath ); |
| |
| rtl::OUString aUri(aBuf.makeStringAndClear() ); |
| return aUri; |
| } |
| |
| // ------------------------------------------------------------------- |
| // virtual |
| sal_Bool CurlSession::CanUse( const rtl::OUString & inUri ) |
| { |
| try |
| { |
| CurlUri theUri( inUri ); |
| if ( ( theUri.GetPort() == m_aUri.GetPort() ) && |
| ( theUri.GetHost() == m_aUri.GetHost() ) && |
| ( theUri.GetScheme() == m_aUri.GetScheme() ) ) |
| { |
| return sal_True; |
| } |
| } |
| catch ( DAVException const & ) |
| { |
| return sal_False; |
| } |
| return sal_False; |
| } |
| |
| // ------------------------------------------------------------------- |
| // virtual |
| sal_Bool CurlSession::UsesProxy() |
| { |
| Init(); |
| return ( m_aProxyName.getLength() > 0 ); |
| } |
| |
| int CurlSession::Curl_DebugCallback( CURL *, curl_infotype type, unsigned char *data, size_t size, void* userdata ) |
| { |
| CurlSession *session = static_cast< CurlSession* >( userdata ); |
| return session->curlDebugOutput( type, reinterpret_cast<char*>( data ), size ); |
| } |
| |
| int CurlSession::curlDebugOutput( curl_infotype type, char *data, int size ) |
| { |
| const char *prefix; |
| switch ( type ) |
| { |
| case CURLINFO_TEXT: |
| prefix = "[CurlINFO ]"; |
| break; |
| case CURLINFO_HEADER_IN: |
| prefix = "[CurlHDR <-]"; |
| break; |
| case CURLINFO_HEADER_OUT: |
| prefix = "[CurlHDR ->]"; |
| break; |
| case CURLINFO_DATA_IN: |
| prefix = "[CurlData<-]"; |
| break; |
| case CURLINFO_DATA_OUT: |
| prefix = "[CurlData->]"; |
| break; |
| default: |
| return 0; |
| } |
| |
| // Trim the trailing \r\n |
| if ( size >= 1 && ( data[size - 1] == '\r' || data[size - 1] == '\n' ) ) |
| --size; |
| if ( size >= 1 && ( data[size - 1] == '\r' || data[size - 1] == '\n' ) ) |
| --size; |
| rtl::OString message( data, size ); |
| m_aLogger.log( LogLevel::FINEST, "$1$ $2$", prefix, message ); |
| return 0; |
| } |
| |
| CURLcode CurlSession::Curl_SSLContextCallback( CURL *, void *ssl_ctx, void *userptr ) |
| { |
| CurlSession *session = static_cast<CurlSession*>( userptr ); |
| SSL_CTX *context = static_cast<SSL_CTX*>( ssl_ctx ); |
| SSL_CTX_set_app_data( context, session ); |
| SSL_CTX_set_cert_verify_callback( context, OPENSSL_VerifyCertificate, session ); |
| return CURLE_OK; |
| } |
| |
| int CurlSession::OPENSSL_VerifyCertificate( X509_STORE_CTX *x509_ctx, void *arg ) |
| { |
| CurlSession *session = static_cast<CurlSession*>( arg ); |
| int verifyResult = session->verifyServerX509Certificate( x509_ctx ); |
| // We have to both return 1 or 0, and set the X509_V_* error code with X509_STORE_CTX_set_error(): |
| X509_STORE_CTX_set_error( x509_ctx, verifyResult ); |
| return verifyResult == X509_V_OK ? 1 : 0; |
| } |
| |
| static uno::Sequence< sal_Int8 > convertCertificateToAsn1Der( X509 *certificate ) |
| { |
| uno::Sequence< sal_Int8 > asn1DerCertificate; |
| int len = i2d_X509( certificate, NULL ); |
| if ( len < 0 ) |
| return asn1DerCertificate; |
| asn1DerCertificate.realloc( len ); |
| unsigned char *end = reinterpret_cast< unsigned char *>( asn1DerCertificate.getArray() ); |
| len = i2d_X509( certificate, &end ); |
| if ( len >= 0 ) |
| return asn1DerCertificate; |
| else |
| return uno::Sequence< sal_Int8 >(); |
| } |
| |
| int CurlSession::verifyServerX509Certificate( X509_STORE_CTX *x509StoreContext ) |
| { |
| X509 *serverCertificate = X509_STORE_CTX_get0_cert( x509StoreContext ); |
| STACK_OF(X509) *chain = X509_STORE_CTX_get0_untrusted( x509StoreContext ); |
| |
| std::vector< uno::Sequence< sal_Int8 > > asn1DerCertificates; |
| int verifyResult = X509_V_OK; |
| if ( chain != NULL ) { |
| int nCertificates = sk_X509_num( chain ); |
| for ( int i = 0; i < nCertificates && verifyResult == X509_V_OK; i++ ) { |
| X509 *certificate = sk_X509_value( chain, i ); |
| uno::Sequence< sal_Int8 > asn1DerCertificate = convertCertificateToAsn1Der( certificate ); |
| if( asn1DerCertificate.getLength() > 0 ) |
| asn1DerCertificates.push_back( asn1DerCertificate ); |
| else |
| verifyResult = X509_V_ERR_UNSPECIFIED; |
| } |
| } else { |
| uno::Sequence< sal_Int8 > asn1DerCertificate = convertCertificateToAsn1Der( serverCertificate ); |
| if( asn1DerCertificate.getLength() > 0 ) |
| asn1DerCertificates.push_back( asn1DerCertificate ); |
| else |
| verifyResult = X509_V_ERR_UNSPECIFIED; |
| } |
| if( verifyResult == X509_V_OK ) |
| verifyResult = verifyCertificateChain( asn1DerCertificates ); |
| |
| rtl::OUString verifyErrorString = rtl::OUString::createFromAscii( X509_verify_cert_error_string( verifyResult ) ); |
| m_aLogger.log( LogLevel::FINE, "validateServerX509Certificate() verifyResult=$1$ ($2$)", |
| (sal_Int32)verifyResult, verifyErrorString ); |
| return verifyResult; |
| } |
| |
| int CurlSession::verifyCertificateChain ( |
| std::vector< uno::Sequence< sal_Int8 > > &asn1DerCertificates ) |
| { |
| // Check arguments. |
| if( asn1DerCertificates.size() <= 0 ) |
| { |
| m_aLogger.log( LogLevel::WARNING, "No certificates to verify - failing!" ); |
| return X509_V_ERR_UNSPECIFIED; |
| } |
| |
| // Decode the server certificate. |
| uno::Reference< security::XCertificate > xServerCertificate( |
| m_xSecurityEnv->createCertificateFromRaw( asn1DerCertificates[0] ) ); |
| if ( ! xServerCertificate.is()) |
| { |
| m_aLogger.log( LogLevel::WARNING, "Failed to create XCertificate" ); |
| return X509_V_ERR_UNSPECIFIED; |
| } |
| |
| // Get the subject from the server certificate. |
| ::rtl::OUString sServerCertificateSubject (xServerCertificate->getSubjectName()); |
| sal_Int32 nIndex = 0; |
| while (nIndex >= 0) |
| { |
| const ::rtl::OUString sToken (sServerCertificateSubject.getToken(0, ',', nIndex)); |
| if (sToken.compareToAscii("CN=", 3) == 0) |
| { |
| sServerCertificateSubject = sToken.copy(3); |
| break; |
| } |
| else if (sToken.compareToAscii(" CN=", 4) == 0) |
| { |
| 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 ( |
| m_xCertificateContainer->hasCertificate( |
| getHostName(), sServerCertificateSubject ) ); |
| if (eStatus != security::CertificateContainerStatus_NOCERT) |
| { |
| m_aLogger.log( LogLevel::FINER, "Cached certificate found with status=$1$", |
| eStatus == security::CertificateContainerStatus_TRUSTED ? "trusted" : "untrusted" ); |
| return eStatus == security::CertificateContainerStatus_TRUSTED |
| ? X509_V_OK |
| : X509_V_ERR_CERT_UNTRUSTED; |
| } |
| |
| // 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=0; nIndex < asn1DerCertificates.size(); ++nIndex) |
| { |
| uno::Reference< security::XCertificate > xCertificate( |
| m_xSecurityEnv->createCertificateFromRaw( asn1DerCertificates[ nIndex ] ) ); |
| if ( ! xCertificate.is()) |
| { |
| m_aLogger.log( LogLevel::WARNING, "Failed to create XCertificate $1$", nIndex ); |
| return X509_V_ERR_UNSPECIFIED; |
| } |
| aChain.push_back(xCertificate); |
| } |
| const sal_Int64 nVerificationResult (m_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 rtl::OString aId ( (const sal_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< ::rtl::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] ); |
| } |
| |
| } |
| m_aLogger.log( LogLevel::FINE, "URL hostname $1$ certificate hostname", |
| bHostnameMatchesCertHostnames ? "matches" : "DOESN'T MATCH" ); |
| if ( bHostnameMatchesCertHostnames ) |
| { |
| if (nVerificationResult == 0) |
| { |
| m_aLogger.log( LogLevel::FINE, "Certificate (chain) is valid" ); |
| m_xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, sal_True); |
| return X509_V_OK; |
| } |
| 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.) |
| m_aLogger.log( LogLevel::WARNING, "Certificate (chain) is incomplete" ); |
| return X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; |
| } |
| else if ((nVerificationResult & security::CertificateValidity::REVOKED) != 0) |
| { |
| // Certificate (chain) is invalid. |
| m_aLogger.log( LogLevel::WARNING, "Certificate (chain) is revoked" ); |
| m_xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, sal_False); |
| return X509_V_ERR_CERT_REVOKED; |
| } |
| else |
| { |
| // For all other we have to ask the user. |
| m_aLogger.log( LogLevel::FINE, "Promping user to validate the certificate" ); |
| } |
| } |
| |
| // 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() ) |
| { |
| m_aLogger.log( LogLevel::FINE, "The user approved the certificate" ); |
| m_xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_True ); |
| return X509_V_OK; |
| } |
| else |
| { |
| // Don't trust cert |
| m_aLogger.log( LogLevel::WARNING, "The user REJECTED the certificate" ); |
| m_xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_False ); |
| return X509_V_ERR_CERT_REJECTED; |
| } |
| } |
| } |
| else |
| { |
| // Don't trust cert |
| m_aLogger.log( LogLevel::WARNING, "Couldn't create the interaction handler for user feedback, rejecting the certificate" ); |
| m_xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_False ); |
| return X509_V_ERR_CERT_REJECTED; |
| } |
| } |
| m_aLogger.log( LogLevel::WARNING, "No XCommandEnvironment, rejecting the certificate" ); |
| |
| return X509_V_ERR_CERT_REJECTED; |
| } |
| |
| bool CurlSession::Curl_ProvideCredentials( long statusCode, void *userdata ) throw (DAVException) |
| { |
| CredentialsData *credentialsData = (CredentialsData*)userdata; |
| return credentialsData->session->provideCredentials( credentialsData->env, credentialsData->request, statusCode ); |
| } |
| |
| bool CurlSession::provideCredentials( const DAVRequestEnvironment &env, CurlRequest &request, long statusCode ) throw (DAVException) |
| { |
| DAVAuthListener * pListener = env.m_xAuthListener.get(); |
| if ( !pListener ) |
| { |
| // abort |
| m_aLogger.log( LogLevel::FINE, "No DAVAuthListener found, failing credentials entry" ); |
| return false; |
| } |
| |
| rtl::OUString theUserName; |
| rtl::OUString thePassWord; |
| try |
| { |
| CurlUri uri( env.m_aRequestURI ); |
| theUserName = uri.GetUserName(); |
| thePassWord = uri.GetPassword(); |
| } |
| catch ( DAVException const &e ) |
| { |
| // abort |
| m_aLogger.log( |
| LogLevel::WARNING, |
| "Error extracing userinfo from URI: exceptionCode=$1$, status=$2$, data=$3$, owner=$4$, extendedError=$5%", |
| (sal_Int32)e.getError(), e.getStatus(), e.getData(), e.getOwner(), e.getExtendedError() |
| ); |
| return false; |
| } |
| |
| bool canUseSystemCreds = false; |
| long authMethods = 0; |
| CURLcode rc = CURLE_OK; |
| if ( statusCode == 401 ) |
| rc = curl_easy_getinfo( m_pCurl, CURLINFO_HTTPAUTH_AVAIL, &authMethods ); |
| else if ( statusCode == 407 ) |
| rc = curl_easy_getinfo( m_pCurl, CURLINFO_PROXYAUTH_AVAIL, &authMethods ); |
| if ( rc == 0 ) |
| canUseSystemCreds = (authMethods & CURLAUTH_NEGOTIATE) || (authMethods & CURLAUTH_NTLM); |
| m_aLogger.log( LogLevel::FINE, "authMethods=$1$, canUseSystemCreds=$2$", |
| (sal_Int64)authMethods, (sal_Int32)canUseSystemCreds ); |
| |
| const CurlRequest::Header *authHeader = NULL; |
| if ( statusCode == 401 ) |
| authHeader = request.findResponseHeader( "WWW-Authenticate" ); |
| else if ( statusCode == 407 ) |
| authHeader = request.findResponseHeader( "Proxy-Authenticate" ); |
| rtl::OUString realm; |
| if ( authHeader != NULL ) |
| { |
| int realmStart = authHeader->value.indexOf( "realm=\"" ); |
| if ( realmStart >= 0 ) |
| { |
| realmStart += 7; |
| int realmEnd = authHeader->value.indexOf( "\"", realmStart ); |
| if ( realmEnd > 0 ) |
| realm = rtl::OStringToOUString( authHeader->value.copy( realmStart, realmEnd - realmStart ), RTL_TEXTENCODING_UTF8 ); |
| } |
| } |
| |
| int theRetVal = pListener->authenticate( realm, |
| getHostName(), |
| theUserName, |
| thePassWord, |
| canUseSystemCreds, |
| // Authenticating with both the proxy |
| // and the destination server requires sal_True here, |
| // and needs filling out 2 x password dialogs. |
| sal_True ); |
| |
| if ( theRetVal == 0 ) |
| { |
| m_aLogger.log( LogLevel::FINEST, "got credentials for user=$1$ on realm=$2$", theUserName, realm ); |
| // "System credentials" means username and password are empty |
| const char *curlUsername = NULL; |
| const char *curlPassword = NULL; |
| if ( !theUserName.isEmpty() ) |
| curlUsername = rtl::OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ).getStr(); |
| if ( !thePassWord.isEmpty() ) |
| curlPassword = rtl::OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ).getStr(); |
| if ( statusCode == 401 ) |
| { |
| curl_easy_setopt( m_pCurl, CURLOPT_USERNAME, curlUsername ); |
| curl_easy_setopt( m_pCurl, CURLOPT_PASSWORD, curlPassword ); |
| } |
| else |
| { |
| curl_easy_setopt( m_pCurl, CURLOPT_PROXYUSERNAME, curlUsername ); |
| curl_easy_setopt( m_pCurl, CURLOPT_PROXYPASSWORD, curlPassword ); |
| } |
| return true; |
| } |
| m_aLogger.log( LogLevel::WARNING, "credentials entry cancelled or failed" ); |
| |
| return false; |
| } |
| |
| void CurlSession::addEnvironmentRequestHeaders( CurlRequest &curlRequest, const DAVRequestEnvironment &env ) |
| throw ( DAVException ) |
| { |
| bool bHasUserAgent( false ); |
| DAVRequestHeaders::const_iterator aHeaderIter( env.m_aRequestHeaders.begin() ); |
| const DAVRequestHeaders::const_iterator aEnd( env.m_aRequestHeaders.end() ); |
| |
| while ( aHeaderIter != aEnd ) |
| { |
| const rtl::OString aHeader = rtl::OUStringToOString( aHeaderIter->first, |
| RTL_TEXTENCODING_UTF8 ); |
| const rtl::OString aValue = rtl::OUStringToOString( aHeaderIter->second, |
| RTL_TEXTENCODING_UTF8 ); |
| |
| if ( !bHasUserAgent ) |
| bHasUserAgent = aHeaderIter->first.equalsAsciiL( |
| RTL_CONSTASCII_STRINGPARAM( "User-Agent" ) ); |
| |
| curlRequest.addHeader( aHeader, aValue ); |
| |
| ++aHeaderIter; |
| } |
| |
| if ( !bHasUserAgent ) |
| { |
| const rtl::OUString &rUserAgent = WebDAVUserAgent::get(); |
| curlRequest.addHeader( "User-Agent", rtl::OUStringToOString( rUserAgent, RTL_TEXTENCODING_UTF8 ) ); |
| } |
| } |
| |
| void CurlSession::processResponse( CurlRequest &curlRequest, CURLcode curlCode ) |
| throw( DAVException ) |
| { |
| long statusCode = 0; |
| CURLcode curlRes; |
| curlRes = curl_easy_getinfo( m_pCurl, CURLINFO_RESPONSE_CODE, &statusCode ); |
| if ( curlRes != 0 || statusCode == 0 ) |
| statusCode = curlRequest.getStatusCode(); |
| |
| // check header according: |
| // http://tools.ietf.org/html/rfc7231#section-7.4.2 |
| // need to do this so we can adjust the protocol accordingly |
| const CurlRequest::Header *server = curlRequest.findResponseHeader( "server" ); |
| if ( server != NULL ) |
| m_aServerHeaderField = server->value; |
| |
| if ( curlCode != 0 ) |
| { |
| m_aLogger.log( LogLevel::WARNING, "Curl request failed with CURLcode $1$", (sal_Int64)curlCode ); |
| DAVException::ExceptionCode exCode = DAVException::DAV_HTTP_ERROR; |
| rtl::OUString exData; |
| switch (curlCode) { |
| case CURLE_COULDNT_RESOLVE_HOST: |
| exCode = DAVException::DAV_HTTP_LOOKUP; |
| exData = CurlUri::makeConnectionEndPointString( getHostName(), |
| getPort() ); |
| break; |
| case CURLE_COULDNT_CONNECT: |
| exCode = DAVException::DAV_HTTP_CONNECT; |
| exData = CurlUri::makeConnectionEndPointString( getHostName(), |
| getPort() ); |
| break; |
| case CURLE_OPERATION_TIMEDOUT: |
| exCode = DAVException::DAV_HTTP_TIMEOUT; |
| exData = CurlUri::makeConnectionEndPointString( getHostName(), |
| getPort() ); |
| break; |
| case CURLE_LOGIN_DENIED: |
| case CURLE_AUTH_ERROR: |
| exCode = DAVException::DAV_HTTP_AUTH; |
| exData = CurlUri::makeConnectionEndPointString( getHostName(), |
| getPort() ); |
| break; |
| default: |
| { |
| const char *s = curl_easy_strerror(curlCode); |
| exCode = DAVException::DAV_HTTP_ERROR; |
| exData = ::rtl::OUString(s, strlen(s), |
| RTL_TEXTENCODING_UTF8); |
| break; |
| } |
| } |
| throw DAVException( exCode, exData ); |
| } |
| |
| rtl::OUString reasonPhrase = rtl::OStringToOUString( curlRequest.getReasonPhrase(), RTL_TEXTENCODING_UTF8 ); |
| if ( statusCode != 0 && statusCode / 100 != 2 ) |
| { |
| switch (statusCode) |
| { |
| case SC_MOVED_PERMANENTLY: // 301 |
| case SC_MOVED_TEMPORARILY: // 302 |
| case SC_SEE_OTHER: // 303 |
| case SC_TEMPORARY_REDIRECT: // 307 |
| { |
| // new location for certain redirections |
| |
| const CurlRequest::Header *location = curlRequest.findResponseHeader( "location" ); |
| if ( location != NULL ) |
| { |
| m_aLogger.log( LogLevel::FINE, "HTTP $1$ response with new location = $2$", |
| statusCode, location->value ); |
| throw DAVException( DAVException::DAV_HTTP_REDIRECT, |
| rtl::OStringToOUString( location->value, RTL_TEXTENCODING_UTF8 ) ); |
| } |
| break; |
| } |
| case SC_UNAUTHORIZED: // 401 |
| case SC_PROXY_AUTHENTICATION_REQUIRED: // 407 |
| { |
| throw DAVException( DAVException::DAV_HTTP_ERROR, |
| reasonPhrase, |
| statusCode ); |
| break; |
| } |
| case SC_REQUEST_ENTITY_TOO_LARGE: // 413 |
| { |
| if ( m_bTransferEncodingSwitched ) |
| throw DAVException( DAVException::DAV_HTTP_ERROR, |
| reasonPhrase, |
| statusCode ); |
| m_bTransferEncodingSwitched = true; |
| curlRequest.setChunkedEncoding( !curlRequest.isChunkedEncoding() ); |
| break; |
| } |
| case SC_LOCKED: // 423 |
| throw DAVException( DAVException::DAV_LOCKED, |
| reasonPhrase, |
| statusCode ); |
| default: |
| throw DAVException( DAVException::DAV_HTTP_ERROR, |
| reasonPhrase, |
| statusCode ); |
| } |
| } |
| } |
| |
| static void responseHeadersToDAVResource( const std::vector< CurlRequest::Header> &responseHeaders, |
| const std::vector< ::rtl::OUString > &inHeaderNames, |
| DAVResource &ioResource ) |
| { |
| std::vector< CurlRequest::Header >::const_iterator it( responseHeaders.begin() ); |
| const std::vector< CurlRequest::Header >::const_iterator end( responseHeaders.end() ); |
| while ( it != end ) |
| { |
| bool storeHeader = false; |
| if ( inHeaderNames.size() == 0 ) |
| storeHeader = true; |
| else |
| { |
| std::vector< ::rtl::OUString >::const_iterator reqIt( inHeaderNames.begin() ); |
| const std::vector< ::rtl::OUString >::const_iterator reqEnd( inHeaderNames.end() ); |
| while ( reqIt != reqEnd ) |
| { |
| // header names are case insensitive |
| if ( (*reqIt).equalsIgnoreAsciiCase( rtl::OStringToOUString( (*it).name, RTL_TEXTENCODING_UTF8 ) ) ) |
| { |
| storeHeader = true; |
| break; |
| } |
| else |
| { |
| ++reqIt; |
| } |
| } |
| } |
| |
| if ( storeHeader ) |
| { |
| DAVPropertyValue thePropertyValue; |
| thePropertyValue.IsCaseSensitive = false; |
| thePropertyValue.Name = rtl::OStringToOUString( (*it).name, RTL_TEXTENCODING_UTF8 ); |
| thePropertyValue.Value <<= rtl::OStringToOUString( (*it).value, RTL_TEXTENCODING_UTF8 ); |
| ioResource.properties.push_back( thePropertyValue ); |
| } |
| |
| it++; |
| } |
| } |
| |
| // ------------------------------------------------------------------- |
| // PROPFIND - allprop & named |
| // ------------------------------------------------------------------- |
| |
| void CurlSession::propfind( CurlRequest &curlRequest, |
| const rtl::OUString &inPath, |
| const Depth inDepth, |
| const std::vector< ::rtl::OUString > * inPropNames, |
| const bool onlyPropertyNames, |
| const DAVRequestEnvironment & rEnv ) |
| { |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| if ( inDepth == DAVZERO ) |
| curlRequest.addHeader( "Depth", "0" ); |
| else if ( inDepth == DAVONE ) |
| curlRequest.addHeader( "Depth", "1" ); |
| else if ( inDepth == DAVINFINITY ) |
| curlRequest.addHeader( "Depth", "infinity" ); |
| |
| rtl::OString xml = PropfindRequest::generatePROPFINDRequestBody( inPropNames, onlyPropertyNames ); |
| if ( xml.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "Content-Type", "application/xml" ); |
| curlRequest.setRequestBody( xml.getStr(), xml.getLength() ); |
| } |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| CURLcode rc = curlRequest.propfind( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| } |
| |
| void CurlSession::PROPFIND( const rtl::OUString & inPath, |
| const Depth inDepth, |
| const std::vector< rtl::OUString > & inPropNames, |
| std::vector< DAVResource > & ioResources, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "PROPFIND line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| propfind( curlRequest, inPath, inDepth, &inPropNames, false, rEnv ); |
| |
| const std::vector< DAVResource > rResources( parseWebDAVPropFindResponse( curlRequest.getResponseBody().get() ) ); |
| std::vector< DAVResource > *pIoResources = &ioResources; |
| *pIoResources = rResources; |
| } |
| |
| // ------------------------------------------------------------------- |
| // PROPFIND - propnames |
| // ------------------------------------------------------------------- |
| void CurlSession::PROPFIND( const rtl::OUString & inPath, |
| const Depth inDepth, |
| std::vector< DAVResourceInfo > & ioResInfo, |
| const DAVRequestEnvironment & rEnv ) |
| throw( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "PROPFIND line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| propfind( curlRequest, inPath, inDepth, NULL, true, rEnv ); |
| |
| const std::vector< DAVResourceInfo > rResInfo( parseWebDAVPropNameResponse( curlRequest.getResponseBody().get() ) ); |
| std::vector< DAVResourceInfo > *pIoResInfo = &ioResInfo; |
| *pIoResInfo = rResInfo; |
| } |
| |
| // ------------------------------------------------------------------- |
| // PROPPATCH |
| // ------------------------------------------------------------------- |
| void CurlSession::PROPPATCH( const rtl::OUString & inPath, |
| const std::vector< ProppatchValue > & inValues, |
| const DAVRequestEnvironment & rEnv ) |
| throw( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "PROPPATCH line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| // check whether a lock on this resource is already owned |
| rtl::OUString aUri( composeCurrentUri( inPath ) ); |
| ucb::Lock inLock; |
| CurlLock * pLock = m_aCurlLockStore.findByUri( aUri ); |
| if ( pLock ) |
| { |
| inLock = pLock->getLock(); |
| } |
| if ( inLock.LockTokens.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "If", |
| ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() ); |
| } |
| |
| rtl::OString xml = ProppatchRequest::generatePROPPATCHRequestBody( inValues ); |
| if ( xml.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "Content-Type", "application/xml" ); |
| curlRequest.setRequestBody( xml.getStr(), xml.getLength() ); |
| } |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| CURLcode rc = curlRequest.proppatch( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // HEAD |
| // ------------------------------------------------------------------- |
| void CurlSession::HEAD( const ::rtl::OUString & inPath, |
| const std::vector< ::rtl::OUString > & inHeaderNames, |
| DAVResource & ioResource, |
| const DAVRequestEnvironment & rEnv ) |
| throw( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "HEAD line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init(rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| ioResource.uri = inPath; |
| ioResource.properties.clear(); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| CURLcode rc = curlRequest.head( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| responseHeadersToDAVResource( curlRequest.getResponseHeaders(), inHeaderNames, ioResource ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // GET |
| // ------------------------------------------------------------------- |
| uno::Reference< io::XInputStream > |
| CurlSession::GET( const rtl::OUString & inPath, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "GET line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| CURLcode rc = curlRequest.get( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| |
| return uno::Reference< io::XInputStream >( curlRequest.getResponseBody().get() ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // GET |
| // ------------------------------------------------------------------- |
| void CurlSession::GET( const rtl::OUString & inPath, |
| uno::Reference< io::XOutputStream > & ioOutputStream, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "GET line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| curlRequest.saveResponseBodyTo( ioOutputStream ); |
| CURLcode rc = curlRequest.get( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // GET |
| // ------------------------------------------------------------------- |
| uno::Reference< io::XInputStream > |
| CurlSession::GET( const rtl::OUString & inPath, |
| const std::vector< ::rtl::OUString > & inHeaderNames, |
| DAVResource & ioResource, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "GET line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| CURLcode rc = curlRequest.get( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| responseHeadersToDAVResource( curlRequest.getResponseHeaders(), inHeaderNames, ioResource ); |
| |
| return uno::Reference< io::XInputStream >( curlRequest.getResponseBody().get() ); |
| } |
| |
| |
| // ------------------------------------------------------------------- |
| // GET |
| // ------------------------------------------------------------------- |
| void CurlSession::GET( const rtl::OUString & inPath, |
| uno::Reference< io::XOutputStream > & ioOutputStream, |
| const std::vector< ::rtl::OUString > & inHeaderNames, |
| DAVResource & ioResource, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "GET line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| curlRequest.saveResponseBodyTo( ioOutputStream ); |
| CURLcode rc = curlRequest.get( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| responseHeadersToDAVResource( curlRequest.getResponseHeaders(), inHeaderNames, ioResource ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // PUT |
| // ------------------------------------------------------------------- |
| void CurlSession::PUT( const rtl::OUString & inPath, |
| const uno::Reference< io::XInputStream > & inInputStream, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "PUT line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| uno::Sequence< sal_Int8 > aDataToSend; |
| if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) ) |
| throw DAVException( DAVException::DAV_INVALID_ARG ); |
| curlRequest.setRequestBody( reinterpret_cast< const char * >( aDataToSend.getConstArray() ), |
| aDataToSend.getLength() ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| // check whether a lock on this resource is already owned |
| rtl::OUString aUri( composeCurrentUri( inPath ) ); |
| ucb::Lock inLock; |
| CurlLock * pLock = m_aCurlLockStore.findByUri( aUri ); |
| if ( pLock ) |
| { |
| inLock = pLock->getLock(); |
| } |
| if ( inLock.LockTokens.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "If", |
| ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() ); |
| } |
| |
| CURLcode rc = curlRequest.put( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // POST |
| // ------------------------------------------------------------------- |
| uno::Reference< io::XInputStream > |
| CurlSession::POST( const rtl::OUString & inPath, |
| const rtl::OUString & rContentType, |
| const rtl::OUString & rReferer, |
| const uno::Reference< io::XInputStream > & inInputStream, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "POST line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| uno::Sequence< sal_Int8 > aDataToSend; |
| if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) ) |
| throw DAVException( DAVException::DAV_INVALID_ARG ); |
| curlRequest.setRequestBody( reinterpret_cast< const char * >( aDataToSend.getConstArray() ), |
| aDataToSend.getLength() ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| if ( !rContentType.isEmpty() ) |
| curlRequest.addHeader( "Content-Type", rtl::OUStringToOString( rContentType, RTL_TEXTENCODING_UTF8 ).getStr() ); |
| if ( !rReferer.isEmpty() ) |
| curlRequest.addHeader( "Referer", rtl::OUStringToOString( rReferer, RTL_TEXTENCODING_UTF8 ).getStr() ); |
| |
| // check whether a lock on this resource is already owned |
| rtl::OUString aUri( composeCurrentUri( inPath ) ); |
| ucb::Lock inLock; |
| CurlLock * pLock = m_aCurlLockStore.findByUri( aUri ); |
| if ( pLock ) |
| { |
| inLock = pLock->getLock(); |
| } |
| if ( inLock.LockTokens.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "If", |
| ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() ); |
| } |
| |
| CURLcode rc = curlRequest.post( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| return uno::Reference< io::XInputStream >( curlRequest.getResponseBody().get() ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // POST |
| // ------------------------------------------------------------------- |
| void CurlSession::POST( const rtl::OUString & inPath, |
| const rtl::OUString & rContentType, |
| const rtl::OUString & rReferer, |
| const uno::Reference< io::XInputStream > & inInputStream, |
| uno::Reference< io::XOutputStream > & oOutputStream, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "POST line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| uno::Sequence< sal_Int8 > aDataToSend; |
| if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) ) |
| throw DAVException( DAVException::DAV_INVALID_ARG ); |
| curlRequest.setRequestBody( reinterpret_cast< const char * >( aDataToSend.getConstArray() ), |
| aDataToSend.getLength() ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| if ( !rContentType.isEmpty() ) |
| curlRequest.addHeader( "Content-Type", rtl::OUStringToOString( rContentType, RTL_TEXTENCODING_UTF8 ).getStr() ); |
| if ( !rReferer.isEmpty() ) |
| curlRequest.addHeader( "Referer", rtl::OUStringToOString( rReferer, RTL_TEXTENCODING_UTF8 ).getStr() ); |
| |
| // check whether a lock on this resource is already owned |
| rtl::OUString aUri( composeCurrentUri( inPath ) ); |
| ucb::Lock inLock; |
| CurlLock * pLock = m_aCurlLockStore.findByUri( aUri ); |
| if ( pLock ) |
| { |
| inLock = pLock->getLock(); |
| } |
| if ( inLock.LockTokens.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "If", |
| ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() ); |
| } |
| |
| curlRequest.saveResponseBodyTo( oOutputStream ); |
| CURLcode rc = curlRequest.post( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // MKCOL |
| // ------------------------------------------------------------------- |
| void CurlSession::MKCOL( const rtl::OUString & inPath, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "MKCOL line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| // check whether a lock on this resource is already owned |
| rtl::OUString aUri( composeCurrentUri( inPath ) ); |
| ucb::Lock inLock; |
| CurlLock * pLock = m_aCurlLockStore.findByUri( aUri ); |
| if ( pLock ) |
| { |
| inLock = pLock->getLock(); |
| } |
| if ( inLock.LockTokens.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "If", |
| ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() ); |
| } |
| |
| CURLcode rc = curlRequest.mkcol( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // COPY |
| // ------------------------------------------------------------------- |
| void CurlSession::COPY( const rtl::OUString & inSourceURL, |
| const rtl::OUString & inDestinationURL, |
| const DAVRequestEnvironment & rEnv, |
| sal_Bool inOverWrite ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "COPY line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| curlRequest.addHeader( "Destination", rtl::OUStringToOString( inDestinationURL, RTL_TEXTENCODING_UTF8 ).getStr() ); |
| curlRequest.addHeader( "Overwrite", inOverWrite? "T" : "F" ); |
| |
| // check whether a lock on the destination resource is already owned |
| rtl::OUString aUri( composeCurrentUri( inDestinationURL ) ); |
| ucb::Lock inLock; |
| CurlLock * pLock = m_aCurlLockStore.findByUri( aUri ); |
| if ( pLock ) |
| { |
| inLock = pLock->getLock(); |
| } |
| if ( inLock.LockTokens.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "If", |
| ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() ); |
| } |
| |
| CURLcode rc = curlRequest.copy( m_aUri, CurlUri( inSourceURL ).GetPath() ); |
| processResponse( curlRequest, rc ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // MOVE |
| // ------------------------------------------------------------------- |
| void CurlSession::MOVE( const rtl::OUString & inSourceURL, |
| const rtl::OUString & inDestinationURL, |
| const DAVRequestEnvironment & rEnv, |
| sal_Bool inOverWrite ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "MOVE line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| curlRequest.addHeader( "Destination", rtl::OUStringToOString( inDestinationURL, RTL_TEXTENCODING_UTF8 ).getStr() ); |
| curlRequest.addHeader( "Overwrite", inOverWrite? "T" : "F" ); |
| |
| // check whether a lock on the destination resource is already owned |
| rtl::OUString aUri( composeCurrentUri( inDestinationURL ) ); |
| ucb::Lock inLock; |
| CurlLock * pLock = m_aCurlLockStore.findByUri( aUri ); |
| if ( pLock ) |
| { |
| inLock = pLock->getLock(); |
| } |
| if ( inLock.LockTokens.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "If", |
| ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() ); |
| } |
| |
| CURLcode rc = curlRequest.copy( m_aUri, CurlUri( inSourceURL ).GetPath() ); |
| processResponse( curlRequest, rc ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // DESTROY |
| // ------------------------------------------------------------------- |
| void CurlSession::DESTROY( const rtl::OUString & inPath, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "DESTROY line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| // check whether a lock on this resource is already owned |
| rtl::OUString aUri( composeCurrentUri( inPath ) ); |
| ucb::Lock inLock; |
| CurlLock * pLock = m_aCurlLockStore.findByUri( aUri ); |
| if ( pLock ) |
| { |
| inLock = pLock->getLock(); |
| } |
| if ( inLock.LockTokens.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "If", |
| ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() ); |
| } |
| |
| CURLcode rc = curlRequest.delete_( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| namespace |
| { |
| sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart, |
| sal_Int32 timeout ) |
| { |
| TimeValue aEnd; |
| osl_getSystemTime( &aEnd ); |
| |
| // Try to estimate a safe absolute time for sending the |
| // lock refresh request. |
| sal_Int32 lastChanceToSendRefreshRequest = DAVINFINITY; |
| if ( timeout != DAVINFINITY ) |
| { |
| sal_Int32 calltime = aEnd.Seconds - rStart.Seconds; |
| if ( calltime <= timeout ) |
| { |
| lastChanceToSendRefreshRequest |
| = aEnd.Seconds + timeout - calltime; |
| } |
| else |
| { |
| OSL_TRACE( "No chance to refresh lock before timeout!" ); |
| } |
| } |
| return lastChanceToSendRefreshRequest; |
| } |
| |
| } // namespace |
| |
| // ------------------------------------------------------------------- |
| // LOCK (set new lock) |
| // ------------------------------------------------------------------- |
| void CurlSession::LOCK( const ::rtl::OUString & inPath, |
| ucb::Lock & inLock, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "LOCK line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| // before locking, search in the lock store if we already own a lock for this resource |
| // if present, return with exception DAV_LOCKED_SELF |
| rtl::OUString aUri( composeCurrentUri( inPath ) ); |
| CurlLock * pLock = m_aCurlLockStore.findByUri( aUri ); |
| if ( pLock ) |
| { |
| // already present, meaning already locked by the same AOO session and already in the lockstore |
| // just return, nothing to do |
| return; |
| } |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| if ( inLock.Timeout == -1 ) |
| curlRequest.addHeader( "Timeout", "Infinite" ); |
| else |
| curlRequest.addHeader( "Timeout", "Second-" + rtl::OString::valueOf( inLock.Timeout ) ); |
| |
| switch ( inLock.Depth ) |
| { |
| //i126305 TODO investigate on this case... |
| case ucb::LockDepth_MAKE_FIXED_SIZE: |
| |
| case ucb::LockDepth_ZERO: |
| curlRequest.addHeader( "Depth", "0" ); |
| break; |
| case ucb::LockDepth_ONE: |
| curlRequest.addHeader( "Depth", "1" ); |
| break; |
| case ucb::LockDepth_INFINITY: |
| curlRequest.addHeader( "Depth", "infinity" ); |
| break; |
| } |
| |
| rtl::OString xml = LockRequest::generateRequestBody( inLock ); |
| curlRequest.addHeader( "Content-Type", "application/xml" ); |
| curlRequest.setRequestBody( xml.getStr(), xml.getLength() ); |
| |
| TimeValue startCall; |
| osl_getSystemTime( &startCall ); |
| |
| CURLcode rc = curlRequest.lock( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| |
| // the returned property, a sequence of locks |
| // only the first is used |
| const DAVPropertyValue outLock( parseWebDAVLockResponse( curlRequest.getResponseBody().get() ) ); |
| if(outLock.Name.compareToAscii(RTL_CONSTASCII_STRINGPARAM( "DAV:lockdiscovery" )) == 0 ) |
| { |
| // got a lock, use only the first returned |
| uno::Sequence< ucb::Lock > aLocks; |
| outLock.Value >>= aLocks; |
| ucb::Lock aLock = aLocks[0]; |
| |
| CurlLock* aNewLock = new CurlLock( aLock, aUri, inPath ); |
| // add the store the new lock |
| m_aCurlLockStore.addLock(aNewLock,this, |
| lastChanceToSendRefreshRequest( |
| startCall, static_cast< sal_Int32 >(aLock.Timeout) ) ); |
| } |
| } |
| |
| // ------------------------------------------------------------------- |
| // LOCK (refresh existing lock from DAVResourceAccess) |
| // ------------------------------------------------------------------- |
| sal_Int64 CurlSession::LOCK( const ::rtl::OUString & /*inPath*/, |
| sal_Int64 nTimeout, |
| const DAVRequestEnvironment & /*rEnv*/ ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "LOCK line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| return nTimeout; |
| /* |
| // Try to get the neon lock from lock store |
| CurlLock * theLock |
| = m_aCurlLockStore.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_aCurlLockStore.updateLock( theLock, |
| lastChanceToSendRefreshRequest( |
| startCall, theLock->timeout ) ); |
| } |
| |
| HandleError( theRetVal, inPath, rEnv ); |
| |
| return theLock->timeout; |
| */ |
| } |
| |
| // ------------------------------------------------------------------- |
| // LOCK (refresh existing lock from CurlLockStore) |
| // ------------------------------------------------------------------- |
| bool CurlSession::LOCK( CurlLock * pLock, |
| sal_Int32 & rlastChanceToSendRefreshRequest ) |
| { |
| m_aLogger.log( LogLevel::INFO, "LOCK line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init(); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| const ucb::Lock & inLock = pLock->getLock(); |
| rtl::OUString inPath = pLock->getResourcePath(); |
| |
| if ( inLock.Timeout == -1 ) |
| curlRequest.addHeader( "Timeout", "Infinite" ); |
| else |
| curlRequest.addHeader( "Timeout", "Second-" + rtl::OString::valueOf( inLock.Timeout ) ); |
| |
| switch ( inLock.Depth ) |
| { |
| //i126305 TODO investigate on this case... |
| case ucb::LockDepth_MAKE_FIXED_SIZE: |
| |
| case ucb::LockDepth_ZERO: |
| curlRequest.addHeader( "Depth", "0" ); |
| break; |
| case ucb::LockDepth_ONE: |
| curlRequest.addHeader( "Depth", "1" ); |
| break; |
| case ucb::LockDepth_INFINITY: |
| curlRequest.addHeader( "Depth", "infinity" ); |
| break; |
| } |
| |
| if ( inLock.LockTokens.getLength() > 0 ) |
| { |
| curlRequest.addHeader( "If", |
| ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() ); |
| } |
| |
| rtl::OString xml = LockRequest::generateRequestBody( inLock ); |
| curlRequest.addHeader( "Content-Type", "application/xml" ); |
| curlRequest.setRequestBody( xml.getStr(), xml.getLength() ); |
| |
| TimeValue startCall; |
| osl_getSystemTime( &startCall ); |
| |
| CURLcode rc = curlRequest.lock( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| |
| // the returned property, a sequence of locks |
| // only the first is used |
| const DAVPropertyValue outLock( parseWebDAVLockResponse( curlRequest.getResponseBody().get() ) ); |
| uno::Sequence< ucb::Lock > aLocks; |
| outLock.Value >>= aLocks; |
| ucb::Lock aLock = aLocks[0]; |
| |
| // if ok, update the lastchance refresh time in lock |
| rlastChanceToSendRefreshRequest |
| = lastChanceToSendRefreshRequest( startCall, static_cast< sal_Int32 >(aLock.Timeout) ); |
| |
| return true; |
| } |
| |
| // ------------------------------------------------------------------- |
| // UNLOCK called from external (DAVResourceAccess) |
| // ------------------------------------------------------------------- |
| void CurlSession::UNLOCK( const ::rtl::OUString & inPath, |
| const DAVRequestEnvironment & rEnv ) |
| throw ( DAVException ) |
| { |
| m_aLogger.log( LogLevel::INFO, "UNLOCK line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| rtl::OUString aUri( composeCurrentUri( inPath ) ); |
| CurlLock * pLock = m_aCurlLockStore.findByUri( aUri ); |
| if ( !pLock ) |
| { |
| throw DAVException( DAVException::DAV_NOT_LOCKED ); |
| } |
| |
| Init( rEnv ); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| addEnvironmentRequestHeaders( curlRequest, rEnv ); |
| |
| CredentialsData credsData( this, curlRequest, rEnv ); |
| curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData ); |
| |
| ucb::Lock inLock = pLock->getLock(); |
| curlRequest.addHeader( "Lock-Token", |
| ( "<" + rtl::OUStringToOString( inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">" ).getStr() ); |
| |
| // remove lock from lockstore |
| // so, if something goes wrong, we don't refresh it anymore |
| m_aCurlLockStore.removeLock( pLock ); |
| delete pLock; |
| |
| CURLcode rc = curlRequest.unlock( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| } |
| |
| // ------------------------------------------------------------------- |
| // UNLOCK (called from CurlLockStore) |
| // ------------------------------------------------------------------- |
| bool CurlSession::UNLOCK( CurlLock * pLock ) |
| { |
| m_aLogger.log( LogLevel::INFO, "UNLOCK line $1$", (sal_Int32)__LINE__ ); |
| |
| osl::Guard< osl::Mutex > theGuard( m_aMutex ); |
| |
| Init(); |
| CurlRequest curlRequest( m_pCurl ); |
| |
| rtl::OUString inPath = pLock->getResourcePath(); |
| ucb::Lock inLock = pLock->getLock(); |
| curlRequest.addHeader( "Lock-Token", |
| ( "<" + rtl::OUStringToOString( inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">" ).getStr() ); |
| |
| CURLcode rc = curlRequest.unlock( m_aUri, inPath ); |
| processResponse( curlRequest, rc ); |
| return true; |
| } |
| |
| // ------------------------------------------------------------------- |
| void CurlSession::abort() |
| throw ( DAVException ) |
| { |
| // 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 ); |
| } |
| |
| // ------------------------------------------------------------------- |
| const ucbhelper::InternetProxyServer & CurlSession::getProxySettings() const |
| { |
| if ( m_aUri.GetScheme().equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "http" ) ) || |
| m_aUri.GetScheme().equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "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(), |
| rtl::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< rtl::OUString > & rTokens |
| = rLocks[ n ].LockTokens; |
| for ( sal_Int32 m = 0; m < rTokens.getLength(); ++m ) |
| { |
| if ( rTokens[ m ].equalsAscii( token ) ) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace |
| */ |
| |
| // ------------------------------------------------------------------- |
| // This method doesn't seem to be used. |
| // In any case the default behavior is to ask a lock with a life of 3 minutes |
| // it will then be refreshed automatically (see CurlLockStore class) |
| // In case of AOO crash the lock will expire by itself |
| bool CurlSession::removeExpiredLocktoken( const rtl::OUString & /*inURL*/, |
| const DAVRequestEnvironment & /*rEnv*/ ) |
| { |
| return true; |
| /* |
| CurlLock * theLock = m_aCurlLockStore.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< rtl::OUString > aPropNames; |
| aPropNames.push_back( DAVProperties::LOCKDISCOVERY ); |
| |
| PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv ); |
| |
| if ( aResources.size() == 0 ) |
| 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 |
| OSL_TRACE( "CurlSession::removeExpiredLocktoken: Removing " |
| " expired lock token for %s. token: %s", |
| rtl::OUStringToOString( inURL, |
| RTL_TEXTENCODING_UTF8 ).getStr(), |
| theLock->token ); |
| |
| m_aCurlLockStore.removeLock( theLock ); |
| ne_lock_destroy( theLock ); |
| return true; |
| } |
| catch ( DAVException const & ) |
| { |
| } |
| return false; |
| */ |
| } |
| |
| // ------------------------------------------------------------------- |
| // static |
| bool |
| CurlSession::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 ); |
| rtl_copyMemory( (void*)( rData.getArray() + nPos ), |
| (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; |
| } |
| |
| // --------------------------------------------------------------------- |
| sal_Bool |
| CurlSession::isDomainMatch( rtl::OUString certHostName ) |
| { |
| rtl::OUString hostName = getHostName(); |
| |
| if (hostName.equalsIgnoreAsciiCase( certHostName ) ) |
| return sal_True; |
| |
| if ( 0 == certHostName.indexOf( rtl::OUString::createFromAscii( "*" ) ) && |
| hostName.getLength() >= certHostName.getLength() ) |
| { |
| rtl::OUString cmpStr = certHostName.copy( 1 ); |
| |
| if ( hostName.matchIgnoreAsciiCase( |
| cmpStr, hostName.getLength() - cmpStr.getLength() ) ) |
| return sal_True; |
| } |
| return sal_False; |
| } |