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