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