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