bump product version to 5.0.4.1
[LibreOffice.git] / ucb / source / ucp / webdav / SerfSession.cxx
bloba4d3ed38e4d71517b757c63ac21f37eaa538c79f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vector>
21 #include <string.h>
22 #include <rtl/string.h>
23 #include "comphelper/processfactory.hxx"
24 #include "comphelper/sequence.hxx"
25 #include "ucbhelper/simplecertificatevalidationrequest.hxx"
27 #include "AprEnv.hxx"
28 #include <apr_strings.h>
30 #include "DAVAuthListener.hxx"
31 #include "SerfSession.hxx"
32 #include "SerfUri.hxx"
33 #include "SerfRequestProcessor.hxx"
34 #include "SerfCallbacks.hxx"
35 #include "SerfInputStream.hxx"
36 #include "UCBDeadPropertyValue.hxx"
38 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
39 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
40 #include <com/sun/star/security/XCertificate.hpp>
41 #include <com/sun/star/security/CertificateValidity.hpp>
42 #include <com/sun/star/security/CertificateContainerStatus.hpp>
43 #include <com/sun/star/security/CertificateContainer.hpp>
44 #include <com/sun/star/security/XCertificateContainer.hpp>
45 #include <com/sun/star/security/CertAltNameEntry.hpp>
46 #include <com/sun/star/security/XSanExtension.hpp>
47 #define OID_SUBJECT_ALTERNATIVE_NAME "2.5.29.17"
49 #include <com/sun/star/ucb/Lock.hpp>
50 #include <com/sun/star/xml/crypto/XSEInitializer.hpp>
52 using namespace com::sun::star;
53 using namespace http_dav_ucp;
55 // Constructor
57 SerfSession::SerfSession(
58 const rtl::Reference< DAVSessionFactory > & rSessionFactory,
59 const OUString& inUri,
60 const ucbhelper::InternetProxyDecider & rProxyDecider )
61 throw ( DAVException )
62 : DAVSession( rSessionFactory )
63 , m_aMutex()
64 , m_aUri( inUri )
65 , m_aProxyName()
66 , m_nProxyPort( 0 )
67 , m_pSerfConnection( 0 )
68 , m_pSerfContext( 0 )
69 , m_bIsHeadRequestInProgress( false )
70 , m_bUseChunkedEncoding( false )
71 , m_bNoOfTransferEncodingSwitches( 0 )
72 , m_rProxyDecider( rProxyDecider )
73 , m_aEnv()
75 m_pSerfContext = serf_context_create( getAprPool() );
77 m_pSerfBucket_Alloc = serf_bucket_allocator_create( getAprPool(), NULL, NULL );
81 // Destructor
83 SerfSession::~SerfSession( )
85 if ( m_pSerfConnection )
87 serf_connection_close( m_pSerfConnection );
88 m_pSerfConnection = 0;
93 void SerfSession::Init( const DAVRequestEnvironment & rEnv )
94 throw ( DAVException )
96 osl::Guard< osl::Mutex > theGuard( m_aMutex );
97 m_aEnv = rEnv;
98 Init();
102 void SerfSession::Init()
103 throw ( DAVException )
105 osl::Guard< osl::Mutex > theGuard( m_aMutex );
107 bool bCreateNewSession = false;
109 if ( m_pSerfConnection == 0 )
111 const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
113 m_aProxyName = rProxyCfg.aName;
114 m_nProxyPort = rProxyCfg.nPort;
116 // Not yet initialized. Create new session.
117 bCreateNewSession = true;
119 else
121 const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
123 if ( ( rProxyCfg.aName != m_aProxyName )
124 || ( rProxyCfg.nPort != m_nProxyPort ) )
126 m_aProxyName = rProxyCfg.aName;
127 m_nProxyPort = rProxyCfg.nPort;
129 // new session needed, destroy old first
130 serf_connection_close( m_pSerfConnection );
131 m_pSerfConnection = 0;
132 bCreateNewSession = true;
136 if ( bCreateNewSession )
138 // TODO - close_connection callback
139 apr_status_t status = serf_connection_create2( &m_pSerfConnection,
140 m_pSerfContext,
141 m_aUri.getAprUri(),
142 Serf_ConnectSetup, this,
143 0 /* close connection callback */, 0 /* close connection baton */,
144 getAprPool() );
146 if ( m_pSerfConnection == 0 ||status != APR_SUCCESS )
148 throw DAVException( DAVException::DAV_SESSION_CREATE,
149 SerfUri::makeConnectionEndPointString( m_aUri.GetHost(), m_aUri.GetPort() ) );
152 // Register the session with the lock store
153 // m_aSerfLockStore.registerSession( m_pSerfConnection );
155 if ( m_aProxyName.getLength() )
157 apr_sockaddr_t *proxy_address = NULL;
158 status = apr_sockaddr_info_get( &proxy_address,
159 OUStringToOString( m_aProxyName, RTL_TEXTENCODING_UTF8 ).getStr(),
160 APR_UNSPEC,
161 static_cast<apr_port_t>(m_nProxyPort),
162 0, getAprPool() );
164 if ( status != APR_SUCCESS )
166 throw DAVException( DAVException::DAV_SESSION_CREATE,
167 SerfUri::makeConnectionEndPointString( m_aUri.GetHost(), m_aUri.GetPort() ) );
170 serf_config_proxy( m_pSerfContext, proxy_address );
174 serf_config_credentials_callback( m_pSerfContext, Serf_Credentials );
176 m_bUseChunkedEncoding = isSSLNeeded();
180 apr_pool_t* SerfSession::getAprPool()
182 return apr_environment::AprEnv::getAprEnv()->getAprPool();
185 serf_bucket_alloc_t* SerfSession::getSerfBktAlloc()
187 return m_pSerfBucket_Alloc;
190 serf_context_t* SerfSession::getSerfContext()
192 return m_pSerfContext;
195 serf_connection_t* SerfSession::getSerfConnection()
197 return m_pSerfConnection;
200 bool SerfSession::isHeadRequestInProgress()
202 return m_bIsHeadRequestInProgress;
205 bool SerfSession::isSSLNeeded()
207 return m_aUri.GetScheme().equalsIgnoreAsciiCase( "https" );
210 char* SerfSession::getHostinfo()
212 return m_aUri.getAprUri().hostinfo;
217 // virtual
218 bool SerfSession::CanUse( const OUString & inUri )
222 SerfUri theUri( inUri );
223 if ( ( theUri.GetPort() == m_aUri.GetPort() ) &&
224 ( theUri.GetHost() == m_aUri.GetHost() ) &&
225 ( theUri.GetScheme() == m_aUri.GetScheme() ) )
227 return true;
230 catch ( DAVException const & )
232 return false;
234 return false;
238 // virtual
239 bool SerfSession::UsesProxy()
241 Init();
242 return ( m_aProxyName.getLength() > 0 );
245 apr_status_t SerfSession::setupSerfConnection( apr_socket_t * inAprSocket,
246 serf_bucket_t **outSerfInputBucket,
247 serf_bucket_t **outSerfOutputBucket,
248 apr_pool_t* /*inAprPool*/ )
250 serf_bucket_t *tmpInputBkt;
251 tmpInputBkt = serf_context_bucket_socket_create( getSerfContext(),
252 inAprSocket,
253 getSerfBktAlloc() );
255 if ( isSSLNeeded() )
257 tmpInputBkt = serf_bucket_ssl_decrypt_create( tmpInputBkt,
259 getSerfBktAlloc() );
260 /** Set the callback that is called to authenticate the
261 certifcate (chain).
263 serf_ssl_server_cert_chain_callback_set(
264 serf_bucket_ssl_decrypt_context_get(tmpInputBkt),
265 NULL,
266 Serf_CertificateChainValidation,
267 this);
268 serf_ssl_set_hostname( serf_bucket_ssl_decrypt_context_get( tmpInputBkt ),
269 getHostinfo() );
271 *outSerfOutputBucket = serf_bucket_ssl_encrypt_create( *outSerfOutputBucket,
272 serf_bucket_ssl_decrypt_context_get( tmpInputBkt ),
273 getSerfBktAlloc() );
276 *outSerfInputBucket = tmpInputBkt;
278 return APR_SUCCESS;
281 apr_status_t SerfSession::provideSerfCredentials( bool bGiveProvidedCredentialsASecondTry,
282 char ** outUsername,
283 char ** outPassword,
284 serf_request_t * /*inRequest*/,
285 int /*inCode*/,
286 const char *inAuthProtocol,
287 const char *inRealm,
288 apr_pool_t *inAprPool )
290 DAVAuthListener * pListener = getRequestEnvironment().m_xAuthListener.get();
291 if ( !pListener )
293 // abort
294 return SERF_ERROR_AUTHN_FAILED;
297 OUString theUserName;
298 OUString thePassWord;
301 SerfUri uri( getRequestEnvironment().m_aRequestURI );
302 OUString aUserInfo( uri.GetUserInfo() );
303 if ( aUserInfo.getLength() )
305 sal_Int32 nPos = aUserInfo.indexOf( '@' );
306 if ( nPos == -1 )
308 theUserName = aUserInfo;
310 else
312 theUserName = aUserInfo.copy( 0, nPos );
313 thePassWord = aUserInfo.copy( nPos + 1 );
317 catch ( DAVException const & )
319 // abort
320 return SERF_ERROR_AUTHN_FAILED;
323 const bool bCanUseSystemCreds = ( ( strcasecmp( inAuthProtocol, "NTLM" ) == 0 ) ||
324 ( strcasecmp( inAuthProtocol, "Negotiate" ) == 0 ) );
326 int theRetVal = pListener->authenticate( OUString::createFromAscii( inRealm ),
327 getHostName(),
328 theUserName,
329 thePassWord,
330 bCanUseSystemCreds,
331 bGiveProvidedCredentialsASecondTry ? sal_False : sal_True );
333 if ( theRetVal == 0 )
335 *outUsername = apr_pstrdup( inAprPool, OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ).getStr() );
336 *outPassword = apr_pstrdup( inAprPool, OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ).getStr() );
339 return theRetVal != 0 ? SERF_ERROR_AUTHN_FAILED : APR_SUCCESS;
342 apr_status_t SerfSession::verifySerfCertificateChain (
343 int,
344 const serf_ssl_certificate_t * const * pCertificateChainBase64Encoded,
345 int nCertificateChainLength)
347 // Check arguments.
348 if (pCertificateChainBase64Encoded == NULL || nCertificateChainLength<=0)
350 assert(pCertificateChainBase64Encoded != NULL);
351 assert(nCertificateChainLength>0);
352 return SERF_SSL_CERT_UNKNOWN_FAILURE;
355 // When called from SerfLockStore::~SerfLockStore(),
356 // css::xml::crypto::SEInitializer::create() will fail
357 // but we want to send unlock commands anyway,
358 // so just ignore certificates and return here.
359 if (apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->finishing())
360 return APR_SUCCESS;
362 // Create some crypto objects to decode and handle the base64
363 // encoded certificate chain.
364 uno::Reference< security::XCertificateContainer > xCertificateContainer;
365 uno::Reference< xml::crypto::XXMLSecurityContext > xSecurityContext;
366 uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnv;
369 css::uno::Reference< css::uno::XComponentContext > xContext =
370 ::comphelper::getProcessComponentContext();
371 // Create a certificate container.
372 xCertificateContainer = security::CertificateContainer::create( xContext );
374 css::uno::Reference< css::xml::crypto::XSEInitializer > xSEInitializer =
375 css::xml::crypto::SEInitializer::create( xContext );
377 xSecurityContext = xSEInitializer->createSecurityContext( OUString() );
378 if (xSecurityContext.is())
379 xSecurityEnv = xSecurityContext->getSecurityEnvironment();
381 if ( ! xSecurityContext.is() || ! xSecurityEnv.is())
383 // Do we have to dispose xSEInitializer or xCertificateContainer?
384 return SERF_SSL_CERT_UNKNOWN_FAILURE;
387 catch ( uno::Exception const &)
389 return SERF_SSL_CERT_UNKNOWN_FAILURE;
392 // Decode the server certificate.
393 const char* sBase64EncodedServerCertificate (
394 serf_ssl_cert_export(
395 pCertificateChainBase64Encoded[0],
396 getAprPool()));
397 uno::Reference< security::XCertificate > xServerCertificate(
398 xSecurityEnv->createCertificateFromAscii(
399 OUString::createFromAscii(sBase64EncodedServerCertificate)));
400 if ( ! xServerCertificate.is())
401 return SERF_SSL_CERT_UNKNOWN_FAILURE;
403 // Get the subject from the server certificate.
404 OUString sServerCertificateSubject (xServerCertificate->getSubjectName());
405 sal_Int32 nIndex = 0;
406 while (nIndex >= 0)
408 const OUString sToken (sServerCertificateSubject.getToken(0, ',', nIndex));
409 if (sToken.startsWith("CN="))
411 sServerCertificateSubject = sToken.copy(3);
412 break;
414 else if (sToken.startsWith(" CN="))
416 sServerCertificateSubject = sToken.copy(4);
417 break;
421 // When the certificate container already contains a (trusted)
422 // entry for the server then we do not have to authenticate any
423 // certificate.
424 const security::CertificateContainerStatus eStatus (
425 xCertificateContainer->hasCertificate(
426 getHostName(), sServerCertificateSubject ) );
427 if (eStatus != security::CertificateContainerStatus_NOCERT)
429 return eStatus == security::CertificateContainerStatus_TRUSTED
430 ? APR_SUCCESS
431 : SERF_SSL_CERT_UNKNOWN_FAILURE;
434 // The shortcut failed, so try to verify the whole chain. This is
435 // done outside the isDomainMatch() block because the result is
436 // used by the interaction handler.
437 std::vector< uno::Reference< security::XCertificate > > aChain;
438 for (nIndex = 1; nIndex < nCertificateChainLength; ++nIndex)
440 const char* sBase64EncodedCertificate (
441 serf_ssl_cert_export(
442 pCertificateChainBase64Encoded[nIndex],
443 getAprPool()));
444 uno::Reference< security::XCertificate > xCertificate(
445 xSecurityEnv->createCertificateFromAscii(
446 OUString::createFromAscii(sBase64EncodedCertificate)));
447 if ( ! xCertificate.is())
448 return SERF_SSL_CERT_UNKNOWN_FAILURE;
449 aChain.push_back(xCertificate);
451 const sal_Int64 nVerificationResult (xSecurityEnv->verifyCertificate(
452 xServerCertificate,
453 ::comphelper::containerToSequence(aChain)));
455 // When the certificate matches the host name then we can use the
456 // result of the verification.
457 bool bHostnameMatchesCertHostnames = false;
459 uno::Sequence< uno::Reference< security::XCertificateExtension > > extensions = xServerCertificate->getExtensions();
460 uno::Sequence< security::CertAltNameEntry > altNames;
461 for (sal_Int32 i = 0 ; i < extensions.getLength(); ++i)
463 uno::Reference< security::XCertificateExtension >element = extensions[i];
465 const rtl::OString aId ( (const sal_Char *)element->getExtensionId().getArray(), element->getExtensionId().getLength());
466 if ( aId.equals( OID_SUBJECT_ALTERNATIVE_NAME ) )
468 uno::Reference< security::XSanExtension > sanExtension ( element, uno::UNO_QUERY );
469 altNames = sanExtension->getAlternativeNames();
470 break;
474 uno::Sequence< ::rtl::OUString > certHostNames(altNames.getLength() + 1);
475 certHostNames[0] = sServerCertificateSubject;
476 for( int n = 0; n < altNames.getLength(); ++n )
478 if (altNames[n].Type == security::ExtAltNameType_DNS_NAME)
480 altNames[n].Value >>= certHostNames[n+1];
484 for ( int i = 0; i < certHostNames.getLength() && !bHostnameMatchesCertHostnames; ++i )
486 bHostnameMatchesCertHostnames = isDomainMatch( certHostNames[i] );
490 if ( bHostnameMatchesCertHostnames )
493 if (nVerificationResult == 0)
495 // Certificate (chain) is valid.
496 xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, sal_True);
497 return APR_SUCCESS;
499 else if ((nVerificationResult & security::CertificateValidity::CHAIN_INCOMPLETE) != 0)
501 // We do not have enough information for verification,
502 // neither automatically (as we just discovered) nor
503 // manually (so there is no point in showing any dialog.)
504 return SERF_SSL_CERT_UNKNOWN_FAILURE;
506 else if ((nVerificationResult &
507 (security::CertificateValidity::INVALID | security::CertificateValidity::REVOKED)) != 0)
509 // Certificate (chain) is invalid.
510 xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, sal_False);
511 return SERF_SSL_CERT_UNKNOWN_FAILURE;
513 else
515 // For all other we have to ask the user.
519 // We have not been able to automatically verify (or falsify) the
520 // certificate chain. To resolve this we have to ask the user.
521 const uno::Reference< ucb::XCommandEnvironment > xEnv( getRequestEnvironment().m_xEnv );
522 if ( xEnv.is() )
524 uno::Reference< task::XInteractionHandler > xIH( xEnv->getInteractionHandler() );
525 if ( xIH.is() )
527 rtl::Reference< ucbhelper::SimpleCertificateValidationRequest >
528 xRequest( new ucbhelper::SimpleCertificateValidationRequest(
529 static_cast<sal_Int32>(nVerificationResult), xServerCertificate, getHostName() ) );
530 xIH->handle( xRequest.get() );
532 rtl::Reference< ucbhelper::InteractionContinuation > xSelection
533 = xRequest->getSelection();
535 if ( xSelection.is() )
537 uno::Reference< task::XInteractionApprove > xApprove( xSelection.get(), uno::UNO_QUERY );
538 if ( xApprove.is() )
540 xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_True );
541 return APR_SUCCESS;
543 else
545 // Don't trust cert
546 xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_False );
547 return SERF_SSL_CERT_UNKNOWN_FAILURE;
551 else
553 // Don't trust cert
554 xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, sal_False );
555 return SERF_SSL_CERT_UNKNOWN_FAILURE;
559 return SERF_SSL_CERT_UNKNOWN_FAILURE;
562 serf_bucket_t* SerfSession::acceptSerfResponse( serf_request_t * inSerfRequest,
563 serf_bucket_t * inSerfStreamBucket,
564 apr_pool_t* /*inAprPool*/ )
566 // get the per-request bucket allocator
567 serf_bucket_alloc_t* SerfBktAlloc = serf_request_get_alloc( inSerfRequest );
569 // create a barrier bucket so the response doesn't eat us!
570 serf_bucket_t *responseBkt = serf_bucket_barrier_create( inSerfStreamBucket,
571 SerfBktAlloc );
573 // create response bucket
574 responseBkt = serf_bucket_response_create( responseBkt,
575 SerfBktAlloc );
577 if ( isHeadRequestInProgress() )
579 // advise the response bucket that this was from a HEAD request and that it should not expect to see a response body.
580 serf_bucket_response_set_head( responseBkt );
583 return responseBkt;
586 SerfRequestProcessor* SerfSession::createReqProc( const OUString & inPath )
588 return new SerfRequestProcessor( *this,
589 inPath,
590 m_bUseChunkedEncoding );
594 // PROPFIND - allprop & named
596 void SerfSession::PROPFIND( const OUString & inPath,
597 const Depth inDepth,
598 const std::vector< OUString > & inPropNames,
599 std::vector< DAVResource > & ioResources,
600 const DAVRequestEnvironment & rEnv )
601 throw ( DAVException )
603 osl::Guard< osl::Mutex > theGuard( m_aMutex );
605 Init( rEnv );
607 apr_status_t status = APR_SUCCESS;
608 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
609 aReqProc->processPropFind( inDepth,
610 inPropNames,
611 ioResources,
612 status );
614 if ( status == APR_SUCCESS &&
615 aReqProc->mpDAVException == 0 &&
616 ioResources.empty() )
618 m_aEnv = DAVRequestEnvironment();
619 throw DAVException( DAVException::DAV_HTTP_ERROR, inPath, (sal_uInt16)APR_EGENERAL );
621 HandleError( aReqProc );
625 // PROPFIND - propnames
627 void SerfSession::PROPFIND( const OUString & inPath,
628 const Depth inDepth,
629 std::vector< DAVResourceInfo > & ioResInfo,
630 const DAVRequestEnvironment & rEnv )
631 throw( DAVException )
633 osl::Guard< osl::Mutex > theGuard( m_aMutex );
635 Init( rEnv );
637 apr_status_t status = APR_SUCCESS;
638 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
639 aReqProc->processPropFind( inDepth,
640 ioResInfo,
641 status );
643 if ( status == APR_SUCCESS &&
644 aReqProc->mpDAVException == 0 &&
645 ioResInfo.empty() )
647 m_aEnv = DAVRequestEnvironment();
648 throw DAVException( DAVException::DAV_HTTP_ERROR, inPath, (sal_uInt16)APR_EGENERAL );
650 HandleError( aReqProc );
654 // PROPPATCH
656 void SerfSession::PROPPATCH( const OUString & inPath,
657 const std::vector< ProppatchValue > & inValues,
658 const DAVRequestEnvironment & rEnv )
659 throw( DAVException )
661 osl::Guard< osl::Mutex > theGuard( m_aMutex );
663 Init( rEnv );
665 apr_status_t status = APR_SUCCESS;
666 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
667 aReqProc->processPropPatch( inValues,
668 status );
670 HandleError( aReqProc );
674 // HEAD
676 void SerfSession::HEAD( const OUString & inPath,
677 const std::vector< OUString > & inHeaderNames,
678 DAVResource & ioResource,
679 const DAVRequestEnvironment & rEnv )
680 throw( DAVException )
682 osl::Guard< osl::Mutex > theGuard( m_aMutex );
684 Init( rEnv );
686 m_bIsHeadRequestInProgress = true;
688 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
689 ioResource.uri = inPath;
690 ioResource.properties.clear();
691 apr_status_t status = APR_SUCCESS;
692 aReqProc->processHead( inHeaderNames,
693 ioResource,
694 status );
696 m_bIsHeadRequestInProgress = false;
698 HandleError( aReqProc );
702 // GET
704 uno::Reference< io::XInputStream >
705 SerfSession::GET( const OUString & inPath,
706 const DAVRequestEnvironment & rEnv )
707 throw ( DAVException )
709 osl::Guard< osl::Mutex > theGuard( m_aMutex );
711 Init( rEnv );
713 uno::Reference< SerfInputStream > xInputStream( new SerfInputStream );
714 apr_status_t status = APR_SUCCESS;
715 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
716 aReqProc->processGet( xInputStream,
717 status );
719 HandleError( aReqProc );
721 return uno::Reference< io::XInputStream >( xInputStream.get() );
725 // GET
727 void SerfSession::GET( const OUString & inPath,
728 uno::Reference< io::XOutputStream > & ioOutputStream,
729 const DAVRequestEnvironment & rEnv )
730 throw ( DAVException )
732 osl::Guard< osl::Mutex > theGuard( m_aMutex );
734 Init( rEnv );
736 apr_status_t status = APR_SUCCESS;
737 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
738 aReqProc->processGet( ioOutputStream,
739 status );
741 HandleError( aReqProc );
745 // GET
747 uno::Reference< io::XInputStream >
748 SerfSession::GET( const OUString & inPath,
749 const std::vector< OUString > & inHeaderNames,
750 DAVResource & ioResource,
751 const DAVRequestEnvironment & rEnv )
752 throw ( DAVException )
754 osl::Guard< osl::Mutex > theGuard( m_aMutex );
756 Init( rEnv );
758 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
759 uno::Reference< SerfInputStream > xInputStream( new SerfInputStream );
760 ioResource.uri = inPath;
761 ioResource.properties.clear();
762 apr_status_t status = APR_SUCCESS;
763 aReqProc->processGet( xInputStream,
764 inHeaderNames,
765 ioResource,
766 status );
768 HandleError( aReqProc );
770 return uno::Reference< io::XInputStream >( xInputStream.get() );
775 // GET
777 void SerfSession::GET( const OUString & inPath,
778 uno::Reference< io::XOutputStream > & ioOutputStream,
779 const std::vector< OUString > & inHeaderNames,
780 DAVResource & ioResource,
781 const DAVRequestEnvironment & rEnv )
782 throw ( DAVException )
784 osl::Guard< osl::Mutex > theGuard( m_aMutex );
786 Init( rEnv );
788 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
789 ioResource.uri = inPath;
790 ioResource.properties.clear();
791 apr_status_t status = APR_SUCCESS;
792 aReqProc->processGet( ioOutputStream,
793 inHeaderNames,
794 ioResource,
795 status );
797 HandleError( aReqProc );
801 // PUT
803 void SerfSession::PUT( const OUString & inPath,
804 const uno::Reference< io::XInputStream > & inInputStream,
805 const DAVRequestEnvironment & rEnv )
806 throw ( DAVException )
808 osl::Guard< osl::Mutex > theGuard( m_aMutex );
810 Init( rEnv );
812 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
813 uno::Sequence< sal_Int8 > aDataToSend;
814 if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
815 throw DAVException( DAVException::DAV_INVALID_ARG );
816 apr_status_t status = APR_SUCCESS;
817 aReqProc->processPut( reinterpret_cast< const char * >( aDataToSend.getConstArray() ),
818 aDataToSend.getLength(),
819 status );
821 HandleError( aReqProc );
825 // POST
827 uno::Reference< io::XInputStream >
828 SerfSession::POST( const OUString & inPath,
829 const OUString & rContentType,
830 const OUString & rReferer,
831 const uno::Reference< io::XInputStream > & inInputStream,
832 const DAVRequestEnvironment & rEnv )
833 throw ( DAVException )
835 osl::Guard< osl::Mutex > theGuard( m_aMutex );
837 uno::Sequence< sal_Int8 > aDataToSend;
838 if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
840 throw DAVException( DAVException::DAV_INVALID_ARG );
843 Init( rEnv );
845 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
846 uno::Reference< SerfInputStream > xInputStream( new SerfInputStream );
847 apr_status_t status = APR_SUCCESS;
848 aReqProc->processPost( reinterpret_cast< const char * >( aDataToSend.getConstArray() ),
849 aDataToSend.getLength(),
850 rContentType,
851 rReferer,
852 xInputStream,
853 status );
855 HandleError( aReqProc );
856 return uno::Reference< io::XInputStream >( xInputStream.get() );
860 // POST
862 void SerfSession::POST( const OUString & inPath,
863 const OUString & rContentType,
864 const OUString & rReferer,
865 const uno::Reference< io::XInputStream > & inInputStream,
866 uno::Reference< io::XOutputStream > & oOutputStream,
867 const DAVRequestEnvironment & rEnv )
868 throw ( DAVException )
870 osl::Guard< osl::Mutex > theGuard( m_aMutex );
872 uno::Sequence< sal_Int8 > aDataToSend;
873 if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
875 throw DAVException( DAVException::DAV_INVALID_ARG );
878 Init( rEnv );
880 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
881 apr_status_t status = APR_SUCCESS;
882 aReqProc->processPost( reinterpret_cast< const char * >( aDataToSend.getConstArray() ),
883 aDataToSend.getLength(),
884 rContentType,
885 rReferer,
886 oOutputStream,
887 status );
889 HandleError( aReqProc );
893 // MKCOL
895 void SerfSession::MKCOL( const OUString & inPath,
896 const DAVRequestEnvironment & rEnv )
897 throw ( DAVException )
899 osl::Guard< osl::Mutex > theGuard( m_aMutex );
901 Init( rEnv );
903 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
904 apr_status_t status = APR_SUCCESS;
905 aReqProc->processMkCol( status );
907 HandleError( aReqProc );
911 // COPY
913 void SerfSession::COPY( const OUString & inSourceURL,
914 const OUString & inDestinationURL,
915 const DAVRequestEnvironment & rEnv,
916 bool inOverWrite )
917 throw ( DAVException )
919 osl::Guard< osl::Mutex > theGuard( m_aMutex );
921 Init( rEnv );
923 SerfUri theSourceUri( inSourceURL );
924 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( theSourceUri.GetPath() ) );
925 apr_status_t status = APR_SUCCESS;
926 aReqProc->processCopy( inDestinationURL,
927 (inOverWrite ? true : false),
928 status );
930 HandleError( aReqProc );
934 // MOVE
936 void SerfSession::MOVE( const OUString & inSourceURL,
937 const OUString & inDestinationURL,
938 const DAVRequestEnvironment & rEnv,
939 bool inOverWrite )
940 throw ( DAVException )
942 osl::Guard< osl::Mutex > theGuard( m_aMutex );
944 Init( rEnv );
946 SerfUri theSourceUri( inSourceURL );
947 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( theSourceUri.GetPath() ) );
948 apr_status_t status = APR_SUCCESS;
949 aReqProc->processMove( inDestinationURL,
950 (inOverWrite ? true : false),
951 status );
953 HandleError( aReqProc );
957 // DESTROY
959 void SerfSession::DESTROY( const OUString & inPath,
960 const DAVRequestEnvironment & rEnv )
961 throw ( DAVException )
963 osl::Guard< osl::Mutex > theGuard( m_aMutex );
965 Init( rEnv );
967 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
968 apr_status_t status = APR_SUCCESS;
969 aReqProc->processDelete( status );
971 HandleError( aReqProc );
976 namespace
978 sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
979 int timeout )
981 TimeValue aEnd;
982 osl_getSystemTime( &aEnd );
984 // Try to estimate a safe absolute time for sending the
985 // lock refresh request.
986 sal_Int32 lastChanceToSendRefreshRequest = -1;
987 if ( timeout != NE_TIMEOUT_INFINITE )
989 sal_Int32 calltime = aEnd.Seconds - rStart.Seconds;
990 if ( calltime <= timeout )
992 lastChanceToSendRefreshRequest
993 = aEnd.Seconds + timeout - calltime;
995 else
997 SAL_INFO("ucb.ucp.webdav", "No chance to refresh lock before timeout!" );
1000 return lastChanceToSendRefreshRequest;
1003 } // namespace
1006 // LOCK (set new lock)
1008 void SerfSession::LOCK( const OUString & inPath,
1009 ucb::Lock & rLock,
1010 const DAVRequestEnvironment & rEnv )
1011 throw ( DAVException )
1013 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1015 Init( rEnv );
1017 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
1018 aReqProc->processLock( rLock );
1020 HandleError( aReqProc );
1024 // LOCK (refresh existing lock)
1026 sal_Int64 SerfSession::LOCK( const OUString & /*inPath*/,
1027 sal_Int64 nTimeout,
1028 const DAVRequestEnvironment & /*rEnv*/ )
1029 throw ( DAVException )
1031 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1033 return nTimeout;
1035 // Try to get the neon lock from lock store
1036 SerfLock * theLock
1037 = m_aSerfLockStore.findByUri( makeAbsoluteURL( inPath ) );
1038 if ( !theLock )
1039 throw DAVException( DAVException::DAV_NOT_LOCKED );
1041 Init( rEnv );
1043 // refresh existing lock.
1044 theLock->timeout = static_cast< long >( nTimeout );
1046 TimeValue startCall;
1047 osl_getSystemTime( &startCall );
1049 int theRetVal = ne_lock_refresh( m_pHttpSession, theLock );
1051 if ( theRetVal == NE_OK )
1053 m_aSerfLockStore.updateLock( theLock,
1054 lastChanceToSendRefreshRequest(
1055 startCall, theLock->timeout ) );
1058 HandleError( theRetVal, inPath, rEnv );
1060 return theLock->timeout;
1065 // LOCK (refresh existing lock)
1067 bool SerfSession::LOCK( const OUString& rLock,
1068 sal_Int32 *plastChanceToSendRefreshRequest )
1070 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1072 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( rLock ) );
1073 aReqProc->processLock( ucb::Lock(), plastChanceToSendRefreshRequest );
1077 HandleError( aReqProc );
1078 SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock << " succeeded." );
1079 return true;
1081 catch(...)
1083 SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock << " failed!" );
1084 return false;
1089 // UNLOCK
1091 void SerfSession::UNLOCK( const OUString & inPath,
1092 const DAVRequestEnvironment & rEnv )
1093 throw ( DAVException )
1095 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1097 Init( rEnv );
1099 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
1100 aReqProc->processUnlock();
1104 HandleError( aReqProc );
1105 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath << " succeeded." );
1106 apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->removeLock( inPath );
1108 catch(...)
1110 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath << " failed!" );
1115 // UNLOCK
1117 void SerfSession::UNLOCK( const OUString& rLock )
1119 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1121 boost::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( rLock ) );
1122 aReqProc->processUnlock();
1126 HandleError( aReqProc );
1127 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << rLock << " succeeded." );
1129 catch(...)
1131 SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << rLock << " failed!" );
1136 void SerfSession::abort()
1137 throw ( DAVException )
1139 // 11.11.09 (tkr): The following code lines causing crashes if
1140 // closing a ongoing connection. It turned out that this existing
1141 // solution doesn't work in multi-threading environments.
1142 // So I disabled them in 3.2. . Issue #73893# should fix it in OOo 3.3.
1143 //if ( m_pHttpSession )
1144 // ne_close_connection( m_pHttpSession );
1148 const ucbhelper::InternetProxyServer & SerfSession::getProxySettings() const
1150 if ( m_aUri.GetScheme() == "http" || m_aUri.GetScheme() == "https" )
1152 return m_rProxyDecider.getProxy( m_aUri.GetScheme(),
1153 m_aUri.GetHost(),
1154 m_aUri.GetPort() );
1156 else
1158 // TODO: figure out, if this case can occur
1159 return m_rProxyDecider.getProxy( m_aUri.GetScheme(),
1160 OUString() /* not used */,
1161 -1 /* not used */ );
1167 namespace {
1169 bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
1170 const char * token )
1172 for ( sal_Int32 n = 0; n < rLocks.getLength(); ++n )
1174 const uno::Sequence< OUString > & rTokens
1175 = rLocks[ n ].LockTokens;
1176 for ( sal_Int32 m = 0; m < rTokens.getLength(); ++m )
1178 if ( rTokens[ m ].equalsAscii( token ) )
1179 return true;
1182 return false;
1185 } // namespace
1189 bool SerfSession::removeExpiredLocktoken( const OUString & /*inURL*/,
1190 const DAVRequestEnvironment & /*rEnv*/ )
1192 return true;
1194 SerfLock * theLock = m_aSerfLockStore.findByUri( inURL );
1195 if ( !theLock )
1196 return false;
1198 // do a lockdiscovery to check whether this lock is still valid.
1201 // @@@ Alternative: use ne_lock_discover() => less overhead
1203 std::vector< DAVResource > aResources;
1204 std::vector< OUString > aPropNames;
1205 aPropNames.push_back( DAVProperties::LOCKDISCOVERY );
1207 PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv );
1209 if ( aResources.size() == 0 )
1210 return false;
1212 std::vector< DAVPropertyValue >::const_iterator it
1213 = aResources[ 0 ].properties.begin();
1214 std::vector< DAVPropertyValue >::const_iterator end
1215 = aResources[ 0 ].properties.end();
1217 while ( it != end )
1219 if ( (*it).Name.equals( DAVProperties::LOCKDISCOVERY ) )
1221 uno::Sequence< ucb::Lock > aLocks;
1222 if ( !( (*it).Value >>= aLocks ) )
1223 return false;
1225 if ( !containsLocktoken( aLocks, theLock->token ) )
1227 // expired!
1228 break;
1231 // still valid.
1232 return false;
1234 ++it;
1237 // No lockdiscovery prop in propfind result / locktoken not found
1238 // in propfind result -> not locked
1239 SAL_INFO("ucb.ucp.webdav", "SerfSession::removeExpiredLocktoken: Removing "
1240 " expired lock token for " << inURL << ". token: " << theLock->token );
1242 m_aSerfLockStore.removeLock( theLock );
1243 ne_lock_destroy( theLock );
1244 return true;
1246 catch ( DAVException const & )
1249 return false;
1254 // HandleError
1255 // Common Error Handler
1257 void SerfSession::HandleError( boost::shared_ptr<SerfRequestProcessor> rReqProc )
1258 throw ( DAVException )
1260 m_aEnv = DAVRequestEnvironment();
1262 if ( rReqProc->mpDAVException )
1264 DAVException* mpDAVExp( rReqProc->mpDAVException );
1266 serf_connection_reset( getSerfConnection() );
1268 if ( mpDAVExp->getStatus() == 413 &&
1269 m_bNoOfTransferEncodingSwitches < 2 )
1271 m_bUseChunkedEncoding = !m_bUseChunkedEncoding;
1272 ++m_bNoOfTransferEncodingSwitches;
1275 throw DAVException( mpDAVExp->getError(),
1276 mpDAVExp->getData(),
1277 mpDAVExp->getStatus() );
1281 // Map error code to DAVException.
1282 switch ( nError )
1284 case NE_OK:
1285 return;
1287 case NE_ERROR: // Generic error
1289 OUString aText = OUString::createFromAscii(
1290 ne_get_error( m_pHttpSession ) );
1292 sal_uInt16 code = makeStatusCode( aText );
1294 if ( code == SC_LOCKED )
1296 if ( m_aSerfLockStore.findByUri(
1297 makeAbsoluteURL( inPath ) ) == 0 )
1299 // locked by 3rd party
1300 throw DAVException( DAVException::DAV_LOCKED );
1302 else
1304 // locked by ourself
1305 throw DAVException( DAVException::DAV_LOCKED_SELF );
1309 // Special handling for 400 and 412 status codes, which may indicate
1310 // that a lock previously obtained by us has been released meanwhile
1311 // by the server. Unfortunately, RFC is not clear at this point,
1312 // thus server implementations behave different...
1313 else if ( code == SC_BAD_REQUEST || code == SC_PRECONDITION_FAILED )
1315 if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) )
1316 throw DAVException( DAVException::DAV_LOCK_EXPIRED );
1319 throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
1321 case NE_LOOKUP: // Name lookup failed.
1322 throw DAVException( DAVException::DAV_HTTP_LOOKUP,
1323 SerfUri::makeConnectionEndPointString(
1324 m_aHostName, m_nPort ) );
1326 case NE_AUTH: // User authentication failed on server
1327 throw DAVException( DAVException::DAV_HTTP_AUTH,
1328 SerfUri::makeConnectionEndPointString(
1329 m_aHostName, m_nPort ) );
1331 case NE_PROXYAUTH: // User authentication failed on proxy
1332 throw DAVException( DAVException::DAV_HTTP_AUTHPROXY,
1333 SerfUri::makeConnectionEndPointString(
1334 m_aProxyName, m_nProxyPort ) );
1336 case NE_CONNECT: // Could not connect to server
1337 throw DAVException( DAVException::DAV_HTTP_CONNECT,
1338 SerfUri::makeConnectionEndPointString(
1339 m_aHostName, m_nPort ) );
1341 case NE_TIMEOUT: // Connection timed out
1342 throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
1343 SerfUri::makeConnectionEndPointString(
1344 m_aHostName, m_nPort ) );
1346 case NE_FAILED: // The precondition failed
1347 throw DAVException( DAVException::DAV_HTTP_FAILED,
1348 SerfUri::makeConnectionEndPointString(
1349 m_aHostName, m_nPort ) );
1351 case NE_RETRY: // Retry request (ne_end_request ONLY)
1352 throw DAVException( DAVException::DAV_HTTP_RETRY,
1353 SerfUri::makeConnectionEndPointString(
1354 m_aHostName, m_nPort ) );
1356 case NE_REDIRECT:
1358 SerfUri aUri( ne_redirect_location( m_pHttpSession ) );
1359 throw DAVException(
1360 DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() );
1362 default:
1364 SAL_INFO("ucb.ucp.webdav", "SerfSession::HandleError : Unknown Serf error code!" );
1365 throw DAVException( DAVException::DAV_HTTP_ERROR,
1366 OUString::createFromAscii(
1367 ne_get_error( m_pHttpSession ) ) );
1374 // static
1375 bool
1376 SerfSession::getDataFromInputStream(
1377 const uno::Reference< io::XInputStream > & xStream,
1378 uno::Sequence< sal_Int8 > & rData,
1379 bool bAppendTrailingZeroByte )
1381 if ( xStream.is() )
1383 uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY );
1384 if ( xSeekable.is() )
1388 sal_Int32 nSize
1389 = sal::static_int_cast<sal_Int32>(xSeekable->getLength());
1390 sal_Int32 nRead
1391 = xStream->readBytes( rData, nSize );
1393 if ( nRead == nSize )
1395 if ( bAppendTrailingZeroByte )
1397 rData.realloc( nSize + 1 );
1398 rData[ nSize ] = sal_Int8( 0 );
1400 return true;
1403 catch ( io::NotConnectedException const & )
1405 // readBytes
1407 catch ( io::BufferSizeExceededException const & )
1409 // readBytes
1411 catch ( io::IOException const & )
1413 // getLength, readBytes
1416 else
1420 uno::Sequence< sal_Int8 > aBuffer;
1421 sal_Int32 nPos = 0;
1423 sal_Int32 nRead = xStream->readSomeBytes( aBuffer, 65536 );
1424 while ( nRead > 0 )
1426 if ( rData.getLength() < ( nPos + nRead ) )
1427 rData.realloc( nPos + nRead );
1429 aBuffer.realloc( nRead );
1430 memcpy( (void*)( rData.getArray() + nPos ),
1431 (const void*)aBuffer.getConstArray(),
1432 nRead );
1433 nPos += nRead;
1435 aBuffer.realloc( 0 );
1436 nRead = xStream->readSomeBytes( aBuffer, 65536 );
1439 if ( bAppendTrailingZeroByte )
1441 rData.realloc( nPos + 1 );
1442 rData[ nPos ] = sal_Int8( 0 );
1444 return true;
1446 catch ( io::NotConnectedException const & )
1448 // readBytes
1450 catch ( io::BufferSizeExceededException const & )
1452 // readBytes
1454 catch ( io::IOException const & )
1456 // readBytes
1460 return false;
1464 bool
1465 SerfSession::isDomainMatch( const OUString & certHostName )
1467 OUString hostName = getHostName();
1469 if (hostName.equalsIgnoreAsciiCase( certHostName ) )
1470 return true;
1472 if ( certHostName.startsWith( "*" ) &&
1473 hostName.getLength() >= certHostName.getLength() )
1475 OUString cmpStr = certHostName.copy( 1 );
1477 if ( hostName.matchIgnoreAsciiCase(
1478 cmpStr, hostName.getLength() - cmpStr.getLength() ) )
1479 return true;
1481 return false;
1486 OUString SerfSession::makeAbsoluteURL( OUString const & rURL ) const
1490 // Is URL relative or already absolute?
1491 if ( rURL[ 0 ] != '/' )
1493 // absolute.
1494 return OUString( rURL );
1496 else
1498 ne_uri aUri;
1499 memset( &aUri, 0, sizeof( aUri ) );
1501 ne_fill_server_uri( m_pHttpSession, &aUri );
1502 aUri.path
1503 = ne_strdup( OUStringToOString(
1504 rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
1505 SerfUri aSerfUri( &aUri );
1506 ne_uri_free( &aUri );
1507 return aSerfUri.GetURI();
1510 catch ( DAVException const & )
1513 // error.
1514 return OUString();
1518 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */