xref: /trunk/main/ucb/source/ucp/webdav/CurlSession.cxx (revision f7b97bf7)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_webdav.hxx"
26 
27 #include <hash_map>
28 #include <vector>
29 #include <string.h>
30 #include <rtl/string.h>
31 #include <rtl/strbuf.hxx>
32 #include <rtl/ustrbuf.hxx>
33 #include <osl/time.h>
34 #include "comphelper/sequence.hxx"
35 #include "ucbhelper/simplecertificatevalidationrequest.hxx"
36 
37 #include "DAVAuthListener.hxx"
38 #include "CurlTypes.hxx"
39 #include "CurlSession.hxx"
40 #include "LockRequest.hxx"
41 #include "PropfindRequest.hxx"
42 #include "ProppatchRequest.hxx"
43 #include "CurlInputStream.hxx"
44 #include "UCBDeadPropertyValue.hxx"
45 #include "webdavuseragent.hxx"
46 #include "webdavresponseparser.hxx"
47 #include "webdavprovider.hxx"
48 
49 
50 #include <com/sun/star/logging/LogLevel.hpp>
51 #include <com/sun/star/security/XCertificate.hpp>
52 #include <com/sun/star/security/CertificateValidity.hpp>
53 #include <com/sun/star/security/CertificateContainerStatus.hpp>
54 #include <com/sun/star/security/CertAltNameEntry.hpp>
55 #include <com/sun/star/security/XSanExtension.hpp>
56 #include <com/sun/star/ucb/Lock.hpp>
57 #include <com/sun/star/xml/crypto/XSEInitializer.hpp>
58 
59 using namespace com::sun::star;
60 using namespace com::sun::star::logging;
61 using namespace http_dav_ucp;
62 
63 #define OID_SUBJECT_ALTERNATIVE_NAME "2.5.29.17"
64 
65 struct CredentialsData
66 {
CredentialsDataCredentialsData67     CredentialsData( CurlSession *curlSession, CurlRequest &curlRequest, const DAVRequestEnvironment &requestEnvironment )
68     : session( curlSession)
69     , request( curlRequest )
70     , env( requestEnvironment )
71     {}
72 
73     CurlSession *session;
74     CurlRequest &request;
75     const DAVRequestEnvironment &env;
76 };
77 
78 // -------------------------------------------------------------------
79 // static members!
80 CurlLockStore CurlSession::m_aCurlLockStore;
81 
82 
83 // -------------------------------------------------------------------
84 // Constructor
85 // -------------------------------------------------------------------
CurlSession(const rtl::Reference<DAVSessionFactory> & rSessionFactory,const rtl::OUString & inUri,const ucbhelper::InternetProxyDecider & rProxyDecider)86 CurlSession::CurlSession(
87         const rtl::Reference< DAVSessionFactory > & rSessionFactory,
88         const rtl::OUString& inUri,
89         const ucbhelper::InternetProxyDecider & rProxyDecider )
90     throw ( DAVException )
91     : DAVSession( rSessionFactory )
92     , m_aMutex()
93     , m_aContext( m_xFactory->getServiceFactory() )
94     , m_aLogger( m_aContext.getUNOContext(), WEBDAV_CONTENT_PROVIDER_SERVICE_NAME )
95     , m_aUri( inUri )
96     , m_aProxyName()
97     , m_nProxyPort( 0 )
98     , m_aServerHeaderField()
99     , m_pCurl( 0 )
100     , m_bUseChunkedEncoding( false )
101     , m_bTransferEncodingSwitched( false )
102     , m_rProxyDecider( rProxyDecider )
103     , m_aEnv()
104 {
105     m_pCurl = curl_easy_init();
106 
107     curl_easy_setopt( m_pCurl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
108     curl_easy_setopt( m_pCurl, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
109 
110     curl_easy_setopt( m_pCurl, CURLOPT_SSL_CTX_FUNCTION, Curl_SSLContextCallback );
111     curl_easy_setopt( m_pCurl, CURLOPT_SSL_CTX_DATA, this );
112 
113     // If a certificate's commmon name / alt name doesn't match the hostname we are
114     // connecting to, Curl will refuse to connect. Disable this, as we do that check
115     // ourselves, and give the user the option of connecting anyway.
116     //
117     // Note also, how "man CURLOPT_SSL_VERIFYHOST" tells us that setting 0 here
118     // disables SNI, which is bad news, some servers require SNI. However reading Curl
119     // 8.6.0's Curl_ssl_peer_init() in file lib/vtls/vtls.c shows that SNI is sent
120     // regardless, as long as we are connecting to a domain name, NOT an IP address.
121     // Tests confirm this. For OpenSSL anyway - other Curl crypto providers are stricter...
122     curl_easy_setopt( m_pCurl, CURLOPT_SSL_VERIFYHOST, 0 );
123 
124     if ( m_aLogger.getLogLevel() == LogLevel::FINEST )
125     {
126         curl_easy_setopt( m_pCurl, CURLOPT_DEBUGFUNCTION, Curl_DebugCallback );
127         curl_easy_setopt( m_pCurl, CURLOPT_DEBUGDATA, this );
128         curl_easy_setopt( m_pCurl, CURLOPT_VERBOSE, 1L);
129     }
130 
131     // Create a certificate container.
132     if( !m_aContext.createComponent( "com.sun.star.security.CertificateContainer", m_xCertificateContainer ) )
133         throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Failed to create com.sun.star.security.CertificateContainer" ) );
134     uno::Reference< xml::crypto::XSEInitializer > xSEInitializer;
135     if( !m_aContext.createComponent( "com.sun.star.xml.crypto.SEInitializer", xSEInitializer ) )
136         throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Failed to create com.sun.star.xml.crypto.SEInitializer" ) );
137     m_xSecurityContext = xSEInitializer->createSecurityContext( rtl::OUString() );
138     if( m_xSecurityContext.is() )
139         m_xSecurityEnv = m_xSecurityContext->getSecurityEnvironment();
140     if ( ! m_xSecurityContext.is() || ! m_xSecurityEnv.is())
141         throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii( "Failure creating security services for certificate verification" ) );
142 
143     // Populate one nonsense certificate, which we won't ever really use, just to get Curl to initialize:
144     struct curl_blob blob;
145     blob.data = (void*)
146         "-----BEGIN CERTIFICATE-----\n"
147         "MIIC/zCCAeegAwIBAgIUQYFHL3Bv7alQBtXQWy9SXGusm5YwDQYJKoZIhvcNAQEL\n"
148         "BQAwDzENMAsGA1UEAwwEVEVTVDAeFw0yNDA0MjExNzU3MzdaFw0yNDA0MjIxNzU3\n"
149         "MzdaMA8xDTALBgNVBAMMBFRFU1QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n"
150         "AoIBAQCZSXla2TE7GU6xOfie5uilpRf7KQflWcQRgwTCFhk0yzbsSPJYdqbuUqfx\n"
151         "k0pV9Sx8GIkvc7jKQBwS79T15qn6dAZOF40x/k2jEMq150oc/80+dqeNP2jWvxv7\n"
152         "FjgBKSiuGUaHldy6XU3NhrA9G1Ys2/yHQRXER1NTeknEzPiPlobRUk1sNR2Prc5r\n"
153         "0u6cdUWGhbDOKDV9jjvA/14jmaAK+vUqrzzAdiOHVrkglA5oyBKX0BUokRCa8jID\n"
154         "34tH9zeuvozA3xXCi8l9to+HOgT/n7LAGeOSnNPeSHC/xkwumt/rJ05tL9DXg6Ud\n"
155         "3Pjf8KZM+FWJsjoJkcwBR0P2Qh3FAgMBAAGjUzBRMB0GA1UdDgQWBBR7pCl5msAz\n"
156         "rGApirAQ+/tFuHl5kDAfBgNVHSMEGDAWgBR7pCl5msAzrGApirAQ+/tFuHl5kDAP\n"
157         "BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBDJ1S51MKlafDAfFbU\n"
158         "DJcxw3JNHn+VxQuaQQpeeqLIn3rgKHRBV9eOTYHf8AMoCYdQfPs1z45vqBmcyrDw\n"
159         "LoXL6vlUbSLUuYFyfCaFup3bbh2lLozsLcD6bcvV07amX6V3u0ZOKpwqhg+k/IJd\n"
160         "cPVM8jYAnNZZYD6rMHWnW5ZgMFSzSj3Jyyaov/3zwixvFZdViBG+R2RmJZVgMiFP\n"
161         "PNxY3USKiHqdwZIszf3G63Ku0EYtFf3KN8YpoqSMDCDfjL0NhJOtkBUs5HL+4XfK\n"
162         "hToBqJojDMLFRdVIhPQX1LoPd92CUwhueIrYTikScAqY2TIwXpPH0kBjfrVDus8s\n"
163         "vPAk\n"
164         "-----END CERTIFICATE-----";
165     blob.len = strlen( (char*) blob.data ) + 1;
166     blob.flags = CURL_BLOB_COPY;
167     CURLcode rc;
168     rc = curl_easy_setopt( m_pCurl, CURLOPT_CAINFO_BLOB, &blob );
169     if( rc != CURLE_OK )
170         throw DAVException( DAVException::DAV_SESSION_CREATE, rtl::OUString::createFromAscii("Error initializing Curl certificate" ) );
171 
172     m_aLogger.log( LogLevel::INFO, "CurlSession::CurlSession with URL $1$",
173         rtl::OUStringToOString( inUri, RTL_TEXTENCODING_UTF8 ).getStr() );
174 }
175 
176 // -------------------------------------------------------------------
177 // Destructor
178 // -------------------------------------------------------------------
~CurlSession()179 CurlSession::~CurlSession( )
180 {
181     if ( m_pCurl )
182     {
183         curl_easy_cleanup( m_pCurl );
184         m_pCurl = 0;
185         m_aLogger.log( LogLevel::INFO, "CurlSession::~CurlSession: closed curl session");
186     }
187 }
188 
189 // -------------------------------------------------------------------
Init(const DAVRequestEnvironment & rEnv)190 void CurlSession::Init( const DAVRequestEnvironment & rEnv )
191   throw ( DAVException )
192 {
193     osl::Guard< osl::Mutex > theGuard( m_aMutex );
194     m_aEnv = rEnv;
195     Init();
196 }
197 
198 // -------------------------------------------------------------------
Init()199 void CurlSession::Init()
200     throw ( DAVException )
201 {
202     osl::Guard< osl::Mutex > theGuard( m_aMutex );
203 
204     const sal_Char *url = rtl::OUStringToOString( m_aUri.GetURI(), RTL_TEXTENCODING_UTF8 ).getStr();
205     CURLcode rc;
206     rc = curl_easy_setopt( m_pCurl, CURLOPT_URL, url );
207     if ( rc != CURLE_OK  )
208         throw DAVException( DAVException::DAV_SESSION_CREATE,
209                             CurlUri::makeConnectionEndPointString( m_aUri.GetHost(), m_aUri.GetPort() ) );
210 
211     const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
212     if ( ( rProxyCfg.aName != m_aProxyName )
213         || ( rProxyCfg.nPort != m_nProxyPort ) )
214     {
215         m_aProxyName = rProxyCfg.aName;
216         m_nProxyPort = rProxyCfg.nPort;
217         if ( !m_aProxyName.isEmpty() )
218         {
219             m_aLogger.log( LogLevel::INFO, "Using $1$ proxy server at $2$:$3$",
220                 m_aUri.GetScheme(), m_aProxyName, m_nProxyPort );
221             curl_easy_setopt( m_pCurl, CURLOPT_PROXY, rtl::OUStringToOString( m_aProxyName, RTL_TEXTENCODING_UTF8 ).getStr() );
222             curl_easy_setopt( m_pCurl, CURLOPT_PROXYPORT, (long)m_nProxyPort );
223             if ( m_aUri.GetScheme().equalsAscii( "https" ) )
224                 curl_easy_setopt( m_pCurl, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS );
225             else
226                 curl_easy_setopt( m_pCurl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
227             // no other proxy types are implemented by AOO
228         }
229         else
230         {
231             // Empty string as opposed to NULL, means don't use the default curl proxy.
232             m_aLogger.log( LogLevel::INFO, "Not using a proxy server" );
233             curl_easy_setopt( m_pCurl, CURLOPT_PROXY, "" );
234         }
235         // if we change the proxy settings, clear the credentials for the previous proxy too
236         curl_easy_setopt( m_pCurl, CURLOPT_PROXYUSERNAME, "" );
237         curl_easy_setopt( m_pCurl, CURLOPT_PROXYPASSWORD, "" );
238     }
239 }
240 
isSSLNeeded()241 bool CurlSession::isSSLNeeded()
242 {
243     return m_aUri.GetScheme().equalsIgnoreAsciiCase( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "https" ) ) );
244 }
245 
246 // -------------------------------------------------------------------
247 // helper function
248 // it composes the uri for lockstore registration
composeCurrentUri(const rtl::OUString & inPath)249 rtl::OUString CurlSession::composeCurrentUri(const rtl::OUString & inPath)
250 {
251     rtl::OUString aScheme( m_aUri.GetScheme() );
252     rtl::OUStringBuffer aBuf( aScheme );
253     aBuf.appendAscii( "://" );
254     if ( m_aUri.GetUserName().getLength() > 0 )
255     {
256         aBuf.append( m_aUri.GetUserName() );
257         if ( m_aUri.GetPassword().getLength() > 0 )
258         {
259             aBuf.appendAscii( ":" );
260             aBuf.append( m_aUri.GetPassword() );
261         }
262         aBuf.appendAscii( "@" );
263     }
264     // Is host a numeric IPv6 address?
265     if ( ( m_aUri.GetHost().indexOf( ':' ) != -1 ) &&
266          ( m_aUri.GetHost()[ 0 ] != sal_Unicode( '[' ) ) )
267     {
268         aBuf.appendAscii( "[" );
269         aBuf.append( m_aUri.GetHost() );
270         aBuf.appendAscii( "]" );
271     }
272     else
273     {
274         aBuf.append( m_aUri.GetHost() );
275     }
276 
277     // append port, but only, if not default port.
278     bool bAppendPort = true;
279     sal_Int32 aPort = m_aUri.GetPort();
280     switch ( aPort )
281     {
282     case DEFAULT_HTTP_PORT:
283         bAppendPort = aScheme.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "http" ) );
284         break;
285 
286     case DEFAULT_HTTPS_PORT:
287         bAppendPort = !aScheme.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "https" ) );
288         break;
289     }
290     if ( bAppendPort )
291     {
292         aBuf.appendAscii( ":" );
293         aBuf.append( rtl::OUString::valueOf( aPort ) );
294     }
295     aBuf.append( inPath );
296 
297     rtl::OUString   aUri(aBuf.makeStringAndClear() );
298     return aUri;
299 }
300 
301 // -------------------------------------------------------------------
302 // virtual
CanUse(const rtl::OUString & inUri)303 sal_Bool CurlSession::CanUse( const rtl::OUString & inUri )
304 {
305     try
306     {
307         CurlUri theUri( inUri );
308         if ( ( theUri.GetPort() == m_aUri.GetPort() ) &&
309              ( theUri.GetHost() == m_aUri.GetHost() ) &&
310              ( theUri.GetScheme() == m_aUri.GetScheme() ) )
311         {
312             return sal_True;
313         }
314     }
315     catch ( DAVException const & )
316     {
317         return sal_False;
318     }
319     return sal_False;
320 }
321 
322 // -------------------------------------------------------------------
323 // virtual
UsesProxy()324 sal_Bool CurlSession::UsesProxy()
325 {
326     Init();
327     return ( m_aProxyName.getLength() > 0 );
328 }
329 
Curl_DebugCallback(CURL *,curl_infotype type,unsigned char * data,size_t size,void * userdata)330 int CurlSession::Curl_DebugCallback( CURL *, curl_infotype type, unsigned char *data, size_t size, void* userdata )
331 {
332     CurlSession *session = static_cast< CurlSession* >( userdata );
333     return session->curlDebugOutput( type, reinterpret_cast<char*>( data ), size );
334 }
335 
curlDebugOutput(curl_infotype type,char * data,int size)336 int CurlSession::curlDebugOutput( curl_infotype type, char *data, int size )
337 {
338     const char *prefix;
339     switch ( type )
340     {
341         case CURLINFO_TEXT:
342             prefix = "[CurlINFO  ]";
343             break;
344         case CURLINFO_HEADER_IN:
345             prefix = "[CurlHDR <-]";
346             break;
347         case CURLINFO_HEADER_OUT:
348             prefix = "[CurlHDR ->]";
349             break;
350         case CURLINFO_DATA_IN:
351             prefix = "[CurlData<-]";
352             break;
353         case CURLINFO_DATA_OUT:
354             prefix = "[CurlData->]";
355             break;
356         default:
357             return 0;
358     }
359 
360     // Trim the trailing \r\n
361     if ( size >= 1 && ( data[size - 1] == '\r' || data[size - 1] == '\n' ) )
362         --size;
363     if ( size >= 1 && ( data[size - 1] == '\r' || data[size - 1] == '\n' ) )
364         --size;
365     rtl::OString message( data, size );
366     m_aLogger.log( LogLevel::FINEST, "$1$ $2$", prefix, message );
367     return 0;
368 }
369 
Curl_SSLContextCallback(CURL *,void * ssl_ctx,void * userptr)370 CURLcode CurlSession::Curl_SSLContextCallback( CURL *, void *ssl_ctx, void *userptr )
371 {
372     CurlSession *session = static_cast<CurlSession*>( userptr );
373     SSL_CTX *context = static_cast<SSL_CTX*>( ssl_ctx );
374     SSL_CTX_set_app_data( context, session );
375     SSL_CTX_set_cert_verify_callback( context, OPENSSL_VerifyCertificate, session );
376     return CURLE_OK;
377 }
378 
OPENSSL_VerifyCertificate(X509_STORE_CTX * x509_ctx,void * arg)379 int CurlSession::OPENSSL_VerifyCertificate( X509_STORE_CTX *x509_ctx, void *arg )
380 {
381     CurlSession *session = static_cast<CurlSession*>( arg );
382     int verifyResult = session->verifyServerX509Certificate( x509_ctx );
383     // We have to both return 1 or 0, and set the X509_V_* error code with X509_STORE_CTX_set_error():
384     X509_STORE_CTX_set_error( x509_ctx, verifyResult );
385     return verifyResult == X509_V_OK ? 1 : 0;
386 }
387 
convertCertificateToAsn1Der(X509 * certificate)388 static uno::Sequence< sal_Int8 > convertCertificateToAsn1Der( X509 *certificate )
389 {
390     uno::Sequence< sal_Int8 > asn1DerCertificate;
391     int len = i2d_X509( certificate, NULL );
392     if ( len < 0 )
393         return asn1DerCertificate;
394     asn1DerCertificate.realloc( len );
395     unsigned char *end = reinterpret_cast< unsigned char *>( asn1DerCertificate.getArray() );
396     len = i2d_X509( certificate, &end );
397     if ( len >= 0 )
398         return asn1DerCertificate;
399     else
400         return uno::Sequence< sal_Int8 >();
401 }
402 
verifyServerX509Certificate(X509_STORE_CTX * x509StoreContext)403 int CurlSession::verifyServerX509Certificate( X509_STORE_CTX *x509StoreContext )
404 {
405     X509 *serverCertificate = X509_STORE_CTX_get0_cert( x509StoreContext );
406     STACK_OF(X509) *chain = X509_STORE_CTX_get0_untrusted( x509StoreContext );
407 
408     std::vector< uno::Sequence< sal_Int8 > > asn1DerCertificates;
409     int verifyResult = X509_V_OK;
410     if ( chain != NULL ) {
411         int nCertificates = sk_X509_num( chain );
412         for ( int i = 0; i < nCertificates && verifyResult == X509_V_OK; i++ ) {
413             X509 *certificate = sk_X509_value( chain, i );
414             uno::Sequence< sal_Int8 > asn1DerCertificate = convertCertificateToAsn1Der( certificate );
415             if( asn1DerCertificate.getLength() > 0 )
416                 asn1DerCertificates.push_back( asn1DerCertificate );
417             else
418                 verifyResult = X509_V_ERR_UNSPECIFIED;
419         }
420     } else {
421         uno::Sequence< sal_Int8 > asn1DerCertificate = convertCertificateToAsn1Der( serverCertificate );
422         if( asn1DerCertificate.getLength() > 0 )
423             asn1DerCertificates.push_back( asn1DerCertificate );
424         else
425             verifyResult = X509_V_ERR_UNSPECIFIED;
426     }
427     if( verifyResult == X509_V_OK )
428         verifyResult = verifyCertificateChain( asn1DerCertificates );
429 
430     rtl::OUString verifyErrorString = rtl::OUString::createFromAscii( X509_verify_cert_error_string( verifyResult ) );
431     m_aLogger.log( LogLevel::FINE, "validateServerX509Certificate() verifyResult=$1$ ($2$)",
432         (sal_Int32)verifyResult, verifyErrorString );
433     return verifyResult;
434 }
435 
verifyCertificateChain(std::vector<uno::Sequence<sal_Int8>> & asn1DerCertificates)436 int CurlSession::verifyCertificateChain (
437     std::vector< uno::Sequence< sal_Int8 > > &asn1DerCertificates )
438 {
439     // Check arguments.
440     if( asn1DerCertificates.size() <= 0 )
441     {
442         m_aLogger.log( LogLevel::WARNING, "No certificates to verify - failing!" );
443         return X509_V_ERR_UNSPECIFIED;
444     }
445 
446     // Decode the server certificate.
447     uno::Reference< security::XCertificate > xServerCertificate(
448         m_xSecurityEnv->createCertificateFromRaw( asn1DerCertificates[0] ) );
449     if ( ! xServerCertificate.is())
450     {
451         m_aLogger.log( LogLevel::WARNING, "Failed to create XCertificate" );
452         return X509_V_ERR_UNSPECIFIED;
453     }
454 
455     // Get the subject from the server certificate.
456     ::rtl::OUString sServerCertificateSubject (xServerCertificate->getSubjectName());
457     sal_Int32 nIndex = 0;
458     while (nIndex >= 0)
459     {
460         const ::rtl::OUString sToken (sServerCertificateSubject.getToken(0, ',', nIndex));
461         if (sToken.compareToAscii("CN=", 3) == 0)
462         {
463             sServerCertificateSubject = sToken.copy(3);
464             break;
465         }
466         else if (sToken.compareToAscii(" CN=", 4) == 0)
467         {
468             sServerCertificateSubject = sToken.copy(4);
469             break;
470         }
471     }
472 
473     // When the certificate container already contains a (trusted)
474     // entry for the server then we do not have to authenticate any
475     // certificate.
476     const security::CertificateContainerStatus eStatus (
477         m_xCertificateContainer->hasCertificate(
478             getHostName(), sServerCertificateSubject ) );
479     if (eStatus != security::CertificateContainerStatus_NOCERT)
480     {
481         m_aLogger.log( LogLevel::FINER, "Cached certificate found with status=$1$",
482                 eStatus == security::CertificateContainerStatus_TRUSTED ? "trusted" : "untrusted" );
483         return eStatus == security::CertificateContainerStatus_TRUSTED
484                ? X509_V_OK
485                : X509_V_ERR_CERT_UNTRUSTED;
486     }
487 
488     // The shortcut failed, so try to verify the whole chain. This is
489     // done outside the isDomainMatch() block because the result is
490     // used by the interaction handler.
491     std::vector< uno::Reference< security::XCertificate > > aChain;
492     for (nIndex=0; nIndex < asn1DerCertificates.size(); ++nIndex)
493     {
494         uno::Reference< security::XCertificate > xCertificate(
495             m_xSecurityEnv->createCertificateFromRaw( asn1DerCertificates[ nIndex ] ) );
496         if ( ! xCertificate.is())
497         {
498             m_aLogger.log( LogLevel::WARNING, "Failed to create XCertificate $1$", nIndex );
499             return X509_V_ERR_UNSPECIFIED;
500         }
501         aChain.push_back(xCertificate);
502     }
503     const sal_Int64 nVerificationResult (m_xSecurityEnv->verifyCertificate(
504             xServerCertificate,
505             ::comphelper::containerToSequence(aChain)));
506 
507     // When the certificate matches the host name then we can use the
508     // result of the verification.
509     bool bHostnameMatchesCertHostnames = false;
510     {
511         uno::Sequence< uno::Reference< security::XCertificateExtension > > extensions = xServerCertificate->getExtensions();
512         uno::Sequence< security::CertAltNameEntry > altNames;
513         for (sal_Int32 i = 0 ; i < extensions.getLength(); ++i)
514         {
515             uno::Reference< security::XCertificateExtension >element = extensions[i];
516 
517             const rtl::OString aId ( (const sal_Char *)element->getExtensionId().getArray(), element->getExtensionId().getLength());
518             if ( aId.equals( OID_SUBJECT_ALTERNATIVE_NAME ) )
519             {
520                 uno::Reference< security::XSanExtension > sanExtension ( element, uno::UNO_QUERY );
521                 altNames = sanExtension->getAlternativeNames();
522                 break;
523             }
524         }
525 
526         uno::Sequence< ::rtl::OUString > certHostNames(altNames.getLength() + 1);
527         certHostNames[0] = sServerCertificateSubject;
528         for( int n = 0; n < altNames.getLength(); ++n )
529         {
530             if (altNames[n].Type == security::ExtAltNameType_DNS_NAME)
531             {
532                 altNames[n].Value >>= certHostNames[n+1];
533             }
534         }
535 
536         for ( int i = 0; i < certHostNames.getLength() && !bHostnameMatchesCertHostnames; ++i )
537         {
538             bHostnameMatchesCertHostnames = isDomainMatch( certHostNames[i] );
539         }
540 
541     }
542     m_aLogger.log( LogLevel::FINE, "URL hostname $1$ certificate hostname",
543         bHostnameMatchesCertHostnames ? "matches" : "DOESN'T MATCH" );
544     if ( bHostnameMatchesCertHostnames )
545     {
546         if (nVerificationResult == 0)
547         {
548             m_aLogger.log( LogLevel::FINE, "Certificate (chain) is valid" );
549             m_xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, sal_True);
550             return X509_V_OK;
551         }
552         else if ((nVerificationResult & security::CertificateValidity::CHAIN_INCOMPLETE) != 0)
553         {
554             // We do not have enough information for verification,
555             // neither automatically (as we just discovered) nor
556             // manually (so there is no point in showing any dialog.)
557             m_aLogger.log( LogLevel::WARNING, "Certificate (chain) is incomplete" );
558             return X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
559         }
560         else if ((nVerificationResult & security::CertificateValidity::REVOKED) != 0)
561         {
562             // Certificate (chain) is invalid.
563             m_aLogger.log( LogLevel::WARNING, "Certificate (chain) is revoked" );
564             m_xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject,  sal_False);
565             return X509_V_ERR_CERT_REVOKED;
566         }
567         else
568         {
569             // For all other we have to ask the user.
570             m_aLogger.log( LogLevel::FINE, "Promping user to validate the certificate" );
571         }
572     }
573 
574     // We have not been able to automatically verify (or falsify) the
575     // certificate chain. To resolve this we have to ask the user.
576     const uno::Reference< ucb::XCommandEnvironment > xEnv( getRequestEnvironment().m_xEnv );
577     if ( xEnv.is() )
578     {
579         uno::Reference< task::XInteractionHandler > xIH( xEnv->getInteractionHandler() );
580         if ( xIH.is() )
581         {
582             rtl::Reference< ucbhelper::SimpleCertificateValidationRequest >
583                 xRequest( new ucbhelper::SimpleCertificateValidationRequest(
584                         static_cast<sal_Int32>(nVerificationResult), xServerCertificate, getHostName() ) );
585             xIH->handle( xRequest.get() );
586 
587             rtl::Reference< ucbhelper::InteractionContinuation > xSelection
588                 = xRequest->getSelection();
589 
590             if ( xSelection.is() )
591             {
592                 uno::Reference< task::XInteractionApprove > xApprove( xSelection.get(), uno::UNO_QUERY );
593                 if ( xApprove.is() )
594                 {
595                     m_aLogger.log( LogLevel::FINE, "The user approved the certificate" );
596                     m_xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_True );
597                     return X509_V_OK;
598                 }
599                 else
600                 {
601                     // Don't trust cert
602                     m_aLogger.log( LogLevel::WARNING, "The user REJECTED the certificate" );
603                     m_xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_False );
604                     return X509_V_ERR_CERT_REJECTED;
605                 }
606             }
607         }
608         else
609         {
610             // Don't trust cert
611             m_aLogger.log( LogLevel::WARNING, "Couldn't create the interaction handler for user feedback, rejecting the certificate" );
612             m_xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_False );
613             return X509_V_ERR_CERT_REJECTED;
614         }
615     }
616     m_aLogger.log( LogLevel::WARNING, "No XCommandEnvironment, rejecting the certificate" );
617 
618     return X509_V_ERR_CERT_REJECTED;
619 }
620 
Curl_ProvideCredentials(long statusCode,void * userdata)621 bool CurlSession::Curl_ProvideCredentials( long statusCode, void *userdata ) throw (DAVException)
622 {
623     CredentialsData *credentialsData = (CredentialsData*)userdata;
624     return credentialsData->session->provideCredentials( credentialsData->env, credentialsData->request, statusCode );
625 }
626 
provideCredentials(const DAVRequestEnvironment & env,CurlRequest & request,long statusCode)627 bool CurlSession::provideCredentials( const DAVRequestEnvironment &env, CurlRequest &request, long statusCode ) throw (DAVException)
628 {
629     DAVAuthListener * pListener = env.m_xAuthListener.get();
630     if ( !pListener )
631     {
632         // abort
633         m_aLogger.log( LogLevel::FINE, "No DAVAuthListener found, failing credentials entry" );
634         return false;
635     }
636 
637     rtl::OUString theUserName;
638     rtl::OUString thePassWord;
639     try
640     {
641         CurlUri uri( env.m_aRequestURI );
642         theUserName = uri.GetUserName();
643         thePassWord = uri.GetPassword();
644     }
645     catch ( DAVException const &e )
646     {
647         // abort
648         m_aLogger.log(
649             LogLevel::WARNING,
650             "Error extracing userinfo from URI: exceptionCode=$1$, status=$2$, data=$3$, owner=$4$, extendedError=$5%",
651             (sal_Int32)e.getError(), e.getStatus(), e.getData(), e.getOwner(), e.getExtendedError()
652         );
653         return false;
654     }
655 
656     bool canUseSystemCreds = false;
657     long authMethods = 0;
658     CURLcode rc = CURLE_OK;
659     if ( statusCode == 401 )
660         rc = curl_easy_getinfo( m_pCurl, CURLINFO_HTTPAUTH_AVAIL, &authMethods );
661     else if ( statusCode == 407 )
662         rc = curl_easy_getinfo( m_pCurl, CURLINFO_PROXYAUTH_AVAIL, &authMethods );
663     if ( rc == 0 )
664         canUseSystemCreds = (authMethods & CURLAUTH_NEGOTIATE) || (authMethods & CURLAUTH_NTLM);
665     m_aLogger.log( LogLevel::FINE, "authMethods=$1$, canUseSystemCreds=$2$",
666         (sal_Int64)authMethods, (sal_Int32)canUseSystemCreds );
667 
668     const CurlRequest::Header *authHeader = NULL;
669     if ( statusCode == 401 )
670         authHeader = request.findResponseHeader( "WWW-Authenticate" );
671     else if ( statusCode == 407 )
672         authHeader = request.findResponseHeader( "Proxy-Authenticate" );
673     rtl::OUString realm;
674     if ( authHeader != NULL )
675     {
676         int realmStart = authHeader->value.indexOf( "realm=\"" );
677         if ( realmStart >= 0 )
678         {
679             realmStart += 7;
680             int realmEnd = authHeader->value.indexOf( "\"", realmStart );
681             if ( realmEnd > 0 )
682                 realm = rtl::OStringToOUString( authHeader->value.copy( realmStart, realmEnd - realmStart ), RTL_TEXTENCODING_UTF8 );
683         }
684     }
685 
686     int theRetVal = pListener->authenticate( realm,
687                                              getHostName(),
688                                              theUserName,
689                                              thePassWord,
690                                              canUseSystemCreds,
691                                              // Authenticating with both the proxy
692                                              // and the destination server requires sal_True here,
693                                              // and needs filling out 2 x password dialogs.
694                                              sal_True );
695 
696     if ( theRetVal == 0 )
697     {
698         m_aLogger.log( LogLevel::FINEST, "got credentials for user=$1$ on realm=$2$", theUserName, realm );
699         // "System credentials" means username and password are empty
700         const char *curlUsername = NULL;
701         const char *curlPassword = NULL;
702         if ( !theUserName.isEmpty() )
703             curlUsername = rtl::OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ).getStr();
704         if ( !thePassWord.isEmpty() )
705             curlPassword = rtl::OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ).getStr();
706         if ( statusCode == 401 )
707         {
708             curl_easy_setopt( m_pCurl, CURLOPT_USERNAME, curlUsername );
709             curl_easy_setopt( m_pCurl, CURLOPT_PASSWORD, curlPassword );
710         }
711         else
712         {
713             curl_easy_setopt( m_pCurl, CURLOPT_PROXYUSERNAME, curlUsername );
714             curl_easy_setopt( m_pCurl, CURLOPT_PROXYPASSWORD, curlPassword );
715         }
716         return true;
717     }
718     m_aLogger.log( LogLevel::WARNING, "credentials entry cancelled or failed" );
719 
720     return false;
721 }
722 
addEnvironmentRequestHeaders(CurlRequest & curlRequest,const DAVRequestEnvironment & env)723 void CurlSession::addEnvironmentRequestHeaders( CurlRequest &curlRequest, const DAVRequestEnvironment &env )
724     throw ( DAVException )
725 {
726     bool bHasUserAgent( false );
727     DAVRequestHeaders::const_iterator aHeaderIter( env.m_aRequestHeaders.begin() );
728     const DAVRequestHeaders::const_iterator aEnd( env.m_aRequestHeaders.end() );
729 
730     while ( aHeaderIter != aEnd )
731     {
732         const rtl::OString aHeader = rtl::OUStringToOString( aHeaderIter->first,
733                                                              RTL_TEXTENCODING_UTF8 );
734         const rtl::OString aValue = rtl::OUStringToOString( aHeaderIter->second,
735                                                             RTL_TEXTENCODING_UTF8 );
736 
737         if ( !bHasUserAgent )
738             bHasUserAgent = aHeaderIter->first.equalsAsciiL(
739                 RTL_CONSTASCII_STRINGPARAM( "User-Agent" ) );
740 
741         curlRequest.addHeader( aHeader, aValue );
742 
743         ++aHeaderIter;
744     }
745 
746     if ( !bHasUserAgent )
747     {
748         const rtl::OUString &rUserAgent = WebDAVUserAgent::get();
749         curlRequest.addHeader( "User-Agent", rtl::OUStringToOString( rUserAgent, RTL_TEXTENCODING_UTF8 ) );
750     }
751 }
752 
processResponse(CurlRequest & curlRequest,CURLcode curlCode)753 void CurlSession::processResponse( CurlRequest &curlRequest, CURLcode curlCode )
754     throw( DAVException )
755 {
756     long statusCode = 0;
757     CURLcode curlRes;
758     curlRes = curl_easy_getinfo( m_pCurl, CURLINFO_RESPONSE_CODE, &statusCode );
759     if ( curlRes != 0 || statusCode == 0 )
760         statusCode = curlRequest.getStatusCode();
761 
762     // check header according:
763     // http://tools.ietf.org/html/rfc7231#section-7.4.2
764     // need to do this so we can adjust the protocol accordingly
765     const CurlRequest::Header *server = curlRequest.findResponseHeader( "server" );
766     if ( server != NULL )
767         m_aServerHeaderField = server->value;
768 
769     if ( curlCode != 0 )
770     {
771         m_aLogger.log( LogLevel::WARNING, "Curl request failed with CURLcode $1$", (sal_Int64)curlCode );
772         DAVException::ExceptionCode exCode = DAVException::DAV_HTTP_ERROR;
773         rtl::OUString exData;
774         switch (curlCode) {
775         case CURLE_COULDNT_RESOLVE_HOST:
776             exCode = DAVException::DAV_HTTP_LOOKUP;
777             exData = CurlUri::makeConnectionEndPointString( getHostName(),
778                                                             getPort() );
779             break;
780         case CURLE_COULDNT_CONNECT:
781             exCode = DAVException::DAV_HTTP_CONNECT;
782             exData = CurlUri::makeConnectionEndPointString( getHostName(),
783                                                             getPort() );
784             break;
785         case CURLE_OPERATION_TIMEDOUT:
786             exCode = DAVException::DAV_HTTP_TIMEOUT;
787             exData = CurlUri::makeConnectionEndPointString( getHostName(),
788                                                             getPort() );
789             break;
790         case CURLE_LOGIN_DENIED:
791         case CURLE_AUTH_ERROR:
792             exCode = DAVException::DAV_HTTP_AUTH;
793             exData = CurlUri::makeConnectionEndPointString( getHostName(),
794                                                             getPort() );
795             break;
796         default:
797             {
798                 const char *s = curl_easy_strerror(curlCode);
799                 exCode = DAVException::DAV_HTTP_ERROR;
800                 exData = ::rtl::OUString(s, strlen(s),
801                                          RTL_TEXTENCODING_UTF8);
802                 break;
803             }
804         }
805         throw DAVException( exCode, exData );
806     }
807 
808     rtl::OUString reasonPhrase = rtl::OStringToOUString( curlRequest.getReasonPhrase(), RTL_TEXTENCODING_UTF8 );
809     if ( statusCode != 0 && statusCode / 100 != 2 )
810     {
811         switch (statusCode)
812         {
813             case SC_MOVED_PERMANENTLY:             // 301
814             case SC_MOVED_TEMPORARILY:             // 302
815             case SC_SEE_OTHER:                     // 303
816             case SC_TEMPORARY_REDIRECT:            // 307
817             {
818                 // new location for certain redirections
819 
820                 const CurlRequest::Header *location = curlRequest.findResponseHeader( "location" );
821                 if ( location != NULL )
822                 {
823                     m_aLogger.log( LogLevel::FINE, "HTTP $1$ response with new location = $2$",
824                         statusCode, location->value );
825                     throw DAVException( DAVException::DAV_HTTP_REDIRECT,
826                                         rtl::OStringToOUString( location->value, RTL_TEXTENCODING_UTF8 ) );
827                 }
828                 break;
829             }
830             case SC_UNAUTHORIZED:                  // 401
831             case SC_PROXY_AUTHENTICATION_REQUIRED: // 407
832             {
833                 throw DAVException( DAVException::DAV_HTTP_ERROR,
834                                     reasonPhrase,
835                                     statusCode );
836                 break;
837             }
838             case SC_REQUEST_ENTITY_TOO_LARGE:      // 413
839             {
840                 if ( m_bTransferEncodingSwitched )
841                     throw DAVException( DAVException::DAV_HTTP_ERROR,
842                                         reasonPhrase,
843                                         statusCode );
844                 m_bTransferEncodingSwitched = true;
845                 curlRequest.setChunkedEncoding( !curlRequest.isChunkedEncoding() );
846                 break;
847             }
848             case SC_LOCKED:                        // 423
849                 throw DAVException( DAVException::DAV_LOCKED,
850                                     reasonPhrase,
851                                     statusCode );
852             default:
853                 throw DAVException( DAVException::DAV_HTTP_ERROR,
854                                     reasonPhrase,
855                                     statusCode );
856         }
857     }
858 }
859 
responseHeadersToDAVResource(const std::vector<CurlRequest::Header> & responseHeaders,const std::vector<::rtl::OUString> & inHeaderNames,DAVResource & ioResource)860 static void responseHeadersToDAVResource( const std::vector< CurlRequest::Header> &responseHeaders,
861                                           const std::vector< ::rtl::OUString > &inHeaderNames,
862                                           DAVResource &ioResource )
863 {
864     std::vector< CurlRequest::Header >::const_iterator it( responseHeaders.begin() );
865     const std::vector< CurlRequest::Header >::const_iterator end( responseHeaders.end() );
866     while ( it != end )
867     {
868         bool storeHeader = false;
869         if ( inHeaderNames.size() == 0 )
870             storeHeader = true;
871         else
872         {
873             std::vector< ::rtl::OUString >::const_iterator reqIt( inHeaderNames.begin() );
874             const std::vector< ::rtl::OUString >::const_iterator reqEnd( inHeaderNames.end() );
875             while ( reqIt != reqEnd )
876             {
877                 // header names are case insensitive
878                 if ( (*reqIt).equalsIgnoreAsciiCase( rtl::OStringToOUString( (*it).name, RTL_TEXTENCODING_UTF8 ) ) )
879                 {
880                     storeHeader = true;
881                     break;
882                 }
883                 else
884                 {
885                     ++reqIt;
886                 }
887             }
888         }
889 
890         if ( storeHeader )
891         {
892             DAVPropertyValue thePropertyValue;
893             thePropertyValue.IsCaseSensitive = false;
894             thePropertyValue.Name = rtl::OStringToOUString( (*it).name, RTL_TEXTENCODING_UTF8 );
895             thePropertyValue.Value <<= rtl::OStringToOUString( (*it).value, RTL_TEXTENCODING_UTF8 );
896             ioResource.properties.push_back( thePropertyValue );
897         }
898 
899         it++;
900     }
901 }
902 
903 // -------------------------------------------------------------------
904 // PROPFIND - allprop & named
905 // -------------------------------------------------------------------
906 
propfind(CurlRequest & curlRequest,const rtl::OUString & inPath,const Depth inDepth,const std::vector<::rtl::OUString> * inPropNames,const bool onlyPropertyNames,const DAVRequestEnvironment & rEnv)907 void CurlSession::propfind( CurlRequest &curlRequest,
908                             const rtl::OUString &inPath,
909                             const Depth inDepth,
910                             const std::vector< ::rtl::OUString > * inPropNames,
911                             const bool onlyPropertyNames,
912                             const DAVRequestEnvironment & rEnv )
913 {
914     addEnvironmentRequestHeaders( curlRequest, rEnv );
915 
916     if ( inDepth == DAVZERO )
917         curlRequest.addHeader( "Depth", "0" );
918     else if ( inDepth == DAVONE )
919         curlRequest.addHeader( "Depth", "1" );
920     else if ( inDepth == DAVINFINITY )
921         curlRequest.addHeader( "Depth", "infinity" );
922 
923     rtl::OString xml = PropfindRequest::generatePROPFINDRequestBody( inPropNames, onlyPropertyNames );
924     if ( xml.getLength() > 0 )
925     {
926         curlRequest.addHeader( "Content-Type", "application/xml" );
927         curlRequest.setRequestBody( xml.getStr(), xml.getLength() );
928     }
929 
930     CredentialsData credsData( this, curlRequest, rEnv );
931     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
932 
933     CURLcode rc = curlRequest.propfind( m_aUri, inPath );
934     processResponse( curlRequest, rc );
935 }
936 
PROPFIND(const rtl::OUString & inPath,const Depth inDepth,const std::vector<rtl::OUString> & inPropNames,std::vector<DAVResource> & ioResources,const DAVRequestEnvironment & rEnv)937 void CurlSession::PROPFIND( const rtl::OUString & inPath,
938                             const Depth inDepth,
939                             const std::vector< rtl::OUString > & inPropNames,
940                             std::vector< DAVResource > & ioResources,
941                             const DAVRequestEnvironment & rEnv )
942     throw ( DAVException )
943 {
944     m_aLogger.log( LogLevel::INFO, "PROPFIND line $1$", (sal_Int32)__LINE__ );
945 
946     osl::Guard< osl::Mutex > theGuard( m_aMutex );
947 
948     Init( rEnv );
949     CurlRequest curlRequest( m_pCurl );
950 
951     propfind( curlRequest, inPath, inDepth, &inPropNames, false, rEnv );
952 
953     const std::vector< DAVResource > rResources( parseWebDAVPropFindResponse( curlRequest.getResponseBody().get() ) );
954     std::vector< DAVResource > *pIoResources = &ioResources;
955     *pIoResources = rResources;
956 }
957 
958 // -------------------------------------------------------------------
959 // PROPFIND - propnames
960 // -------------------------------------------------------------------
PROPFIND(const rtl::OUString & inPath,const Depth inDepth,std::vector<DAVResourceInfo> & ioResInfo,const DAVRequestEnvironment & rEnv)961 void CurlSession::PROPFIND( const rtl::OUString & inPath,
962                             const Depth inDepth,
963                             std::vector< DAVResourceInfo > & ioResInfo,
964                             const DAVRequestEnvironment & rEnv )
965     throw( DAVException )
966 {
967     m_aLogger.log( LogLevel::INFO, "PROPFIND line $1$", (sal_Int32)__LINE__ );
968 
969     osl::Guard< osl::Mutex > theGuard( m_aMutex );
970 
971     Init( rEnv );
972     CurlRequest curlRequest( m_pCurl );
973 
974     propfind( curlRequest, inPath, inDepth, NULL, true, rEnv );
975 
976     const std::vector< DAVResourceInfo > rResInfo( parseWebDAVPropNameResponse( curlRequest.getResponseBody().get() ) );
977     std::vector< DAVResourceInfo > *pIoResInfo = &ioResInfo;
978     *pIoResInfo = rResInfo;
979 }
980 
981 // -------------------------------------------------------------------
982 // PROPPATCH
983 // -------------------------------------------------------------------
PROPPATCH(const rtl::OUString & inPath,const std::vector<ProppatchValue> & inValues,const DAVRequestEnvironment & rEnv)984 void CurlSession::PROPPATCH( const rtl::OUString & inPath,
985                              const std::vector< ProppatchValue > & inValues,
986                              const DAVRequestEnvironment & rEnv )
987     throw( DAVException )
988 {
989     m_aLogger.log( LogLevel::INFO, "PROPPATCH line $1$", (sal_Int32)__LINE__ );
990 
991     osl::Guard< osl::Mutex > theGuard( m_aMutex );
992 
993     Init( rEnv );
994     CurlRequest curlRequest( m_pCurl );
995 
996     addEnvironmentRequestHeaders( curlRequest, rEnv );
997 
998     // check whether a lock on this resource is already owned
999     rtl::OUString aUri( composeCurrentUri( inPath ) );
1000     ucb::Lock inLock;
1001     CurlLock * pLock = m_aCurlLockStore.findByUri( aUri );
1002     if ( pLock )
1003     {
1004         inLock = pLock->getLock();
1005     }
1006     if ( inLock.LockTokens.getLength() > 0 )
1007     {
1008         curlRequest.addHeader( "If",
1009             ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() );
1010     }
1011 
1012     rtl::OString xml = ProppatchRequest::generatePROPPATCHRequestBody( inValues );
1013     if ( xml.getLength() > 0 )
1014     {
1015         curlRequest.addHeader( "Content-Type", "application/xml" );
1016         curlRequest.setRequestBody( xml.getStr(), xml.getLength() );
1017     }
1018 
1019     CredentialsData credsData( this, curlRequest, rEnv );
1020     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1021 
1022     CURLcode rc = curlRequest.proppatch( m_aUri, inPath );
1023     processResponse( curlRequest, rc );
1024 }
1025 
1026 // -------------------------------------------------------------------
1027 // HEAD
1028 // -------------------------------------------------------------------
HEAD(const::rtl::OUString & inPath,const std::vector<::rtl::OUString> & inHeaderNames,DAVResource & ioResource,const DAVRequestEnvironment & rEnv)1029 void CurlSession::HEAD( const ::rtl::OUString & inPath,
1030                         const std::vector< ::rtl::OUString > & inHeaderNames,
1031                         DAVResource & ioResource,
1032                         const DAVRequestEnvironment & rEnv )
1033     throw( DAVException )
1034 {
1035     m_aLogger.log( LogLevel::INFO, "HEAD line $1$", (sal_Int32)__LINE__ );
1036 
1037     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1038 
1039     Init(rEnv );
1040     CurlRequest curlRequest( m_pCurl );
1041 
1042     addEnvironmentRequestHeaders( curlRequest, rEnv );
1043 
1044     ioResource.uri = inPath;
1045     ioResource.properties.clear();
1046 
1047     CredentialsData credsData( this, curlRequest, rEnv );
1048     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1049 
1050     CURLcode rc = curlRequest.head( m_aUri, inPath );
1051     processResponse( curlRequest, rc );
1052     responseHeadersToDAVResource( curlRequest.getResponseHeaders(), inHeaderNames, ioResource );
1053 }
1054 
1055 // -------------------------------------------------------------------
1056 // GET
1057 // -------------------------------------------------------------------
1058 uno::Reference< io::XInputStream >
GET(const rtl::OUString & inPath,const DAVRequestEnvironment & rEnv)1059 CurlSession::GET( const rtl::OUString & inPath,
1060                   const DAVRequestEnvironment & rEnv )
1061     throw ( DAVException )
1062 {
1063     m_aLogger.log( LogLevel::INFO, "GET line $1$", (sal_Int32)__LINE__ );
1064 
1065     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1066 
1067     Init( rEnv );
1068     CurlRequest curlRequest( m_pCurl );
1069 
1070     addEnvironmentRequestHeaders( curlRequest, rEnv );
1071 
1072     CredentialsData credsData( this, curlRequest, rEnv );
1073     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1074 
1075     CURLcode rc = curlRequest.get( m_aUri, inPath );
1076     processResponse( curlRequest, rc );
1077 
1078     return uno::Reference< io::XInputStream >( curlRequest.getResponseBody().get() );
1079 }
1080 
1081 // -------------------------------------------------------------------
1082 // GET
1083 // -------------------------------------------------------------------
GET(const rtl::OUString & inPath,uno::Reference<io::XOutputStream> & ioOutputStream,const DAVRequestEnvironment & rEnv)1084 void CurlSession::GET( const rtl::OUString & inPath,
1085                        uno::Reference< io::XOutputStream > & ioOutputStream,
1086                        const DAVRequestEnvironment & rEnv )
1087     throw ( DAVException )
1088 {
1089     m_aLogger.log( LogLevel::INFO, "GET line $1$", (sal_Int32)__LINE__ );
1090 
1091     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1092 
1093     Init( rEnv );
1094     CurlRequest curlRequest( m_pCurl );
1095 
1096     addEnvironmentRequestHeaders( curlRequest, rEnv );
1097 
1098     CredentialsData credsData( this, curlRequest, rEnv );
1099     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1100 
1101     curlRequest.saveResponseBodyTo( ioOutputStream );
1102     CURLcode rc = curlRequest.get( m_aUri, inPath );
1103     processResponse( curlRequest, rc );
1104 }
1105 
1106 // -------------------------------------------------------------------
1107 // GET
1108 // -------------------------------------------------------------------
1109 uno::Reference< io::XInputStream >
GET(const rtl::OUString & inPath,const std::vector<::rtl::OUString> & inHeaderNames,DAVResource & ioResource,const DAVRequestEnvironment & rEnv)1110 CurlSession::GET( const rtl::OUString & inPath,
1111                   const std::vector< ::rtl::OUString > & inHeaderNames,
1112                   DAVResource & ioResource,
1113                   const DAVRequestEnvironment & rEnv )
1114     throw ( DAVException )
1115 {
1116     m_aLogger.log( LogLevel::INFO, "GET line $1$", (sal_Int32)__LINE__ );
1117 
1118     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1119 
1120     Init( rEnv );
1121     CurlRequest curlRequest( m_pCurl );
1122 
1123     addEnvironmentRequestHeaders( curlRequest, rEnv );
1124 
1125     CredentialsData credsData( this, curlRequest, rEnv );
1126     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1127 
1128     CURLcode rc = curlRequest.get( m_aUri, inPath );
1129     processResponse( curlRequest, rc );
1130     responseHeadersToDAVResource( curlRequest.getResponseHeaders(), inHeaderNames, ioResource );
1131 
1132     return uno::Reference< io::XInputStream >( curlRequest.getResponseBody().get() );
1133 }
1134 
1135 
1136 // -------------------------------------------------------------------
1137 // GET
1138 // -------------------------------------------------------------------
GET(const rtl::OUString & inPath,uno::Reference<io::XOutputStream> & ioOutputStream,const std::vector<::rtl::OUString> & inHeaderNames,DAVResource & ioResource,const DAVRequestEnvironment & rEnv)1139 void CurlSession::GET( const rtl::OUString & inPath,
1140                        uno::Reference< io::XOutputStream > & ioOutputStream,
1141                        const std::vector< ::rtl::OUString > & inHeaderNames,
1142                        DAVResource & ioResource,
1143                        const DAVRequestEnvironment & rEnv )
1144     throw ( DAVException )
1145 {
1146     m_aLogger.log( LogLevel::INFO, "GET line $1$", (sal_Int32)__LINE__ );
1147 
1148     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1149 
1150     Init( rEnv );
1151     CurlRequest curlRequest( m_pCurl );
1152 
1153     addEnvironmentRequestHeaders( curlRequest, rEnv );
1154 
1155     CredentialsData credsData( this, curlRequest, rEnv );
1156     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1157 
1158     curlRequest.saveResponseBodyTo( ioOutputStream );
1159     CURLcode rc = curlRequest.get( m_aUri, inPath );
1160     processResponse( curlRequest, rc );
1161     responseHeadersToDAVResource( curlRequest.getResponseHeaders(), inHeaderNames, ioResource );
1162 }
1163 
1164 // -------------------------------------------------------------------
1165 // PUT
1166 // -------------------------------------------------------------------
PUT(const rtl::OUString & inPath,const uno::Reference<io::XInputStream> & inInputStream,const DAVRequestEnvironment & rEnv)1167 void CurlSession::PUT( const rtl::OUString & inPath,
1168                        const uno::Reference< io::XInputStream > & inInputStream,
1169                        const DAVRequestEnvironment & rEnv )
1170     throw ( DAVException )
1171 {
1172     m_aLogger.log( LogLevel::INFO, "PUT line $1$", (sal_Int32)__LINE__ );
1173 
1174     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1175 
1176     Init( rEnv );
1177     CurlRequest curlRequest( m_pCurl );
1178 
1179     addEnvironmentRequestHeaders( curlRequest, rEnv );
1180 
1181     uno::Sequence< sal_Int8 > aDataToSend;
1182     if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
1183         throw DAVException( DAVException::DAV_INVALID_ARG );
1184     curlRequest.setRequestBody( reinterpret_cast< const char * >( aDataToSend.getConstArray() ),
1185                                 aDataToSend.getLength() );
1186 
1187     CredentialsData credsData( this, curlRequest, rEnv );
1188     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1189 
1190     // check whether a lock on this resource is already owned
1191     rtl::OUString aUri( composeCurrentUri( inPath ) );
1192     ucb::Lock inLock;
1193     CurlLock * pLock = m_aCurlLockStore.findByUri( aUri );
1194     if ( pLock )
1195     {
1196         inLock = pLock->getLock();
1197     }
1198     if ( inLock.LockTokens.getLength() > 0 )
1199     {
1200         curlRequest.addHeader( "If",
1201             ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() );
1202     }
1203 
1204     CURLcode rc = curlRequest.put( m_aUri, inPath );
1205     processResponse( curlRequest, rc );
1206 }
1207 
1208 // -------------------------------------------------------------------
1209 // POST
1210 // -------------------------------------------------------------------
1211 uno::Reference< io::XInputStream >
POST(const rtl::OUString & inPath,const rtl::OUString & rContentType,const rtl::OUString & rReferer,const uno::Reference<io::XInputStream> & inInputStream,const DAVRequestEnvironment & rEnv)1212 CurlSession::POST( const rtl::OUString & inPath,
1213                    const rtl::OUString & rContentType,
1214                    const rtl::OUString & rReferer,
1215                    const uno::Reference< io::XInputStream > & inInputStream,
1216                    const DAVRequestEnvironment & rEnv )
1217     throw ( DAVException )
1218 {
1219     m_aLogger.log( LogLevel::INFO, "POST line $1$", (sal_Int32)__LINE__ );
1220 
1221     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1222 
1223     Init( rEnv );
1224     CurlRequest curlRequest( m_pCurl );
1225 
1226     addEnvironmentRequestHeaders( curlRequest, rEnv );
1227 
1228     uno::Sequence< sal_Int8 > aDataToSend;
1229     if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
1230         throw DAVException( DAVException::DAV_INVALID_ARG );
1231     curlRequest.setRequestBody( reinterpret_cast< const char * >( aDataToSend.getConstArray() ),
1232                                 aDataToSend.getLength() );
1233 
1234     CredentialsData credsData( this, curlRequest, rEnv );
1235     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1236 
1237     if ( !rContentType.isEmpty() )
1238         curlRequest.addHeader( "Content-Type", rtl::OUStringToOString( rContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
1239     if ( !rReferer.isEmpty() )
1240         curlRequest.addHeader( "Referer", rtl::OUStringToOString( rReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
1241 
1242     // check whether a lock on this resource is already owned
1243     rtl::OUString aUri( composeCurrentUri( inPath ) );
1244     ucb::Lock inLock;
1245     CurlLock * pLock = m_aCurlLockStore.findByUri( aUri );
1246     if ( pLock )
1247     {
1248         inLock = pLock->getLock();
1249     }
1250     if ( inLock.LockTokens.getLength() > 0 )
1251     {
1252         curlRequest.addHeader( "If",
1253             ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() );
1254     }
1255 
1256     CURLcode rc = curlRequest.post( m_aUri, inPath );
1257     processResponse( curlRequest, rc );
1258     return uno::Reference< io::XInputStream >( curlRequest.getResponseBody().get() );
1259 }
1260 
1261 // -------------------------------------------------------------------
1262 // POST
1263 // -------------------------------------------------------------------
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)1264 void CurlSession::POST( const rtl::OUString & inPath,
1265                         const rtl::OUString & rContentType,
1266                         const rtl::OUString & rReferer,
1267                         const uno::Reference< io::XInputStream > & inInputStream,
1268                         uno::Reference< io::XOutputStream > & oOutputStream,
1269                         const DAVRequestEnvironment & rEnv )
1270     throw ( DAVException )
1271 {
1272     m_aLogger.log( LogLevel::INFO, "POST line $1$", (sal_Int32)__LINE__ );
1273 
1274     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1275 
1276     Init( rEnv );
1277     CurlRequest curlRequest( m_pCurl );
1278 
1279     addEnvironmentRequestHeaders( curlRequest, rEnv );
1280 
1281     uno::Sequence< sal_Int8 > aDataToSend;
1282     if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
1283         throw DAVException( DAVException::DAV_INVALID_ARG );
1284     curlRequest.setRequestBody( reinterpret_cast< const char * >( aDataToSend.getConstArray() ),
1285                                 aDataToSend.getLength() );
1286 
1287     CredentialsData credsData( this, curlRequest, rEnv );
1288     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1289 
1290     if ( !rContentType.isEmpty() )
1291         curlRequest.addHeader( "Content-Type", rtl::OUStringToOString( rContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
1292     if ( !rReferer.isEmpty() )
1293         curlRequest.addHeader( "Referer", rtl::OUStringToOString( rReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
1294 
1295     // check whether a lock on this resource is already owned
1296     rtl::OUString aUri( composeCurrentUri( inPath ) );
1297     ucb::Lock inLock;
1298     CurlLock * pLock = m_aCurlLockStore.findByUri( aUri );
1299     if ( pLock )
1300     {
1301         inLock = pLock->getLock();
1302     }
1303     if ( inLock.LockTokens.getLength() > 0 )
1304     {
1305         curlRequest.addHeader( "If",
1306             ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() );
1307     }
1308 
1309     curlRequest.saveResponseBodyTo( oOutputStream );
1310     CURLcode rc = curlRequest.post( m_aUri, inPath );
1311     processResponse( curlRequest, rc );
1312 }
1313 
1314 // -------------------------------------------------------------------
1315 // MKCOL
1316 // -------------------------------------------------------------------
MKCOL(const rtl::OUString & inPath,const DAVRequestEnvironment & rEnv)1317 void CurlSession::MKCOL( const rtl::OUString & inPath,
1318                          const DAVRequestEnvironment & rEnv )
1319     throw ( DAVException )
1320 {
1321     m_aLogger.log( LogLevel::INFO, "MKCOL line $1$", (sal_Int32)__LINE__ );
1322 
1323     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1324 
1325     Init( rEnv );
1326     CurlRequest curlRequest( m_pCurl );
1327 
1328     addEnvironmentRequestHeaders( curlRequest, rEnv );
1329 
1330     CredentialsData credsData( this, curlRequest, rEnv );
1331     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1332 
1333     // check whether a lock on this resource is already owned
1334     rtl::OUString aUri( composeCurrentUri( inPath ) );
1335     ucb::Lock inLock;
1336     CurlLock * pLock = m_aCurlLockStore.findByUri( aUri );
1337     if ( pLock )
1338     {
1339         inLock = pLock->getLock();
1340     }
1341     if ( inLock.LockTokens.getLength() > 0 )
1342     {
1343         curlRequest.addHeader( "If",
1344             ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() );
1345     }
1346 
1347     CURLcode rc = curlRequest.mkcol( m_aUri, inPath );
1348     processResponse( curlRequest, rc );
1349 }
1350 
1351 // -------------------------------------------------------------------
1352 // COPY
1353 // -------------------------------------------------------------------
COPY(const rtl::OUString & inSourceURL,const rtl::OUString & inDestinationURL,const DAVRequestEnvironment & rEnv,sal_Bool inOverWrite)1354 void CurlSession::COPY( const rtl::OUString & inSourceURL,
1355                         const rtl::OUString & inDestinationURL,
1356                         const DAVRequestEnvironment & rEnv,
1357                         sal_Bool inOverWrite )
1358     throw ( DAVException )
1359 {
1360     m_aLogger.log( LogLevel::INFO, "COPY line $1$", (sal_Int32)__LINE__ );
1361 
1362     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1363 
1364     Init( rEnv );
1365     CurlRequest curlRequest( m_pCurl );
1366 
1367     addEnvironmentRequestHeaders( curlRequest, rEnv );
1368 
1369     CredentialsData credsData( this, curlRequest, rEnv );
1370     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1371 
1372     curlRequest.addHeader( "Destination", rtl::OUStringToOString( inDestinationURL, RTL_TEXTENCODING_UTF8 ).getStr() );
1373     curlRequest.addHeader( "Overwrite", inOverWrite? "T" : "F" );
1374 
1375     // check whether a lock on the destination resource is already owned
1376     rtl::OUString aUri( composeCurrentUri( inDestinationURL ) );
1377     ucb::Lock inLock;
1378     CurlLock * pLock = m_aCurlLockStore.findByUri( aUri );
1379     if ( pLock )
1380     {
1381         inLock = pLock->getLock();
1382     }
1383     if ( inLock.LockTokens.getLength() > 0 )
1384     {
1385         curlRequest.addHeader( "If",
1386             ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() );
1387     }
1388 
1389     CURLcode rc = curlRequest.copy( m_aUri, CurlUri( inSourceURL ).GetPath() );
1390     processResponse( curlRequest, rc );
1391 }
1392 
1393 // -------------------------------------------------------------------
1394 // MOVE
1395 // -------------------------------------------------------------------
MOVE(const rtl::OUString & inSourceURL,const rtl::OUString & inDestinationURL,const DAVRequestEnvironment & rEnv,sal_Bool inOverWrite)1396 void CurlSession::MOVE( const rtl::OUString & inSourceURL,
1397                         const rtl::OUString & inDestinationURL,
1398                         const DAVRequestEnvironment & rEnv,
1399                         sal_Bool inOverWrite )
1400     throw ( DAVException )
1401 {
1402     m_aLogger.log( LogLevel::INFO, "MOVE line $1$", (sal_Int32)__LINE__ );
1403 
1404     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1405 
1406     Init( rEnv );
1407     CurlRequest curlRequest( m_pCurl );
1408 
1409     addEnvironmentRequestHeaders( curlRequest, rEnv );
1410 
1411     CredentialsData credsData( this, curlRequest, rEnv );
1412     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1413 
1414     curlRequest.addHeader( "Destination", rtl::OUStringToOString( inDestinationURL, RTL_TEXTENCODING_UTF8 ).getStr() );
1415     curlRequest.addHeader( "Overwrite", inOverWrite? "T" : "F" );
1416 
1417     // check whether a lock on the destination resource is already owned
1418     rtl::OUString aUri( composeCurrentUri( inDestinationURL ) );
1419     ucb::Lock inLock;
1420     CurlLock * pLock = m_aCurlLockStore.findByUri( aUri );
1421     if ( pLock )
1422     {
1423         inLock = pLock->getLock();
1424     }
1425     if ( inLock.LockTokens.getLength() > 0 )
1426     {
1427         curlRequest.addHeader( "If",
1428             ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() );
1429     }
1430 
1431     CURLcode rc = curlRequest.copy( m_aUri, CurlUri( inSourceURL ).GetPath() );
1432     processResponse( curlRequest, rc );
1433 }
1434 
1435 // -------------------------------------------------------------------
1436 // DESTROY
1437 // -------------------------------------------------------------------
DESTROY(const rtl::OUString & inPath,const DAVRequestEnvironment & rEnv)1438 void CurlSession::DESTROY( const rtl::OUString & inPath,
1439                            const DAVRequestEnvironment & rEnv )
1440     throw ( DAVException )
1441 {
1442     m_aLogger.log( LogLevel::INFO, "DESTROY line $1$", (sal_Int32)__LINE__ );
1443 
1444     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1445 
1446     Init( rEnv );
1447     CurlRequest curlRequest( m_pCurl );
1448 
1449     addEnvironmentRequestHeaders( curlRequest, rEnv );
1450 
1451     CredentialsData credsData( this, curlRequest, rEnv );
1452     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1453 
1454     // check whether a lock on this resource is already owned
1455     rtl::OUString aUri( composeCurrentUri( inPath ) );
1456     ucb::Lock inLock;
1457     CurlLock * pLock = m_aCurlLockStore.findByUri( aUri );
1458     if ( pLock )
1459     {
1460         inLock = pLock->getLock();
1461     }
1462     if ( inLock.LockTokens.getLength() > 0 )
1463     {
1464         curlRequest.addHeader( "If",
1465             ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() );
1466     }
1467 
1468     CURLcode rc = curlRequest.delete_( m_aUri, inPath );
1469     processResponse( curlRequest, rc );
1470 }
1471 
1472 // -------------------------------------------------------------------
1473 
1474 namespace
1475 {
lastChanceToSendRefreshRequest(TimeValue const & rStart,sal_Int32 timeout)1476     sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
1477                                               sal_Int32 timeout )
1478     {
1479         TimeValue aEnd;
1480         osl_getSystemTime( &aEnd );
1481 
1482         // Try to estimate a safe absolute time for sending the
1483         // lock refresh request.
1484         sal_Int32 lastChanceToSendRefreshRequest = DAVINFINITY;
1485         if ( timeout != DAVINFINITY )
1486         {
1487             sal_Int32 calltime = aEnd.Seconds - rStart.Seconds;
1488             if ( calltime <= timeout )
1489             {
1490                 lastChanceToSendRefreshRequest
1491                     = aEnd.Seconds + timeout - calltime;
1492             }
1493             else
1494             {
1495                 OSL_TRACE( "No chance to refresh lock before timeout!" );
1496             }
1497         }
1498         return lastChanceToSendRefreshRequest;
1499     }
1500 
1501 } // namespace
1502 
1503 // -------------------------------------------------------------------
1504 // LOCK (set new lock)
1505 // -------------------------------------------------------------------
LOCK(const::rtl::OUString & inPath,ucb::Lock & inLock,const DAVRequestEnvironment & rEnv)1506 void CurlSession::LOCK( const ::rtl::OUString & inPath,
1507                         ucb::Lock & inLock,
1508                         const DAVRequestEnvironment & rEnv )
1509     throw ( DAVException )
1510 {
1511     m_aLogger.log( LogLevel::INFO, "LOCK line $1$", (sal_Int32)__LINE__ );
1512 
1513     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1514 
1515     // before locking, search in the lock store if we already own a lock for this resource
1516     // if present, return with exception DAV_LOCKED_SELF
1517     rtl::OUString   aUri( composeCurrentUri( inPath ) );
1518     CurlLock * pLock = m_aCurlLockStore.findByUri( aUri );
1519     if ( pLock )
1520     {
1521 // already present, meaning already locked by the same AOO session and already in the lockstore
1522 // just return, nothing to do
1523         return;
1524     }
1525 
1526     Init( rEnv );
1527     CurlRequest curlRequest( m_pCurl );
1528 
1529     addEnvironmentRequestHeaders( curlRequest, rEnv );
1530 
1531     CredentialsData credsData( this, curlRequest, rEnv );
1532     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1533 
1534     if ( inLock.Timeout == -1 )
1535         curlRequest.addHeader( "Timeout", "Infinite" );
1536     else
1537         curlRequest.addHeader( "Timeout", "Second-" + rtl::OString::valueOf( inLock.Timeout ) );
1538 
1539     switch ( inLock.Depth )
1540     {
1541         //i126305 TODO investigate on this case...
1542     case ucb::LockDepth_MAKE_FIXED_SIZE:
1543 
1544     case ucb::LockDepth_ZERO:
1545         curlRequest.addHeader( "Depth", "0" );
1546         break;
1547     case ucb::LockDepth_ONE:
1548         curlRequest.addHeader( "Depth", "1" );
1549         break;
1550     case ucb::LockDepth_INFINITY:
1551         curlRequest.addHeader( "Depth", "infinity" );
1552         break;
1553     }
1554 
1555     rtl::OString xml = LockRequest::generateRequestBody( inLock );
1556     curlRequest.addHeader( "Content-Type", "application/xml" );
1557     curlRequest.setRequestBody( xml.getStr(), xml.getLength() );
1558 
1559     TimeValue startCall;
1560     osl_getSystemTime( &startCall );
1561 
1562     CURLcode rc = curlRequest.lock( m_aUri, inPath );
1563     processResponse( curlRequest, rc );
1564 
1565     // the returned property, a sequence of locks
1566     // only the first is used
1567     const DAVPropertyValue outLock( parseWebDAVLockResponse( curlRequest.getResponseBody().get() ) );
1568     if(outLock.Name.compareToAscii(RTL_CONSTASCII_STRINGPARAM( "DAV:lockdiscovery" )) == 0 )
1569     {
1570         // got a lock, use only the first returned
1571         uno::Sequence< ucb::Lock >      aLocks;
1572         outLock.Value >>= aLocks;
1573         ucb::Lock aLock = aLocks[0];
1574 
1575         CurlLock* aNewLock = new CurlLock( aLock, aUri, inPath );
1576         // add the store the new lock
1577         m_aCurlLockStore.addLock(aNewLock,this,
1578                                  lastChanceToSendRefreshRequest(
1579                                      startCall, static_cast< sal_Int32 >(aLock.Timeout) ) );
1580     }
1581 }
1582 
1583 // -------------------------------------------------------------------
1584 // LOCK (refresh existing lock from DAVResourceAccess)
1585 // -------------------------------------------------------------------
LOCK(const::rtl::OUString &,sal_Int64 nTimeout,const DAVRequestEnvironment &)1586 sal_Int64 CurlSession::LOCK( const ::rtl::OUString & /*inPath*/,
1587                              sal_Int64 nTimeout,
1588                              const DAVRequestEnvironment & /*rEnv*/ )
1589     throw ( DAVException )
1590 {
1591     m_aLogger.log( LogLevel::INFO, "LOCK line $1$", (sal_Int32)__LINE__ );
1592 
1593     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1594 
1595     return nTimeout;
1596     /*
1597     // Try to get the neon lock from lock store
1598     CurlLock * theLock
1599         = m_aCurlLockStore.findByUri( makeAbsoluteURL( inPath ) );
1600     if ( !theLock )
1601          throw DAVException( DAVException::DAV_NOT_LOCKED );
1602 
1603     Init( rEnv );
1604 
1605     // refresh existing lock.
1606     theLock->timeout = static_cast< long >( nTimeout );
1607 
1608     TimeValue startCall;
1609     osl_getSystemTime( &startCall );
1610 
1611     int theRetVal = ne_lock_refresh( m_pHttpSession, theLock );
1612 
1613     if ( theRetVal == NE_OK )
1614     {
1615         m_aCurlLockStore.updateLock( theLock,
1616                                      lastChanceToSendRefreshRequest(
1617                                          startCall, theLock->timeout ) );
1618     }
1619 
1620     HandleError( theRetVal, inPath, rEnv );
1621 
1622     return theLock->timeout;
1623     */
1624 }
1625 
1626 // -------------------------------------------------------------------
1627 // LOCK (refresh existing lock from CurlLockStore)
1628 // -------------------------------------------------------------------
LOCK(CurlLock * pLock,sal_Int32 & rlastChanceToSendRefreshRequest)1629 bool CurlSession::LOCK( CurlLock * pLock,
1630                         sal_Int32 & rlastChanceToSendRefreshRequest )
1631 {
1632     m_aLogger.log( LogLevel::INFO, "LOCK line $1$", (sal_Int32)__LINE__ );
1633 
1634     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1635 
1636     Init();
1637     CurlRequest curlRequest( m_pCurl );
1638 
1639     const ucb::Lock & inLock = pLock->getLock();
1640     rtl::OUString inPath = pLock->getResourcePath();
1641 
1642     if ( inLock.Timeout == -1 )
1643         curlRequest.addHeader( "Timeout", "Infinite" );
1644     else
1645         curlRequest.addHeader( "Timeout", "Second-" + rtl::OString::valueOf( inLock.Timeout ) );
1646 
1647     switch ( inLock.Depth )
1648     {
1649         //i126305 TODO investigate on this case...
1650     case ucb::LockDepth_MAKE_FIXED_SIZE:
1651 
1652     case ucb::LockDepth_ZERO:
1653         curlRequest.addHeader( "Depth", "0" );
1654         break;
1655     case ucb::LockDepth_ONE:
1656         curlRequest.addHeader( "Depth", "1" );
1657         break;
1658     case ucb::LockDepth_INFINITY:
1659         curlRequest.addHeader( "Depth", "infinity" );
1660         break;
1661     }
1662 
1663     if ( inLock.LockTokens.getLength() > 0 )
1664     {
1665         curlRequest.addHeader( "If",
1666             ( "(<" + rtl::OUStringToOString(inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">)" ).getStr() );
1667     }
1668 
1669     rtl::OString xml = LockRequest::generateRequestBody( inLock );
1670     curlRequest.addHeader( "Content-Type", "application/xml" );
1671     curlRequest.setRequestBody( xml.getStr(), xml.getLength() );
1672 
1673     TimeValue startCall;
1674     osl_getSystemTime( &startCall );
1675 
1676     CURLcode rc = curlRequest.lock( m_aUri, inPath );
1677     processResponse( curlRequest, rc );
1678 
1679     // the returned property, a sequence of locks
1680     // only the first is used
1681     const DAVPropertyValue outLock( parseWebDAVLockResponse( curlRequest.getResponseBody().get() ) );
1682     uno::Sequence< ucb::Lock >      aLocks;
1683     outLock.Value >>= aLocks;
1684     ucb::Lock aLock = aLocks[0];
1685 
1686     // if ok, update the lastchance refresh time in lock
1687     rlastChanceToSendRefreshRequest
1688         = lastChanceToSendRefreshRequest( startCall, static_cast< sal_Int32 >(aLock.Timeout) );
1689 
1690     return true;
1691 }
1692 
1693 // -------------------------------------------------------------------
1694 // UNLOCK called from external (DAVResourceAccess)
1695 // -------------------------------------------------------------------
UNLOCK(const::rtl::OUString & inPath,const DAVRequestEnvironment & rEnv)1696 void CurlSession::UNLOCK( const ::rtl::OUString & inPath,
1697                           const DAVRequestEnvironment & rEnv )
1698     throw ( DAVException )
1699 {
1700     m_aLogger.log( LogLevel::INFO, "UNLOCK line $1$", (sal_Int32)__LINE__ );
1701 
1702     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1703 
1704     rtl::OUString aUri( composeCurrentUri( inPath ) );
1705     CurlLock * pLock = m_aCurlLockStore.findByUri( aUri );
1706     if ( !pLock )
1707     {
1708         throw DAVException( DAVException::DAV_NOT_LOCKED );
1709     }
1710 
1711     Init( rEnv );
1712     CurlRequest curlRequest( m_pCurl );
1713 
1714     addEnvironmentRequestHeaders( curlRequest, rEnv );
1715 
1716     CredentialsData credsData( this, curlRequest, rEnv );
1717     curlRequest.setProvideCredentialsCallback( Curl_ProvideCredentials, &credsData );
1718 
1719     ucb::Lock inLock = pLock->getLock();
1720     curlRequest.addHeader( "Lock-Token",
1721             ( "<" + rtl::OUStringToOString( inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">" ).getStr() );
1722 
1723     // remove lock from lockstore
1724     // so, if something goes wrong, we don't refresh it anymore
1725     m_aCurlLockStore.removeLock( pLock );
1726     delete pLock;
1727 
1728     CURLcode rc = curlRequest.unlock( m_aUri, inPath );
1729     processResponse( curlRequest, rc );
1730 }
1731 
1732 // -------------------------------------------------------------------
1733 // UNLOCK (called from CurlLockStore)
1734 // -------------------------------------------------------------------
UNLOCK(CurlLock * pLock)1735 bool CurlSession::UNLOCK( CurlLock * pLock )
1736 {
1737     m_aLogger.log( LogLevel::INFO, "UNLOCK line $1$", (sal_Int32)__LINE__ );
1738 
1739     osl::Guard< osl::Mutex > theGuard( m_aMutex );
1740 
1741     Init();
1742     CurlRequest curlRequest( m_pCurl );
1743 
1744     rtl::OUString inPath = pLock->getResourcePath();
1745     ucb::Lock inLock = pLock->getLock();
1746     curlRequest.addHeader( "Lock-Token",
1747             ( "<" + rtl::OUStringToOString( inLock.LockTokens[0], RTL_TEXTENCODING_UTF8 ) + ">" ).getStr() );
1748 
1749     CURLcode rc = curlRequest.unlock( m_aUri, inPath );
1750     processResponse( curlRequest, rc );
1751     return true;
1752 }
1753 
1754 // -------------------------------------------------------------------
abort()1755 void CurlSession::abort()
1756     throw ( DAVException )
1757 {
1758     // 11.11.09 (tkr): The following code lines causing crashes if
1759     // closing a ongoing connection. It turned out that this existing
1760     // solution doesn't work in multi-threading environments.
1761     // So I disabled them in 3.2. . Issue #73893# should fix it in OOo 3.3.
1762     //if ( m_pHttpSession )
1763     //    ne_close_connection( m_pHttpSession );
1764 }
1765 
1766 // -------------------------------------------------------------------
getProxySettings() const1767 const ucbhelper::InternetProxyServer & CurlSession::getProxySettings() const
1768 {
1769     if ( m_aUri.GetScheme().equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "http" ) ) ||
1770          m_aUri.GetScheme().equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "https" ) ) )
1771     {
1772         return m_rProxyDecider.getProxy( m_aUri.GetScheme(),
1773                                          m_aUri.GetHost(),
1774                                          m_aUri.GetPort() );
1775     }
1776     else
1777     {
1778         // TODO: figure out, if this case can occur
1779         return m_rProxyDecider.getProxy( m_aUri.GetScheme(),
1780                                          rtl::OUString() /* not used */,
1781                                          -1 /* not used */ );
1782     }
1783 }
1784 
1785 /*
1786 // -------------------------------------------------------------------
1787 namespace {
1788 
1789 bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
1790                         const char * token )
1791 {
1792     for ( sal_Int32 n = 0; n < rLocks.getLength(); ++n )
1793     {
1794         const uno::Sequence< rtl::OUString > & rTokens
1795             = rLocks[ n ].LockTokens;
1796         for ( sal_Int32 m = 0; m < rTokens.getLength(); ++m )
1797         {
1798             if ( rTokens[ m ].equalsAscii( token ) )
1799                 return true;
1800         }
1801     }
1802     return false;
1803 }
1804 
1805 } // namespace
1806 */
1807 
1808 // -------------------------------------------------------------------
1809 // This method doesn't seem to be used.
1810 // In any case the default behavior is to ask a lock with a life of 3 minutes
1811 // it will then be refreshed automatically (see CurlLockStore class)
1812 // In case of AOO crash the lock will expire by itself
removeExpiredLocktoken(const rtl::OUString &,const DAVRequestEnvironment &)1813 bool CurlSession::removeExpiredLocktoken( const rtl::OUString & /*inURL*/,
1814                                           const DAVRequestEnvironment & /*rEnv*/ )
1815 {
1816     return true;
1817     /*
1818     CurlLock * theLock = m_aCurlLockStore.findByUri( inURL );
1819     if ( !theLock )
1820         return false;
1821 
1822     // do a lockdiscovery to check whether this lock is still valid.
1823     try
1824     {
1825         // @@@ Alternative: use ne_lock_discover() => less overhead
1826 
1827         std::vector< DAVResource > aResources;
1828         std::vector< rtl::OUString > aPropNames;
1829         aPropNames.push_back( DAVProperties::LOCKDISCOVERY );
1830 
1831         PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv );
1832 
1833         if ( aResources.size() == 0 )
1834             return false;
1835 
1836         std::vector< DAVPropertyValue >::const_iterator it
1837             = aResources[ 0 ].properties.begin();
1838         std::vector< DAVPropertyValue >::const_iterator end
1839             = aResources[ 0 ].properties.end();
1840 
1841         while ( it != end )
1842         {
1843             if ( (*it).Name.equals( DAVProperties::LOCKDISCOVERY ) )
1844             {
1845                 uno::Sequence< ucb::Lock > aLocks;
1846                 if ( !( (*it).Value >>= aLocks ) )
1847                     return false;
1848 
1849                 if ( !containsLocktoken( aLocks, theLock->token ) )
1850                 {
1851                     // expired!
1852                     break;
1853                 }
1854 
1855                 // still valid.
1856                 return false;
1857             }
1858             ++it;
1859         }
1860 
1861         // No lockdiscovery prop in propfind result / locktoken not found
1862         // in propfind result -> not locked
1863         OSL_TRACE( "CurlSession::removeExpiredLocktoken: Removing "
1864                    " expired lock token for %s. token: %s",
1865                    rtl::OUStringToOString( inURL,
1866                                            RTL_TEXTENCODING_UTF8 ).getStr(),
1867                    theLock->token );
1868 
1869         m_aCurlLockStore.removeLock( theLock );
1870         ne_lock_destroy( theLock );
1871         return true;
1872     }
1873     catch ( DAVException const & )
1874     {
1875     }
1876     return false;
1877     */
1878 }
1879 
1880 // -------------------------------------------------------------------
1881 // static
1882 bool
getDataFromInputStream(const uno::Reference<io::XInputStream> & xStream,uno::Sequence<sal_Int8> & rData,bool bAppendTrailingZeroByte)1883 CurlSession::getDataFromInputStream(
1884     const uno::Reference< io::XInputStream > & xStream,
1885     uno::Sequence< sal_Int8 > & rData,
1886     bool bAppendTrailingZeroByte )
1887 {
1888     if ( xStream.is() )
1889     {
1890         uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY );
1891         if ( xSeekable.is() )
1892         {
1893             try
1894             {
1895                 sal_Int32 nSize
1896                     = sal::static_int_cast<sal_Int32>(xSeekable->getLength());
1897                 sal_Int32 nRead
1898                     = xStream->readBytes( rData, nSize );
1899 
1900                 if ( nRead == nSize )
1901                 {
1902                     if ( bAppendTrailingZeroByte )
1903                     {
1904                         rData.realloc( nSize + 1 );
1905                         rData[ nSize ] = sal_Int8( 0 );
1906                     }
1907                     return true;
1908                 }
1909             }
1910             catch ( io::NotConnectedException const & )
1911             {
1912                 // readBytes
1913             }
1914             catch ( io::BufferSizeExceededException const & )
1915             {
1916                 // readBytes
1917             }
1918             catch ( io::IOException const & )
1919             {
1920                 // getLength, readBytes
1921             }
1922         }
1923         else
1924         {
1925             try
1926             {
1927                 uno::Sequence< sal_Int8 > aBuffer;
1928                 sal_Int32 nPos = 0;
1929 
1930                 sal_Int32 nRead = xStream->readSomeBytes( aBuffer, 65536 );
1931                 while ( nRead > 0 )
1932                 {
1933                     if ( rData.getLength() < ( nPos + nRead ) )
1934                         rData.realloc( nPos + nRead );
1935 
1936                     aBuffer.realloc( nRead );
1937                     rtl_copyMemory( (void*)( rData.getArray() + nPos ),
1938                                     (const void*)aBuffer.getConstArray(),
1939                                     nRead );
1940                     nPos += nRead;
1941 
1942                     aBuffer.realloc( 0 );
1943                     nRead = xStream->readSomeBytes( aBuffer, 65536 );
1944                 }
1945 
1946                 if ( bAppendTrailingZeroByte )
1947                 {
1948                     rData.realloc( nPos + 1 );
1949                     rData[ nPos ] = sal_Int8( 0 );
1950                 }
1951                 return true;
1952             }
1953             catch ( io::NotConnectedException const & )
1954             {
1955                 // readBytes
1956             }
1957             catch ( io::BufferSizeExceededException const & )
1958             {
1959                 // readBytes
1960             }
1961             catch ( io::IOException const & )
1962             {
1963                 // readBytes
1964             }
1965         }
1966     }
1967     return false;
1968 }
1969 
1970 // ---------------------------------------------------------------------
1971 sal_Bool
isDomainMatch(rtl::OUString certHostName)1972 CurlSession::isDomainMatch( rtl::OUString certHostName )
1973 {
1974     rtl::OUString hostName = getHostName();
1975 
1976     if (hostName.equalsIgnoreAsciiCase( certHostName ) )
1977         return sal_True;
1978 
1979     if ( 0 == certHostName.indexOf( rtl::OUString::createFromAscii( "*" ) ) &&
1980          hostName.getLength() >= certHostName.getLength() )
1981     {
1982         rtl::OUString cmpStr = certHostName.copy( 1 );
1983 
1984         if ( hostName.matchIgnoreAsciiCase(
1985                 cmpStr, hostName.getLength() - cmpStr.getLength() ) )
1986             return sal_True;
1987     }
1988     return sal_False;
1989 }
1990