fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / ucb / source / ucp / webdav-neon / NeonSession.cxx
bloba35a124aafee530f5579aff6f42f98d1c7f74ed7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include <unordered_map>
31 #include <vector>
32 #include <string.h>
33 #include "osl/diagnose.h"
34 #include "osl/time.h"
35 #include <rtl/string.h>
36 #include <ne_socket.h>
37 #include <ne_auth.h>
38 #include <ne_redirect.h>
39 #include <ne_ssl.h>
41 // old neon versions forgot to set this
42 extern "C" {
43 #include <ne_compress.h>
46 #include "libxml/parser.h"
47 #include "rtl/ustrbuf.hxx"
48 #include "comphelper/processfactory.hxx"
49 #include "comphelper/sequence.hxx"
50 #include <comphelper/stl_types.hxx>
51 #include "ucbhelper/simplecertificatevalidationrequest.hxx"
53 #include "DAVAuthListener.hxx"
54 #include "NeonTypes.hxx"
55 #include "NeonSession.hxx"
56 #include "NeonInputStream.hxx"
57 #include "NeonPropFindRequest.hxx"
58 #include "NeonHeadRequest.hxx"
59 #include "NeonUri.hxx"
60 #include "LinkSequence.hxx"
61 #include "UCBDeadPropertyValue.hxx"
63 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
64 #include <com/sun/star/security/XCertificate.hpp>
65 #include <com/sun/star/security/CertificateValidity.hpp>
66 #include <com/sun/star/security/CertificateContainerStatus.hpp>
67 #include <com/sun/star/security/CertificateContainer.hpp>
68 #include <com/sun/star/security/XCertificateContainer.hpp>
69 #include <com/sun/star/ucb/Lock.hpp>
70 #include <com/sun/star/beans/NamedValue.hpp>
71 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
73 #include <boost/bind.hpp>
75 using namespace com::sun::star;
76 using namespace webdav_ucp;
78 #ifndef EOL
79 # define EOL "\r\n"
80 #endif
82 struct RequestData
84 // POST
85 OUString aContentType;
86 OUString aReferer;
88 RequestData() {}
89 RequestData( const OUString & rContentType,
90 const OUString & rReferer )
91 : aContentType( rContentType ), aReferer( rReferer ) {}
94 struct equalPtr
96 bool operator()( const ne_request* p1, const ne_request* p2 ) const
98 return p1 == p2;
102 struct hashPtr
104 size_t operator()( const ne_request* p ) const
106 return reinterpret_cast<size_t>(p);
110 typedef std::unordered_map
112 ne_request*,
113 RequestData,
114 hashPtr,
115 equalPtr
117 RequestDataMap;
119 static sal_uInt16 makeStatusCode( const OUString & rStatusText )
121 // Extract status code from session error string. Unfortunately
122 // neon provides no direct access to the status code...
124 if ( rStatusText.getLength() < 3 )
126 OSL_FAIL(
127 "makeStatusCode - status text string to short!" );
128 return 0;
131 sal_Int32 nPos = rStatusText.indexOf( ' ' );
132 if ( nPos == -1 )
134 OSL_FAIL( "makeStatusCode - wrong status text format!" );
135 return 0;
138 return sal_uInt16( rStatusText.copy( 0, nPos ).toInt32() );
141 static bool noKeepAlive( const uno::Sequence< beans::NamedValue >& rFlags )
143 if ( !rFlags.hasElements() )
144 return false;
146 // find "KeepAlive" property
147 const beans::NamedValue* pAry(rFlags.getConstArray());
148 const sal_Int32 nLen(rFlags.getLength());
149 const beans::NamedValue* pValue(
150 std::find_if(pAry,pAry+nLen,
151 boost::bind(comphelper::TNamedValueEqualFunctor(),
153 OUString("KeepAlive"))));
154 if ( pValue != pAry+nLen && !pValue->Value.get<sal_Bool>() )
155 return true;
157 return false;
160 struct NeonRequestContext
162 uno::Reference< io::XOutputStream > xOutputStream;
163 rtl::Reference< NeonInputStream > xInputStream;
164 const std::vector< OUString > * pHeaderNames;
165 DAVResource * pResource;
167 NeonRequestContext( uno::Reference< io::XOutputStream > & xOutStrm )
168 : xOutputStream( xOutStrm ), xInputStream( 0 ),
169 pHeaderNames( 0 ), pResource( 0 ) {}
171 NeonRequestContext( const rtl::Reference< NeonInputStream > & xInStrm )
172 : xOutputStream( 0 ), xInputStream( xInStrm ),
173 pHeaderNames( 0 ), pResource( 0 ) {}
175 NeonRequestContext( uno::Reference< io::XOutputStream > & xOutStrm,
176 const std::vector< OUString > & inHeaderNames,
177 DAVResource & ioResource )
178 : xOutputStream( xOutStrm ), xInputStream( 0 ),
179 pHeaderNames( &inHeaderNames ), pResource( &ioResource ) {}
181 NeonRequestContext( const rtl::Reference< NeonInputStream > & xInStrm,
182 const std::vector< OUString > & inHeaderNames,
183 DAVResource & ioResource )
184 : xOutputStream( 0 ), xInputStream( xInStrm ),
185 pHeaderNames( &inHeaderNames ), pResource( &ioResource ) {}
188 // A simple Neon response_block_reader for use with an XInputStream
189 extern "C" int NeonSession_ResponseBlockReader(void * inUserData,
190 const char * inBuf,
191 size_t inLen )
193 // neon sometimes calls this function with (inLen == 0)...
194 if ( inLen > 0 )
196 NeonRequestContext * pCtx
197 = static_cast< NeonRequestContext * >( inUserData );
199 rtl::Reference< NeonInputStream > xInputStream(
200 pCtx->xInputStream );
202 if ( xInputStream.is() )
203 xInputStream->AddToStream( inBuf, inLen );
205 return 0;
208 // A simple Neon response_block_reader for use with an XOutputStream
209 extern "C" int NeonSession_ResponseBlockWriter( void * inUserData,
210 const char * inBuf,
211 size_t inLen )
213 // neon calls this function with (inLen == 0)...
214 if ( inLen > 0 )
216 NeonRequestContext * pCtx
217 = static_cast< NeonRequestContext * >( inUserData );
218 uno::Reference< io::XOutputStream > xOutputStream
219 = pCtx->xOutputStream;
221 if ( xOutputStream.is() )
223 const uno::Sequence< sal_Int8 > aSeq( reinterpret_cast<sal_Int8 const *>(inBuf), inLen );
224 xOutputStream->writeBytes( aSeq );
227 return 0;
230 extern "C" int NeonSession_NeonAuth( void * inUserData,
231 #if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
232 const char * inAuthProtocol,
233 #endif
234 const char * inRealm,
235 int attempt,
236 char * inoutUserName,
237 char * inoutPassWord )
239 /* The callback used to request the username and password in the given
240 * realm. The username and password must be copied into the buffers
241 * which are both of size NE_ABUFSIZ. The 'attempt' parameter is zero
242 * on the first call to the callback, and increases by one each time
243 * an attempt to authenticate fails.
245 * The callback must return zero to indicate that authentication
246 * should be attempted with the username/password, or non-zero to
247 * cancel the request. (if non-zero, username and password are
248 * ignored.) */
250 NeonSession * theSession = static_cast< NeonSession * >( inUserData );
251 DAVAuthListener * pListener
252 = theSession->getRequestEnvironment().m_xAuthListener.get();
253 if ( !pListener )
255 // abort
256 return -1;
258 OUString theUserName;
259 OUString thePassWord;
261 if ( attempt == 0 )
263 // neon does not handle username supplied with request URI (for
264 // instance when doing FTP over proxy - last checked: 0.23.5 )
268 NeonUri uri( theSession->getRequestEnvironment().m_aRequestURI );
269 OUString aUserInfo( uri.GetUserInfo() );
270 if ( !aUserInfo.isEmpty() )
272 sal_Int32 nPos = aUserInfo.indexOf( '@' );
273 if ( nPos == -1 )
275 theUserName = aUserInfo;
277 else
279 theUserName = aUserInfo.copy( 0, nPos );
280 thePassWord = aUserInfo.copy( nPos + 1 );
284 catch ( DAVException const & )
286 // abort
287 return -1;
290 else
292 // username buffer is prefilled with user name from last attempt.
293 theUserName = OUString::createFromAscii( inoutUserName );
294 // @@@ Neon does not initialize password buffer (last checked: 0.22.0).
295 //thePassWord = OUString::createFromAscii( inoutPassWord );
298 bool bCanUseSystemCreds = false;
300 #if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
301 bCanUseSystemCreds
302 = (attempt == 0) && // avoid endless loops
303 ne_has_support( NE_FEATURE_SSPI ) && // Windows-only feature.
304 ( ( ne_strcasecmp( inAuthProtocol, "NTLM" ) == 0 ) ||
305 ( ne_strcasecmp( inAuthProtocol, "Negotiate" ) == 0 ) );
306 #endif
308 int theRetVal = pListener->authenticate(
309 OUString::createFromAscii( inRealm ),
310 theSession->getHostName(),
311 theUserName,
312 thePassWord,
313 bCanUseSystemCreds);
315 OString aUser(
316 OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ) );
317 if ( aUser.getLength() > ( NE_ABUFSIZ - 1 ) )
319 OSL_FAIL(
320 "NeonSession_NeonAuth - username to long!" );
321 return -1;
324 OString aPass(
325 OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ) );
326 if ( aPass.getLength() > ( NE_ABUFSIZ - 1 ) )
328 OSL_FAIL(
329 "NeonSession_NeonAuth - password to long!" );
330 return -1;
333 strcpy( inoutUserName, // #100211# - checked
334 OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ).getStr() );
336 strcpy( inoutPassWord, // #100211# - checked
337 OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ).getStr() );
339 return theRetVal;
342 namespace {
343 OUString GetHostnamePart( const OUString& _rRawString )
345 OUString sPart;
346 OUString sPartId("CN=");
347 sal_Int32 nContStart = _rRawString.indexOf( sPartId );
348 if ( nContStart != -1 )
350 nContStart += sPartId.getLength();
351 sal_Int32 nContEnd = _rRawString.indexOf( ',', nContStart );
352 sPart = nContEnd != -1 ?
353 _rRawString.copy(nContStart, nContEnd - nContStart) :
354 _rRawString.copy(nContStart);
356 return sPart;
358 } // namespace
360 extern "C" int NeonSession_CertificationNotify( void *userdata,
361 int failures,
362 const ne_ssl_certificate *cert )
364 OSL_ASSERT( cert );
366 NeonSession * pSession = static_cast< NeonSession * >( userdata );
367 uno::Reference< security::XCertificateContainer > xCertificateContainer;
370 xCertificateContainer = security::CertificateContainer::create( pSession->getComponentContext() );
372 catch ( uno::Exception const & )
376 if ( !xCertificateContainer.is() )
377 return 1;
379 failures = 0;
381 char * dn = ne_ssl_readable_dname( ne_ssl_cert_subject( cert ) );
382 OUString cert_subject( dn, strlen( dn ), RTL_TEXTENCODING_UTF8, 0 );
384 ne_free( dn );
386 security::CertificateContainerStatus certificateContainer(
387 xCertificateContainer->hasCertificate(
388 pSession->getHostName(), cert_subject ) );
390 if ( certificateContainer != security::CertificateContainerStatus_NOCERT )
391 return
392 certificateContainer == security::CertificateContainerStatus_TRUSTED
394 : 1;
396 uno::Reference< xml::crypto::XSEInitializer > xSEInitializer;
399 xSEInitializer = xml::crypto::SEInitializer::create( pSession->getComponentContext() );
401 catch ( uno::Exception const & )
405 if ( !xSEInitializer.is() )
406 return 1;
408 uno::Reference< xml::crypto::XXMLSecurityContext > xSecurityContext(
409 xSEInitializer->createSecurityContext( OUString() ) );
411 uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnv(
412 xSecurityContext->getSecurityEnvironment() );
414 //The end entity certificate
415 char * eeCertB64 = ne_ssl_cert_export( cert );
417 OString sEECertB64( eeCertB64 );
419 uno::Reference< security::XCertificate > xEECert(
420 xSecurityEnv->createCertificateFromAscii(
421 OStringToOUString( sEECertB64, RTL_TEXTENCODING_ASCII_US ) ) );
423 ne_free( eeCertB64 );
424 eeCertB64 = 0;
426 std::vector< uno::Reference< security::XCertificate > > vecCerts;
427 const ne_ssl_certificate * issuerCert = cert;
430 //get the intermediate certificate
431 //the returned value is const ! Therfore it does not need to be freed
432 //with ne_ssl_cert_free, which takes a non-const argument
433 issuerCert = ne_ssl_cert_signedby( issuerCert );
434 if ( NULL == issuerCert )
435 break;
437 char * imCertB64 = ne_ssl_cert_export( issuerCert );
438 OString sInterMediateCertB64( imCertB64 );
439 ne_free( imCertB64 );
441 uno::Reference< security::XCertificate> xImCert(
442 xSecurityEnv->createCertificateFromAscii(
443 OStringToOUString( sInterMediateCertB64, RTL_TEXTENCODING_ASCII_US ) ) );
444 if ( xImCert.is() )
445 vecCerts.push_back( xImCert );
447 while ( true );
449 sal_Int64 certValidity = xSecurityEnv->verifyCertificate( xEECert,
450 ::comphelper::containerToSequence( vecCerts ) );
452 if ( pSession->isDomainMatch(
453 GetHostnamePart( xEECert.get()->getSubjectName() ) ) )
455 // if host name matched with certificate then look if the
456 // certificate was ok
457 if( certValidity == security::CertificateValidity::VALID )
458 return 0;
461 const uno::Reference< ucb::XCommandEnvironment > xEnv(
462 pSession->getRequestEnvironment().m_xEnv );
463 if ( xEnv.is() )
465 failures = static_cast< int >( certValidity );
467 uno::Reference< task::XInteractionHandler > xIH(
468 xEnv->getInteractionHandler() );
469 if ( xIH.is() )
471 rtl::Reference< ucbhelper::SimpleCertificateValidationRequest >
472 xRequest( new ucbhelper::SimpleCertificateValidationRequest(
473 (sal_Int32)failures, xEECert, pSession->getHostName() ) );
474 xIH->handle( xRequest.get() );
476 rtl::Reference< ucbhelper::InteractionContinuation > xSelection
477 = xRequest->getSelection();
479 if ( xSelection.is() )
481 uno::Reference< task::XInteractionApprove > xApprove(
482 xSelection.get(), uno::UNO_QUERY );
483 if ( xApprove.is() )
485 xCertificateContainer->addCertificate(
486 pSession->getHostName(), cert_subject, sal_True );
487 return 0;
489 else
491 // Don't trust cert
492 xCertificateContainer->addCertificate(
493 pSession->getHostName(), cert_subject, sal_False );
494 return 1;
498 else
500 // Don't trust cert
501 xCertificateContainer->addCertificate(
502 pSession->getHostName(), cert_subject, sal_False );
503 return 1;
506 return 1;
509 extern "C" void NeonSession_PreSendRequest( ne_request * req,
510 void * userdata,
511 ne_buffer * headers )
513 // userdata -> value returned by 'create'
515 NeonSession * pSession = static_cast< NeonSession * >( userdata );
516 if ( pSession )
518 // If there is a proxy server in between, it shall never use
519 // cached data. We always want 'up-to-date' data.
520 ne_buffer_concat( headers, "Pragma: no-cache", EOL, NULL );
521 // alternative, but understoud by HTTP 1.1 servers only:
522 // ne_buffer_concat( headers, "Cache-Control: max-age=0", EOL, NULL );
524 const RequestDataMap * pRequestData
525 = static_cast< const RequestDataMap* >(
526 pSession->getRequestData() );
528 RequestDataMap::const_iterator it = pRequestData->find( req );
529 if ( it != pRequestData->end() )
531 if ( !(*it).second.aContentType.isEmpty() )
533 char * pData = headers->data;
534 if ( strstr( pData, "Content-Type:" ) == NULL )
536 OString aType
537 = OUStringToOString( (*it).second.aContentType,
538 RTL_TEXTENCODING_UTF8 );
539 ne_buffer_concat( headers, "Content-Type: ",
540 aType.getStr(), EOL, NULL );
544 if ( !(*it).second.aReferer.isEmpty() )
546 char * pData = headers->data;
547 if ( strstr( pData, "Referer:" ) == NULL )
549 OString aReferer
550 = OUStringToOString( (*it).second.aReferer,
551 RTL_TEXTENCODING_UTF8 );
552 ne_buffer_concat( headers, "Referer: ",
553 aReferer.getStr(), EOL, NULL );
558 const DAVRequestHeaders & rHeaders
559 = pSession->getRequestEnvironment().m_aRequestHeaders;
561 DAVRequestHeaders::const_iterator it1( rHeaders.begin() );
562 const DAVRequestHeaders::const_iterator end1( rHeaders.end() );
564 while ( it1 != end1 )
566 OString aHeader
567 = OUStringToOString( (*it1).first,
568 RTL_TEXTENCODING_UTF8 );
569 OString aValue
570 = OUStringToOString( (*it1).second,
571 RTL_TEXTENCODING_UTF8 );
572 ne_buffer_concat( headers, aHeader.getStr(), ": ",
573 aValue.getStr(), EOL, NULL );
575 ++it1;
580 // static members
581 bool NeonSession::m_bGlobalsInited = false;
582 //See https://bugzilla.redhat.com/show_bug.cgi?id=544619#c4
583 //neon is threadsafe, but uses gnutls which is only thread-safe
584 //if initialized to be thread-safe. cups, unfortunately, generally
585 //initializes it first, and as non-thread-safe, leaving the entire
586 //stack unsafe
587 osl::Mutex aGlobalNeonMutex;
588 NeonLockStore NeonSession::m_aNeonLockStore;
590 NeonSession::NeonSession( const rtl::Reference< DAVSessionFactory > & rSessionFactory,
591 const OUString& inUri,
592 const uno::Sequence< beans::NamedValue >& rFlags,
593 const ucbhelper::InternetProxyDecider & rProxyDecider )
594 throw ( std::exception )
595 : DAVSession( rSessionFactory )
596 , m_nProxyPort( 0 )
597 , m_aFlags( rFlags )
598 , m_pHttpSession( 0 )
599 , m_pRequestData( new RequestDataMap )
600 , m_rProxyDecider( rProxyDecider )
602 NeonUri theUri( inUri );
603 m_aScheme = theUri.GetScheme();
604 m_aHostName = theUri.GetHost();
605 m_nPort = theUri.GetPort();
608 NeonSession::~NeonSession( )
610 if ( m_pHttpSession )
613 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
614 ne_session_destroy( m_pHttpSession );
616 m_pHttpSession = 0;
618 delete static_cast< RequestDataMap * >( m_pRequestData );
621 void NeonSession::Init( const DAVRequestEnvironment & rEnv )
622 throw (css::uno::RuntimeException, std::exception)
624 osl::Guard< osl::Mutex > theGuard( m_aMutex );
625 m_aEnv = rEnv;
626 Init();
629 void NeonSession::Init()
630 throw (css::uno::RuntimeException, std::exception)
632 osl::Guard< osl::Mutex > theGuard( m_aMutex );
634 bool bCreateNewSession = false;
636 if ( m_pHttpSession == 0 )
638 // Ensure that Neon sockets are initialized
639 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
640 if ( !m_bGlobalsInited )
642 if ( ne_sock_init() != 0 )
643 throw DAVException( DAVException::DAV_SESSION_CREATE,
644 NeonUri::makeConnectionEndPointString(
645 m_aHostName, m_nPort ) );
647 // #122205# - libxml2 needs to be initialized once if used by
648 // multithreaded programs like OOo.
649 xmlInitParser();
650 #if OSL_DEBUG_LEVEL > 0
651 // for more debug flags see ne_utils.h; NE_DEBUGGING must be defined
652 // while compiling neon in order to actually activate neon debug
653 // output.
654 ne_debug_init( stderr, NE_DBG_FLUSH
655 | NE_DBG_HTTP
656 // | NE_DBG_HTTPBODY
657 // | NE_DBG_HTTPAUTH
658 // | NE_DBG_XML
659 // | NE_DBG_XMLPARSE
660 | NE_DBG_LOCKS
661 | NE_DBG_SSL
663 #endif
664 m_bGlobalsInited = true;
667 const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
669 m_aProxyName = rProxyCfg.aName;
670 m_nProxyPort = rProxyCfg.nPort;
672 // Not yet initialized. Create new session.
673 bCreateNewSession = true;
675 else
677 // #112271# Check whether proxy settings are still valid (They may
678 // change at any time). If not, create new Neon session.
680 const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
682 if ( ( rProxyCfg.aName != m_aProxyName )
683 || ( rProxyCfg.nPort != m_nProxyPort ) )
685 m_aProxyName = rProxyCfg.aName;
686 m_nProxyPort = rProxyCfg.nPort;
688 // new session needed, destroy old first
690 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
691 ne_session_destroy( m_pHttpSession );
693 m_pHttpSession = 0;
694 bCreateNewSession = true;
698 if ( bCreateNewSession )
700 // @@@ For FTP over HTTP proxy inUserInfo is needed to be able to
701 // build the complete request URI (including user:pass), but
702 // currently (0.22.0) neon does not allow to pass the user info
703 // to the session
706 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
707 m_pHttpSession = ne_session_create(
708 OUStringToOString( m_aScheme, RTL_TEXTENCODING_UTF8 ).getStr(),
709 /* theUri.GetUserInfo(),
710 @@@ for FTP via HTTP proxy, but not supported by Neon */
711 OUStringToOString( m_aHostName, RTL_TEXTENCODING_UTF8 ).getStr(),
712 m_nPort );
715 if ( m_pHttpSession == 0 )
716 throw DAVException( DAVException::DAV_SESSION_CREATE,
717 NeonUri::makeConnectionEndPointString(
718 m_aHostName, m_nPort ) );
720 // Register the session with the lock store
721 m_aNeonLockStore.registerSession( m_pHttpSession );
723 if ( m_aScheme.equalsIgnoreAsciiCase(
724 OUString( "https" ) ) )
726 // Set a failure callback for certificate check
727 ne_ssl_set_verify(
728 m_pHttpSession, NeonSession_CertificationNotify, this);
730 // Tell Neon to tell the SSL library used (OpenSSL or
731 // GnuTLS, I guess) to use a default set of root
732 // certificates.
733 ne_ssl_trust_default_ca(m_pHttpSession);
736 // Add hooks (i.e. for adding additional headers to the request)
738 #if 0
739 /* Hook called when a request is created. */
740 //typedef void (*ne_create_request_fn)(ne_request *req, void *userdata,
741 // const char *method, const char *path);
743 ne_hook_create_request( m_pHttpSession, create_req_hook_fn, this );
744 #endif
746 /* Hook called before the request is sent. 'header' is the raw HTTP
747 * header before the trailing CRLF is added: add in more here. */
748 //typedef void (*ne_pre_send_fn)(ne_request *req, void *userdata,
749 // ne_buffer *header);
751 ne_hook_pre_send( m_pHttpSession, NeonSession_PreSendRequest, this );
752 #if 0
753 /* Hook called after the request is sent. May return:
754 * NE_OK everything is okay
755 * NE_RETRY try sending the request again.
756 * anything else signifies an error, and the request is failed. The
757 * return code is passed back the _dispatch caller, so the session error
758 * must also be set appropriately (ne_set_error).
760 //typedef int (*ne_post_send_fn)(ne_request *req, void *userdata,
761 // const ne_status *status);
763 ne_hook_post_send( m_pHttpSession, post_send_req_hook_fn, this );
765 /* Hook called when the request is destroyed. */
766 //typedef void (*ne_destroy_req_fn)(ne_request *req, void *userdata);
768 ne_hook_destroy_request( m_pHttpSession, destroy_req_hook_fn, this );
770 /* Hook called when the session is destroyed. */
771 //typedef void (*ne_destroy_sess_fn)(void *userdata);
773 ne_hook_destroy_session( m_pHttpSession, destroy_sess_hook_fn, this );
774 #endif
776 if ( !m_aProxyName.isEmpty() )
778 ne_session_proxy( m_pHttpSession,
779 OUStringToOString(
780 m_aProxyName,
781 RTL_TEXTENCODING_UTF8 ).getStr(),
782 m_nProxyPort );
785 // avoid KeepAlive?
786 if ( noKeepAlive(m_aFlags) )
787 ne_set_session_flag( m_pHttpSession, NE_SESSFLAG_PERSIST, 0 );
789 // Register for redirects.
790 ne_redirect_register( m_pHttpSession );
792 // authentication callbacks.
793 #if 1
794 ne_add_server_auth( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
795 ne_add_proxy_auth ( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
796 #else
797 ne_set_server_auth( m_pHttpSession, NeonSession_NeonAuth, this );
798 ne_set_proxy_auth ( m_pHttpSession, NeonSession_NeonAuth, this );
799 #endif
803 bool NeonSession::CanUse( const OUString & inUri,
804 const uno::Sequence< beans::NamedValue >& rFlags )
808 NeonUri theUri( inUri );
809 if ( ( theUri.GetPort() == m_nPort ) &&
810 ( theUri.GetHost() == m_aHostName ) &&
811 ( theUri.GetScheme() == m_aScheme ) &&
812 ( rFlags == m_aFlags ) )
813 return true;
815 catch ( DAVException const & )
817 return false;
819 return false;
822 bool NeonSession::UsesProxy()
824 Init();
825 return !m_aProxyName.isEmpty() ;
828 void NeonSession::OPTIONS( const OUString & inPath,
829 DAVCapabilities & outCapabilities,
830 const DAVRequestEnvironment & rEnv )
831 throw( std::exception )
833 osl::Guard< osl::Mutex > theGuard( m_aMutex );
835 Init( rEnv );
837 HttpServerCapabilities servercaps;
838 memset( &servercaps, 0, sizeof( servercaps ) );
840 int theRetVal = ne_options( m_pHttpSession,
841 OUStringToOString(
842 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
843 &servercaps );
845 HandleError( theRetVal, inPath, rEnv );
847 outCapabilities.class1 = !!servercaps.dav_class1;
848 outCapabilities.class2 = !!servercaps.dav_class2;
849 outCapabilities.executable = !!servercaps.dav_executable;
852 void NeonSession::PROPFIND( const OUString & inPath,
853 const Depth inDepth,
854 const std::vector< OUString > & inPropNames,
855 std::vector< DAVResource > & ioResources,
856 const DAVRequestEnvironment & rEnv )
857 throw ( std::exception )
859 osl::Guard< osl::Mutex > theGuard( m_aMutex );
861 Init( rEnv );
863 int theRetVal = NE_OK;
864 NeonPropFindRequest theRequest( m_pHttpSession,
865 OUStringToOString(
866 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
867 inDepth,
868 inPropNames,
869 ioResources,
870 theRetVal );
872 HandleError( theRetVal, inPath, rEnv );
875 void NeonSession::PROPFIND( const OUString & inPath,
876 const Depth inDepth,
877 std::vector< DAVResourceInfo > & ioResInfo,
878 const DAVRequestEnvironment & rEnv )
879 throw( std::exception )
881 osl::Guard< osl::Mutex > theGuard( m_aMutex );
883 Init( rEnv );
885 int theRetVal = NE_OK;
886 NeonPropFindRequest theRequest( m_pHttpSession,
887 OUStringToOString(
888 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
889 inDepth,
890 ioResInfo,
891 theRetVal );
893 HandleError( theRetVal, inPath, rEnv );
896 void NeonSession::PROPPATCH( const OUString & inPath,
897 const std::vector< ProppatchValue > & inValues,
898 const DAVRequestEnvironment & rEnv )
899 throw( std::exception )
901 /* @@@ Which standard live properties can be set by the client?
902 This is a known WebDAV RFC issue ( verified: 04/10/2001 )
903 --> http://www.ics.uci.edu/pub/ietf/webdav/protocol/issues.html
905 mod_dav implementation:
907 creationdate r ( File System prop )
908 displayname w
909 getcontentlanguage r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
910 getcontentlength r ( File System prop )
911 getcontenttype r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
912 getetag r ( File System prop )
913 getlastmodified r ( File System prop )
914 lockdiscovery r
915 resourcetype r
916 source w
917 supportedlock r
918 executable w ( #ifndef WIN32 )
920 All dead properties are of course writable.
923 int theRetVal = NE_OK;
925 int n; // for the "for" loop
927 // Generate the list of properties we want to set.
928 int nPropCount = inValues.size();
929 ne_proppatch_operation* pItems
930 = new ne_proppatch_operation[ nPropCount + 1 ];
931 for ( n = 0; n < nPropCount; ++n )
933 const ProppatchValue & rValue = inValues[ n ];
935 // Split fullname into namespace and name!
936 ne_propname * pName = new ne_propname;
937 DAVProperties::createNeonPropName( rValue.name, *pName );
938 pItems[ n ].name = pName;
940 if ( rValue.operation == PROPSET )
942 pItems[ n ].type = ne_propset;
944 OUString aStringValue;
945 if ( DAVProperties::isUCBDeadProperty( *pName ) )
947 // DAV dead property added by WebDAV UCP?
948 if ( !UCBDeadPropertyValue::toXML( rValue.value,
949 aStringValue ) )
951 // Error!
952 pItems[ n ].value = 0;
953 theRetVal = NE_ERROR;
954 nPropCount = n + 1;
955 break;
958 else if ( !( rValue.value >>= aStringValue ) )
960 // complex properties...
961 if ( rValue.name == DAVProperties::SOURCE )
963 uno::Sequence< ucb::Link > aLinks;
964 if ( rValue.value >>= aLinks )
966 LinkSequence::toXML( aLinks, aStringValue );
968 else
970 // Error!
971 pItems[ n ].value = 0;
972 theRetVal = NE_ERROR;
973 nPropCount = n + 1;
974 break;
977 else
979 OSL_FAIL( "NeonSession::PROPPATCH - unsupported type!" );
980 // Error!
981 pItems[ n ].value = 0;
982 theRetVal = NE_ERROR;
983 nPropCount = n + 1;
984 break;
987 pItems[ n ].value
988 = strdup( OUStringToOString( aStringValue,
989 RTL_TEXTENCODING_UTF8 ).getStr() );
991 else
993 pItems[ n ].type = ne_propremove;
994 pItems[ n ].value = 0;
998 if ( theRetVal == NE_OK )
1000 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1002 Init( rEnv );
1004 pItems[ n ].name = 0;
1006 theRetVal = ne_proppatch( m_pHttpSession,
1007 OUStringToOString(
1008 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1009 pItems );
1012 for ( n = 0; n < nPropCount; ++n )
1014 free( (void *)pItems[ n ].name->name );
1015 delete pItems[ n ].name;
1016 free( (void *)pItems[ n ].value );
1019 delete [] pItems;
1021 HandleError( theRetVal, inPath, rEnv );
1024 void NeonSession::HEAD( const OUString & inPath,
1025 const std::vector< OUString > & inHeaderNames,
1026 DAVResource & ioResource,
1027 const DAVRequestEnvironment & rEnv )
1028 throw( std::exception )
1030 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1032 Init( rEnv );
1034 int theRetVal = NE_OK;
1035 NeonHeadRequest theRequest( m_pHttpSession,
1036 inPath,
1037 inHeaderNames,
1038 ioResource,
1039 theRetVal );
1041 HandleError( theRetVal, inPath, rEnv );
1044 uno::Reference< io::XInputStream >
1045 NeonSession::GET( const OUString & inPath,
1046 const DAVRequestEnvironment & rEnv )
1047 throw ( std::exception )
1049 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1051 Init( rEnv );
1053 rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
1054 NeonRequestContext aCtx( xInputStream );
1055 int theRetVal = GET( m_pHttpSession,
1056 OUStringToOString(
1057 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1058 NeonSession_ResponseBlockReader,
1059 false,
1060 &aCtx );
1062 HandleError( theRetVal, inPath, rEnv );
1064 return uno::Reference< io::XInputStream >( xInputStream.get() );
1067 void NeonSession::GET( const OUString & inPath,
1068 uno::Reference< io::XOutputStream > & ioOutputStream,
1069 const DAVRequestEnvironment & rEnv )
1070 throw ( std::exception )
1072 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1074 Init( rEnv );
1076 NeonRequestContext aCtx( ioOutputStream );
1077 int theRetVal = GET( m_pHttpSession,
1078 OUStringToOString(
1079 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1080 NeonSession_ResponseBlockWriter,
1081 false,
1082 &aCtx );
1084 HandleError( theRetVal, inPath, rEnv );
1087 uno::Reference< io::XInputStream >
1088 NeonSession::GET( const OUString & inPath,
1089 const std::vector< OUString > & inHeaderNames,
1090 DAVResource & ioResource,
1091 const DAVRequestEnvironment & rEnv )
1092 throw ( std::exception )
1094 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1096 Init( rEnv );
1098 ioResource.uri = inPath;
1099 ioResource.properties.clear();
1101 rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
1102 NeonRequestContext aCtx( xInputStream, inHeaderNames, ioResource );
1103 int theRetVal = GET( m_pHttpSession,
1104 OUStringToOString(
1105 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1106 NeonSession_ResponseBlockReader,
1107 true,
1108 &aCtx );
1110 HandleError( theRetVal, inPath, rEnv );
1112 return uno::Reference< io::XInputStream >( xInputStream.get() );
1115 void NeonSession::GET( const OUString & inPath,
1116 uno::Reference< io::XOutputStream > & ioOutputStream,
1117 const std::vector< OUString > & inHeaderNames,
1118 DAVResource & ioResource,
1119 const DAVRequestEnvironment & rEnv )
1120 throw ( std::exception )
1122 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1124 Init( rEnv );
1126 ioResource.uri = inPath;
1127 ioResource.properties.clear();
1129 NeonRequestContext aCtx( ioOutputStream, inHeaderNames, ioResource );
1130 int theRetVal = GET( m_pHttpSession,
1131 OUStringToOString(
1132 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1133 NeonSession_ResponseBlockWriter,
1134 true,
1135 &aCtx );
1137 HandleError( theRetVal, inPath, rEnv );
1140 void NeonSession::PUT( const OUString & inPath,
1141 const uno::Reference< io::XInputStream > & inInputStream,
1142 const DAVRequestEnvironment & rEnv )
1143 throw ( std::exception )
1145 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1147 uno::Sequence< sal_Int8 > aDataToSend;
1148 if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
1149 throw DAVException( DAVException::DAV_INVALID_ARG );
1151 Init( rEnv );
1153 int theRetVal = PUT( m_pHttpSession,
1154 OUStringToOString(
1155 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1156 reinterpret_cast< const char * >(
1157 aDataToSend.getConstArray() ),
1158 aDataToSend.getLength() );
1160 HandleError( theRetVal, inPath, rEnv );
1163 uno::Reference< io::XInputStream >
1164 NeonSession::POST( const OUString & inPath,
1165 const OUString & rContentType,
1166 const OUString & rReferer,
1167 const uno::Reference< io::XInputStream > & inInputStream,
1168 const DAVRequestEnvironment & rEnv )
1169 throw ( std::exception )
1171 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1173 uno::Sequence< sal_Int8 > aDataToSend;
1174 if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
1175 throw DAVException( DAVException::DAV_INVALID_ARG );
1177 Init( rEnv );
1179 rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
1180 NeonRequestContext aCtx( xInputStream );
1181 int theRetVal = POST( m_pHttpSession,
1182 OUStringToOString(
1183 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1184 reinterpret_cast< const char * >(
1185 aDataToSend.getConstArray() ),
1186 NeonSession_ResponseBlockReader,
1187 &aCtx,
1188 rContentType,
1189 rReferer );
1191 HandleError( theRetVal, inPath, rEnv );
1193 return uno::Reference< io::XInputStream >( xInputStream.get() );
1196 void NeonSession::POST( const OUString & inPath,
1197 const OUString & rContentType,
1198 const OUString & rReferer,
1199 const uno::Reference< io::XInputStream > & inInputStream,
1200 uno::Reference< io::XOutputStream > & oOutputStream,
1201 const DAVRequestEnvironment & rEnv )
1202 throw ( std::exception )
1204 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1206 uno::Sequence< sal_Int8 > aDataToSend;
1207 if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
1208 throw DAVException( DAVException::DAV_INVALID_ARG );
1210 Init( rEnv );
1212 NeonRequestContext aCtx( oOutputStream );
1213 int theRetVal = POST( m_pHttpSession,
1214 OUStringToOString(
1215 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1216 reinterpret_cast< const char * >(
1217 aDataToSend.getConstArray() ),
1218 NeonSession_ResponseBlockWriter,
1219 &aCtx,
1220 rContentType,
1221 rReferer );
1223 HandleError( theRetVal, inPath, rEnv );
1226 void NeonSession::MKCOL( const OUString & inPath,
1227 const DAVRequestEnvironment & rEnv )
1228 throw ( std::exception )
1230 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1232 Init( rEnv );
1234 int theRetVal = ne_mkcol( m_pHttpSession,
1235 OUStringToOString(
1236 inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
1238 HandleError( theRetVal, inPath, rEnv );
1241 void NeonSession::COPY( const OUString & inSourceURL,
1242 const OUString & inDestinationURL,
1243 const DAVRequestEnvironment & rEnv,
1244 bool inOverWrite )
1245 throw ( std::exception )
1247 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1249 Init( rEnv );
1251 NeonUri theSourceUri( inSourceURL );
1252 NeonUri theDestinationUri( inDestinationURL );
1254 int theRetVal = ne_copy( m_pHttpSession,
1255 inOverWrite ? 1 : 0,
1256 NE_DEPTH_INFINITE,
1257 OUStringToOString(
1258 theSourceUri.GetPath(),
1259 RTL_TEXTENCODING_UTF8 ).getStr(),
1260 OUStringToOString(
1261 theDestinationUri.GetPath(),
1262 RTL_TEXTENCODING_UTF8 ).getStr() );
1264 HandleError( theRetVal, inSourceURL, rEnv );
1267 void NeonSession::MOVE( const OUString & inSourceURL,
1268 const OUString & inDestinationURL,
1269 const DAVRequestEnvironment & rEnv,
1270 bool inOverWrite )
1271 throw ( std::exception )
1273 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1275 Init( rEnv );
1277 NeonUri theSourceUri( inSourceURL );
1278 NeonUri theDestinationUri( inDestinationURL );
1279 int theRetVal = ne_move( m_pHttpSession,
1280 inOverWrite ? 1 : 0,
1281 OUStringToOString(
1282 theSourceUri.GetPath(),
1283 RTL_TEXTENCODING_UTF8 ).getStr(),
1284 OUStringToOString(
1285 theDestinationUri.GetPath(),
1286 RTL_TEXTENCODING_UTF8 ).getStr() );
1288 HandleError( theRetVal, inSourceURL, rEnv );
1291 void NeonSession::DESTROY( const OUString & inPath,
1292 const DAVRequestEnvironment & rEnv )
1293 throw ( std::exception )
1295 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1297 Init( rEnv );
1299 int theRetVal = ne_delete( m_pHttpSession,
1300 OUStringToOString(
1301 inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
1303 HandleError( theRetVal, inPath, rEnv );
1306 namespace
1308 sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
1309 int timeout )
1311 TimeValue aEnd;
1312 osl_getSystemTime( &aEnd );
1314 // Try to estimate a safe absolute time for sending the
1315 // lock refresh request.
1316 sal_Int32 lastChanceToSendRefreshRequest = -1;
1317 if ( timeout != NE_TIMEOUT_INFINITE )
1319 sal_Int32 calltime = aEnd.Seconds - rStart.Seconds;
1320 if ( calltime <= timeout )
1322 lastChanceToSendRefreshRequest
1323 = aEnd.Seconds + timeout - calltime;
1325 else
1327 OSL_TRACE( "No chance to refresh lock before timeout!" );
1330 return lastChanceToSendRefreshRequest;
1333 } // namespace
1335 // Set new lock
1336 void NeonSession::LOCK( const OUString & inPath,
1337 ucb::Lock & rLock,
1338 const DAVRequestEnvironment & rEnv )
1339 throw ( std::exception )
1341 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1343 Init( rEnv );
1345 /* Create a depth zero, exclusive write lock, with default timeout
1346 * (allowing a server to pick a default). token, owner and uri are
1347 * unset. */
1348 NeonLock * theLock = ne_lock_create();
1350 // Set the lock uri
1351 ne_uri aUri;
1352 ne_uri_parse( OUStringToOString( makeAbsoluteURL( inPath ),
1353 RTL_TEXTENCODING_UTF8 ).getStr(),
1354 &aUri );
1355 theLock->uri = aUri;
1357 // Set the lock depth
1358 switch( rLock.Depth )
1360 case ucb::LockDepth_ZERO:
1361 theLock->depth = NE_DEPTH_ZERO;
1362 break;
1363 case ucb::LockDepth_ONE:
1364 theLock->depth = NE_DEPTH_ONE;
1365 break;
1366 case ucb::LockDepth_INFINITY:
1367 theLock->depth = NE_DEPTH_INFINITE;
1368 break;
1369 default:
1370 throw DAVException( DAVException::DAV_INVALID_ARG );
1373 // Set the lock scope
1374 switch ( rLock.Scope )
1376 case ucb::LockScope_EXCLUSIVE:
1377 theLock->scope = ne_lockscope_exclusive;
1378 break;
1379 case ucb::LockScope_SHARED:
1380 theLock->scope = ne_lockscope_shared;
1381 break;
1382 default:
1383 throw DAVException( DAVException::DAV_INVALID_ARG );
1386 // Set the lock timeout
1387 theLock->timeout = (long)rLock.Timeout;
1389 // Set the lock owner
1390 OUString aValue;
1391 rLock.Owner >>= aValue;
1392 theLock->owner =
1393 ne_strdup( OUStringToOString( aValue,
1394 RTL_TEXTENCODING_UTF8 ).getStr() );
1395 TimeValue startCall;
1396 osl_getSystemTime( &startCall );
1398 int theRetVal = ne_lock( m_pHttpSession, theLock );
1400 if ( theRetVal == NE_OK )
1402 m_aNeonLockStore.addLock( theLock,
1403 this,
1404 lastChanceToSendRefreshRequest(
1405 startCall, theLock->timeout ) );
1407 uno::Sequence< OUString > aTokens( 1 );
1408 aTokens[ 0 ] = OUString::createFromAscii( theLock->token );
1409 rLock.LockTokens = aTokens;
1411 OSL_TRACE( "NeonSession::LOCK: created lock for %s. token: %s",
1412 OUStringToOString( makeAbsoluteURL( inPath ),
1413 RTL_TEXTENCODING_UTF8 ).getStr(),
1414 theLock->token );
1416 else
1418 ne_lock_destroy( theLock );
1420 OSL_TRACE( "NeonSession::LOCK: obtaining lock for %s failed!",
1421 OUStringToOString( makeAbsoluteURL( inPath ),
1422 RTL_TEXTENCODING_UTF8 ).getStr() );
1425 HandleError( theRetVal, inPath, rEnv );
1428 // Refresh existing lock
1429 sal_Int64 NeonSession::LOCK( const OUString & inPath,
1430 sal_Int64 nTimeout,
1431 const DAVRequestEnvironment & rEnv )
1432 throw ( std::exception )
1434 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1436 // Try to get the neon lock from lock store
1437 NeonLock * theLock
1438 = m_aNeonLockStore.findByUri( makeAbsoluteURL( inPath ) );
1439 if ( !theLock )
1440 throw DAVException( DAVException::DAV_NOT_LOCKED );
1442 Init( rEnv );
1444 // refresh existing lock.
1445 theLock->timeout = static_cast< long >( nTimeout );
1447 TimeValue startCall;
1448 osl_getSystemTime( &startCall );
1450 int theRetVal = ne_lock_refresh( m_pHttpSession, theLock );
1452 if ( theRetVal == NE_OK )
1454 m_aNeonLockStore.updateLock( theLock,
1455 lastChanceToSendRefreshRequest(
1456 startCall, theLock->timeout ) );
1459 HandleError( theRetVal, inPath, rEnv );
1461 return theLock->timeout;
1464 // Refresh existing lock
1465 bool NeonSession::LOCK( NeonLock * pLock,
1466 sal_Int32 & rlastChanceToSendRefreshRequest )
1468 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1470 #if OSL_DEBUG_LEVEL > 0
1471 char * p = ne_uri_unparse( &(pLock->uri) );
1472 OSL_TRACE( "NeonSession::LOCK: Refreshing lock for %s.", p );
1473 ne_free( p );
1474 #endif
1476 // refresh existing lock.
1478 TimeValue startCall;
1479 osl_getSystemTime( &startCall );
1481 if ( ne_lock_refresh( m_pHttpSession, pLock ) == NE_OK )
1483 rlastChanceToSendRefreshRequest
1484 = lastChanceToSendRefreshRequest( startCall, pLock->timeout );
1486 OSL_TRACE( "Lock successfully refreshed." );
1487 return true;
1489 else
1491 OSL_TRACE( "Lock not refreshed!" );
1492 return false;
1496 void NeonSession::UNLOCK( const OUString & inPath,
1497 const DAVRequestEnvironment & rEnv )
1498 throw ( std::exception )
1500 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1502 // get the neon lock from lock store
1503 NeonLock * theLock
1504 = m_aNeonLockStore.findByUri( makeAbsoluteURL( inPath ) );
1505 if ( !theLock )
1506 throw DAVException( DAVException::DAV_NOT_LOCKED );
1508 Init( rEnv );
1510 int theRetVal = ne_unlock( m_pHttpSession, theLock );
1512 if ( theRetVal == NE_OK )
1514 m_aNeonLockStore.removeLock( theLock );
1515 ne_lock_destroy( theLock );
1517 else
1519 OSL_TRACE( "NeonSession::UNLOCK: unlocking of %s failed.",
1520 OUStringToOString( makeAbsoluteURL( inPath ),
1521 RTL_TEXTENCODING_UTF8 ).getStr() );
1524 HandleError( theRetVal, inPath, rEnv );
1527 bool NeonSession::UNLOCK( NeonLock * pLock )
1529 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1531 #if OSL_DEBUG_LEVEL > 0
1532 char * p = ne_uri_unparse( &(pLock->uri) );
1533 OSL_TRACE( "NeonSession::UNLOCK: Unlocking %s.", p );
1534 ne_free( p );
1535 #endif
1537 if ( ne_unlock( m_pHttpSession, pLock ) == NE_OK )
1539 OSL_TRACE( "UNLOCK succeeded." );
1540 return true;
1542 else
1544 OSL_TRACE( "UNLOCK failed!" );
1545 return false;
1549 void NeonSession::abort()
1550 throw ( std::exception )
1552 SAL_INFO("ucb.ucp.webdav", "neon commands cannot be aborted");
1555 const ucbhelper::InternetProxyServer & NeonSession::getProxySettings() const
1557 if ( m_aScheme == "http" || m_aScheme == "https" )
1559 return m_rProxyDecider.getProxy( m_aScheme,
1560 m_aHostName,
1561 m_nPort );
1563 else
1565 return m_rProxyDecider.getProxy( m_aScheme,
1566 OUString() /* not used */,
1567 -1 /* not used */ );
1571 namespace {
1573 bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
1574 const char * token )
1576 for ( sal_Int32 n = 0; n < rLocks.getLength(); ++n )
1578 const uno::Sequence< OUString > & rTokens
1579 = rLocks[ n ].LockTokens;
1580 for ( sal_Int32 m = 0; m < rTokens.getLength(); ++m )
1582 if ( rTokens[ m ].equalsAscii( token ) )
1583 return true;
1586 return false;
1589 } // namespace
1591 bool NeonSession::removeExpiredLocktoken( const OUString & inURL,
1592 const DAVRequestEnvironment & rEnv )
1594 NeonLock * theLock = m_aNeonLockStore.findByUri( inURL );
1595 if ( !theLock )
1596 return false;
1598 // do a lockdiscovery to check whether this lock is still valid.
1601 // @@@ Alternative: use ne_lock_discover() => less overhead
1603 std::vector< DAVResource > aResources;
1604 std::vector< OUString > aPropNames;
1605 aPropNames.push_back( DAVProperties::LOCKDISCOVERY );
1607 PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv );
1609 if ( aResources.empty() )
1610 return false;
1612 std::vector< DAVPropertyValue >::const_iterator it
1613 = aResources[ 0 ].properties.begin();
1614 std::vector< DAVPropertyValue >::const_iterator end
1615 = aResources[ 0 ].properties.end();
1617 while ( it != end )
1619 if ( (*it).Name.equals( DAVProperties::LOCKDISCOVERY ) )
1621 uno::Sequence< ucb::Lock > aLocks;
1622 if ( !( (*it).Value >>= aLocks ) )
1623 return false;
1625 if ( !containsLocktoken( aLocks, theLock->token ) )
1627 // expired!
1628 break;
1631 // still valid.
1632 return false;
1634 ++it;
1637 // No lockdiscovery prop in propfind result / locktoken not found
1638 // in propfind result -> not locked
1639 OSL_TRACE( "NeonSession::removeExpiredLocktoken: Removing "
1640 " expired lock token for %s. token: %s",
1641 OUStringToOString( inURL,
1642 RTL_TEXTENCODING_UTF8 ).getStr(),
1643 theLock->token );
1645 m_aNeonLockStore.removeLock( theLock );
1646 ne_lock_destroy( theLock );
1647 return true;
1649 catch ( DAVException const & )
1652 return false;
1655 // Common error handler
1656 void NeonSession::HandleError( int nError,
1657 const OUString & inPath,
1658 const DAVRequestEnvironment & rEnv )
1659 throw ( std::exception )
1661 m_aEnv = DAVRequestEnvironment();
1663 // Map error code to DAVException.
1664 switch ( nError )
1666 case NE_OK:
1667 return;
1669 case NE_ERROR: // Generic error
1671 OUString aText = OUString::createFromAscii(
1672 ne_get_error( m_pHttpSession ) );
1674 sal_uInt16 code = makeStatusCode( aText );
1676 if ( code == SC_LOCKED )
1678 if ( m_aNeonLockStore.findByUri(
1679 makeAbsoluteURL( inPath ) ) == 0 )
1681 // locked by 3rd party
1682 throw DAVException( DAVException::DAV_LOCKED );
1684 else
1686 // locked by ourself
1687 throw DAVException( DAVException::DAV_LOCKED_SELF );
1691 // Special handling for 400 and 412 status codes, which may indicate
1692 // that a lock previously obtained by us has been released meanwhile
1693 // by the server. Unfortunately, RFC is not clear at this point,
1694 // thus server implementations behave different...
1695 else if ( code == SC_BAD_REQUEST || code == SC_PRECONDITION_FAILED )
1697 if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) )
1698 throw DAVException( DAVException::DAV_LOCK_EXPIRED );
1701 throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
1703 case NE_LOOKUP: // Name lookup failed.
1704 throw DAVException( DAVException::DAV_HTTP_LOOKUP,
1705 NeonUri::makeConnectionEndPointString(
1706 m_aHostName, m_nPort ) );
1708 case NE_AUTH: // User authentication failed on server
1709 throw DAVException( DAVException::DAV_HTTP_AUTH,
1710 NeonUri::makeConnectionEndPointString(
1711 m_aHostName, m_nPort ) );
1713 case NE_PROXYAUTH: // User authentication failed on proxy
1714 throw DAVException( DAVException::DAV_HTTP_AUTHPROXY,
1715 NeonUri::makeConnectionEndPointString(
1716 m_aProxyName, m_nProxyPort ) );
1718 case NE_CONNECT: // Could not connect to server
1719 throw DAVException( DAVException::DAV_HTTP_CONNECT,
1720 NeonUri::makeConnectionEndPointString(
1721 m_aHostName, m_nPort ) );
1723 case NE_TIMEOUT: // Connection timed out
1724 throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
1725 NeonUri::makeConnectionEndPointString(
1726 m_aHostName, m_nPort ) );
1728 case NE_FAILED: // The precondition failed
1729 throw DAVException( DAVException::DAV_HTTP_FAILED,
1730 NeonUri::makeConnectionEndPointString(
1731 m_aHostName, m_nPort ) );
1733 case NE_RETRY: // Retry request (ne_end_request ONLY)
1734 throw DAVException( DAVException::DAV_HTTP_RETRY,
1735 NeonUri::makeConnectionEndPointString(
1736 m_aHostName, m_nPort ) );
1738 case NE_REDIRECT:
1740 NeonUri aUri( ne_redirect_location( m_pHttpSession ) );
1741 throw DAVException(
1742 DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() );
1744 default:
1746 OSL_TRACE( "NeonSession::HandleError : Unknown Neon error code!" );
1747 throw DAVException( DAVException::DAV_HTTP_ERROR,
1748 OUString::createFromAscii(
1749 ne_get_error( m_pHttpSession ) ) );
1754 namespace {
1756 void runResponseHeaderHandler( void * userdata,
1757 const char * value )
1759 OUString aHeader( OUString::createFromAscii( value ) );
1760 sal_Int32 nPos = aHeader.indexOf( ':' );
1762 if ( nPos != -1 )
1764 OUString aHeaderName( aHeader.copy( 0, nPos ) );
1766 NeonRequestContext * pCtx
1767 = static_cast< NeonRequestContext * >( userdata );
1769 // Note: Empty vector means that all headers are requested.
1770 bool bIncludeIt = ( pCtx->pHeaderNames->empty() );
1772 if ( !bIncludeIt )
1774 // Check whether this header was requested.
1775 std::vector< OUString >::const_iterator it(
1776 pCtx->pHeaderNames->begin() );
1777 const std::vector< OUString >::const_iterator end(
1778 pCtx->pHeaderNames->end() );
1780 while ( it != end )
1782 // header names are case insensitive
1783 if ( (*it).equalsIgnoreAsciiCase( aHeaderName ) )
1785 aHeaderName = (*it);
1786 break;
1788 ++it;
1791 if ( it != end )
1792 bIncludeIt = true;
1795 if ( bIncludeIt )
1797 // Create & set the PropertyValue
1798 DAVPropertyValue thePropertyValue;
1799 thePropertyValue.IsCaseSensitive = false;
1800 thePropertyValue.Name = aHeaderName;
1802 if ( nPos < aHeader.getLength() )
1803 thePropertyValue.Value <<= aHeader.copy( nPos + 1 ).trim();
1805 // Add the newly created PropertyValue
1806 pCtx->pResource->properties.push_back( thePropertyValue );
1811 } // namespace
1813 int NeonSession::GET( ne_session * sess,
1814 const char * uri,
1815 ne_block_reader reader,
1816 bool getheaders,
1817 void * userdata )
1819 //struct get_context ctx;
1820 ne_request * req = ne_request_create( sess, "GET", uri );
1821 int ret;
1823 ne_decompress * dc
1824 = ne_decompress_reader( req, ne_accept_2xx, reader, userdata );
1827 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
1828 ret = ne_request_dispatch( req );
1831 if ( getheaders )
1833 void *cursor = NULL;
1834 const char *name, *value;
1835 while ( ( cursor = ne_response_header_iterate(
1836 req, cursor, &name, &value ) ) != NULL )
1838 char buffer[8192];
1840 ne_snprintf(buffer, sizeof buffer, "%s: %s", name, value);
1841 runResponseHeaderHandler(userdata, buffer);
1845 if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
1846 ret = NE_ERROR;
1848 if ( dc != 0 )
1849 ne_decompress_destroy(dc);
1851 ne_request_destroy( req );
1852 return ret;
1855 int NeonSession::PUT( ne_session * sess,
1856 const char * uri,
1857 const char * buffer,
1858 size_t size)
1860 ne_request * req = ne_request_create( sess, "PUT", uri );
1861 int ret;
1863 ne_lock_using_resource( req, uri, 0 );
1864 ne_lock_using_parent( req, uri );
1866 ne_set_request_body_buffer( req, buffer, size );
1869 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
1870 ret = ne_request_dispatch( req );
1873 if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
1874 ret = NE_ERROR;
1876 ne_request_destroy( req );
1877 return ret;
1880 int NeonSession::POST( ne_session * sess,
1881 const char * uri,
1882 const char * buffer,
1883 ne_block_reader reader,
1884 void * userdata,
1885 const OUString & rContentType,
1886 const OUString & rReferer )
1888 ne_request * req = ne_request_create( sess, "POST", uri );
1889 //struct get_context ctx;
1890 int ret;
1892 RequestDataMap * pData = 0;
1894 if ( !rContentType.isEmpty() || !rReferer.isEmpty() )
1896 // Remember contenttype and referer. Data will be added to HTTP request
1897 // header in 'PreSendRequest' callback.
1898 pData = static_cast< RequestDataMap* >( m_pRequestData );
1899 (*pData)[ req ] = RequestData( rContentType, rReferer );
1902 //ctx.total = -1;
1903 //ctx.fd = fd;
1904 //ctx.error = 0;
1905 //ctx.session = sess;
1907 ///* Read the value of the Content-Length header into ctx.total */
1908 //ne_add_response_header_handler( req, "Content-Length",
1909 // ne_handle_numeric_header, &ctx.total );
1911 ne_add_response_body_reader( req, ne_accept_2xx, reader, userdata );
1913 ne_set_request_body_buffer( req, buffer, strlen( buffer ) );
1916 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
1917 ret = ne_request_dispatch( req );
1920 //if ( ctx.error )
1921 // ret = NE_ERROR;
1922 //else
1923 if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
1924 ret = NE_ERROR;
1926 ne_request_destroy( req );
1928 if ( pData )
1930 // Remove request data from session's list.
1931 RequestDataMap::iterator it = pData->find( req );
1932 if ( it != pData->end() )
1933 pData->erase( it );
1936 return ret;
1939 bool
1940 NeonSession::getDataFromInputStream(
1941 const uno::Reference< io::XInputStream > & xStream,
1942 uno::Sequence< sal_Int8 > & rData,
1943 bool bAppendTrailingZeroByte )
1945 if ( xStream.is() )
1947 uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY );
1948 if ( xSeekable.is() )
1952 sal_Int32 nSize
1953 = sal::static_int_cast<sal_Int32>(xSeekable->getLength());
1954 sal_Int32 nRead
1955 = xStream->readBytes( rData, nSize );
1957 if ( nRead == nSize )
1959 if ( bAppendTrailingZeroByte )
1961 rData.realloc( nSize + 1 );
1962 rData[ nSize ] = sal_Int8( 0 );
1964 return true;
1967 catch ( io::NotConnectedException const & )
1969 // readBytes
1971 catch ( io::BufferSizeExceededException const & )
1973 // readBytes
1975 catch ( io::IOException const & )
1977 // getLength, readBytes
1980 else
1984 uno::Sequence< sal_Int8 > aBuffer;
1985 sal_Int32 nPos = 0;
1987 sal_Int32 nRead = xStream->readSomeBytes( aBuffer, 65536 );
1988 while ( nRead > 0 )
1990 if ( rData.getLength() < ( nPos + nRead ) )
1991 rData.realloc( nPos + nRead );
1993 aBuffer.realloc( nRead );
1994 memcpy( (void*)( rData.getArray() + nPos ),
1995 (const void*)aBuffer.getConstArray(),
1996 nRead );
1997 nPos += nRead;
1999 aBuffer.realloc( 0 );
2000 nRead = xStream->readSomeBytes( aBuffer, 65536 );
2003 if ( bAppendTrailingZeroByte )
2005 rData.realloc( nPos + 1 );
2006 rData[ nPos ] = sal_Int8( 0 );
2008 return true;
2010 catch ( io::NotConnectedException const & )
2012 // readBytes
2014 catch ( io::BufferSizeExceededException const & )
2016 // readBytes
2018 catch ( io::IOException const & )
2020 // readBytes
2024 return false;
2027 bool
2028 NeonSession::isDomainMatch( const OUString& certHostName )
2030 OUString hostName = getHostName();
2032 if (hostName.equalsIgnoreAsciiCase( certHostName ) )
2033 return true;
2035 if ( certHostName.startsWith( "*" ) &&
2036 hostName.getLength() >= certHostName.getLength() )
2038 OUString cmpStr = certHostName.copy( 1 );
2040 if ( hostName.matchIgnoreAsciiCase(
2041 cmpStr, hostName.getLength() - cmpStr.getLength() ) )
2042 return true;
2044 return false;
2047 OUString NeonSession::makeAbsoluteURL( OUString const & rURL ) const
2051 // Is URL relative or already absolute?
2052 if ( !rURL.isEmpty() && rURL[ 0 ] != '/' )
2054 // absolute.
2055 return OUString( rURL );
2057 else
2059 ne_uri aUri;
2060 memset( &aUri, 0, sizeof( aUri ) );
2062 ne_fill_server_uri( m_pHttpSession, &aUri );
2063 aUri.path
2064 = ne_strdup( OUStringToOString(
2065 rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
2066 NeonUri aNeonUri( &aUri );
2067 ne_uri_free( &aUri );
2068 return aNeonUri.GetURI();
2071 catch ( DAVException const & )
2074 // error.
2075 return OUString();
2078 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */