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