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