Stop leaking all ScPostIt instances.
[LibreOffice.git] / ucb / source / ucp / webdav-neon / NeonSession.cxx
blobc493a7f945f27f065bdb8c0b6e4b17079d00d6b4
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 <boost/unordered_map.hpp>
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 (size_t)p;
110 typedef boost::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( (sal_Int8 *)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 = _rRawString.copy( nContStart, nContEnd - nContStart );
354 return sPart;
356 } // namespace
358 extern "C" int NeonSession_CertificationNotify( void *userdata,
359 int failures,
360 const ne_ssl_certificate *cert )
362 OSL_ASSERT( cert );
364 NeonSession * pSession = static_cast< NeonSession * >( userdata );
365 uno::Reference< security::XCertificateContainer > xCertificateContainer;
368 xCertificateContainer = security::CertificateContainer::create( pSession->getComponentContext() );
370 catch ( uno::Exception const & )
374 if ( !xCertificateContainer.is() )
375 return 1;
377 failures = 0;
379 char * dn = ne_ssl_readable_dname( ne_ssl_cert_subject( cert ) );
380 OUString cert_subject( dn, strlen( dn ), RTL_TEXTENCODING_UTF8, 0 );
382 ne_free( dn );
384 security::CertificateContainerStatus certificateContainer(
385 xCertificateContainer->hasCertificate(
386 pSession->getHostName(), cert_subject ) );
388 if ( certificateContainer != security::CertificateContainerStatus_NOCERT )
389 return
390 certificateContainer == security::CertificateContainerStatus_TRUSTED
392 : 1;
394 uno::Reference< xml::crypto::XSEInitializer > xSEInitializer;
397 xSEInitializer = xml::crypto::SEInitializer::create( pSession->getComponentContext() );
399 catch ( uno::Exception const & )
403 if ( !xSEInitializer.is() )
404 return 1;
406 uno::Reference< xml::crypto::XXMLSecurityContext > xSecurityContext(
407 xSEInitializer->createSecurityContext( OUString() ) );
409 uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnv(
410 xSecurityContext->getSecurityEnvironment() );
412 //The end entity certificate
413 char * eeCertB64 = ne_ssl_cert_export( cert );
415 OString sEECertB64( eeCertB64 );
417 uno::Reference< security::XCertificate > xEECert(
418 xSecurityEnv->createCertificateFromAscii(
419 OStringToOUString( sEECertB64, RTL_TEXTENCODING_ASCII_US ) ) );
421 ne_free( eeCertB64 );
422 eeCertB64 = 0;
424 std::vector< uno::Reference< security::XCertificate > > vecCerts;
425 const ne_ssl_certificate * issuerCert = cert;
428 //get the intermediate certificate
429 //the returned value is const ! Therfore it does not need to be freed
430 //with ne_ssl_cert_free, which takes a non-const argument
431 issuerCert = ne_ssl_cert_signedby( issuerCert );
432 if ( NULL == issuerCert )
433 break;
435 char * imCertB64 = ne_ssl_cert_export( issuerCert );
436 OString sInterMediateCertB64( imCertB64 );
437 ne_free( imCertB64 );
439 uno::Reference< security::XCertificate> xImCert(
440 xSecurityEnv->createCertificateFromAscii(
441 OStringToOUString( sInterMediateCertB64, RTL_TEXTENCODING_ASCII_US ) ) );
442 if ( xImCert.is() )
443 vecCerts.push_back( xImCert );
445 while ( 1 );
447 sal_Int64 certValidity = xSecurityEnv->verifyCertificate( xEECert,
448 ::comphelper::containerToSequence( vecCerts ) );
450 if ( pSession->isDomainMatch(
451 GetHostnamePart( xEECert.get()->getSubjectName() ) ) )
453 // if host name matched with certificate then look if the
454 // certificate was ok
455 if( certValidity == security::CertificateValidity::VALID )
456 return 0;
459 const uno::Reference< ucb::XCommandEnvironment > xEnv(
460 pSession->getRequestEnvironment().m_xEnv );
461 if ( xEnv.is() )
463 failures = static_cast< int >( certValidity );
465 uno::Reference< task::XInteractionHandler > xIH(
466 xEnv->getInteractionHandler() );
467 if ( xIH.is() )
469 rtl::Reference< ucbhelper::SimpleCertificateValidationRequest >
470 xRequest( new ucbhelper::SimpleCertificateValidationRequest(
471 (sal_Int32)failures, xEECert, pSession->getHostName() ) );
472 xIH->handle( xRequest.get() );
474 rtl::Reference< ucbhelper::InteractionContinuation > xSelection
475 = xRequest->getSelection();
477 if ( xSelection.is() )
479 uno::Reference< task::XInteractionApprove > xApprove(
480 xSelection.get(), uno::UNO_QUERY );
481 if ( xApprove.is() )
483 xCertificateContainer->addCertificate(
484 pSession->getHostName(), cert_subject, sal_True );
485 return 0;
487 else
489 // Don't trust cert
490 xCertificateContainer->addCertificate(
491 pSession->getHostName(), cert_subject, sal_False );
492 return 1;
496 else
498 // Don't trust cert
499 xCertificateContainer->addCertificate(
500 pSession->getHostName(), cert_subject, sal_False );
501 return 1;
504 return 1;
507 extern "C" void NeonSession_PreSendRequest( ne_request * req,
508 void * userdata,
509 ne_buffer * headers )
511 // userdata -> value returned by 'create'
513 NeonSession * pSession = static_cast< NeonSession * >( userdata );
514 if ( pSession )
516 // If there is a proxy server in between, it shall never use
517 // cached data. We always want 'up-to-date' data.
518 ne_buffer_concat( headers, "Pragma: no-cache", EOL, NULL );
519 // alternative, but understoud by HTTP 1.1 servers only:
520 // ne_buffer_concat( headers, "Cache-Control: max-age=0", EOL, NULL );
522 const RequestDataMap * pRequestData
523 = static_cast< const RequestDataMap* >(
524 pSession->getRequestData() );
526 RequestDataMap::const_iterator it = pRequestData->find( req );
527 if ( it != pRequestData->end() )
529 if ( !(*it).second.aContentType.isEmpty() )
531 char * pData = headers->data;
532 if ( strstr( pData, "Content-Type:" ) == NULL )
534 OString aType
535 = OUStringToOString( (*it).second.aContentType,
536 RTL_TEXTENCODING_UTF8 );
537 ne_buffer_concat( headers, "Content-Type: ",
538 aType.getStr(), EOL, NULL );
542 if ( !(*it).second.aReferer.isEmpty() )
544 char * pData = headers->data;
545 if ( strstr( pData, "Referer:" ) == NULL )
547 OString aReferer
548 = OUStringToOString( (*it).second.aReferer,
549 RTL_TEXTENCODING_UTF8 );
550 ne_buffer_concat( headers, "Referer: ",
551 aReferer.getStr(), EOL, NULL );
556 const DAVRequestHeaders & rHeaders
557 = pSession->getRequestEnvironment().m_aRequestHeaders;
559 DAVRequestHeaders::const_iterator it1( rHeaders.begin() );
560 const DAVRequestHeaders::const_iterator end1( rHeaders.end() );
562 while ( it1 != end1 )
564 OString aHeader
565 = OUStringToOString( (*it1).first,
566 RTL_TEXTENCODING_UTF8 );
567 OString aValue
568 = OUStringToOString( (*it1).second,
569 RTL_TEXTENCODING_UTF8 );
570 ne_buffer_concat( headers, aHeader.getStr(), ": ",
571 aValue.getStr(), EOL, NULL );
573 ++it1;
578 // static members
579 bool NeonSession::m_bGlobalsInited = false;
580 //See https://bugzilla.redhat.com/show_bug.cgi?id=544619#c4
581 //neon is threadsafe, but uses gnutls which is only thread-safe
582 //if initialized to be thread-safe. cups, unfortunately, generally
583 //initializes it first, and as non-thread-safe, leaving the entire
584 //stack unsafe
585 osl::Mutex aGlobalNeonMutex;
586 NeonLockStore NeonSession::m_aNeonLockStore;
588 NeonSession::NeonSession(
589 const rtl::Reference< DAVSessionFactory > & rSessionFactory,
590 const OUString& inUri,
591 const uno::Sequence< beans::NamedValue >& rFlags,
592 const ucbhelper::InternetProxyDecider & rProxyDecider )
593 throw ( DAVException )
594 : DAVSession( rSessionFactory ),
595 m_aFlags( rFlags ),
596 m_pHttpSession( 0 ),
597 m_pRequestData( new RequestDataMap ),
598 m_rProxyDecider( rProxyDecider )
600 NeonUri theUri( inUri );
601 m_aScheme = theUri.GetScheme();
602 m_aHostName = theUri.GetHost();
603 m_nPort = theUri.GetPort();
606 NeonSession::~NeonSession( )
608 if ( m_pHttpSession )
611 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
612 ne_session_destroy( m_pHttpSession );
614 m_pHttpSession = 0;
616 delete static_cast< RequestDataMap * >( m_pRequestData );
619 void NeonSession::Init( const DAVRequestEnvironment & rEnv )
620 throw ( DAVException )
622 osl::Guard< osl::Mutex > theGuard( m_aMutex );
623 m_aEnv = rEnv;
624 Init();
627 void NeonSession::Init()
628 throw ( DAVException )
630 osl::Guard< osl::Mutex > theGuard( m_aMutex );
632 bool bCreateNewSession = false;
634 if ( m_pHttpSession == 0 )
636 // Ensure that Neon sockets are initialized
637 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
638 if ( !m_bGlobalsInited )
640 if ( ne_sock_init() != 0 )
641 throw DAVException( DAVException::DAV_SESSION_CREATE,
642 NeonUri::makeConnectionEndPointString(
643 m_aHostName, m_nPort ) );
645 // #122205# - libxml2 needs to be initialized once if used by
646 // multithreaded programs like OOo.
647 xmlInitParser();
648 #if OSL_DEBUG_LEVEL > 0
649 // for more debug flags see ne_utils.h; NE_DEBUGGING must be defined
650 // while compiling neon in order to actually activate neon debug
651 // output.
652 ne_debug_init( stderr, NE_DBG_FLUSH
653 | NE_DBG_HTTP
654 // | NE_DBG_HTTPBODY
655 // | NE_DBG_HTTPAUTH
656 // | NE_DBG_XML
657 // | NE_DBG_XMLPARSE
658 | NE_DBG_LOCKS
659 | NE_DBG_SSL
661 #endif
662 m_bGlobalsInited = true;
665 const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
667 m_aProxyName = rProxyCfg.aName;
668 m_nProxyPort = rProxyCfg.nPort;
670 // Not yet initialized. Create new session.
671 bCreateNewSession = true;
673 else
675 // #112271# Check whether proxy settings are still valid (They may
676 // change at any time). If not, create new Neon session.
678 const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
680 if ( ( rProxyCfg.aName != m_aProxyName )
681 || ( rProxyCfg.nPort != m_nProxyPort ) )
683 m_aProxyName = rProxyCfg.aName;
684 m_nProxyPort = rProxyCfg.nPort;
686 // new session needed, destroy old first
688 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
689 ne_session_destroy( m_pHttpSession );
691 m_pHttpSession = 0;
692 bCreateNewSession = true;
696 if ( bCreateNewSession )
698 // @@@ For FTP over HTTP proxy inUserInfo is needed to be able to
699 // build the complete request URI (including user:pass), but
700 // currently (0.22.0) neon does not allow to pass the user info
701 // to the session
704 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
705 m_pHttpSession = ne_session_create(
706 OUStringToOString( m_aScheme, RTL_TEXTENCODING_UTF8 ).getStr(),
707 /* theUri.GetUserInfo(),
708 @@@ for FTP via HTTP proxy, but not supported by Neon */
709 OUStringToOString( m_aHostName, RTL_TEXTENCODING_UTF8 ).getStr(),
710 m_nPort );
713 if ( m_pHttpSession == 0 )
714 throw DAVException( DAVException::DAV_SESSION_CREATE,
715 NeonUri::makeConnectionEndPointString(
716 m_aHostName, m_nPort ) );
718 // Register the session with the lock store
719 m_aNeonLockStore.registerSession( m_pHttpSession );
721 if ( m_aScheme.equalsIgnoreAsciiCase(
722 OUString( "https" ) ) )
724 // Set a failure callback for certificate check
725 ne_ssl_set_verify(
726 m_pHttpSession, NeonSession_CertificationNotify, this);
728 // Tell Neon to tell the SSL library used (OpenSSL or
729 // GnuTLS, I guess) to use a default set of root
730 // certificates.
731 ne_ssl_trust_default_ca(m_pHttpSession);
734 // Add hooks (i.e. for adding additional headers to the request)
736 #if 0
737 /* Hook called when a request is created. */
738 //typedef void (*ne_create_request_fn)(ne_request *req, void *userdata,
739 // const char *method, const char *path);
741 ne_hook_create_request( m_pHttpSession, create_req_hook_fn, this );
742 #endif
744 /* Hook called before the request is sent. 'header' is the raw HTTP
745 * header before the trailing CRLF is added: add in more here. */
746 //typedef void (*ne_pre_send_fn)(ne_request *req, void *userdata,
747 // ne_buffer *header);
749 ne_hook_pre_send( m_pHttpSession, NeonSession_PreSendRequest, this );
750 #if 0
751 /* Hook called after the request is sent. May return:
752 * NE_OK everything is okay
753 * NE_RETRY try sending the request again.
754 * anything else signifies an error, and the request is failed. The
755 * return code is passed back the _dispatch caller, so the session error
756 * must also be set appropriately (ne_set_error).
758 //typedef int (*ne_post_send_fn)(ne_request *req, void *userdata,
759 // const ne_status *status);
761 ne_hook_post_send( m_pHttpSession, post_send_req_hook_fn, this );
763 /* Hook called when the request is destroyed. */
764 //typedef void (*ne_destroy_req_fn)(ne_request *req, void *userdata);
766 ne_hook_destroy_request( m_pHttpSession, destroy_req_hook_fn, this );
768 /* Hook called when the session is destroyed. */
769 //typedef void (*ne_destroy_sess_fn)(void *userdata);
771 ne_hook_destroy_session( m_pHttpSession, destroy_sess_hook_fn, this );
772 #endif
774 if ( !m_aProxyName.isEmpty() )
776 ne_session_proxy( m_pHttpSession,
777 OUStringToOString(
778 m_aProxyName,
779 RTL_TEXTENCODING_UTF8 ).getStr(),
780 m_nProxyPort );
783 // avoid KeepAlive?
784 if ( noKeepAlive(m_aFlags) )
785 ne_set_session_flag( m_pHttpSession, NE_SESSFLAG_PERSIST, 0 );
787 // Register for redirects.
788 ne_redirect_register( m_pHttpSession );
790 // authentication callbacks.
791 #if 1
792 ne_add_server_auth( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
793 ne_add_proxy_auth ( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
794 #else
795 ne_set_server_auth( m_pHttpSession, NeonSession_NeonAuth, this );
796 ne_set_proxy_auth ( m_pHttpSession, NeonSession_NeonAuth, this );
797 #endif
801 sal_Bool NeonSession::CanUse( const OUString & inUri,
802 const uno::Sequence< beans::NamedValue >& rFlags )
806 NeonUri theUri( inUri );
807 if ( ( theUri.GetPort() == m_nPort ) &&
808 ( theUri.GetHost() == m_aHostName ) &&
809 ( theUri.GetScheme() == m_aScheme ) &&
810 ( rFlags == m_aFlags ) )
811 return sal_True;
813 catch ( DAVException const & )
815 return sal_False;
817 return sal_False;
820 sal_Bool NeonSession::UsesProxy()
822 Init();
823 return !m_aProxyName.isEmpty() ;
826 void NeonSession::OPTIONS( const OUString & inPath,
827 DAVCapabilities & outCapabilities,
828 const DAVRequestEnvironment & rEnv )
829 throw( DAVException )
831 osl::Guard< osl::Mutex > theGuard( m_aMutex );
833 Init( rEnv );
835 HttpServerCapabilities servercaps;
836 memset( &servercaps, 0, sizeof( servercaps ) );
838 int theRetVal = ne_options( m_pHttpSession,
839 OUStringToOString(
840 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
841 &servercaps );
843 HandleError( theRetVal, inPath, rEnv );
845 outCapabilities.class1 = !!servercaps.dav_class1;
846 outCapabilities.class2 = !!servercaps.dav_class2;
847 outCapabilities.executable = !!servercaps.dav_executable;
850 void NeonSession::PROPFIND( const OUString & inPath,
851 const Depth inDepth,
852 const std::vector< OUString > & inPropNames,
853 std::vector< DAVResource > & ioResources,
854 const DAVRequestEnvironment & rEnv )
855 throw ( DAVException )
857 osl::Guard< osl::Mutex > theGuard( m_aMutex );
859 Init( rEnv );
861 int theRetVal = NE_OK;
862 NeonPropFindRequest theRequest( m_pHttpSession,
863 OUStringToOString(
864 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
865 inDepth,
866 inPropNames,
867 ioResources,
868 theRetVal );
870 HandleError( theRetVal, inPath, rEnv );
873 void NeonSession::PROPFIND( const OUString & inPath,
874 const Depth inDepth,
875 std::vector< DAVResourceInfo > & ioResInfo,
876 const DAVRequestEnvironment & rEnv )
877 throw( DAVException )
879 osl::Guard< osl::Mutex > theGuard( m_aMutex );
881 Init( rEnv );
883 int theRetVal = NE_OK;
884 NeonPropFindRequest theRequest( m_pHttpSession,
885 OUStringToOString(
886 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
887 inDepth,
888 ioResInfo,
889 theRetVal );
891 HandleError( theRetVal, inPath, rEnv );
894 void NeonSession::PROPPATCH( const OUString & inPath,
895 const std::vector< ProppatchValue > & inValues,
896 const DAVRequestEnvironment & rEnv )
897 throw( DAVException )
899 /* @@@ Which standard live properties can be set by the client?
900 This is a known WebDAV RFC issue ( verified: 04/10/2001 )
901 --> http://www.ics.uci.edu/pub/ietf/webdav/protocol/issues.html
903 mod_dav implementation:
905 creationdate r ( File System prop )
906 displayname w
907 getcontentlanguage r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
908 getcontentlength r ( File System prop )
909 getcontenttype r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
910 getetag r ( File System prop )
911 getlastmodified r ( File System prop )
912 lockdiscovery r
913 resourcetype r
914 source w
915 supportedlock r
916 executable w ( #ifndef WIN32 )
918 All dead properties are of course writable.
921 int theRetVal = NE_OK;
923 int n; // for the "for" loop
925 // Generate the list of properties we want to set.
926 int nPropCount = inValues.size();
927 ne_proppatch_operation* pItems
928 = new ne_proppatch_operation[ nPropCount + 1 ];
929 for ( n = 0; n < nPropCount; ++n )
931 const ProppatchValue & rValue = inValues[ n ];
933 // Split fullname into namespace and name!
934 ne_propname * pName = new ne_propname;
935 DAVProperties::createNeonPropName( rValue.name, *pName );
936 pItems[ n ].name = pName;
938 if ( rValue.operation == PROPSET )
940 pItems[ n ].type = ne_propset;
942 OUString aStringValue;
943 if ( DAVProperties::isUCBDeadProperty( *pName ) )
945 // DAV dead property added by WebDAV UCP?
946 if ( !UCBDeadPropertyValue::toXML( rValue.value,
947 aStringValue ) )
949 // Error!
950 pItems[ n ].value = 0;
951 theRetVal = NE_ERROR;
952 nPropCount = n + 1;
953 break;
956 else if ( !( rValue.value >>= aStringValue ) )
958 // complex properties...
959 if ( rValue.name == DAVProperties::SOURCE )
961 uno::Sequence< ucb::Link > aLinks;
962 if ( rValue.value >>= aLinks )
964 LinkSequence::toXML( aLinks, aStringValue );
966 else
968 // Error!
969 pItems[ n ].value = 0;
970 theRetVal = NE_ERROR;
971 nPropCount = n + 1;
972 break;
975 else
977 OSL_FAIL( "NeonSession::PROPPATCH - unsupported type!" );
978 // Error!
979 pItems[ n ].value = 0;
980 theRetVal = NE_ERROR;
981 nPropCount = n + 1;
982 break;
985 pItems[ n ].value
986 = strdup( OUStringToOString( aStringValue,
987 RTL_TEXTENCODING_UTF8 ).getStr() );
989 else
991 pItems[ n ].type = ne_propremove;
992 pItems[ n ].value = 0;
996 if ( theRetVal == NE_OK )
998 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1000 Init( rEnv );
1002 pItems[ n ].name = 0;
1004 theRetVal = ne_proppatch( m_pHttpSession,
1005 OUStringToOString(
1006 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1007 pItems );
1010 for ( n = 0; n < nPropCount; ++n )
1012 free( (void *)pItems[ n ].name->name );
1013 delete pItems[ n ].name;
1014 free( (void *)pItems[ n ].value );
1017 delete [] pItems;
1019 HandleError( theRetVal, inPath, rEnv );
1022 void NeonSession::HEAD( const OUString & inPath,
1023 const std::vector< OUString > & inHeaderNames,
1024 DAVResource & ioResource,
1025 const DAVRequestEnvironment & rEnv )
1026 throw( DAVException )
1028 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1030 Init( rEnv );
1032 int theRetVal = NE_OK;
1033 NeonHeadRequest theRequest( m_pHttpSession,
1034 inPath,
1035 inHeaderNames,
1036 ioResource,
1037 theRetVal );
1039 HandleError( theRetVal, inPath, rEnv );
1042 uno::Reference< io::XInputStream >
1043 NeonSession::GET( const OUString & inPath,
1044 const DAVRequestEnvironment & rEnv )
1045 throw ( DAVException )
1047 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1049 Init( rEnv );
1051 rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
1052 NeonRequestContext aCtx( xInputStream );
1053 int theRetVal = GET( m_pHttpSession,
1054 OUStringToOString(
1055 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1056 NeonSession_ResponseBlockReader,
1057 false,
1058 &aCtx );
1060 HandleError( theRetVal, inPath, rEnv );
1062 return uno::Reference< io::XInputStream >( xInputStream.get() );
1065 void NeonSession::GET( const OUString & inPath,
1066 uno::Reference< io::XOutputStream > & ioOutputStream,
1067 const DAVRequestEnvironment & rEnv )
1068 throw ( DAVException )
1070 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1072 Init( rEnv );
1074 NeonRequestContext aCtx( ioOutputStream );
1075 int theRetVal = GET( m_pHttpSession,
1076 OUStringToOString(
1077 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1078 NeonSession_ResponseBlockWriter,
1079 false,
1080 &aCtx );
1082 HandleError( theRetVal, inPath, rEnv );
1085 uno::Reference< io::XInputStream >
1086 NeonSession::GET( const OUString & inPath,
1087 const std::vector< OUString > & inHeaderNames,
1088 DAVResource & ioResource,
1089 const DAVRequestEnvironment & rEnv )
1090 throw ( DAVException )
1092 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1094 Init( rEnv );
1096 ioResource.uri = inPath;
1097 ioResource.properties.clear();
1099 rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
1100 NeonRequestContext aCtx( xInputStream, inHeaderNames, ioResource );
1101 int theRetVal = GET( m_pHttpSession,
1102 OUStringToOString(
1103 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1104 NeonSession_ResponseBlockReader,
1105 true,
1106 &aCtx );
1108 HandleError( theRetVal, inPath, rEnv );
1110 return uno::Reference< io::XInputStream >( xInputStream.get() );
1113 void NeonSession::GET( const OUString & inPath,
1114 uno::Reference< io::XOutputStream > & ioOutputStream,
1115 const std::vector< OUString > & inHeaderNames,
1116 DAVResource & ioResource,
1117 const DAVRequestEnvironment & rEnv )
1118 throw ( DAVException )
1120 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1122 Init( rEnv );
1124 ioResource.uri = inPath;
1125 ioResource.properties.clear();
1127 NeonRequestContext aCtx( ioOutputStream, inHeaderNames, ioResource );
1128 int theRetVal = GET( m_pHttpSession,
1129 OUStringToOString(
1130 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1131 NeonSession_ResponseBlockWriter,
1132 true,
1133 &aCtx );
1135 HandleError( theRetVal, inPath, rEnv );
1138 void NeonSession::PUT( const OUString & inPath,
1139 const uno::Reference< io::XInputStream > & inInputStream,
1140 const DAVRequestEnvironment & rEnv )
1141 throw ( DAVException )
1143 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1145 uno::Sequence< sal_Int8 > aDataToSend;
1146 if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
1147 throw DAVException( DAVException::DAV_INVALID_ARG );
1149 Init( rEnv );
1151 int theRetVal = PUT( m_pHttpSession,
1152 OUStringToOString(
1153 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1154 reinterpret_cast< const char * >(
1155 aDataToSend.getConstArray() ),
1156 aDataToSend.getLength() );
1158 HandleError( theRetVal, inPath, rEnv );
1161 uno::Reference< io::XInputStream >
1162 NeonSession::POST( const OUString & inPath,
1163 const OUString & rContentType,
1164 const OUString & rReferer,
1165 const uno::Reference< io::XInputStream > & inInputStream,
1166 const DAVRequestEnvironment & rEnv )
1167 throw ( DAVException )
1169 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1171 uno::Sequence< sal_Int8 > aDataToSend;
1172 if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
1173 throw DAVException( DAVException::DAV_INVALID_ARG );
1175 Init( rEnv );
1177 rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
1178 NeonRequestContext aCtx( xInputStream );
1179 int theRetVal = POST( m_pHttpSession,
1180 OUStringToOString(
1181 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1182 reinterpret_cast< const char * >(
1183 aDataToSend.getConstArray() ),
1184 NeonSession_ResponseBlockReader,
1185 &aCtx,
1186 rContentType,
1187 rReferer );
1189 HandleError( theRetVal, inPath, rEnv );
1191 return uno::Reference< io::XInputStream >( xInputStream.get() );
1194 void NeonSession::POST( const OUString & inPath,
1195 const OUString & rContentType,
1196 const OUString & rReferer,
1197 const uno::Reference< io::XInputStream > & inInputStream,
1198 uno::Reference< io::XOutputStream > & oOutputStream,
1199 const DAVRequestEnvironment & rEnv )
1200 throw ( DAVException )
1202 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1204 uno::Sequence< sal_Int8 > aDataToSend;
1205 if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
1206 throw DAVException( DAVException::DAV_INVALID_ARG );
1208 Init( rEnv );
1210 NeonRequestContext aCtx( oOutputStream );
1211 int theRetVal = POST( m_pHttpSession,
1212 OUStringToOString(
1213 inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1214 reinterpret_cast< const char * >(
1215 aDataToSend.getConstArray() ),
1216 NeonSession_ResponseBlockWriter,
1217 &aCtx,
1218 rContentType,
1219 rReferer );
1221 HandleError( theRetVal, inPath, rEnv );
1224 void NeonSession::MKCOL( const OUString & inPath,
1225 const DAVRequestEnvironment & rEnv )
1226 throw ( DAVException )
1228 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1230 Init( rEnv );
1232 int theRetVal = ne_mkcol( m_pHttpSession,
1233 OUStringToOString(
1234 inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
1236 HandleError( theRetVal, inPath, rEnv );
1239 void NeonSession::COPY( const OUString & inSourceURL,
1240 const OUString & inDestinationURL,
1241 const DAVRequestEnvironment & rEnv,
1242 sal_Bool inOverWrite )
1243 throw ( DAVException )
1245 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1247 Init( rEnv );
1249 NeonUri theSourceUri( inSourceURL );
1250 NeonUri theDestinationUri( inDestinationURL );
1252 int theRetVal = ne_copy( m_pHttpSession,
1253 inOverWrite ? 1 : 0,
1254 NE_DEPTH_INFINITE,
1255 OUStringToOString(
1256 theSourceUri.GetPath(),
1257 RTL_TEXTENCODING_UTF8 ).getStr(),
1258 OUStringToOString(
1259 theDestinationUri.GetPath(),
1260 RTL_TEXTENCODING_UTF8 ).getStr() );
1262 HandleError( theRetVal, inSourceURL, rEnv );
1265 void NeonSession::MOVE( const OUString & inSourceURL,
1266 const OUString & inDestinationURL,
1267 const DAVRequestEnvironment & rEnv,
1268 sal_Bool inOverWrite )
1269 throw ( DAVException )
1271 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1273 Init( rEnv );
1275 NeonUri theSourceUri( inSourceURL );
1276 NeonUri theDestinationUri( inDestinationURL );
1277 int theRetVal = ne_move( m_pHttpSession,
1278 inOverWrite ? 1 : 0,
1279 OUStringToOString(
1280 theSourceUri.GetPath(),
1281 RTL_TEXTENCODING_UTF8 ).getStr(),
1282 OUStringToOString(
1283 theDestinationUri.GetPath(),
1284 RTL_TEXTENCODING_UTF8 ).getStr() );
1286 HandleError( theRetVal, inSourceURL, rEnv );
1289 void NeonSession::DESTROY( const OUString & inPath,
1290 const DAVRequestEnvironment & rEnv )
1291 throw ( DAVException )
1293 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1295 Init( rEnv );
1297 int theRetVal = ne_delete( m_pHttpSession,
1298 OUStringToOString(
1299 inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
1301 HandleError( theRetVal, inPath, rEnv );
1304 namespace
1306 sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
1307 int timeout )
1309 TimeValue aEnd;
1310 osl_getSystemTime( &aEnd );
1312 // Try to estimate a safe absolute time for sending the
1313 // lock refresh request.
1314 sal_Int32 lastChanceToSendRefreshRequest = -1;
1315 if ( timeout != NE_TIMEOUT_INFINITE )
1317 sal_Int32 calltime = aEnd.Seconds - rStart.Seconds;
1318 if ( calltime <= timeout )
1320 lastChanceToSendRefreshRequest
1321 = aEnd.Seconds + timeout - calltime;
1323 else
1325 OSL_TRACE( "No chance to refresh lock before timeout!" );
1328 return lastChanceToSendRefreshRequest;
1331 } // namespace
1333 // Set new lock
1334 void NeonSession::LOCK( const OUString & inPath,
1335 ucb::Lock & rLock,
1336 const DAVRequestEnvironment & rEnv )
1337 throw ( DAVException )
1339 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1341 Init( rEnv );
1343 /* Create a depth zero, exclusive write lock, with default timeout
1344 * (allowing a server to pick a default). token, owner and uri are
1345 * unset. */
1346 NeonLock * theLock = ne_lock_create();
1348 // Set the lock uri
1349 ne_uri aUri;
1350 ne_uri_parse( OUStringToOString( makeAbsoluteURL( inPath ),
1351 RTL_TEXTENCODING_UTF8 ).getStr(),
1352 &aUri );
1353 theLock->uri = aUri;
1355 // Set the lock depth
1356 switch( rLock.Depth )
1358 case ucb::LockDepth_ZERO:
1359 theLock->depth = NE_DEPTH_ZERO;
1360 break;
1361 case ucb::LockDepth_ONE:
1362 theLock->depth = NE_DEPTH_ONE;
1363 break;
1364 case ucb::LockDepth_INFINITY:
1365 theLock->depth = NE_DEPTH_INFINITE;
1366 break;
1367 default:
1368 throw DAVException( DAVException::DAV_INVALID_ARG );
1371 // Set the lock scope
1372 switch ( rLock.Scope )
1374 case ucb::LockScope_EXCLUSIVE:
1375 theLock->scope = ne_lockscope_exclusive;
1376 break;
1377 case ucb::LockScope_SHARED:
1378 theLock->scope = ne_lockscope_shared;
1379 break;
1380 default:
1381 throw DAVException( DAVException::DAV_INVALID_ARG );
1384 // Set the lock timeout
1385 theLock->timeout = (long)rLock.Timeout;
1387 // Set the lock owner
1388 OUString aValue;
1389 rLock.Owner >>= aValue;
1390 theLock->owner =
1391 ne_strdup( OUStringToOString( aValue,
1392 RTL_TEXTENCODING_UTF8 ).getStr() );
1393 TimeValue startCall;
1394 osl_getSystemTime( &startCall );
1396 int theRetVal = ne_lock( m_pHttpSession, theLock );
1398 if ( theRetVal == NE_OK )
1400 m_aNeonLockStore.addLock( theLock,
1401 this,
1402 lastChanceToSendRefreshRequest(
1403 startCall, theLock->timeout ) );
1405 uno::Sequence< OUString > aTokens( 1 );
1406 aTokens[ 0 ] = OUString::createFromAscii( theLock->token );
1407 rLock.LockTokens = aTokens;
1409 OSL_TRACE( "NeonSession::LOCK: created lock for %s. token: %s",
1410 OUStringToOString( makeAbsoluteURL( inPath ),
1411 RTL_TEXTENCODING_UTF8 ).getStr(),
1412 theLock->token );
1414 else
1416 ne_lock_destroy( theLock );
1418 OSL_TRACE( "NeonSession::LOCK: obtaining lock for %s failed!",
1419 OUStringToOString( makeAbsoluteURL( inPath ),
1420 RTL_TEXTENCODING_UTF8 ).getStr() );
1423 HandleError( theRetVal, inPath, rEnv );
1426 // Refresh existing lock
1427 sal_Int64 NeonSession::LOCK( const OUString & inPath,
1428 sal_Int64 nTimeout,
1429 const DAVRequestEnvironment & rEnv )
1430 throw ( DAVException )
1432 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1434 // Try to get the neon lock from lock store
1435 NeonLock * theLock
1436 = m_aNeonLockStore.findByUri( makeAbsoluteURL( inPath ) );
1437 if ( !theLock )
1438 throw DAVException( DAVException::DAV_NOT_LOCKED );
1440 Init( rEnv );
1442 // refresh existing lock.
1443 theLock->timeout = static_cast< long >( nTimeout );
1445 TimeValue startCall;
1446 osl_getSystemTime( &startCall );
1448 int theRetVal = ne_lock_refresh( m_pHttpSession, theLock );
1450 if ( theRetVal == NE_OK )
1452 m_aNeonLockStore.updateLock( theLock,
1453 lastChanceToSendRefreshRequest(
1454 startCall, theLock->timeout ) );
1457 HandleError( theRetVal, inPath, rEnv );
1459 return theLock->timeout;
1462 // Refresh existing lock
1463 bool NeonSession::LOCK( NeonLock * pLock,
1464 sal_Int32 & rlastChanceToSendRefreshRequest )
1466 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1468 #if OSL_DEBUG_LEVEL > 0
1469 char * p = ne_uri_unparse( &(pLock->uri) );
1470 OSL_TRACE( "NeonSession::LOCK: Refreshing lock for %s.", p );
1471 ne_free( p );
1472 #endif
1474 // refresh existing lock.
1476 TimeValue startCall;
1477 osl_getSystemTime( &startCall );
1479 if ( ne_lock_refresh( m_pHttpSession, pLock ) == NE_OK )
1481 rlastChanceToSendRefreshRequest
1482 = lastChanceToSendRefreshRequest( startCall, pLock->timeout );
1484 OSL_TRACE( "Lock successfully refreshed." );
1485 return true;
1487 else
1489 OSL_TRACE( "Lock not refreshed!" );
1490 return false;
1494 void NeonSession::UNLOCK( const OUString & inPath,
1495 const DAVRequestEnvironment & rEnv )
1496 throw ( DAVException )
1498 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1500 // get the neon lock from lock store
1501 NeonLock * theLock
1502 = m_aNeonLockStore.findByUri( makeAbsoluteURL( inPath ) );
1503 if ( !theLock )
1504 throw DAVException( DAVException::DAV_NOT_LOCKED );
1506 Init( rEnv );
1508 int theRetVal = ne_unlock( m_pHttpSession, theLock );
1510 if ( theRetVal == NE_OK )
1512 m_aNeonLockStore.removeLock( theLock );
1513 ne_lock_destroy( theLock );
1515 else
1517 OSL_TRACE( "NeonSession::UNLOCK: unlocking of %s failed.",
1518 OUStringToOString( makeAbsoluteURL( inPath ),
1519 RTL_TEXTENCODING_UTF8 ).getStr() );
1522 HandleError( theRetVal, inPath, rEnv );
1525 bool NeonSession::UNLOCK( NeonLock * pLock )
1527 osl::Guard< osl::Mutex > theGuard( m_aMutex );
1529 #if OSL_DEBUG_LEVEL > 0
1530 char * p = ne_uri_unparse( &(pLock->uri) );
1531 OSL_TRACE( "NeonSession::UNLOCK: Unlocking %s.", p );
1532 ne_free( p );
1533 #endif
1535 if ( ne_unlock( m_pHttpSession, pLock ) == NE_OK )
1537 OSL_TRACE( "UNLOCK succeeded." );
1538 return true;
1540 else
1542 OSL_TRACE( "UNLOCK failed!" );
1543 return false;
1547 void NeonSession::abort()
1548 throw ( DAVException )
1550 SAL_INFO("ucb.ucp.webdav", "neon commands cannot be aborted");
1553 const ucbhelper::InternetProxyServer & NeonSession::getProxySettings() const
1555 if ( m_aScheme == "http" || m_aScheme == "https" )
1557 return m_rProxyDecider.getProxy( m_aScheme,
1558 m_aHostName,
1559 m_nPort );
1561 else
1563 return m_rProxyDecider.getProxy( m_aScheme,
1564 OUString() /* not used */,
1565 -1 /* not used */ );
1569 namespace {
1571 bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
1572 const char * token )
1574 for ( sal_Int32 n = 0; n < rLocks.getLength(); ++n )
1576 const uno::Sequence< OUString > & rTokens
1577 = rLocks[ n ].LockTokens;
1578 for ( sal_Int32 m = 0; m < rTokens.getLength(); ++m )
1580 if ( rTokens[ m ].equalsAscii( token ) )
1581 return true;
1584 return false;
1587 } // namespace
1589 bool NeonSession::removeExpiredLocktoken( const OUString & inURL,
1590 const DAVRequestEnvironment & rEnv )
1592 NeonLock * theLock = m_aNeonLockStore.findByUri( inURL );
1593 if ( !theLock )
1594 return false;
1596 // do a lockdiscovery to check whether this lock is still valid.
1599 // @@@ Alternative: use ne_lock_discover() => less overhead
1601 std::vector< DAVResource > aResources;
1602 std::vector< OUString > aPropNames;
1603 aPropNames.push_back( DAVProperties::LOCKDISCOVERY );
1605 PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv );
1607 if ( aResources.empty() )
1608 return false;
1610 std::vector< DAVPropertyValue >::const_iterator it
1611 = aResources[ 0 ].properties.begin();
1612 std::vector< DAVPropertyValue >::const_iterator end
1613 = aResources[ 0 ].properties.end();
1615 while ( it != end )
1617 if ( (*it).Name.equals( DAVProperties::LOCKDISCOVERY ) )
1619 uno::Sequence< ucb::Lock > aLocks;
1620 if ( !( (*it).Value >>= aLocks ) )
1621 return false;
1623 if ( !containsLocktoken( aLocks, theLock->token ) )
1625 // expired!
1626 break;
1629 // still valid.
1630 return false;
1632 ++it;
1635 // No lockdiscovery prop in propfind result / locktoken not found
1636 // in propfind result -> not locked
1637 OSL_TRACE( "NeonSession::removeExpiredLocktoken: Removing "
1638 " expired lock token for %s. token: %s",
1639 OUStringToOString( inURL,
1640 RTL_TEXTENCODING_UTF8 ).getStr(),
1641 theLock->token );
1643 m_aNeonLockStore.removeLock( theLock );
1644 ne_lock_destroy( theLock );
1645 return true;
1647 catch ( DAVException const & )
1650 return false;
1653 // Common error handler
1654 void NeonSession::HandleError( int nError,
1655 const OUString & inPath,
1656 const DAVRequestEnvironment & rEnv )
1657 throw ( DAVException )
1659 m_aEnv = DAVRequestEnvironment();
1661 // Map error code to DAVException.
1662 switch ( nError )
1664 case NE_OK:
1665 return;
1667 case NE_ERROR: // Generic error
1669 OUString aText = OUString::createFromAscii(
1670 ne_get_error( m_pHttpSession ) );
1672 sal_uInt16 code = makeStatusCode( aText );
1674 if ( code == SC_LOCKED )
1676 if ( m_aNeonLockStore.findByUri(
1677 makeAbsoluteURL( inPath ) ) == 0 )
1679 // locked by 3rd party
1680 throw DAVException( DAVException::DAV_LOCKED );
1682 else
1684 // locked by ourself
1685 throw DAVException( DAVException::DAV_LOCKED_SELF );
1689 // Special handling for 400 and 412 status codes, which may indicate
1690 // that a lock previously obtained by us has been released meanwhile
1691 // by the server. Unfortunately, RFC is not clear at this point,
1692 // thus server implementations behave different...
1693 else if ( code == SC_BAD_REQUEST || code == SC_PRECONDITION_FAILED )
1695 if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) )
1696 throw DAVException( DAVException::DAV_LOCK_EXPIRED );
1699 throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
1701 case NE_LOOKUP: // Name lookup failed.
1702 throw DAVException( DAVException::DAV_HTTP_LOOKUP,
1703 NeonUri::makeConnectionEndPointString(
1704 m_aHostName, m_nPort ) );
1706 case NE_AUTH: // User authentication failed on server
1707 throw DAVException( DAVException::DAV_HTTP_AUTH,
1708 NeonUri::makeConnectionEndPointString(
1709 m_aHostName, m_nPort ) );
1711 case NE_PROXYAUTH: // User authentication failed on proxy
1712 throw DAVException( DAVException::DAV_HTTP_AUTHPROXY,
1713 NeonUri::makeConnectionEndPointString(
1714 m_aProxyName, m_nProxyPort ) );
1716 case NE_CONNECT: // Could not connect to server
1717 throw DAVException( DAVException::DAV_HTTP_CONNECT,
1718 NeonUri::makeConnectionEndPointString(
1719 m_aHostName, m_nPort ) );
1721 case NE_TIMEOUT: // Connection timed out
1722 throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
1723 NeonUri::makeConnectionEndPointString(
1724 m_aHostName, m_nPort ) );
1726 case NE_FAILED: // The precondition failed
1727 throw DAVException( DAVException::DAV_HTTP_FAILED,
1728 NeonUri::makeConnectionEndPointString(
1729 m_aHostName, m_nPort ) );
1731 case NE_RETRY: // Retry request (ne_end_request ONLY)
1732 throw DAVException( DAVException::DAV_HTTP_RETRY,
1733 NeonUri::makeConnectionEndPointString(
1734 m_aHostName, m_nPort ) );
1736 case NE_REDIRECT:
1738 NeonUri aUri( ne_redirect_location( m_pHttpSession ) );
1739 throw DAVException(
1740 DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() );
1742 default:
1744 OSL_TRACE( "NeonSession::HandleError : Unknown Neon error code!" );
1745 throw DAVException( DAVException::DAV_HTTP_ERROR,
1746 OUString::createFromAscii(
1747 ne_get_error( m_pHttpSession ) ) );
1752 namespace {
1754 void runResponseHeaderHandler( void * userdata,
1755 const char * value )
1757 OUString aHeader( OUString::createFromAscii( value ) );
1758 sal_Int32 nPos = aHeader.indexOf( ':' );
1760 if ( nPos != -1 )
1762 OUString aHeaderName( aHeader.copy( 0, nPos ) );
1764 NeonRequestContext * pCtx
1765 = static_cast< NeonRequestContext * >( userdata );
1767 // Note: Empty vector means that all headers are requested.
1768 bool bIncludeIt = ( pCtx->pHeaderNames->empty() );
1770 if ( !bIncludeIt )
1772 // Check whether this header was requested.
1773 std::vector< OUString >::const_iterator it(
1774 pCtx->pHeaderNames->begin() );
1775 const std::vector< OUString >::const_iterator end(
1776 pCtx->pHeaderNames->end() );
1778 while ( it != end )
1780 // header names are case insensitive
1781 if ( (*it).equalsIgnoreAsciiCase( aHeaderName ) )
1783 aHeaderName = (*it);
1784 break;
1786 ++it;
1789 if ( it != end )
1790 bIncludeIt = true;
1793 if ( bIncludeIt )
1795 // Create & set the PropertyValue
1796 DAVPropertyValue thePropertyValue;
1797 thePropertyValue.IsCaseSensitive = false;
1798 thePropertyValue.Name = aHeaderName;
1800 if ( nPos < aHeader.getLength() )
1801 thePropertyValue.Value <<= aHeader.copy( nPos + 1 ).trim();
1803 // Add the newly created PropertyValue
1804 pCtx->pResource->properties.push_back( thePropertyValue );
1809 } // namespace
1811 int NeonSession::GET( ne_session * sess,
1812 const char * uri,
1813 ne_block_reader reader,
1814 bool getheaders,
1815 void * userdata )
1817 //struct get_context ctx;
1818 ne_request * req = ne_request_create( sess, "GET", uri );
1819 int ret;
1821 ne_decompress * dc
1822 = ne_decompress_reader( req, ne_accept_2xx, reader, userdata );
1825 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
1826 ret = ne_request_dispatch( req );
1829 if ( getheaders )
1831 void *cursor = NULL;
1832 const char *name, *value;
1833 while ( ( cursor = ne_response_header_iterate(
1834 req, cursor, &name, &value ) ) != NULL )
1836 char buffer[8192];
1838 ne_snprintf(buffer, sizeof buffer, "%s: %s", name, value);
1839 runResponseHeaderHandler(userdata, buffer);
1843 if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
1844 ret = NE_ERROR;
1846 if ( dc != 0 )
1847 ne_decompress_destroy(dc);
1849 ne_request_destroy( req );
1850 return ret;
1853 int NeonSession::PUT( ne_session * sess,
1854 const char * uri,
1855 const char * buffer,
1856 size_t size)
1858 ne_request * req = ne_request_create( sess, "PUT", uri );
1859 int ret;
1861 ne_lock_using_resource( req, uri, 0 );
1862 ne_lock_using_parent( req, uri );
1864 ne_set_request_body_buffer( req, buffer, size );
1867 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
1868 ret = ne_request_dispatch( req );
1871 if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
1872 ret = NE_ERROR;
1874 ne_request_destroy( req );
1875 return ret;
1878 int NeonSession::POST( ne_session * sess,
1879 const char * uri,
1880 const char * buffer,
1881 ne_block_reader reader,
1882 void * userdata,
1883 const OUString & rContentType,
1884 const OUString & rReferer )
1886 ne_request * req = ne_request_create( sess, "POST", uri );
1887 //struct get_context ctx;
1888 int ret;
1890 RequestDataMap * pData = 0;
1892 if ( !rContentType.isEmpty() || !rReferer.isEmpty() )
1894 // Remember contenttype and referer. Data will be added to HTTP request
1895 // header in in 'PreSendRequest' callback.
1896 pData = static_cast< RequestDataMap* >( m_pRequestData );
1897 (*pData)[ req ] = RequestData( rContentType, rReferer );
1900 //ctx.total = -1;
1901 //ctx.fd = fd;
1902 //ctx.error = 0;
1903 //ctx.session = sess;
1905 ///* Read the value of the Content-Length header into ctx.total */
1906 //ne_add_response_header_handler( req, "Content-Length",
1907 // ne_handle_numeric_header, &ctx.total );
1909 ne_add_response_body_reader( req, ne_accept_2xx, reader, userdata );
1911 ne_set_request_body_buffer( req, buffer, strlen( buffer ) );
1914 osl::Guard< osl::Mutex > theGlobalGuard( aGlobalNeonMutex );
1915 ret = ne_request_dispatch( req );
1918 //if ( ctx.error )
1919 // ret = NE_ERROR;
1920 //else
1921 if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
1922 ret = NE_ERROR;
1924 ne_request_destroy( req );
1926 if ( pData )
1928 // Remove request data from session's list.
1929 RequestDataMap::iterator it = pData->find( req );
1930 if ( it != pData->end() )
1931 pData->erase( it );
1934 return ret;
1937 bool
1938 NeonSession::getDataFromInputStream(
1939 const uno::Reference< io::XInputStream > & xStream,
1940 uno::Sequence< sal_Int8 > & rData,
1941 bool bAppendTrailingZeroByte )
1943 if ( xStream.is() )
1945 uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY );
1946 if ( xSeekable.is() )
1950 sal_Int32 nSize
1951 = sal::static_int_cast<sal_Int32>(xSeekable->getLength());
1952 sal_Int32 nRead
1953 = xStream->readBytes( rData, nSize );
1955 if ( nRead == nSize )
1957 if ( bAppendTrailingZeroByte )
1959 rData.realloc( nSize + 1 );
1960 rData[ nSize ] = sal_Int8( 0 );
1962 return true;
1965 catch ( io::NotConnectedException const & )
1967 // readBytes
1969 catch ( io::BufferSizeExceededException const & )
1971 // readBytes
1973 catch ( io::IOException const & )
1975 // getLength, readBytes
1978 else
1982 uno::Sequence< sal_Int8 > aBuffer;
1983 sal_Int32 nPos = 0;
1985 sal_Int32 nRead = xStream->readSomeBytes( aBuffer, 65536 );
1986 while ( nRead > 0 )
1988 if ( rData.getLength() < ( nPos + nRead ) )
1989 rData.realloc( nPos + nRead );
1991 aBuffer.realloc( nRead );
1992 memcpy( (void*)( rData.getArray() + nPos ),
1993 (const void*)aBuffer.getConstArray(),
1994 nRead );
1995 nPos += nRead;
1997 aBuffer.realloc( 0 );
1998 nRead = xStream->readSomeBytes( aBuffer, 65536 );
2001 if ( bAppendTrailingZeroByte )
2003 rData.realloc( nPos + 1 );
2004 rData[ nPos ] = sal_Int8( 0 );
2006 return true;
2008 catch ( io::NotConnectedException const & )
2010 // readBytes
2012 catch ( io::BufferSizeExceededException const & )
2014 // readBytes
2016 catch ( io::IOException const & )
2018 // readBytes
2022 return false;
2025 sal_Bool
2026 NeonSession::isDomainMatch( OUString certHostName )
2028 OUString hostName = getHostName();
2030 if (hostName.equalsIgnoreAsciiCase( certHostName ) )
2031 return sal_True;
2033 if ( certHostName.startsWith( "*" ) &&
2034 hostName.getLength() >= certHostName.getLength() )
2036 OUString cmpStr = certHostName.copy( 1 );
2038 if ( hostName.matchIgnoreAsciiCase(
2039 cmpStr, hostName.getLength() - cmpStr.getLength() ) )
2040 return sal_True;
2042 return sal_False;
2045 OUString NeonSession::makeAbsoluteURL( OUString const & rURL ) const
2049 // Is URL relative or already absolute?
2050 if ( rURL[ 0 ] != '/' )
2052 // absolute.
2053 return OUString( rURL );
2055 else
2057 ne_uri aUri;
2058 memset( &aUri, 0, sizeof( aUri ) );
2060 ne_fill_server_uri( m_pHttpSession, &aUri );
2061 aUri.path
2062 = ne_strdup( OUStringToOString(
2063 rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
2064 NeonUri aNeonUri( &aUri );
2065 ne_uri_free( &aUri );
2066 return aNeonUri.GetURI();
2069 catch ( DAVException const & )
2072 // error.
2073 return OUString();
2076 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */