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