LibreOffice Module ucb (master)  1
NeonSession.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * Copyright 2000, 2010 Oracle and/or its affiliates.
7  *
8  * OpenOffice.org - a multi-platform office productivity suite
9  *
10  * This file is part of OpenOffice.org.
11  *
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.
15  *
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).
21  *
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.
26  *
27  ************************************************************************/
28 
29 
30 #include <unordered_map>
31 #include <vector>
32 #include <string.h>
33 #include <sal/log.hxx>
34 #include <osl/diagnose.h>
35 #include <osl/thread.h>
36 #include <osl/time.h>
37 #include <ne_socket.h>
38 #include <ne_auth.h>
39 #include <ne_redirect.h>
40 #include <ne_ssl.h>
41 
42 // old neon versions forgot to set this
43 extern "C" {
44 #include <ne_compress.h>
45 }
46 
47 #include <libxml/parser.h>
48 #include <comphelper/sequence.hxx>
50 
51 #include "DAVAuthListener.hxx"
52 #include "NeonTypes.hxx"
53 #include "NeonSession.hxx"
54 #include "NeonInputStream.hxx"
55 #include "NeonPropFindRequest.hxx"
56 #include "NeonHeadRequest.hxx"
57 #include "NeonUri.hxx"
58 #include "LinkSequence.hxx"
59 #include "UCBDeadPropertyValue.hxx"
60 
61 #include <officecfg/Inet.hxx>
62 #include <com/sun/star/io/BufferSizeExceededException.hpp>
63 #include <com/sun/star/io/IOException.hpp>
64 #include <com/sun/star/io/NotConnectedException.hpp>
65 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
66 #include <com/sun/star/security/XCertificate.hpp>
67 #include <com/sun/star/security/CertificateValidity.hpp>
68 #include <com/sun/star/security/CertificateContainerStatus.hpp>
69 #include <com/sun/star/security/CertificateContainer.hpp>
70 #include <com/sun/star/security/XCertificateContainer.hpp>
71 #include <com/sun/star/ucb/Lock.hpp>
72 #include <com/sun/star/beans/NamedValue.hpp>
73 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
74 
75 
76 using namespace com::sun::star;
77 using namespace webdav_ucp;
78 
79 #ifndef EOL
80 # define EOL "\r\n"
81 #endif
82 
83 namespace {
84 
85 struct RequestData
86 {
87  // POST
88  OUString aContentType;
89  OUString aReferer;
90 
91  RequestData() {}
92  RequestData( const OUString & rContentType,
93  const OUString & rReferer )
94  : aContentType( rContentType ), aReferer( rReferer ) {}
95 };
96 
97 struct equalPtr
98 {
99  bool operator()( const ne_request* p1, const ne_request* p2 ) const
100  {
101  return p1 == p2;
102  }
103 };
104 
105 struct hashPtr
106 {
107  size_t operator()( const ne_request* p ) const
108  {
109  return reinterpret_cast<size_t>(p);
110  }
111 };
112 
113 }
114 
115 typedef std::unordered_map
116 <
117  ne_request*,
118  RequestData,
119  hashPtr,
120  equalPtr
121 >
123 
124 static sal_uInt16 makeStatusCode( const OUString & rStatusText )
125 {
126  // Extract status code from session error string. Unfortunately
127  // neon provides no direct access to the status code...
128 
129  if ( rStatusText.getLength() < 3 )
130  {
131  SAL_WARN( "ucb.ucp.webdav", "makeStatusCode - status text string to short!" );
132  return 0;
133  }
134 
135  sal_Int32 nPos = rStatusText.indexOf( ' ' );
136  if ( nPos == -1 )
137  {
138  SAL_WARN( "ucb.ucp.webdav", "makeStatusCode - wrong status text format!" );
139  return 0;
140  }
141 
142  return sal_uInt16( rStatusText.copy( 0, nPos ).toInt32() );
143 }
144 
145 static bool noKeepAlive( const uno::Sequence< beans::NamedValue >& rFlags )
146 {
147  if ( !rFlags.hasElements() )
148  return false;
149 
150  // find "KeepAlive" property
151  const beans::NamedValue* pValue(
152  std::find_if(rFlags.begin(), rFlags.end(),
153  [] (beans::NamedValue const& rNV) { return rNV.Name == "KeepAlive"; } ));
154  return pValue != rFlags.end() && !pValue->Value.get<bool>();
155 }
156 
157 namespace {
158 
159 struct NeonRequestContext
160 {
161  uno::Reference< io::XOutputStream > xOutputStream;
163  const std::vector< OUString > * pHeaderNames;
164  DAVResource * pResource;
165 
166  explicit NeonRequestContext( uno::Reference< io::XOutputStream > const & xOutStrm )
167  : xOutputStream( xOutStrm ),
168  pHeaderNames( nullptr ), pResource( nullptr ) {}
169 
170  explicit NeonRequestContext( const rtl::Reference< NeonInputStream > & xInStrm )
171  : xInputStream( xInStrm ),
172  pHeaderNames( nullptr ), pResource( nullptr ) {}
173 
174  NeonRequestContext( uno::Reference< io::XOutputStream > const & xOutStrm,
175  const std::vector< OUString > & inHeaderNames,
176  DAVResource & ioResource )
177  : xOutputStream( xOutStrm ),
178  pHeaderNames( &inHeaderNames ), pResource( &ioResource ) {}
179 
180  NeonRequestContext( const rtl::Reference< NeonInputStream > & xInStrm,
181  const std::vector< OUString > & inHeaderNames,
182  DAVResource & ioResource )
183  : xInputStream( xInStrm ),
184  pHeaderNames( &inHeaderNames ), pResource( &ioResource ) {}
185 
186  void ResponseBlockReader(const char * inBuf, size_t inLen)
187  {
188  if (xInputStream.is())
189  xInputStream->AddToStream( inBuf, inLen );
190  }
191 
192  void ResponseBlockWriter(const char * inBuf, size_t inLen)
193  {
194  if (xOutputStream.is())
195  {
196  const uno::Sequence< sal_Int8 > aSeq( reinterpret_cast<sal_Int8 const *>(inBuf), inLen );
197  xOutputStream->writeBytes( aSeq );
198  }
199  }
200 
201 };
202 
203 }
204 
205 // A simple Neon response_block_reader for use with an XInputStream
206 extern "C" {
207 
208 static int NeonSession_ResponseBlockReader(void * inUserData,
209  const char * inBuf,
210  size_t inLen )
211 {
212  // neon sometimes calls this function with (inLen == 0)...
213  if ( inLen > 0 )
214  {
215  NeonRequestContext * pCtx = static_cast<NeonRequestContext*>(inUserData);
216  pCtx->ResponseBlockReader(inBuf, inLen);
217  }
218  return 0;
219 }
220 
221 // A simple Neon response_block_reader for use with an XOutputStream
222 static int NeonSession_ResponseBlockWriter( void * inUserData,
223  const char * inBuf,
224  size_t inLen )
225 {
226  // neon calls this function with (inLen == 0)...
227  if ( inLen > 0 )
228  {
229  NeonRequestContext * pCtx = static_cast<NeonRequestContext*>(inUserData);
230  pCtx->ResponseBlockWriter(inBuf, inLen);
231  }
232  return 0;
233 }
234 
235 static int NeonSession_NeonAuth( void * inUserData,
236 #if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
237  const char * inAuthProtocol,
238 #endif
239  const char * inRealm,
240  int attempt,
241  char * inoutUserName,
242  char * inoutPassWord )
243 {
244 /* The callback used to request the username and password in the given
245  * realm. The username and password must be copied into the buffers
246  * which are both of size NE_ABUFSIZ. The 'attempt' parameter is zero
247  * on the first call to the callback, and increases by one each time
248  * an attempt to authenticate fails.
249  *
250  * The callback must return zero to indicate that authentication
251  * should be attempted with the username/password, or non-zero to
252  * cancel the request. (if non-zero, username and password are
253  * ignored.) */
254 
255  NeonSession * theSession = static_cast<NeonSession*>(inUserData);
256  const char * pAuthProtocol;
257 #if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
258  pAuthProtocol = inAuthProtocol;
259 #else
260  pAuthProtocol = nullptr;
261 #endif
262  return theSession->NeonAuth(pAuthProtocol, inRealm, attempt, inoutUserName, inoutPassWord);
263 }
264 
265 }
266 
267 int NeonSession::NeonAuth(const char* inAuthProtocol, const char* inRealm,
268  int attempt, char* inoutUserName, char * inoutPassWord)
269 {
270  osl::Guard< osl::Mutex > theGuard( m_aMutex );
271 
272 /* The callback used to request the username and password in the given
273  * realm. The username and password must be copied into the buffers
274  * which are both of size NE_ABUFSIZ. The 'attempt' parameter is zero
275  * on the first call to the callback, and increases by one each time
276  * an attempt to authenticate fails.
277  *
278  * The callback must return zero to indicate that authentication
279  * should be attempted with the username/password, or non-zero to
280  * cancel the request. (if non-zero, username and password are
281  * ignored.) */
282 
283  DAVAuthListener * pListener
284  = getRequestEnvironment().m_xAuthListener.get();
285  if ( !pListener )
286  {
287  // abort
288  return -1;
289  }
290  OUString theUserName;
291  OUString thePassWord;
292 
293  if ( attempt == 0 )
294  {
295  // neon does not handle username supplied with request URI (for
296  // instance when doing FTP over proxy - last checked: 0.23.5 )
297 
298  try
299  {
300  NeonUri uri( getRequestEnvironment().m_aRequestURI );
301  const OUString& aUserInfo( uri.GetUserInfo() );
302  if ( !aUserInfo.isEmpty() )
303  {
304  sal_Int32 nPos = aUserInfo.indexOf( '@' );
305  if ( nPos == -1 )
306  {
307  theUserName = aUserInfo;
308  }
309  else
310  {
311  theUserName = aUserInfo.copy( 0, nPos );
312  thePassWord = aUserInfo.copy( nPos + 1 );
313  }
314  }
315  }
316  catch ( DAVException const & )
317  {
318  // abort
319  return -1;
320  }
321  }
322  else
323  {
324  // username buffer is prefilled with user name from last attempt.
325  theUserName = OUString::createFromAscii( inoutUserName );
326  // @@@ Neon does not initialize password buffer (last checked: 0.22.0).
327  //thePassWord = OUString::createFromAscii( inoutPassWord );
328  }
329 
330 #if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
331  const bool bCanUseSystemCreds
332  = (attempt == 0) && // avoid endless loops
333  ne_has_support( NE_FEATURE_SSPI ) && // Windows-only feature.
334  ( ( ne_strcasecmp( inAuthProtocol, "NTLM" ) == 0 ) ||
335  ( ne_strcasecmp( inAuthProtocol, "Negotiate" ) == 0 ) );
336 #else
337  (void)inAuthProtocol;
338  const bool bCanUseSystemCreds = false;
339 #endif
340 
341  int theRetVal = pListener->authenticate(
342  OUString::createFromAscii( inRealm ),
343  getHostName(),
344  theUserName,
345  thePassWord,
346  bCanUseSystemCreds);
347 
348  OString aUser( OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ) );
349  if ( aUser.getLength() > ( NE_ABUFSIZ - 1 ) )
350  {
351  SAL_WARN( "ucb.ucp.webdav", "NeonSession_NeonAuth - username too long!" );
352  return -1;
353  }
354 
355  OString aPass( OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ) );
356  if ( aPass.getLength() > ( NE_ABUFSIZ - 1 ) )
357  {
358  SAL_WARN( "ucb.ucp.webdav", "NeonSession_NeonAuth - password too long!" );
359  return -1;
360  }
361 
362  // #100211# - checked
363  strcpy( inoutUserName, aUser.getStr() );
364  strcpy( inoutPassWord, aPass.getStr() );
365 
366  return theRetVal;
367 }
368 
369 namespace {
370  OUString GetHostnamePart( const OUString& _rRawString )
371  {
372  OUString sPart;
373  OUString sPartId("CN=");
374  sal_Int32 nContStart = _rRawString.indexOf( sPartId );
375  if ( nContStart != -1 )
376  {
377  nContStart += sPartId.getLength();
378  sal_Int32 nContEnd = _rRawString.indexOf( ',', nContStart );
379  sPart = nContEnd != -1 ?
380  _rRawString.copy(nContStart, nContEnd - nContStart) :
381  _rRawString.copy(nContStart);
382  }
383  return sPart;
384  }
385 } // namespace
386 
387 extern "C" {
388 
389 static int NeonSession_CertificationNotify( void *userdata,
390  int,
391  const ne_ssl_certificate *cert )
392 {
393  NeonSession * pSession = static_cast< NeonSession * >( userdata );
394  return pSession->CertificationNotify(cert);
395 }
396 
397 }
398 
399 int NeonSession::CertificationNotify(const ne_ssl_certificate *cert)
400 {
401  osl::Guard< osl::Mutex > theGuard( m_aMutex );
402 
403  OSL_ASSERT( cert );
404 
405  uno::Reference< security::XCertificateContainer > xCertificateContainer;
406  try
407  {
408  xCertificateContainer = security::CertificateContainer::create( getComponentContext() );
409  }
410  catch ( uno::Exception const & )
411  {
412  }
413 
414  if ( !xCertificateContainer.is() )
415  return 1;
416 
417  char * dn = ne_ssl_readable_dname( ne_ssl_cert_subject( cert ) );
418  OUString cert_subject( dn, strlen( dn ), RTL_TEXTENCODING_UTF8, 0 );
419 
420  ne_free( dn );
421 
422  security::CertificateContainerStatus certificateContainer(
423  xCertificateContainer->hasCertificate(
424  getHostName(), cert_subject ) );
425 
426  if ( certificateContainer != security::CertificateContainerStatus_NOCERT )
427  return
428  certificateContainer == security::CertificateContainerStatus_TRUSTED
429  ? 0
430  : 1;
431 
432  uno::Reference< xml::crypto::XSEInitializer > xSEInitializer;
433  try
434  {
435  xSEInitializer = xml::crypto::SEInitializer::create( getComponentContext() );
436  }
437  catch ( uno::Exception const & )
438  {
439  }
440 
441  if ( !xSEInitializer.is() )
442  return 1;
443 
444  uno::Reference< xml::crypto::XXMLSecurityContext > xSecurityContext(
445  xSEInitializer->createSecurityContext( OUString() ) );
446 
447  uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnv(
448  xSecurityContext->getSecurityEnvironment() );
449 
450  //The end entity certificate
451  char * eeCertB64 = ne_ssl_cert_export( cert );
452 
453  OString sEECertB64( eeCertB64 );
454 
455  uno::Reference< security::XCertificate > xEECert(
456  xSecurityEnv->createCertificateFromAscii(
457  OStringToOUString( sEECertB64, RTL_TEXTENCODING_ASCII_US ) ) );
458 
459  ne_free( eeCertB64 );
460  eeCertB64 = nullptr;
461 
462  std::vector< uno::Reference< security::XCertificate > > vecCerts;
463  const ne_ssl_certificate * issuerCert = cert;
464  do
465  {
466  //get the intermediate certificate
467  //the returned value is const ! Therefore it does not need to be freed
468  //with ne_ssl_cert_free, which takes a non-const argument
469  issuerCert = ne_ssl_cert_signedby( issuerCert );
470  if ( nullptr == issuerCert )
471  break;
472 
473  char * imCertB64 = ne_ssl_cert_export( issuerCert );
474  OString sInterMediateCertB64( imCertB64 );
475  ne_free( imCertB64 );
476 
477  uno::Reference< security::XCertificate> xImCert(
478  xSecurityEnv->createCertificateFromAscii(
479  OStringToOUString( sInterMediateCertB64, RTL_TEXTENCODING_ASCII_US ) ) );
480  if ( xImCert.is() )
481  vecCerts.push_back( xImCert );
482  }
483  while ( true );
484 
485  sal_Int64 certValidity = xSecurityEnv->verifyCertificate( xEECert,
486  ::comphelper::containerToSequence( vecCerts ) );
487 
488  if ( isDomainMatch(
489  GetHostnamePart( xEECert->getSubjectName() ) ) )
490  {
491  // if host name matched with certificate then look if the
492  // certificate was ok
493  if( certValidity == security::CertificateValidity::VALID )
494  return 0;
495  }
496 
497  const uno::Reference< ucb::XCommandEnvironment > xEnv(
498  getRequestEnvironment().m_xEnv );
499  if ( xEnv.is() )
500  {
501  uno::Reference< task::XInteractionHandler > xIH(
502  xEnv->getInteractionHandler() );
503  if ( xIH.is() )
504  {
507  static_cast<sal_Int32>(certValidity), xEECert, getHostName() ) );
508  xIH->handle( xRequest.get() );
509 
511  = xRequest->getSelection();
512 
513  if ( xSelection.is() )
514  {
515  uno::Reference< task::XInteractionApprove > xApprove(
516  xSelection.get(), uno::UNO_QUERY );
517  if ( xApprove.is() )
518  {
519  xCertificateContainer->addCertificate(
520  getHostName(), cert_subject, true );
521  return 0;
522  }
523  else
524  {
525  // Don't trust cert
526  xCertificateContainer->addCertificate(
527  getHostName(), cert_subject, false );
528  return 1;
529  }
530  }
531  }
532  else
533  {
534  // Don't trust cert
535  xCertificateContainer->addCertificate(
536  getHostName(), cert_subject, false );
537  return 1;
538  }
539  }
540  return 1;
541 }
542 
543 extern "C" {
544 
545 static void NeonSession_PreSendRequest( ne_request * req,
546  void * userdata,
547  ne_buffer * headers )
548 {
549  // userdata -> value returned by 'create'
550  NeonSession * pSession = static_cast< NeonSession * >( userdata );
551  if (!pSession)
552  return;
553  pSession->PreSendRequest(req, headers);
554 }
555 
556 }
557 
558 void NeonSession::PreSendRequest(ne_request* req, ne_buffer* headers)
559 {
560  osl::Guard< osl::Mutex > theGuard( m_aMutex );
561 
562  // If there is a proxy server in between, it shall never use
563  // cached data. We always want 'up-to-date' data.
564  ne_buffer_concat( headers, "Pragma: no-cache", EOL, nullptr );
565  // alternative, but understood by HTTP 1.1 servers only:
566  // ne_buffer_concat( headers, "Cache-Control: max-age=0", EOL, NULL );
567 
568  const RequestDataMap * pRequestData
569  = static_cast< const RequestDataMap* >(
570  getRequestData() );
571 
572  RequestDataMap::const_iterator it = pRequestData->find( req );
573  if ( it != pRequestData->end() )
574  {
575  if ( !(*it).second.aContentType.isEmpty() )
576  {
577  char * pData = headers->data;
578  if ( strstr( pData, "Content-Type:" ) == nullptr )
579  {
580  OString aType
581  = OUStringToOString( (*it).second.aContentType,
582  RTL_TEXTENCODING_UTF8 );
583  ne_buffer_concat( headers, "Content-Type: ",
584  aType.getStr(), EOL, nullptr );
585  }
586  }
587 
588  if ( !(*it).second.aReferer.isEmpty() )
589  {
590  char * pData = headers->data;
591  if ( strstr( pData, "Referer:" ) == nullptr )
592  {
593  OString aReferer
594  = OUStringToOString( (*it).second.aReferer,
595  RTL_TEXTENCODING_UTF8 );
596  ne_buffer_concat( headers, "Referer: ",
597  aReferer.getStr(), EOL, nullptr );
598  }
599  }
600  }
601 
602  const DAVRequestHeaders & rHeaders
603  = getRequestEnvironment().m_aRequestHeaders;
604 
605  for ( const auto& rHeader : rHeaders )
606  {
607  OString aHeader
608  = OUStringToOString( rHeader.first,
609  RTL_TEXTENCODING_UTF8 );
610  OString aValue
611  = OUStringToOString( rHeader.second,
612  RTL_TEXTENCODING_UTF8 );
613  ne_buffer_concat( headers, aHeader.getStr(), ": ",
614  aValue.getStr(), EOL, nullptr );
615  }
616 }
617 
618 //See https://bugzilla.redhat.com/show_bug.cgi?id=544619#c4
619 //neon is threadsafe, but uses gnutls which is only thread-safe
620 //if initialized to be thread-safe. cups, unfortunately, generally
621 //initializes it first, and as non-thread-safe, leaving the entire
622 //stack unsafe
623 namespace webdav_ucp
624 {
625  osl::Mutex& getGlobalNeonMutex()
626  {
627  static osl::Mutex aMutex;
628  return aMutex;
629  }
630 }
631 
632 // static members
633 bool NeonSession::m_bGlobalsInited = false;
634 NeonLockStore NeonSession::m_aNeonLockStore;
635 
636 NeonSession::NeonSession( const rtl::Reference< DAVSessionFactory > & rSessionFactory,
637  const OUString& inUri,
638  const uno::Sequence< beans::NamedValue >& rFlags,
639  const ucbhelper::InternetProxyDecider & rProxyDecider )
640  : DAVSession( rSessionFactory )
641  , m_nProxyPort( 0 )
642  , m_aFlags( rFlags )
643  , m_pHttpSession( nullptr )
644  , m_pRequestData( new RequestDataMap )
645  , m_rProxyDecider( rProxyDecider )
646 {
647  NeonUri theUri( inUri );
648  m_aScheme = theUri.GetScheme();
649  m_aHostName = theUri.GetHost();
650  m_nPort = theUri.GetPort();
651  SAL_INFO( "ucb.ucp.webdav", "NeonSession ctor - URL <" << inUri << ">" );
652 }
653 
655 {
656  if ( m_pHttpSession )
657  {
658  {
659  osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
660  ne_session_destroy( m_pHttpSession );
661  }
662  m_pHttpSession = nullptr;
663  }
664  delete static_cast< RequestDataMap * >( m_pRequestData );
665 }
666 
668 {
669  osl::Guard< osl::Mutex > theGuard( m_aMutex );
670  m_aEnv = rEnv;
671  Init();
672 }
673 
675 {
676  osl::Guard< osl::Mutex > theGuard( m_aMutex );
677 
678  bool bCreateNewSession = m_bNeedNewSession;
679  m_bNeedNewSession = false;
680 
681  if ( m_pHttpSession == nullptr )
682  {
683  // Ensure that Neon sockets are initialized
684  osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
685  if (!m_bGlobalsInited )
686  {
687  if ( ne_sock_init() != 0 )
690  m_aHostName, m_nPort ) );
691 
692  // #122205# - libxml2 needs to be initialized once if used by
693  // multithreaded programs like OOo.
694  xmlInitParser();
695 #if OSL_DEBUG_LEVEL > 0
696  // for more debug flags see ne_utils.h; NE_DEBUGGING must be defined
697  // while compiling neon in order to actually activate neon debug
698  // output.
699  ne_debug_init( stderr, NE_DBG_FLUSH
700  | NE_DBG_HTTP
701  // | NE_DBG_HTTPBODY
702  // | NE_DBG_HTTPAUTH
703  // | NE_DBG_XML
704  // | NE_DBG_XMLPARSE
705  | NE_DBG_LOCKS
706  | NE_DBG_SSL
707  );
708 #endif
709  m_bGlobalsInited = true;
710  }
711 
712  const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
713 
714  m_aProxyName = rProxyCfg.aName;
715  m_nProxyPort = rProxyCfg.nPort;
716 
717  // Not yet initialized. Create new session.
718  bCreateNewSession = true;
719  }
720  else
721  {
722  // #112271# Check whether proxy settings are still valid (They may
723  // change at any time). If not, create new Neon session.
724 
725  const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
726 
727  if ( ( rProxyCfg.aName != m_aProxyName )
728  || ( rProxyCfg.nPort != m_nProxyPort ) )
729  {
730  m_aProxyName = rProxyCfg.aName;
731  m_nProxyPort = rProxyCfg.nPort;
732 
733  bCreateNewSession = true;
734  }
735 
736  if (bCreateNewSession)
737  {
738  // new session needed, destroy old first
739  {
740  osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
741  ne_session_destroy( m_pHttpSession );
742  }
743  m_pHttpSession = nullptr;
744  }
745  }
746 
747  if ( !bCreateNewSession )
748  return;
749 
750  const sal_Int32 nConnectTimeoutMax = 180;
751  const sal_Int32 nConnectTimeoutMin = 2;
752  const sal_Int32 nReadTimeoutMax = 180;
753  const sal_Int32 nReadTimeoutMin = 20;
754 
755  // @@@ For FTP over HTTP proxy inUserInfo is needed to be able to
756  // build the complete request URI (including user:pass), but
757  // currently (0.22.0) neon does not allow to pass the user info
758  // to the session
759 
760  {
761  osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
762  m_pHttpSession = ne_session_create(
763  OUStringToOString( m_aScheme, RTL_TEXTENCODING_UTF8 ).getStr(),
764  /* theUri.GetUserInfo(),
765  @@@ for FTP via HTTP proxy, but not supported by Neon */
766  OUStringToOString( m_aHostName, RTL_TEXTENCODING_UTF8 ).getStr(),
767  m_nPort );
768  }
769 
770  if ( m_pHttpSession == nullptr )
773  m_aHostName, m_nPort ) );
774 
775  // Register the session with the lock store
777 
778  if ( m_aScheme.equalsIgnoreAsciiCase("https") )
779  {
780  // Set a failure callback for certificate check
781  ne_ssl_set_verify(
783 
784  // Tell Neon to tell the SSL library used (OpenSSL or
785  // GnuTLS, I guess) to use a default set of root
786  // certificates.
787  ne_ssl_trust_default_ca(m_pHttpSession);
788  }
789 
790  // Add hooks (i.e. for adding additional headers to the request)
791  ne_hook_pre_send( m_pHttpSession, NeonSession_PreSendRequest, this );
792 
793  if ( !m_aProxyName.isEmpty() )
794  {
795  ne_session_proxy( m_pHttpSession,
797  m_aProxyName,
798  RTL_TEXTENCODING_UTF8 ).getStr(),
799  m_nProxyPort );
800  }
801 
802  // avoid KeepAlive?
803  if ( noKeepAlive(m_aFlags) )
804  ne_set_session_flag( m_pHttpSession, NE_SESSFLAG_PERSIST, 0 );
805 
806  // Register for redirects.
807  ne_redirect_register( m_pHttpSession );
808 
809  // authentication callbacks.
810  ne_add_server_auth( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
811  ne_add_proxy_auth ( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
812 
813  // set timeout to connect
814  // if connect_timeout is not set, neon returns NE_CONNECT when the TCP socket default
815  // timeout elapses
816  // with connect_timeout set neon returns NE_TIMEOUT if elapsed when the connection
817  // didn't succeed
818  // grab it from configuration
819  uno::Reference< uno::XComponentContext > rContext = m_xFactory->getComponentContext();
820 
821  // set the timeout (in seconds) used when making a connection
822  sal_Int32 nConnectTimeout = officecfg::Inet::Settings::ConnectTimeout::get( rContext );
823  ne_set_connect_timeout( m_pHttpSession,
824  std::max( nConnectTimeoutMin,
825  std::min( nConnectTimeout, nConnectTimeoutMax ) ) );
826 
827  // provides a read time out facility as well
828  // set the timeout (in seconds) used when reading from a socket.
829  sal_Int32 nReadTimeout = officecfg::Inet::Settings::ReadTimeout::get( rContext );
830  ne_set_read_timeout( m_pHttpSession,
831  std::max( nReadTimeoutMin,
832  std::min( nReadTimeout, nReadTimeoutMax ) ) );
833 
834  ne_set_session_flag(m_pHttpSession, NE_SESSFLAG_SHAREPOINT, 1);
835 }
836 
837 bool NeonSession::CanUse( const OUString & inUri,
838  const uno::Sequence< beans::NamedValue >& rFlags )
839 {
840  try
841  {
842  NeonUri theUri( inUri );
843  if ( ( theUri.GetPort() == m_nPort ) &&
844  ( theUri.GetHost() == m_aHostName ) &&
845  ( theUri.GetScheme() == m_aScheme ) &&
846  ( rFlags == m_aFlags ) )
847  return true;
848  }
849  catch ( DAVException const & )
850  {
851  return false;
852  }
853  return false;
854 }
855 
857 {
858  Init();
859  return !m_aProxyName.isEmpty() ;
860 }
861 
862 void NeonSession::OPTIONS( const OUString & inPath,
863  DAVOptions & rOptions, // contains the name+values of every header
864  const DAVRequestEnvironment & rEnv )
865 {
866  osl::Guard< osl::Mutex > theGuard( m_aMutex );
867 
868  SAL_INFO( "ucb.ucp.webdav", "OPTIONS - relative URL <" << inPath << ">" );
869 
870  rOptions.init();
871 
872  Init( rEnv );
873  int theRetVal;
874 
875  ne_request *req = ne_request_create(m_pHttpSession, "OPTIONS", OUStringToOString(
876  inPath, RTL_TEXTENCODING_UTF8 ).getStr());
877  {
878  osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
879  theRetVal = ne_request_dispatch(req);
880  }
881 
882  //check if http error is in the 200 class (no error)
883  if (theRetVal == NE_OK && ne_get_status(req)->klass != 2) {
884  theRetVal = NE_ERROR;
885  }
886 
887  if ( theRetVal == NE_OK )
888  {
889  void *cursor = nullptr;
890  const char *name, *value;
891  while ( ( cursor = ne_response_header_iterate(
892  req, cursor, &name, &value ) ) != nullptr )
893  {
894  OUString aHeaderName(OUString(name, strlen(name), RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase());
895  OUString aHeaderValue(value, strlen(value), RTL_TEXTENCODING_ASCII_US);
896 
897  // display the single header
898  SAL_INFO( "ucb.ucp.webdav", "OPTIONS - received header: " << aHeaderName << ":" << aHeaderValue );
899 
900  if ( aHeaderName == "allow" )
901  {
902  rOptions.setAllowedMethods( aHeaderValue );
903  }
904  else if ( aHeaderName == "dav" )
905  {
906  // check type of dav capability
907  // need to parse the value, token separator: ","
908  // see <http://tools.ietf.org/html/rfc4918#section-10.1>,
909  // <http://tools.ietf.org/html/rfc4918#section-18>,
910  // and <http://tools.ietf.org/html/rfc7230#section-3.2>
911  // we detect the class (1, 2 and 3), other elements (token, URL)
912  // are not used for now
913  // silly parser written using OUString, not very efficient
914  // but quick and easy to write...
915  sal_Int32 nFromIndex = 0;
916  sal_Int32 nNextIndex = 0;
917  while( ( nNextIndex = aHeaderValue.indexOf( ",", nFromIndex ) ) != -1 )
918  { // found a comma
919  // try to convert from nFromIndex to nNextIndex -1 in a number
920  // if this is 1 or 2 or 3, use for class setting
921  sal_Int32 nClass =
922  aHeaderValue.copy( nFromIndex, nNextIndex - nFromIndex ).toInt32();
923  switch( nClass )
924  {
925  case 1:
926  rOptions.setClass1();
927  break;
928  case 2:
929  rOptions.setClass2();
930  break;
931  case 3:
932  rOptions.setClass3();
933  break;
934  default:
935  ;
936  }
937  // next starting point
938  nFromIndex = nNextIndex + 1;
939  }
940  // check for last fragment
941  if ( nFromIndex < aHeaderValue.getLength() )
942  {
943  sal_Int32 nClass = aHeaderValue.copy( nFromIndex ).toInt32();
944  switch( nClass )
945  {
946  case 1:
947  rOptions.setClass1();
948  break;
949  case 2:
950  rOptions.setClass2();
951  break;
952  case 3:
953  rOptions.setClass3();
954  break;
955  default:
956  ;
957  }
958  }
959  }
960  }
961  // if applicable, check for lock state:
962  if( rOptions.isClass2() || rOptions.isClass3() )
963  {
964  //dav with lock possible, check for locked state
966  makeAbsoluteURL( inPath ) ) != nullptr )
967  {
968  // we own a lock for this URL,
969  // set locked state
970  rOptions.setLocked();
971  }
972  }
973  }
974 
975  ne_request_destroy(req);
976 
977  HandleError( theRetVal, inPath, rEnv );
978 }
979 
980 void NeonSession::PROPFIND( const OUString & inPath,
981  const Depth inDepth,
982  const std::vector< OUString > & inPropNames,
983  std::vector< DAVResource > & ioResources,
984  const DAVRequestEnvironment & rEnv )
985 {
986 
987  osl::Guard< osl::Mutex > theGuard( m_aMutex );
988 
989 #if defined SAL_LOG_INFO
990  { //debug
991  SAL_INFO( "ucb.ucp.webdav", "PROPFIND - relative URL: <" << inPath << "> Depth: " << inDepth );
992  for(const auto& rPropName : inPropNames)
993  {
994  SAL_INFO( "ucb.ucp.webdav", "PROPFIND - property requested: " << rPropName );
995  }
996  } //debug
997 #endif
998 
999  Init( rEnv );
1000 
1001  int theRetVal = NE_OK;
1002  NeonPropFindRequest theRequest( m_pHttpSession,
1004  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1005  inDepth,
1006  inPropNames,
1007  ioResources,
1008  theRetVal );
1009 
1010  HandleError( theRetVal, inPath, rEnv );
1011 }
1012 
1013 void NeonSession::PROPFIND( const OUString & inPath,
1014  const Depth inDepth,
1015  std::vector< DAVResourceInfo > & ioResInfo,
1016  const DAVRequestEnvironment & rEnv )
1017 {
1018  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1019  SAL_INFO( "ucb.ucp.webdav", "PROPFIND - relative URL: <" << inPath << "> Depth: " << inDepth );
1020 
1021  Init( rEnv );
1022 
1023  int theRetVal = NE_OK;
1024  NeonPropFindRequest theRequest( m_pHttpSession,
1026  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1027  inDepth,
1028  ioResInfo,
1029  theRetVal );
1030 
1031 #if defined SAL_LOG_INFO
1032  { //debug
1033  for ( const auto& rResInfo : ioResInfo )
1034  {
1035  for ( const auto& rProp : rResInfo.properties )
1036  {
1037  SAL_INFO( "ucb.ucp.webdav", "PROPFIND - returned property (name only): " << rProp );
1038  }
1039  }
1040  } //debug
1041 #endif
1042 
1043  HandleError( theRetVal, inPath, rEnv );
1044 }
1045 
1046 void NeonSession::PROPPATCH( const OUString & inPath,
1047  const std::vector< ProppatchValue > & inValues,
1048  const DAVRequestEnvironment & rEnv )
1049 {
1050  SAL_INFO( "ucb.ucp.webdav", "PROPPATCH - relative URL <" << inPath << ">" );
1051 
1052  /* @@@ Which standard live properties can be set by the client?
1053  This is a known WebDAV RFC issue ( verified: 04/10/2001 )
1054  --> http://www.ics.uci.edu/pub/ietf/webdav/protocol/issues.html
1055 
1056  mod_dav implementation:
1057 
1058  creationdate r ( File System prop )
1059  displayname w
1060  getcontentlanguage r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
1061  getcontentlength r ( File System prop )
1062  getcontenttype r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
1063  getetag r ( File System prop )
1064  getlastmodified r ( File System prop )
1065  lockdiscovery r
1066  resourcetype r
1067  source w
1068  supportedlock r
1069  executable w ( #ifndef WIN32 )
1070 
1071  All dead properties are of course writable.
1072  */
1073 
1074  int theRetVal = NE_OK;
1075 
1076  int n; // for the "for" loop
1077 
1078  // Generate the list of properties we want to set.
1079  int nPropCount = inValues.size();
1080  std::unique_ptr<ne_proppatch_operation[]> pItems(
1081  new ne_proppatch_operation[ nPropCount + 1 ]);
1082  for ( n = 0; n < nPropCount; ++n )
1083  {
1084  const ProppatchValue & rValue = inValues[ n ];
1085 
1086  // Split fullname into namespace and name!
1087  ne_propname * pName = new ne_propname;
1088  DAVProperties::createNeonPropName( rValue.name, *pName );
1089  pItems[ n ].name = pName;
1090 
1091  if ( rValue.operation == PROPSET )
1092  {
1093  pItems[ n ].type = ne_propset;
1094 
1095  OUString aStringValue;
1096  if ( DAVProperties::isUCBDeadProperty( *pName ) )
1097  {
1098  // DAV dead property added by WebDAV UCP?
1099  if ( !UCBDeadPropertyValue::toXML( rValue.value,
1100  aStringValue ) )
1101  {
1102  // Error!
1103  pItems[ n ].value = nullptr;
1104  theRetVal = NE_ERROR;
1105  nPropCount = n + 1;
1106  break;
1107  }
1108  }
1109  else if ( !( rValue.value >>= aStringValue ) )
1110  {
1111  // complex properties...
1112  if ( rValue.name == DAVProperties::SOURCE )
1113  {
1114  uno::Sequence< ucb::Link > aLinks;
1115  if ( rValue.value >>= aLinks )
1116  {
1117  LinkSequence::toXML( aLinks, aStringValue );
1118  }
1119  else
1120  {
1121  // Error!
1122  pItems[ n ].value = nullptr;
1123  theRetVal = NE_ERROR;
1124  nPropCount = n + 1;
1125  break;
1126  }
1127  }
1128  else
1129  {
1130  SAL_WARN( "ucb.ucp.webdav", "PROPPATCH - Unsupported type!" );
1131  // Error!
1132  pItems[ n ].value = nullptr;
1133  theRetVal = NE_ERROR;
1134  nPropCount = n + 1;
1135  break;
1136  }
1137  }
1138  pItems[ n ].value
1139  = strdup( OUStringToOString( aStringValue,
1140  RTL_TEXTENCODING_UTF8 ).getStr() );
1141  }
1142  else
1143  {
1144  pItems[ n ].type = ne_propremove;
1145  pItems[ n ].value = nullptr;
1146  }
1147  }
1148 
1149  if ( theRetVal == NE_OK )
1150  {
1151  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1152 
1153  Init( rEnv );
1154 
1155  pItems[ n ].name = nullptr;
1156 
1157  theRetVal = ne_proppatch( m_pHttpSession,
1159  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1160  pItems.get() );
1161  }
1162 
1163  for ( n = 0; n < nPropCount; ++n )
1164  {
1165  free( const_cast<char *>(pItems[ n ].name->name) );
1166  delete pItems[ n ].name;
1167  free( const_cast<char *>(pItems[ n ].value) );
1168  }
1169 
1170  HandleError( theRetVal, inPath, rEnv );
1171 }
1172 
1173 void NeonSession::HEAD( const OUString & inPath,
1174  const std::vector< OUString > & inHeaderNames,
1175  DAVResource & ioResource,
1176  const DAVRequestEnvironment & rEnv )
1177 {
1178  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1179  SAL_INFO( "ucb.ucp.webdav", "HEAD - relative URL <" << inPath << ">" );
1180 
1181  Init( rEnv );
1182 
1183  int theRetVal = NE_OK;
1184  NeonHeadRequest theRequest( m_pHttpSession,
1185  inPath,
1186  inHeaderNames,
1187  ioResource,
1188  theRetVal );
1189 
1190  HandleError( theRetVal, inPath, rEnv );
1191 }
1192 
1193 uno::Reference< io::XInputStream >
1194 NeonSession::GET( const OUString & inPath,
1195  const DAVRequestEnvironment & rEnv )
1196 {
1197  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1198  SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
1199 
1200  Init( rEnv );
1201 
1203  NeonRequestContext aCtx( xInputStream );
1204  int theRetVal = GET( m_pHttpSession,
1206  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1208  false,
1209  &aCtx );
1210 
1211  HandleError( theRetVal, inPath, rEnv );
1212 
1213  return uno::Reference< io::XInputStream >( xInputStream.get() );
1214 }
1215 
1216 void NeonSession::GET( const OUString & inPath,
1217  uno::Reference< io::XOutputStream > & ioOutputStream,
1218  const DAVRequestEnvironment & rEnv )
1219 {
1220  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1221  SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
1222 
1223  Init( rEnv );
1224 
1225  NeonRequestContext aCtx( ioOutputStream );
1226  int theRetVal = GET( m_pHttpSession,
1228  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1230  false,
1231  &aCtx );
1232 
1233  HandleError( theRetVal, inPath, rEnv );
1234 }
1235 
1236 uno::Reference< io::XInputStream >
1237 NeonSession::GET( const OUString & inPath,
1238  const std::vector< OUString > & inHeaderNames,
1239  DAVResource & ioResource,
1240  const DAVRequestEnvironment & rEnv )
1241 {
1242  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1243  SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
1244 
1245  Init( rEnv );
1246 
1247  ioResource.uri = inPath;
1248  ioResource.properties.clear();
1249 
1251  NeonRequestContext aCtx( xInputStream, inHeaderNames, ioResource );
1252  int theRetVal = GET( m_pHttpSession,
1254  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1256  true,
1257  &aCtx );
1258 
1259  HandleError( theRetVal, inPath, rEnv );
1260 
1261  return uno::Reference< io::XInputStream >( xInputStream.get() );
1262 }
1263 
1264 void NeonSession::GET0( const OUString & inPath,
1265  const std::vector< OUString > & inHeaderNames,
1266  DAVResource & ioResource,
1267  const DAVRequestEnvironment & rEnv )
1268 {
1269  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1270  SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
1271 
1272  Init( rEnv );
1273 
1274  ioResource.uri = inPath;
1275  ioResource.properties.clear();
1276 
1278  NeonRequestContext aCtx( xInputStream, inHeaderNames, ioResource );
1279  int theRetVal = GET0( m_pHttpSession,
1281  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1282  true,
1283  &aCtx );
1284 
1285  HandleError( theRetVal, inPath, rEnv );
1286 }
1287 
1288 void NeonSession::GET( const OUString & inPath,
1289  uno::Reference< io::XOutputStream > & ioOutputStream,
1290  const std::vector< OUString > & inHeaderNames,
1291  DAVResource & ioResource,
1292  const DAVRequestEnvironment & rEnv )
1293 {
1294  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1295  SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
1296 
1297  Init( rEnv );
1298 
1299  ioResource.uri = inPath;
1300  ioResource.properties.clear();
1301 
1302  NeonRequestContext aCtx( ioOutputStream, inHeaderNames, ioResource );
1303  int theRetVal = GET( m_pHttpSession,
1305  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1307  true,
1308  &aCtx );
1309 
1310  HandleError( theRetVal, inPath, rEnv );
1311 }
1312 
1313 void NeonSession::PUT( const OUString & inPath,
1314  const uno::Reference< io::XInputStream > & inInputStream,
1315  const DAVRequestEnvironment & rEnv )
1316 {
1317  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1318  SAL_INFO( "ucb.ucp.webdav", "PUT - relative URL <" << inPath << ">" );
1319 
1320  uno::Sequence< sal_Int8 > aDataToSend;
1321  if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
1323 
1324  Init( rEnv );
1325 
1326  int theRetVal = PUT( m_pHttpSession,
1328  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1329  reinterpret_cast< const char * >(
1330  aDataToSend.getConstArray() ),
1331  aDataToSend.getLength() );
1332 
1333  HandleError( theRetVal, inPath, rEnv );
1334 }
1335 
1336 uno::Reference< io::XInputStream >
1337 NeonSession::POST( const OUString & inPath,
1338  const OUString & rContentType,
1339  const OUString & rReferer,
1340  const uno::Reference< io::XInputStream > & inInputStream,
1341  const DAVRequestEnvironment & rEnv )
1342 {
1343  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1344  SAL_INFO( "ucb.ucp.webdav", "POST - relative URL <" << inPath << ">" );
1345 
1346  uno::Sequence< sal_Int8 > aDataToSend;
1347  if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
1349 
1350  Init( rEnv );
1351 
1353  NeonRequestContext aCtx( xInputStream );
1354  int theRetVal = POST( m_pHttpSession,
1356  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1357  reinterpret_cast< const char * >(
1358  aDataToSend.getConstArray() ),
1360  &aCtx,
1361  rContentType,
1362  rReferer );
1363 
1364  HandleError( theRetVal, inPath, rEnv );
1365 
1366  return uno::Reference< io::XInputStream >( xInputStream.get() );
1367 }
1368 
1369 void NeonSession::POST( const OUString & inPath,
1370  const OUString & rContentType,
1371  const OUString & rReferer,
1372  const uno::Reference< io::XInputStream > & inInputStream,
1373  uno::Reference< io::XOutputStream > & oOutputStream,
1374  const DAVRequestEnvironment & rEnv )
1375 {
1376  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1377  SAL_INFO( "ucb.ucp.webdav", "POST - relative URL <" << inPath << ">" );
1378 
1379  uno::Sequence< sal_Int8 > aDataToSend;
1380  if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
1382 
1383  Init( rEnv );
1384 
1385  NeonRequestContext aCtx( oOutputStream );
1386  int theRetVal = POST( m_pHttpSession,
1388  inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
1389  reinterpret_cast< const char * >(
1390  aDataToSend.getConstArray() ),
1392  &aCtx,
1393  rContentType,
1394  rReferer );
1395 
1396  HandleError( theRetVal, inPath, rEnv );
1397 }
1398 
1399 void NeonSession::MKCOL( const OUString & inPath,
1400  const DAVRequestEnvironment & rEnv )
1401 {
1402  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1403  SAL_INFO( "ucb.ucp.webdav", "MKCOL - relative URL <" << inPath << ">" );
1404 
1405  Init( rEnv );
1406 
1407  int theRetVal = ne_mkcol( m_pHttpSession,
1409  inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
1410 
1411  HandleError( theRetVal, inPath, rEnv );
1412 }
1413 
1414 void NeonSession::COPY( const OUString & inSourceURL,
1415  const OUString & inDestinationURL,
1416  const DAVRequestEnvironment & rEnv,
1417  bool inOverWrite )
1418 {
1419  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1420  SAL_INFO( "ucb.ucp.webdav", "COPY - inSourceURL: "<<inSourceURL<<" inDestinationURL: "<<inDestinationURL);
1421 
1422  Init( rEnv );
1423 
1424  NeonUri theSourceUri( inSourceURL );
1425  NeonUri theDestinationUri( inDestinationURL );
1426 
1427  int theRetVal = ne_copy( m_pHttpSession,
1428  inOverWrite ? 1 : 0,
1429  NE_DEPTH_INFINITE,
1431  theSourceUri.GetPath(),
1432  RTL_TEXTENCODING_UTF8 ).getStr(),
1434  theDestinationUri.GetPath(),
1435  RTL_TEXTENCODING_UTF8 ).getStr() );
1436 
1437  HandleError( theRetVal, inSourceURL, rEnv );
1438 }
1439 
1440 void NeonSession::MOVE( const OUString & inSourceURL,
1441  const OUString & inDestinationURL,
1442  const DAVRequestEnvironment & rEnv,
1443  bool inOverWrite )
1444 {
1445  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1446  SAL_INFO( "ucb.ucp.webdav", "MOVE - inSourceURL: "<<inSourceURL<<" inDestinationURL: "<<inDestinationURL);
1447 
1448  Init( rEnv );
1449 
1450  NeonUri theSourceUri( inSourceURL );
1451  NeonUri theDestinationUri( inDestinationURL );
1452  int theRetVal = ne_move( m_pHttpSession,
1453  inOverWrite ? 1 : 0,
1455  theSourceUri.GetPath(),
1456  RTL_TEXTENCODING_UTF8 ).getStr(),
1458  theDestinationUri.GetPath(),
1459  RTL_TEXTENCODING_UTF8 ).getStr() );
1460 
1461  HandleError( theRetVal, inSourceURL, rEnv );
1462 }
1463 
1464 void NeonSession::DESTROY( const OUString & inPath,
1465  const DAVRequestEnvironment & rEnv )
1466 {
1467  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1468  SAL_INFO( "ucb.ucp.webdav", "DESTROY - relative URL <" << inPath << ">" );
1469 
1470  Init( rEnv );
1471 
1472  int theRetVal = ne_delete( m_pHttpSession,
1474  inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
1475 
1476  HandleError( theRetVal, inPath, rEnv );
1477 }
1478 
1479 namespace
1480 {
1481  sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
1482  int timeout )
1483  {
1484  TimeValue aEnd;
1485  osl_getSystemTime( &aEnd );
1486 
1487  // Try to estimate a safe absolute time for sending the
1488  // lock refresh request.
1489  sal_Int32 lastChanceToSendRefreshRequest = -1;
1490  if ( timeout != NE_TIMEOUT_INFINITE )
1491  {
1492  sal_Int32 calltime = aEnd.Seconds - rStart.Seconds;
1493  if ( calltime <= timeout )
1494  {
1495  lastChanceToSendRefreshRequest = rStart.Seconds + timeout;
1496  }
1497  else
1498  {
1499  SAL_WARN( "ucb.ucp.webdav", "LOCK - no chance to refresh lock before timeout!" );
1500  }
1501  }
1502  return lastChanceToSendRefreshRequest;
1503  }
1504 
1505 } // namespace
1506 
1507 // Set new lock
1508 void NeonSession::LOCK( const OUString & inPath,
1509  ucb::Lock & rLock,
1510  const DAVRequestEnvironment & rEnv )
1511 {
1512  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1513  SAL_INFO( "ucb.ucp.webdav", "LOCK (create) - relative URL: <" << inPath << ">" );
1514 
1515  // before issuing the lock command,
1516  // better check first if we already have one on this href
1518  makeAbsoluteURL( inPath ) ) != nullptr )
1519  {
1520  // we already own a lock for this href
1521  // no need to ask for another
1522  // TODO: add a lockdiscovery request for confirmation
1523  // checking the locktoken, the only item that's unique
1524  return;
1525  }
1526 
1527  Init( rEnv );
1528 
1529  /* Create a depth zero, exclusive write lock, with default timeout
1530  * (allowing a server to pick a default). token, owner and uri are
1531  * unset. */
1532  NeonLock * theLock = ne_lock_create();
1533 
1534  // Set the lock uri
1535  ne_uri aUri;
1536  ne_uri_parse( OUStringToOString( makeAbsoluteURL( inPath ),
1537  RTL_TEXTENCODING_UTF8 ).getStr(),
1538  &aUri );
1539  theLock->uri = aUri;
1540 
1541  // Set the lock depth
1542  switch( rLock.Depth )
1543  {
1544  case ucb::LockDepth_ZERO:
1545  theLock->depth = NE_DEPTH_ZERO;
1546  break;
1547  case ucb::LockDepth_ONE:
1548  theLock->depth = NE_DEPTH_ONE;
1549  break;
1550  case ucb::LockDepth_INFINITY:
1551  theLock->depth = NE_DEPTH_INFINITE;
1552  break;
1553  default:
1555  }
1556 
1557  // Set the lock scope
1558  switch ( rLock.Scope )
1559  {
1560  case ucb::LockScope_EXCLUSIVE:
1561  theLock->scope = ne_lockscope_exclusive;
1562  break;
1563  case ucb::LockScope_SHARED:
1564  theLock->scope = ne_lockscope_shared;
1565  break;
1566  default:
1568  }
1569 
1570  // Set the lock timeout
1571  theLock->timeout = static_cast<long>(rLock.Timeout);
1572 
1573  // Set the lock owner
1574  OUString aValue;
1575  rLock.Owner >>= aValue;
1576  theLock->owner =
1577  ne_strdup( OUStringToOString( aValue,
1578  RTL_TEXTENCODING_UTF8 ).getStr() );
1579  TimeValue startCall;
1580  osl_getSystemTime( &startCall );
1581 
1582  int theRetVal = ne_lock( m_pHttpSession, theLock );
1583 
1584  if ( theRetVal == NE_OK )
1585  {
1586  m_aNeonLockStore.addLock( theLock,
1587  this,
1588  lastChanceToSendRefreshRequest(
1589  startCall, theLock->timeout ) );
1590 
1591  uno::Sequence< OUString > aTokens( 1 );
1592  aTokens[ 0 ] = OUString::createFromAscii( theLock->token );
1593  rLock.LockTokens = aTokens;
1594 
1595  SAL_INFO( "ucb.ucp.webdav", "LOCK (create) - Created lock for <" << makeAbsoluteURL( inPath )
1596  << "> token: <" << theLock->token << "> timeout: " << theLock->timeout << " sec.");
1597  }
1598  else
1599  {
1600  ne_lock_destroy( theLock );
1601 
1602  SAL_INFO( "ucb.ucp.webdav", "LOCK (create) - Obtaining lock for <"
1603  << makeAbsoluteURL( inPath ) << " failed!" );
1604  }
1605 
1606  HandleError( theRetVal, inPath, rEnv );
1607 }
1608 
1609 // Refresh existing lock
1611  sal_Int32 & rlastChanceToSendRefreshRequest )
1612 {
1613  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1614 
1615 #if defined SAL_LOG_INFO
1616  {
1617  char * p = ne_uri_unparse( &(pLock->uri) );
1618  SAL_INFO( "ucb.ucp.webdav", "LOCK (refresh) - relative URL: <" << p << "> token: <" << pLock->token << ">" );
1619  ne_free( p );
1620  }
1621 #endif
1622 
1623  // refresh existing lock.
1624 
1625  TimeValue startCall;
1626  osl_getSystemTime( &startCall );
1627 
1628  // save the current requested timeout, because ne_lock_refresh uses
1629  // pLock->timeout as an out parameter. This prevents a feedback-loop,
1630  // where we would request a shorter timeout on each refresh.
1631  long timeout = pLock->timeout;
1632  const int theRetVal = ne_lock_refresh(m_pHttpSession, pLock);
1633  if (theRetVal == NE_OK)
1634  {
1635  rlastChanceToSendRefreshRequest
1636  = lastChanceToSendRefreshRequest( startCall, pLock->timeout );
1637 
1638  SAL_INFO( "ucb.ucp.webdav", "LOCK (refresh) - Lock successfully refreshed." );
1639  pLock->timeout = timeout;
1640  return true;
1641  }
1642  else
1643  {
1644 #if defined SAL_LOG_WARN
1645  char * p = ne_uri_unparse( &(pLock->uri) );
1646  SAL_WARN( "ucb.ucp.webdav", "LOCK (refresh) - not refreshed! Relative URL: <" << p << "> token: <" << pLock->token << ">" );
1647  ne_free( p );
1648 #endif
1649  if (theRetVal == NE_AUTH)
1650  {
1651  // tdf#126279: see handling of NE_AUTH in HandleError
1652  m_bNeedNewSession = true;
1654  }
1655  return false;
1656  }
1657 }
1658 
1659 void NeonSession::UNLOCK( const OUString & inPath,
1660  const DAVRequestEnvironment & rEnv )
1661 {
1662  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1663 
1664  // get the neon lock from lock store
1665  NeonLock * theLock
1667  if ( !theLock )
1669 
1670  SAL_INFO( "ucb.ucp.webdav", "UNLOCK - relative URL: <" << inPath << "> token: <" << theLock->token << ">" );
1671  Init( rEnv );
1672 
1673  int theRetVal = ne_unlock( m_pHttpSession, theLock );
1674 
1675  if ( theRetVal == NE_OK )
1676  {
1677  m_aNeonLockStore.removeLock( theLock );
1678  ne_lock_destroy( theLock );
1679  }
1680  else
1681  {
1682  SAL_INFO( "ucb.ucp.webdav", "UNLOCK - Unlocking of <"
1683  << makeAbsoluteURL( inPath ) << "> failed." );
1684  }
1685 
1686  HandleError( theRetVal, inPath, rEnv );
1687 }
1688 
1690 {
1691  osl::Guard< osl::Mutex > theGuard( m_aMutex );
1692 
1693 #if defined SAL_LOG_INFO
1694  {
1695  char * p = ne_uri_unparse( &(pLock->uri) );
1696  SAL_INFO( "ucb.ucp.webdav", "UNLOCK (from store) - relative URL: <" << p << "> token: <" << pLock->token << ">" );
1697  ne_free( p );
1698  }
1699 #endif
1700 
1701  const int theRetVal = ne_unlock(m_pHttpSession, pLock);
1702  if (theRetVal == NE_OK)
1703  {
1704 #if defined SAL_LOG_INFO
1705  {
1706  char * p = ne_uri_unparse( &(pLock->uri) );
1707  SAL_INFO( "ucb.ucp.webdav", "UNLOCK (from store) - relative URL: <" << p << "> token: <" << pLock->token << "> succeeded." );
1708  ne_free( p );
1709  }
1710 #endif
1711  return true;
1712  }
1713  else
1714  {
1715 #if defined SAL_LOG_INFO
1716  {
1717  char * p = ne_uri_unparse( &(pLock->uri) );
1718  SAL_INFO( "ucb.ucp.webdav", "UNLOCK (from store) - relative URL: <" << p << "> token: <" << pLock->token << "> failed!" );
1719  ne_free( p );
1720  }
1721 #endif
1722  if (theRetVal == NE_AUTH)
1723  {
1724  // tdf#126279: see handling of NE_AUTH in HandleError
1725  m_bNeedNewSession = true;
1726  }
1727  return false;
1728  }
1729 }
1730 
1732 {
1733  SAL_INFO( "ucb.ucp.webdav", "neon commands cannot be aborted" );
1734 }
1735 
1737 {
1738  if ( m_aScheme == "http" || m_aScheme == "https" )
1739  {
1741  m_aHostName,
1742  m_nPort );
1743  }
1744  else
1745  {
1747  OUString() /* not used */,
1748  -1 /* not used */ );
1749  }
1750 }
1751 
1752 namespace {
1753 
1754 bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
1755  const char * token )
1756 {
1757  return std::any_of(rLocks.begin(), rLocks.end(), [&token](const ucb::Lock& rLock) {
1758  const uno::Sequence< OUString > & rTokens = rLock.LockTokens;
1759  return std::any_of(rTokens.begin(), rTokens.end(),
1760  [&token](const OUString& rToken) { return rToken.equalsAscii( token ); });
1761  });
1762 }
1763 
1764 } // namespace
1765 
1766 bool NeonSession::removeExpiredLocktoken( const OUString & inURL,
1767  const DAVRequestEnvironment & rEnv )
1768 {
1769  NeonLock * theLock = m_aNeonLockStore.findByUri( inURL );
1770  if ( !theLock )
1771  return false;
1772 
1773  // do a lockdiscovery to check whether this lock is still valid.
1774  try
1775  {
1776  // @@@ Alternative: use ne_lock_discover() => less overhead
1777 
1778  std::vector< DAVResource > aResources;
1779  std::vector< OUString > aPropNames;
1780  aPropNames.push_back( DAVProperties::LOCKDISCOVERY );
1781 
1782  PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv );
1783 
1784  if ( aResources.empty() )
1785  return false;
1786 
1787  for ( const auto& rProp : aResources[ 0 ].properties )
1788  {
1789  if ( rProp.Name == DAVProperties::LOCKDISCOVERY )
1790  {
1791  uno::Sequence< ucb::Lock > aLocks;
1792  if ( !( rProp.Value >>= aLocks ) )
1793  return false;
1794 
1795  if ( !containsLocktoken( aLocks, theLock->token ) )
1796  {
1797  // expired!
1798  break;
1799  }
1800 
1801  // still valid.
1802  return false;
1803  }
1804  }
1805 
1806  // No lockdiscovery prop in propfind result / locktoken not found
1807  // in propfind result -> not locked
1808  SAL_WARN( "ucb.ucp.webdav", "Removing expired lock token for <" << inURL
1809  << "> token: " << theLock->token );
1810 
1811  m_aNeonLockStore.removeLock( theLock );
1812  ne_lock_destroy( theLock );
1813  return true;
1814  }
1815  catch ( DAVException const & )
1816  {
1817  }
1818  return false;
1819 }
1820 
1821 // Common error handler
1822 void NeonSession::HandleError( int nError,
1823  const OUString & inPath,
1824  const DAVRequestEnvironment & rEnv )
1825 {
1826  // Map error code to DAVException.
1827  switch ( nError )
1828  {
1829  case NE_OK:
1830  return;
1831 
1832  case NE_ERROR: // Generic error
1833  {
1834  const char* sErr = ne_get_error(m_pHttpSession);
1835  OUString aText(sErr, strlen(sErr), osl_getThreadTextEncoding());
1836 
1837  sal_uInt16 code = makeStatusCode( aText );
1838 
1839  SAL_WARN( "ucb.ucp.webdav", "Neon returned NE_ERROR, http response status code was: " << code << " '" << aText << "'" );
1840  if ( SC_BAD_REQUEST <= code && code < SC_INTERNAL_SERVER_ERROR )
1841  {
1842  // error codes in the range 4xx
1843  switch ( code )
1844  {
1845  case SC_LOCKED:
1846  {
1848  makeAbsoluteURL( inPath ) ) == nullptr )
1849  {
1850  // locked by 3rd party
1852  }
1853  else
1854  {
1855  // locked by ourself
1857  }
1858  }
1859  break;
1861  case SC_BAD_REQUEST:
1862  {
1863  // Special handling for 400 and 412 status codes, which may indicate
1864  // that a lock previously obtained by us has been released meanwhile
1865  // by the server. Unfortunately, RFC is not clear at this point,
1866  // thus server implementations behave different...
1867  if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) )
1869  }
1870  break;
1871  case SC_REQUEST_TIMEOUT:
1872  {
1875  m_aHostName, m_nPort ) );
1876  }
1877  break;
1878  case SC_UNAUTHORIZED: // User authentication failed on server
1879  {
1882  m_aHostName, m_nPort ) );
1883  }
1884  break;
1885  case SC_GONE:
1886  case SC_LENGTH_REQUIRED:
1891  case SC_EXPECTATION_FAILED:
1893  case SC_FAILED_DEPENDENCY:
1894  case SC_CONFLICT:
1895  case SC_NOT_ACCEPTABLE:
1896  case SC_PAYMENT_REQUIRED:
1898  default:
1899  // set 400 error, if not one of others
1900  code = SC_BAD_REQUEST;
1901  [[fallthrough]];
1902  case SC_FORBIDDEN:
1903  case SC_NOT_FOUND:
1904  case SC_METHOD_NOT_ALLOWED:
1905  throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
1906  break;
1907  }
1908  }
1909  else if ( SC_INTERNAL_SERVER_ERROR <= code )
1910  {
1911  // deal with HTTP response status codes higher then 500
1912  // error codes in the range 5xx, server errors
1913  // but there exists unofficial code in the range 1000 and up
1914  // for example see:
1915  // <https://support.cloudflare.com/hc/en-us/sections/200820298-Error-Pages> (retrieved 2016-10-05)
1916  switch ( code )
1917  {
1918  // the error codes case before the default case are not actively
1919  // managed by LO
1920  case SC_BAD_GATEWAY:
1922  case SC_GATEWAY_TIMEOUT:
1925  default:
1926  // set 500 error, if not one of others
1927  // expand the error code
1928  code = SC_INTERNAL_SERVER_ERROR;
1929  [[fallthrough]];
1931  case SC_NOT_IMPLEMENTED:
1932  throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
1933  break;
1934  }
1935  }
1936  else
1937  throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
1938  }
1939  break;
1940  case NE_LOOKUP: // Name lookup failed.
1941  SAL_WARN( "ucb.ucp.webdav", "Name lookup failed" );
1944  m_aHostName, m_nPort ) );
1945 
1946  case NE_AUTH: // User authentication failed on server
1947  // m_pHttpSession could get invalidated, e.g., as result of clean_session called in
1948  // ah_post_send in case when auth_challenge failed, which invalidates the auth_session
1949  // which we established in Init(): the auth_session's sspi_host gets disposed, and
1950  // next attempt to authenticate would crash in continue_sspi trying to dereference it
1951  m_bNeedNewSession = true;
1954  m_aHostName, m_nPort ) );
1955 
1956  case NE_PROXYAUTH: // User authentication failed on proxy
1957  SAL_WARN( "ucb.ucp.webdav", "DAVException::DAV_HTTP_AUTHPROXY" );
1961 
1962  case NE_CONNECT: // Could not connect to server
1963  SAL_WARN( "ucb.ucp.webdav", "DAVException::DAV_HTTP_CONNECT" );
1966  m_aHostName, m_nPort ) );
1967 
1968  case NE_TIMEOUT: // Connection timed out
1969  SAL_WARN( "ucb.ucp.webdav", "DAVException::DAV_HTTP_TIMEOUT" );
1972  m_aHostName, m_nPort ) );
1973 
1974  case NE_FAILED: // The precondition failed
1975  SAL_WARN( "ucb.ucp.webdav", "The precondition failed" );
1978  m_aHostName, m_nPort ) );
1979 
1980  case NE_RETRY: // Retry request (ne_end_request ONLY)
1983  m_aHostName, m_nPort ) );
1984 
1985  case NE_REDIRECT:
1986  {
1987  NeonUri aUri( ne_redirect_location( m_pHttpSession ) );
1988  SAL_INFO( "ucb.ucp.webdav", "DAVException::DAV_HTTP_REDIRECT: new URI: " << aUri.GetURI() );
1989  throw DAVException(
1991  }
1992  default:
1993  {
1994  SAL_WARN( "ucb.ucp.webdav", "Unknown Neon error code!" );
1995  const char* sErr = ne_get_error(m_pHttpSession);
1997  OUString(sErr, strlen(sErr), osl_getThreadTextEncoding()) );
1998  }
1999  }
2000 }
2001 
2002 namespace {
2003 
2004 void runResponseHeaderHandler( void * userdata,
2005  const char * value )
2006 {
2007  OUString aHeader(value, strlen(value), RTL_TEXTENCODING_ASCII_US);
2008  sal_Int32 nPos = aHeader.indexOf( ':' );
2009 
2010  if ( nPos == -1 )
2011  return;
2012 
2013  OUString aHeaderName( aHeader.copy( 0, nPos ) );
2014 
2015  NeonRequestContext * pCtx
2016  = static_cast< NeonRequestContext * >( userdata );
2017 
2018  // Note: Empty vector means that all headers are requested.
2019  bool bIncludeIt = pCtx->pHeaderNames->empty();
2020 
2021  if ( !bIncludeIt )
2022  {
2023  // Check whether this header was requested.
2024  auto it = std::find_if(pCtx->pHeaderNames->cbegin(), pCtx->pHeaderNames->cend(),
2025  [&aHeaderName](const OUString& rName) {
2026  // header names are case insensitive
2027  return rName.equalsIgnoreAsciiCase( aHeaderName ); });
2028 
2029  if ( it != pCtx->pHeaderNames->end() )
2030  {
2031  aHeaderName = *it;
2032  bIncludeIt = true;
2033  }
2034  }
2035 
2036  if ( !bIncludeIt )
2037  return;
2038 
2039  // Create & set the PropertyValue
2040  DAVPropertyValue thePropertyValue;
2041  // header names are case insensitive, so are the
2042  // corresponding property names.
2043  thePropertyValue.Name = aHeaderName.toAsciiLowerCase();
2044  thePropertyValue.IsCaseSensitive = false;
2045 
2046  if ( nPos < aHeader.getLength() )
2047  thePropertyValue.Value <<= aHeader.copy( nPos + 1 ).trim();
2048 
2049  // Add the newly created PropertyValue
2050  pCtx->pResource->properties.push_back( thePropertyValue );
2051 }
2052 
2053 } // namespace
2054 
2055 int NeonSession::GET( ne_session * sess,
2056  const char * uri,
2057  ne_block_reader reader,
2058  bool getheaders,
2059  void * userdata )
2060 {
2061  //struct get_context ctx;
2062  ne_request * req = ne_request_create( sess, "GET", uri );
2063  int ret;
2064 
2065  ne_decompress * dc
2066  = ne_decompress_reader( req, ne_accept_2xx, reader, userdata );
2067 
2068  {
2069  osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
2070  ret = ne_request_dispatch( req );
2071  }
2072 
2073  if ( getheaders )
2074  {
2075  void *cursor = nullptr;
2076  const char *name, *value;
2077  while ( ( cursor = ne_response_header_iterate(
2078  req, cursor, &name, &value ) ) != nullptr )
2079  {
2080  char buffer[8192];
2081 
2082  SAL_INFO( "ucb.ucp.webdav", "GET - received header: " << name << ": " << value );
2083  ne_snprintf(buffer, sizeof buffer, "%s: %s", name, value);
2084  runResponseHeaderHandler(userdata, buffer);
2085  }
2086  }
2087 
2088  if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
2089  ret = NE_ERROR;
2090 
2091  if ( dc != nullptr )
2092  ne_decompress_destroy(dc);
2093 
2094  ne_request_destroy( req );
2095  return ret;
2096 }
2097 
2098 int NeonSession::GET0( ne_session * sess,
2099  const char * uri,
2100  bool getheaders,
2101  void * userdata )
2102 {
2103  //struct get_context ctx;
2104  ne_request * req = ne_request_create( sess, "GET", uri );
2105  int ret;
2106 
2107  {
2108  osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
2109  ret = ne_request_dispatch( req );
2110  }
2111 
2112  if ( getheaders )
2113  {
2114  void *cursor = nullptr;
2115  const char *name, *value;
2116  while ( ( cursor = ne_response_header_iterate(
2117  req, cursor, &name, &value ) ) != nullptr )
2118  {
2119  char buffer[8192];
2120 
2121  SAL_INFO( "ucb.ucp.webdav", "GET - received header: " << name << ": " << value );
2122  ne_snprintf(buffer, sizeof buffer, "%s: %s", name, value);
2123  runResponseHeaderHandler(userdata, buffer);
2124  }
2125  }
2126 
2127  if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
2128  ret = NE_ERROR;
2129 
2130  ne_request_destroy( req );
2131  return ret;
2132 }
2133 
2134 int NeonSession::PUT( ne_session * sess,
2135  const char * uri,
2136  const char * buffer,
2137  size_t size)
2138 {
2139  ne_request * req = ne_request_create( sess, "PUT", uri );
2140  int ret;
2141 
2142  // tdf#99246
2143  // extract the path of uri
2144  // ne_lock_using_resource below compares path, ignores all the rest.
2145  // in case of Web proxy active, this function uri parameter is instead absolute
2146  ne_uri aUri;
2147  ne_uri_parse( uri, &aUri );
2148  ne_lock_using_resource( req, aUri.path, 0 );
2149  ne_lock_using_parent( req, uri );
2150 
2151  ne_set_request_body_buffer( req, buffer, size );
2152 
2153  {
2154  osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
2155  ret = ne_request_dispatch( req );
2156  }
2157 
2158  if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
2159  ret = NE_ERROR;
2160 
2161  ne_request_destroy( req );
2162  return ret;
2163 }
2164 
2165 int NeonSession::POST( ne_session * sess,
2166  const char * uri,
2167  const char * buffer,
2168  ne_block_reader reader,
2169  void * userdata,
2170  const OUString & rContentType,
2171  const OUString & rReferer )
2172 {
2173  ne_request * req = ne_request_create( sess, "POST", uri );
2174  //struct get_context ctx;
2175  int ret;
2176 
2177  RequestDataMap * pData = nullptr;
2178 
2179  if ( !rContentType.isEmpty() || !rReferer.isEmpty() )
2180  {
2181  // Remember contenttype and referer. Data will be added to HTTP request
2182  // header in 'PreSendRequest' callback.
2183  pData = static_cast< RequestDataMap* >( m_pRequestData );
2184  (*pData)[ req ] = RequestData( rContentType, rReferer );
2185  }
2186 
2187  //ctx.total = -1;
2188  //ctx.fd = fd;
2189  //ctx.error = 0;
2190  //ctx.session = sess;
2191 
2193  //ne_add_response_header_handler( req, "Content-Length",
2194  // ne_handle_numeric_header, &ctx.total );
2195 
2196  ne_add_response_body_reader( req, ne_accept_2xx, reader, userdata );
2197 
2198  ne_set_request_body_buffer( req, buffer, strlen( buffer ) );
2199 
2200  {
2201  osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
2202  ret = ne_request_dispatch( req );
2203  }
2204 
2205  //if ( ctx.error )
2206  // ret = NE_ERROR;
2207  //else
2208  if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
2209  ret = NE_ERROR;
2210 
2211  ne_request_destroy( req );
2212 
2213  if ( pData )
2214  {
2215  // Remove request data from session's list.
2216  RequestDataMap::iterator it = pData->find( req );
2217  if ( it != pData->end() )
2218  pData->erase( it );
2219  }
2220 
2221  return ret;
2222 }
2223 
2224 bool
2226  const uno::Reference< io::XInputStream > & xStream,
2227  uno::Sequence< sal_Int8 > & rData,
2228  bool bAppendTrailingZeroByte )
2229 {
2230  if ( xStream.is() )
2231  {
2232  uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY );
2233  if ( xSeekable.is() )
2234  {
2235  try
2236  {
2237  sal_Int32 nSize
2238  = sal::static_int_cast<sal_Int32>(xSeekable->getLength());
2239  sal_Int32 nRead
2240  = xStream->readBytes( rData, nSize );
2241 
2242  if ( nRead == nSize )
2243  {
2244  if ( bAppendTrailingZeroByte )
2245  {
2246  rData.realloc( nSize + 1 );
2247  rData[ nSize ] = sal_Int8( 0 );
2248  }
2249  return true;
2250  }
2251  }
2252  catch ( io::NotConnectedException const & )
2253  {
2254  // readBytes
2255  }
2256  catch ( io::BufferSizeExceededException const & )
2257  {
2258  // readBytes
2259  }
2260  catch ( io::IOException const & )
2261  {
2262  // getLength, readBytes
2263  }
2264  }
2265  else
2266  {
2267  try
2268  {
2269  uno::Sequence< sal_Int8 > aBuffer;
2270  sal_Int32 nPos = 0;
2271 
2272  sal_Int32 nRead = xStream->readSomeBytes( aBuffer, 65536 );
2273  while ( nRead > 0 )
2274  {
2275  if ( rData.getLength() < ( nPos + nRead ) )
2276  rData.realloc( nPos + nRead );
2277 
2278  aBuffer.realloc( nRead );
2279  memcpy( static_cast<void*>( rData.getArray() + nPos ),
2280  static_cast<const void*>(aBuffer.getConstArray()),
2281  nRead );
2282  nPos += nRead;
2283 
2284  aBuffer.realloc( 0 );
2285  nRead = xStream->readSomeBytes( aBuffer, 65536 );
2286  }
2287 
2288  if ( bAppendTrailingZeroByte )
2289  {
2290  rData.realloc( nPos + 1 );
2291  rData[ nPos ] = sal_Int8( 0 );
2292  }
2293  return true;
2294  }
2295  catch ( io::NotConnectedException const & )
2296  {
2297  // readBytes
2298  }
2299  catch ( io::BufferSizeExceededException const & )
2300  {
2301  // readBytes
2302  }
2303  catch ( io::IOException const & )
2304  {
2305  // readBytes
2306  }
2307  }
2308  }
2309  return false;
2310 }
2311 
2312 bool
2313 NeonSession::isDomainMatch( const OUString& certHostName )
2314 {
2315  OUString hostName = getHostName();
2316 
2317  if (hostName.equalsIgnoreAsciiCase( certHostName ) )
2318  return true;
2319 
2320  if ( certHostName.startsWith( "*" ) &&
2321  hostName.getLength() >= certHostName.getLength() )
2322  {
2323  OUString cmpStr = certHostName.copy( 1 );
2324 
2325  if ( hostName.matchIgnoreAsciiCase(
2326  cmpStr, hostName.getLength() - cmpStr.getLength() ) )
2327  return true;
2328  }
2329  return false;
2330 }
2331 
2332 OUString NeonSession::makeAbsoluteURL( OUString const & rURL ) const
2333 {
2334  try
2335  {
2336  // Is URL relative or already absolute?
2337  if ( !rURL.isEmpty() && rURL[ 0 ] != '/' )
2338  {
2339  // absolute.
2340  return rURL;
2341  }
2342  else
2343  {
2344  ne_uri aUri = {};
2345 
2346  ne_fill_server_uri( m_pHttpSession, &aUri );
2347  aUri.path
2348  = ne_strdup( OUStringToOString(
2349  rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
2350  NeonUri aNeonUri( &aUri );
2351  ne_uri_free( &aUri );
2352  return aNeonUri.GetURI();
2353  }
2354  }
2355  catch ( DAVException const & )
2356  {
2357  }
2358  // error.
2359  return OUString();
2360 }
2361 
2362 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
int NeonAuth(const char *inAuthProtocol, const char *inRealm, int attempt, char *inoutUserName, char *inoutPassWord)
DAVRequestEnvironment m_aEnv
Definition: NeonSession.hxx:66
const sal_uInt16 SC_LOCKED
const OUString & GetUserInfo() const
Definition: NeonUri.hxx:71
const sal_uInt16 SC_BAD_REQUEST
osl::Mutex m_aMutex
const OUString & getHostName() const
std::unique_ptr< ContentProperties > pData
static void NeonSession_PreSendRequest(ne_request *req, void *userdata, ne_buffer *headers)
signed char sal_Int8
const sal_uInt16 SC_GATEWAY_TIMEOUT
std::vector< DAVRequestHeader > DAVRequestHeaders
virtual void LOCK(const OUString &inURL, css::ucb::Lock &inLock, const DAVRequestEnvironment &rEnv) override
virtual void PROPPATCH(const OUString &inPath, const std::vector< ProppatchValue > &inValues, const DAVRequestEnvironment &rEnv) override
static bool toXML(const css::uno::Sequence< css::ucb::Link > &rInData, OUString &rOutData)
sal_Int64 n
static constexpr OUStringLiteral LOCKDISCOVERY
virtual void GET0(const OUString &inPath, const std::vector< OUString > &inHeaderNames, DAVResource &ioResource, const DAVRequestEnvironment &rEnv) override
void setClass2(bool Class2=true)
const sal_uInt16 SC_REQUEST_ENTITY_TOO_LARGE
OUString makeAbsoluteURL(OUString const &rURL) const
void registerSession(HttpSession *pHttpSession)
virtual void abort() override
sal_Unicode code
static sal_uInt16 makeStatusCode(const OUString &rStatusText)
virtual void OPTIONS(const OUString &inPath, DAVOptions &rOptions, const DAVRequestEnvironment &rEnv) override
void setClass1(bool Class1=true)
void removeLockDeferred(NeonLock *pLock)
static NeonLockStore m_aNeonLockStore
Definition: NeonSession.hxx:69
const ucbhelper::InternetProxyDecider & m_rProxyDecider
Definition: NeonSession.hxx:59
virtual int authenticate(const OUString &inRealm, const OUString &inHostName, OUString &inoutUserName, OUString &outPassWord, bool bCanUseSystemCredentials)=0
css::uno::Any const & rValue
const OUString & GetPath() const
Definition: NeonUri.hxx:77
sal_Int32 GetPort() const
Definition: NeonUri.hxx:75
const sal_uInt16 SC_INSUFFICIENT_STORAGE
Mutex aMutex
const sal_uInt16 SC_FAILED_DEPENDENCY
const sal_uInt16 SC_REQUEST_TIMEOUT
virtual void UNLOCK(const OUString &inURL, const DAVRequestEnvironment &rEnv) override
virtual void DESTROY(const OUString &inPath, const DAVRequestEnvironment &rEnv) override
#define EOL
Definition: NeonSession.cxx:80
virtual css::uno::Reference< css::io::XInputStream > POST(const OUString &inPath, const OUString &rContentType, const OUString &rReferer, const css::uno::Reference< css::io::XInputStream > &inInputStream, const DAVRequestEnvironment &rEnv) override
const sal_uInt16 SC_REQUEST_URI_TOO_LONG
const sal_uInt16 SC_NOT_ACCEPTABLE
const sal_uInt16 SC_PAYMENT_REQUIRED
sal_uInt16 char * pName
bool removeExpiredLocktoken(const OUString &inURL, const DAVRequestEnvironment &rEnv)
static bool m_bGlobalsInited
Definition: NeonSession.hxx:68
const sal_uInt16 SC_METHOD_NOT_ALLOWED
const sal_uInt16 SC_SERVICE_UNAVAILABLE
virtual void COPY(const OUString &inSourceURL, const OUString &inDestinationURL, const DAVRequestEnvironment &rEnv, bool inOverWrite) override
const sal_uInt16 SC_NOT_IMPLEMENTED
static int NeonSession_ResponseBlockWriter(void *inUserData, const char *inBuf, size_t inLen)
static bool getDataFromInputStream(const css::uno::Reference< css::io::XInputStream > &xStream, css::uno::Sequence< sal_Int8 > &rData, bool bAppendTrailingZeroByte)
osl::Mutex & getGlobalNeonMutex()
static int NeonSession_CertificationNotify(void *userdata, int, const ne_ssl_certificate *cert)
struct ne_lock NeonLock
Definition: NeonTypes.hxx:53
static int NeonSession_ResponseBlockReader(void *inUserData, const char *inBuf, size_t inLen)
virtual ~NeonSession() override
void PreSendRequest(ne_request *req, ne_buffer *headers)
void setAllowedMethods(const OUString &aAllowedMethods)
NeonLock * findByUri(OUString const &rUri)
HttpSession * m_pHttpSession
Definition: NeonSession.hxx:56
static bool toXML(const css::uno::Any &rInData, OUString &rOutData)
virtual bool UsesProxy() override
const PropertyStruct aPropNames[]
const sal_uInt16 SC_UNSUPPORTED_MEDIA_TYPE
virtual void PROPFIND(const OUString &inPath, const Depth inDepth, const std::vector< OUString > &inPropNames, std::vector< DAVResource > &ioResources, const DAVRequestEnvironment &rEnv) override
const sal_uInt16 SC_BAD_GATEWAY
const OUString & GetHost() const
Definition: NeonUri.hxx:73
ucbhelper::InternetProxyServer getProxySettings() const
const sal_uInt16 SC_GONE
Reference< XComponentContext > getComponentContext(Reference< XMultiServiceFactory > const &factory)
static void createNeonPropName(const OUString &rFullName, NeonPropName &rName)
const sal_uInt16 SC_FORBIDDEN
const sal_uInt16 SC_REQUESTED_RANGE_NOT_SATISFIABLE
static bool isUCBDeadProperty(const NeonPropName &rName)
virtual void MKCOL(const OUString &inPath, const DAVRequestEnvironment &rEnv) override
static constexpr OUStringLiteral SOURCE
virtual bool CanUse(const OUString &inPath, const css::uno::Sequence< css::beans::NamedValue > &rFlags) override
OString OUStringToOString(const OUString &str, ConnectionSettings const *settings)
bool isDomainMatch(const OUString &certHostName)
void addLock(NeonLock *pLock, rtl::Reference< NeonSession > const &xSession, sal_Int32 nLastChanceToSendRefreshRequest)
const sal_uInt16 SC_LENGTH_REQUIRED
std::unique_ptr< char[]> aBuffer
std::vector< DAVPropertyValue > properties
virtual void MOVE(const OUString &inSourceURL, const OUString &inDestinationURL, const DAVRequestEnvironment &rEnv, bool inOverWrite) override
static bool noKeepAlive(const uno::Sequence< beans::NamedValue > &rFlags)
std::unordered_map< ne_request *, RequestData, hashPtr, equalPtr > RequestDataMap
int CertificationNotify(const ne_ssl_certificate *cert)
static int NeonSession_NeonAuth(void *inUserData, const char *inRealm, int attempt, char *inoutUserName, char *inoutPassWord)
const sal_uInt16 SC_CONFLICT
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
#define SAL_INFO(area, stream)
const sal_uInt16 SC_UNPROCESSABLE_ENTITY
void removeLock(NeonLock *pLock)
void HandleError(int nError, const OUString &inPath, const DAVRequestEnvironment &rEnv)
void * p
void setLocked(bool locked=true)
const char * name
Sequence< sal_Int8 > aSeq
Any value
const sal_uInt16 SC_NOT_FOUND
const OUString & GetScheme() const
Definition: NeonUri.hxx:69
const sal_uInt16 SC_PROXY_AUTHENTICATION_REQUIRED
virtual css::uno::Reference< css::io::XInputStream > GET(const OUString &inPath, const DAVRequestEnvironment &rEnv) override
const OUString & GetURI() const
Definition: NeonUri.hxx:67
#define SAL_WARN(area, stream)
const sal_uInt16 SC_PRECONDITION_FAILED
virtual void HEAD(const OUString &inPath, const std::vector< OUString > &inHeaderNames, DAVResource &ioResource, const DAVRequestEnvironment &rEnv) override
css::uno::Sequence< css::beans::NamedValue > m_aFlags
Definition: NeonSession.hxx:55
const sal_uInt16 SC_EXPECTATION_FAILED
ProppatchOperation operation
InternetProxyServer getProxy(const OUString &rProtocol, const OUString &rHost, sal_Int32 nPort) const
const sal_uInt16 SC_UNAUTHORIZED
rtl::Reference< DAVSessionFactory > m_xFactory
virtual void PUT(const OUString &inPath, const css::uno::Reference< css::io::XInputStream > &inInputStream, const DAVRequestEnvironment &rEnv) override
sal_uInt16 nPos
const sal_uInt16 SC_INTERNAL_SERVER_ERROR
const sal_uInt16 SC_HTTP_VERSION_NOT_SUPPORTED
void setClass3(bool Class3=true)
exports com.sun.star. uri
static OUString makeConnectionEndPointString(const OUString &rHostName, int nPort)
Definition: NeonUri.cxx:265
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo