LibreOffice Module ucb (master)  1
neon/webdavcontent.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 /**************************************************************************
31  TODO
32  **************************************************************************
33 
34  *************************************************************************/
35 
36 #include <memory>
37 #include <rtl/uri.hxx>
38 #include <rtl/ustrbuf.hxx>
39 #include <sal/log.hxx>
41 #include <officecfg/Inet.hxx>
46 #include <com/sun/star/beans/IllegalTypeException.hpp>
47 #include <com/sun/star/beans/NotRemoveableException.hpp>
48 #include <com/sun/star/beans/PropertyAttribute.hpp>
49 #include <com/sun/star/beans/PropertyExistException.hpp>
50 #include <com/sun/star/beans/PropertySetInfoChange.hpp>
51 #include <com/sun/star/beans/PropertySetInfoChangeEvent.hpp>
52 #include <com/sun/star/beans/PropertyValue.hpp>
53 #include <com/sun/star/io/XActiveDataSink.hpp>
54 #include <com/sun/star/io/XOutputStream.hpp>
55 #include <com/sun/star/lang/IllegalAccessException.hpp>
56 #include <com/sun/star/sdbc/SQLException.hpp>
57 #include <com/sun/star/task/PasswordContainerInteractionHandler.hpp>
58 #include <com/sun/star/ucb/CommandEnvironment.hpp>
59 #include <com/sun/star/ucb/CommandFailedException.hpp>
60 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
61 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
62 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
63 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
64 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
65 #include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
66 #include <com/sun/star/ucb/InteractiveLockingLockExpiredException.hpp>
67 #include <com/sun/star/ucb/InteractiveLockingNotLockedException.hpp>
68 #include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
69 #include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
70 #include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
71 #include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
72 #include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
73 #include <com/sun/star/ucb/MissingInputStreamException.hpp>
74 #include <com/sun/star/ucb/MissingPropertiesException.hpp>
75 #include <com/sun/star/ucb/NameClash.hpp>
76 #include <com/sun/star/ucb/NameClashException.hpp>
77 #include <com/sun/star/ucb/OpenCommandArgument3.hpp>
78 #include <com/sun/star/ucb/OpenMode.hpp>
79 #include <com/sun/star/ucb/PostCommandArgument2.hpp>
80 #include <com/sun/star/ucb/PropertyCommandArgument.hpp>
81 #include <com/sun/star/ucb/TransferInfo.hpp>
82 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
83 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
84 #include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
85 #include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
86 #include <com/sun/star/ucb/XCommandInfo.hpp>
87 #include <com/sun/star/ucb/XPersistentPropertySet.hpp>
88 #include <ucbhelper/macros.hxx>
89 
90 #include "webdavcontent.hxx"
91 #include "webdavprovider.hxx"
92 #include "webdavresultset.hxx"
93 #include "ContentProperties.hxx"
94 #include "NeonUri.hxx"
95 #include "UCBDeadPropertyValue.hxx"
96 
97 using namespace com::sun::star;
98 using namespace webdav_ucp;
99 
100 namespace
101 {
102  // implement a GET to substitute HEAD, when HEAD not available
103  void lcl_sendPartialGETRequest( bool &bError,
104  DAVException &aLastException,
105  const std::vector< OUString >& rProps,
106  std::vector< OUString > &aHeaderNames,
107  const std::unique_ptr< DAVResourceAccess > &xResAccess,
108  std::unique_ptr< ContentProperties > &xProps,
109  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
110  {
111  DAVResource aResource;
112  DAVRequestHeaders aPartialGet;
113  aPartialGet.emplace_back( OUString( "Range" ), // see <https://tools.ietf.org/html/rfc7233#section-3.1>
114  OUString( "bytes=0-0" ) );
115 
116  bool bIsRequestSize = std::any_of(aHeaderNames.begin(), aHeaderNames.end(),
117  [](const OUString& rHeaderName) { return rHeaderName == "Content-Length"; });
118 
119  if ( bIsRequestSize )
120  {
121  // we need to know if the server accepts range requests for a resource
122  // and the range unit it uses
123  aHeaderNames.emplace_back( "Accept-Ranges" ); // see <https://tools.ietf.org/html/rfc7233#section-2.3>
124  aHeaderNames.emplace_back( "Content-Range" ); // see <https://tools.ietf.org/html/rfc7233#section-4.2>
125  }
126  try
127  {
128  xResAccess->GET0( aPartialGet, aHeaderNames, aResource, xEnv );
129  bError = false;
130 
131  if ( bIsRequestSize )
132  {
133  // the ContentProperties maps "Content-Length" to the UCB "Size" property
134  // This would have an unrealistic value of 1 byte because we did only a partial GET
135  // Solution: if "Content-Range" is present, map it with UCB "Size" property
136  OUString aAcceptRanges, aContentRange, aContentLength;
137  std::vector< DAVPropertyValue > &aResponseProps = aResource.properties;
138  for ( const auto& rResponseProp : aResponseProps )
139  {
140  if ( rResponseProp.Name == "Accept-Ranges" )
141  rResponseProp.Value >>= aAcceptRanges;
142  else if ( rResponseProp.Name == "Content-Range" )
143  rResponseProp.Value >>= aContentRange;
144  else if ( rResponseProp.Name == "Content-Length" )
145  rResponseProp.Value >>= aContentLength;
146  }
147 
148  sal_Int64 nSize = 1;
149  if ( aContentLength.getLength() )
150  {
151  nSize = aContentLength.toInt64();
152  }
153 
154  // according to <> http://tools.ietf.org/html/rfc2616#section-3.12
155  // <https://tools.ietf.org/html/rfc7233#section-2>
156  // needs some explanation for this
157  // probably some changes?
158  // the only range unit defined is "bytes" and implementations
159  // MAY ignore ranges specified using other units.
160  if ( nSize == 1 &&
161  aContentRange.getLength() &&
162  aAcceptRanges == "bytes" )
163  {
164  // Parse the Content-Range to get the size
165  // vid. http://tools.ietf.org/html/rfc2616#section-14.16
166  // Content-Range: <range unit> <bytes range>/<size>
167  sal_Int32 nSlash = aContentRange.lastIndexOf( '/' );
168  if ( nSlash != -1 )
169  {
170  OUString aSize = aContentRange.copy( nSlash + 1 );
171  // "*" means that the instance-length is unknown at the time when the response was generated
172  if ( aSize != "*" )
173  {
174  auto it = std::find_if(aResponseProps.begin(), aResponseProps.end(),
175  [](const DAVPropertyValue& rProp) { return rProp.Name == "Content-Length"; });
176  if (it != aResponseProps.end())
177  {
178  it->Value <<= aSize;
179  }
180  }
181  }
182  }
183  }
184 
185  if (xProps)
186  xProps->addProperties(
187  rProps,
188  ContentProperties( aResource ) );
189  else
190  xProps.reset ( new ContentProperties( aResource ) );
191  }
192  catch ( DAVException const & ex )
193  {
194  aLastException = ex;
195  }
196  }
197 }
198 
199 
200 // Static value, to manage a simple OPTIONS cache
201 // Key is the URL, element is the DAVOptions resulting from an OPTIONS call.
202 // Cached DAVOptions have a lifetime that depends on the errors received or not received
203 // and on the value of received options.
205 
206 
207 // Content Implementation.
208 
209 
210 // ctr for content on an existing webdav resource
211 Content::Content(
212  const uno::Reference< uno::XComponentContext >& rxContext,
213  ContentProvider* pProvider,
214  const uno::Reference< ucb::XContentIdentifier >& Identifier,
215  rtl::Reference< DAVSessionFactory > const & rSessionFactory )
216 : ContentImplHelper( rxContext, pProvider, Identifier ),
217  m_eResourceType( UNKNOWN ),
218  m_eResourceTypeForLocks( UNKNOWN ),
219  m_pProvider( pProvider ),
220  m_bTransient( false ),
221  m_bCollection( false ),
222  m_bDidGetOrHead( false )
223 {
224  try
225  {
226  initOptsCacheLifeTime();
227  m_xResAccess.reset( new DAVResourceAccess(
228  rxContext,
229  rSessionFactory,
230  Identifier->getContentIdentifier() ) );
231 
232  NeonUri aURI( Identifier->getContentIdentifier() );
233  m_aEscapedTitle = aURI.GetPathBaseName();
234  }
235  catch ( DAVException const & )
236  {
237  throw ucb::ContentCreationException();
238  }
239 }
240 
241 
242 // ctr for content on a non-existing webdav resource
243 Content::Content(
244  const uno::Reference< uno::XComponentContext >& rxContext,
245  ContentProvider* pProvider,
246  const uno::Reference< ucb::XContentIdentifier >& Identifier,
247  rtl::Reference< DAVSessionFactory > const & rSessionFactory,
248  bool isCollection )
249 : ContentImplHelper( rxContext, pProvider, Identifier ),
250  m_eResourceType( UNKNOWN ),
251  m_eResourceTypeForLocks( UNKNOWN ),
252  m_pProvider( pProvider ),
253  m_bTransient( true ),
254  m_bCollection( isCollection ),
255  m_bDidGetOrHead( false )
256 {
257  try
258  {
259  initOptsCacheLifeTime();
260  m_xResAccess.reset( new DAVResourceAccess(
261  rxContext, rSessionFactory, Identifier->getContentIdentifier() ) );
262  }
263  catch ( DAVException const & )
264  {
265  throw ucb::ContentCreationException();
266  }
267 
268  // Do not set m_aEscapedTitle here! Content::insert relays on this!!!
269 }
270 
271 
272 // virtual
273 Content::~Content()
274 {
275 }
276 
277 
278 // XInterface methods.
279 
280 
281 // virtual
282 void SAL_CALL Content::acquire()
283  throw( )
284 {
285  ContentImplHelper::acquire();
286 }
287 
288 
289 // virtual
290 void SAL_CALL Content::release()
291  throw( )
292 {
293  ContentImplHelper::release();
294 }
295 
296 
297 // virtual
298 uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
299 {
300  // Note: isFolder may require network activities! So call it only
301  // if it is really necessary!!!
303  rType,
304  static_cast< ucb::XContentCreator * >( this ) );
305  if ( aRet.hasValue() )
306  {
307  try
308  {
309  uno::Reference< task::XInteractionHandler > xIH(
310  task::PasswordContainerInteractionHandler::create( m_xContext ) );
311 
312  // Supply a command env to isFolder() that contains an interaction
313  // handler that uses the password container service to obtain
314  // credentials without displaying a password gui.
315 
316  uno::Reference< ucb::XCommandEnvironment > xCmdEnv(
317  ucb::CommandEnvironment::create(
318  m_xContext,
319  xIH,
320  uno::Reference< ucb::XProgressHandler >() ) );
321 
322  return isFolder( xCmdEnv ) ? aRet : uno::Any();
323  }
324  catch ( uno::RuntimeException const & )
325  {
326  throw;
327  }
328  catch ( uno::Exception const & )
329  {
330  return uno::Any();
331  }
332  }
333  return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType );
334 }
335 
336 
337 // XTypeProvider methods.
338 
339 
341 
342 
343 // virtual
344 uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
345 {
346  bool bFolder = false;
347  try
348  {
349  bFolder
350  = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
351  }
352  catch ( uno::RuntimeException const & )
353  {
354  throw;
355  }
356  catch ( uno::Exception const & )
357  {
358  }
359 
360  if ( bFolder )
361  {
362  static cppu::OTypeCollection s_aFolderTypes(
363  CPPU_TYPE_REF( lang::XTypeProvider ),
364  CPPU_TYPE_REF( lang::XServiceInfo ),
365  CPPU_TYPE_REF( lang::XComponent ),
366  CPPU_TYPE_REF( ucb::XContent ),
367  CPPU_TYPE_REF( ucb::XCommandProcessor ),
368  CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
369  CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
370  CPPU_TYPE_REF( beans::XPropertyContainer ),
371  CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
372  CPPU_TYPE_REF( container::XChild ),
373  CPPU_TYPE_REF( ucb::XContentCreator ) );
374 
375  return s_aFolderTypes.getTypes();
376  }
377  else
378  {
379  static cppu::OTypeCollection s_aDocumentTypes(
380  CPPU_TYPE_REF( lang::XTypeProvider ),
381  CPPU_TYPE_REF( lang::XServiceInfo ),
382  CPPU_TYPE_REF( lang::XComponent ),
383  CPPU_TYPE_REF( ucb::XContent ),
384  CPPU_TYPE_REF( ucb::XCommandProcessor ),
385  CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
386  CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
387  CPPU_TYPE_REF( beans::XPropertyContainer ),
388  CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
389  CPPU_TYPE_REF( container::XChild ) );
390 
391  return s_aDocumentTypes.getTypes();
392  }
393 }
394 
395 
396 // XServiceInfo methods.
397 
398 
399 // virtual
401 {
402  return "com.sun.star.comp.ucb.WebDAVContent";
403 }
404 
405 
406 // virtual
407 uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
408 {
409  uno::Sequence<OUString> aSNS { WEBDAV_CONTENT_SERVICE_NAME };
410  return aSNS;
411 }
412 
413 
414 // XContent methods.
415 
416 
417 // virtual
418 OUString SAL_CALL Content::getContentType()
419 {
420  bool bFolder = false;
421  try
422  {
423  bFolder
424  = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
425  }
426  catch ( uno::RuntimeException const & )
427  {
428  throw;
429  }
430  catch ( uno::Exception const & )
431  {
432  }
433 
434  if ( bFolder )
435  return WEBDAV_COLLECTION_TYPE;
436 
437  return WEBDAV_CONTENT_TYPE;
438 }
439 
440 
441 // XCommandProcessor methods.
442 
443 
444 // virtual
445 uno::Any SAL_CALL Content::execute(
446  const ucb::Command& aCommand,
447  sal_Int32 /*CommandId*/,
448  const uno::Reference< ucb::XCommandEnvironment >& Environment )
449 {
450  SAL_INFO( "ucb.ucp.webdav", "Content::execute: start: command: " <<
451  aCommand.Name << ", env: " <<
452  (Environment.is() ? "present" : "missing") );
453 
454  uno::Any aRet;
455 
456  if ( aCommand.Name == "getPropertyValues" )
457  {
458 
459  // getPropertyValues
460 
461 
462  uno::Sequence< beans::Property > Properties;
463  if ( !( aCommand.Argument >>= Properties ) )
464  {
466  uno::makeAny( lang::IllegalArgumentException(
467  "Wrong argument type!",
468  static_cast< cppu::OWeakObject * >( this ),
469  -1 ) ),
470  Environment );
471  // Unreachable
472  }
473 
474  aRet <<= getPropertyValues( Properties, Environment );
475  }
476  else if ( aCommand.Name == "setPropertyValues" )
477  {
478 
479  // setPropertyValues
480 
481 
482  uno::Sequence< beans::PropertyValue > aProperties;
483  if ( !( aCommand.Argument >>= aProperties ) )
484  {
486  uno::makeAny( lang::IllegalArgumentException(
487  "Wrong argument type!",
488  static_cast< cppu::OWeakObject * >( this ),
489  -1 ) ),
490  Environment );
491  // Unreachable
492  }
493 
494  if ( !aProperties.hasElements() )
495  {
497  uno::makeAny( lang::IllegalArgumentException(
498  "No properties!",
499  static_cast< cppu::OWeakObject * >( this ),
500  -1 ) ),
501  Environment );
502  // Unreachable
503  }
504 
505  aRet <<= setPropertyValues( aProperties, Environment );
506  }
507  else if ( aCommand.Name == "getPropertySetInfo" )
508  {
509 
510  // getPropertySetInfo
511 
512 
513  // Note: Implemented by base class.
514  aRet <<= getPropertySetInfo( Environment,
515  false /* don't cache data */ );
516  }
517  else if ( aCommand.Name == "getCommandInfo" )
518  {
519 
520  // getCommandInfo
521 
522 
523  // Note: Implemented by base class.
524  aRet <<= getCommandInfo( Environment, false );
525  }
526  else if ( aCommand.Name == "open" )
527  {
528 
529  // open
530 
531 
532  ucb::OpenCommandArgument3 aOpenCommand;
533  ucb::OpenCommandArgument2 aTmp;
534  if ( !( aCommand.Argument >>= aTmp ) )
535  {
537  uno::makeAny( lang::IllegalArgumentException(
538  "Wrong argument type!",
539  static_cast< cppu::OWeakObject * >( this ),
540  -1 ) ),
541  Environment );
542  // Unreachable
543  }
544  if ( !( aCommand.Argument >>= aOpenCommand ) )
545  {
546  // compat mode, extract Arg2 info into newer structure
547  aOpenCommand.Mode = aTmp.Mode;
548  aOpenCommand.Priority = aTmp.Priority;
549  aOpenCommand.Sink = aTmp.Sink;
550  aOpenCommand.Properties = aTmp.Properties;
551  aOpenCommand.SortingInfo = aTmp.SortingInfo;
552  }
553 
554  aRet = open( aOpenCommand, Environment );
555 
556  }
557  else if ( aCommand.Name == "insert" )
558  {
559 
560  // insert
561 
562 
563  ucb::InsertCommandArgument arg;
564  if ( !( aCommand.Argument >>= arg ) )
565  {
567  uno::makeAny( lang::IllegalArgumentException(
568  "Wrong argument type!",
569  static_cast< cppu::OWeakObject * >( this ),
570  -1 ) ),
571  Environment );
572  // Unreachable
573  }
574 
575  insert( arg.Data, arg.ReplaceExisting, Environment );
576  }
577  else if ( aCommand.Name == "delete" )
578  {
579 
580  // delete
581 
582 
583  bool bDeletePhysical = false;
584  aCommand.Argument >>= bDeletePhysical;
585 
586 // KSO: Ignore parameter and destroy the content, if you don't support
587 // putting objects into trashcan. ( Since we do not have a trash can
588 // service yet (src603), you actually have no other choice. )
589 // if ( bDeletePhysical )
590 // {
591  try
592  {
593  std::unique_ptr< DAVResourceAccess > xResAccess;
594  {
595  osl::Guard< osl::Mutex > aGuard( m_aMutex );
596  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
597  }
598  aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
599  // clean cached value of PROPFIND property names
600  removeCachedPropertyNames( xResAccess->getURL() );
601  xResAccess->DESTROY( Environment );
602  {
603  osl::Guard< osl::Mutex > aGuard( m_aMutex );
604  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
605  }
606  }
607  catch ( DAVException const & e )
608  {
609  cancelCommandExecution( e, Environment, true );
610  // Unreachable
611  }
612 // }
613 
614  // Propagate destruction.
615  destroy( bDeletePhysical );
616 
617  // Remove own and all children's Additional Core Properties.
619  }
620  else if ( aCommand.Name == "transfer" && isFolder( Environment ) )
621  {
622 
623  // transfer
624  // ( Not available at documents )
625 
626 
627  ucb::TransferInfo transferArgs;
628  if ( !( aCommand.Argument >>= transferArgs ) )
629  {
631  uno::makeAny( lang::IllegalArgumentException(
632  "Wrong argument type!",
633  static_cast< cppu::OWeakObject * >( this ),
634  -1 ) ),
635  Environment );
636  // Unreachable
637  }
638 
639  transfer( transferArgs, Environment );
640  }
641  else if ( aCommand.Name == "post" )
642  {
643 
644  // post
645 
646 
647  ucb::PostCommandArgument2 aArg;
648  if ( !( aCommand.Argument >>= aArg ) )
649  {
651  uno::makeAny( lang::IllegalArgumentException(
652  "Wrong argument type!",
653  static_cast< cppu::OWeakObject * >( this ),
654  -1 ) ),
655  Environment );
656  // Unreachable
657  }
658 
659  post( aArg, Environment );
660  }
661  else if ( aCommand.Name == "lock" )
662  {
663 
664  // lock
665 
666  ResourceType eType = resourceTypeForLocks( Environment );
667  // when the resource is not yet present the lock is used to create it
668  // see: http://tools.ietf.org/html/rfc4918#section-7.3
669  // If the resource doesn't exists and the lock is not enabled (DAV with
670  // no lock or a simple web) the error will be dealt with inside lock() method
671  if ( eType == NOT_FOUND ||
672  eType == DAV )
673  {
674  lock( Environment );
675  if ( eType == NOT_FOUND )
676  {
677  m_eResourceType = UNKNOWN; // lock may have created it, need to check again
679  }
680  }
681  }
682  else if ( aCommand.Name == "unlock" )
683  {
684 
685  // unlock
686  // do not check for a DAV resource
687  // the lock store will be checked before sending
688  unlock( Environment );
689  }
690  else if ( aCommand.Name == "createNewContent" && isFolder( Environment ) )
691  {
692 
693  // createNewContent
694 
695 
696  ucb::ContentInfo aArg;
697  if ( !( aCommand.Argument >>= aArg ) )
698  {
700  uno::makeAny( lang::IllegalArgumentException(
701  "Wrong argument type!",
702  static_cast< cppu::OWeakObject * >( this ),
703  -1 ) ),
704  Environment );
705  // Unreachable
706  }
707 
708  aRet <<= createNewContent( aArg );
709  }
710  else if ( aCommand.Name == "addProperty" )
711  {
712  ucb::PropertyCommandArgument aPropArg;
713  if ( !( aCommand.Argument >>= aPropArg ))
714  {
716  uno::makeAny( lang::IllegalArgumentException(
717  "Wrong argument type!",
718  static_cast< cppu::OWeakObject * >( this ),
719  -1 ) ),
720  Environment );
721  }
722 
723  // TODO when/if XPropertyContainer is removed,
724  // the command execution can be canceled in addProperty
725  try
726  {
727  addProperty( aPropArg, Environment );
728  }
729  catch ( const beans::PropertyExistException &e )
730  {
731  ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
732  }
733  catch ( const beans::IllegalTypeException&e )
734  {
735  ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
736  }
737  catch ( const lang::IllegalArgumentException&e )
738  {
739  ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
740  }
741  }
742  else if ( aCommand.Name == "removeProperty" )
743  {
744  OUString sPropName;
745  if ( !( aCommand.Argument >>= sPropName ) )
746  {
748  uno::makeAny( lang::IllegalArgumentException(
749  "Wrong argument type!",
750  static_cast< cppu::OWeakObject * >( this ),
751  -1 ) ),
752  Environment );
753  }
754 
755  // TODO when/if XPropertyContainer is removed,
756  // the command execution can be canceled in removeProperty
757  try
758  {
759  removeProperty( sPropName, Environment );
760  }
761  catch( const beans::UnknownPropertyException &e )
762  {
763  ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
764  }
765  catch( const beans::NotRemoveableException &e )
766  {
767  ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
768  }
769  }
770  else
771  {
772 
773  // Unsupported command
774 
775 
777  uno::makeAny( ucb::UnsupportedCommandException(
778  aCommand.Name,
779  static_cast< cppu::OWeakObject * >( this ) ) ),
780  Environment );
781  // Unreachable
782  }
783 
784  SAL_INFO( "ucb.ucp.webdav", "Content::execute: end: command: " << aCommand.Name );
785 
786  return aRet;
787 }
788 
789 
790 // virtual
791 void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
792 {
793  try
794  {
795  std::unique_ptr< DAVResourceAccess > xResAccess;
796  {
797  osl::MutexGuard aGuard( m_aMutex );
798  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
799  }
800  xResAccess->abort();
801  {
802  osl::Guard< osl::Mutex > aGuard( m_aMutex );
803  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
804  }
805  }
806  catch ( DAVException const & )
807  {
808  // abort failed!
809  }
810 }
811 
812 
813 // XPropertyContainer methods.
814 
815 
816 void Content::addProperty( const ucb::PropertyCommandArgument& aCmdArg,
817  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
818 {
819 // if ( m_bTransient )
820 // @@@ ???
821 
822  if ( aCmdArg.Property.Name.isEmpty() )
823  throw lang::IllegalArgumentException(
824  "\"addProperty\" with empty Property.Name",
825  static_cast< cppu::OWeakObject * >( this ),
826  -1 );
827 
828  // Check property type.
829  if ( !UCBDeadPropertyValue::supportsType( aCmdArg.Property.Type ) )
830  {
831  throw beans::IllegalTypeException(
832  "\"addProperty\" unsupported Property.Type",
833  static_cast< cppu::OWeakObject * >( this ) );
834  }
835 
836  if ( aCmdArg.DefaultValue.hasValue()
837  && aCmdArg.DefaultValue.getValueType() != aCmdArg.Property.Type )
838  {
839  throw beans::IllegalTypeException(
840  "\"addProperty\" DefaultValue does not match Property.Type",
841  static_cast< ::cppu::OWeakObject * >( this ) );
842  }
843 
844 
845  // Make sure a property with the requested name does not already
846  // exist in dynamic and static(!) properties.
847 
848 
849  // Take into account special properties with custom namespace
850  // using <prop:the_propname xmlns:prop="the_namespace">
851  OUString aSpecialName;
852  bool bIsSpecial = DAVProperties::isUCBSpecialProperty(
853  aCmdArg.Property.Name, aSpecialName );
854 
855  // Note: This requires network access!
856  if ( getPropertySetInfo( xEnv, false /* don't cache data */ )
857  ->hasPropertyByName(
858  bIsSpecial ? aSpecialName : aCmdArg.Property.Name ) )
859  {
860  // Property does already exist.
861  throw beans::PropertyExistException();
862  }
863 
864 
865  // Add a new dynamic property.
866 
867 
868  ProppatchValue aValue(
869  PROPSET, aCmdArg.Property.Name, aCmdArg.DefaultValue );
870 
871  std::vector< ProppatchValue > aProppatchValues;
872  aProppatchValues.push_back( aValue );
873 
874  try
875  {
876  // Set property value at server.
877  std::unique_ptr< DAVResourceAccess > xResAccess;
878  {
879  osl::Guard< osl::Mutex > aGuard( m_aMutex );
880  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
881  }
882  aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
883  // clean cached value of PROPFIND property names
884  // PROPPATCH can change them
885  removeCachedPropertyNames( xResAccess->getURL() );
886  xResAccess->PROPPATCH( aProppatchValues, xEnv );
887  {
888  osl::Guard< osl::Mutex > aGuard( m_aMutex );
889  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
890  }
891 
892  // Notify propertyset info change listeners.
893  beans::PropertySetInfoChangeEvent evt(
894  static_cast< cppu::OWeakObject * >( this ),
895  bIsSpecial ? aSpecialName : aCmdArg.Property.Name,
896  -1, // No handle available
897  beans::PropertySetInfoChange::PROPERTY_INSERTED );
899  }
900  catch ( DAVException const & e )
901  {
902  if ( e.getStatus() == SC_FORBIDDEN )
903  {
904  // Support for setting arbitrary dead properties is optional!
905 
906  // Store property locally.
907  ContentImplHelper::addProperty(
908  bIsSpecial ? aSpecialName : aCmdArg.Property.Name,
909  aCmdArg.Property.Attributes, aCmdArg.DefaultValue );
910  }
911  else
912  {
914  {
915  try
916  {
917  ResourceType eType = getResourceType( xEnv );
918  switch ( eType )
919  {
920  case UNKNOWN:
921  case DAV:
922  throw lang::IllegalArgumentException();
923 
924  case FTP:
925  case NON_DAV:
926  // Store property locally.
927  ContentImplHelper::addProperty(
928  bIsSpecial ? aSpecialName : aCmdArg.Property.Name,
929  aCmdArg.Property.Attributes, aCmdArg.DefaultValue );
930  break;
931 
932  default:
933  SAL_WARN( "ucb.ucp.webdav", "Content::addProperty - "
934  "Unsupported resource type!" );
935  break;
936  }
937  }
938  catch ( uno::Exception const & )
939  {
940  SAL_WARN( "ucb.ucp.webdav", "Content::addProperty - "
941  "Unable to determine resource type!" );
942  }
943  }
944  else
945  {
946  SAL_WARN( "ucb.ucp.webdav", "Content::addProperty - "
947  "Unable to determine resource type!" );
948  }
949  }
950  }
951 }
952 
953 void Content::removeProperty( const OUString& Name,
954  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
955 {
956 
957  // Try to remove property from server.
958 
959 
960  try
961  {
962  std::vector< ProppatchValue > aProppatchValues;
963  ProppatchValue aValue( PROPREMOVE, Name, uno::Any() );
964  aProppatchValues.push_back( aValue );
965 
966  // Remove property value from server.
967  std::unique_ptr< DAVResourceAccess > xResAccess;
968  {
969  osl::Guard< osl::Mutex > aGuard( m_aMutex );
970  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
971  }
972  aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
973  // clean cached value of PROPFIND property names
974  // PROPPATCH can change them
975  removeCachedPropertyNames( xResAccess->getURL() );
976  xResAccess->PROPPATCH( aProppatchValues, xEnv );
977  {
978  osl::Guard< osl::Mutex > aGuard( m_aMutex );
979  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
980  }
981 
982  // Notify propertyset info change listeners.
983  beans::PropertySetInfoChangeEvent evt(
984  static_cast< cppu::OWeakObject * >( this ),
985  Name,
986  -1, // No handle available
987  beans::PropertySetInfoChange::PROPERTY_REMOVED );
989  }
990  catch ( DAVException const & e )
991  {
992  if ( e.getStatus() == SC_FORBIDDEN )
993  {
994  // Support for setting arbitrary dead properties is optional!
995 
996  // Try to remove property from local store.
997  ContentImplHelper::removeProperty( Name );
998  }
999  else
1000  {
1002  {
1003  try
1004  {
1005  ResourceType eType = getResourceType( xEnv );
1006  switch ( eType )
1007  {
1008  case UNKNOWN:
1009  case DAV:
1010  throw beans::UnknownPropertyException(Name);
1011 
1012  case FTP:
1013  case NON_DAV:
1014  // Try to remove property from local store.
1015  ContentImplHelper::removeProperty( Name );
1016  break;
1017 
1018  default:
1019  SAL_WARN( "ucb.ucp.webdav", "Content::removeProperty - "
1020  "Unsupported resource type!" );
1021  break;
1022  }
1023  }
1024  catch ( uno::Exception const & )
1025  {
1026  SAL_WARN( "ucb.ucp.webdav", "Content::removeProperty - "
1027  "Unable to determine resource type!" );
1028  }
1029  }
1030  else
1031  {
1032  SAL_WARN( "ucb.ucp.webdav", "Content::removeProperty - "
1033  "Unable to determine resource type!" );
1034 // throw beans::UnknownPropertyException();
1035  }
1036  }
1037  }
1038 }
1039 
1040 // virtual
1041 void SAL_CALL Content::addProperty( const OUString& Name,
1042  sal_Int16 Attributes,
1043  const uno::Any& DefaultValue )
1044 {
1045  beans::Property aProperty;
1046  aProperty.Name = Name;
1047  aProperty.Type = DefaultValue.getValueType();
1048  aProperty.Attributes = Attributes;
1049  aProperty.Handle = -1;
1050 
1051  addProperty( ucb::PropertyCommandArgument( aProperty, DefaultValue ),
1052  uno::Reference< ucb::XCommandEnvironment >());
1053 }
1054 
1055 // virtual
1056 void SAL_CALL Content::removeProperty( const OUString& Name )
1057 {
1058  removeProperty( Name,
1059  uno::Reference< ucb::XCommandEnvironment >() );
1060 }
1061 
1062 
1063 // XContentCreator methods.
1064 
1065 
1066 // virtual
1067 uno::Sequence< ucb::ContentInfo > SAL_CALL
1069 {
1070  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1071 
1072  uno::Sequence< ucb::ContentInfo > aSeq( 2 );
1073 
1074  // document.
1075  aSeq.getArray()[ 0 ].Type = WEBDAV_CONTENT_TYPE;
1076  aSeq.getArray()[ 0 ].Attributes
1077  = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
1078  | ucb::ContentInfoAttribute::KIND_DOCUMENT;
1079 
1080  beans::Property aProp;
1082  "Title", aProp );
1083 
1084  uno::Sequence< beans::Property > aDocProps( 1 );
1085  aDocProps.getArray()[ 0 ] = aProp;
1086  aSeq.getArray()[ 0 ].Properties = aDocProps;
1087 
1088  // folder.
1089  aSeq.getArray()[ 1 ].Type = WEBDAV_COLLECTION_TYPE;
1090  aSeq.getArray()[ 1 ].Attributes
1091  = ucb::ContentInfoAttribute::KIND_FOLDER;
1092 
1093  uno::Sequence< beans::Property > aFolderProps( 1 );
1094  aFolderProps.getArray()[ 0 ] = aProp;
1095  aSeq.getArray()[ 1 ].Properties = aFolderProps;
1096  return aSeq;
1097 }
1098 
1099 
1100 // virtual
1101 uno::Reference< ucb::XContent > SAL_CALL
1102 Content::createNewContent( const ucb::ContentInfo& Info )
1103 {
1104  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1105 
1106  if ( Info.Type.isEmpty() )
1107  return uno::Reference< ucb::XContent >();
1108 
1109  if ( ( Info.Type != WEBDAV_COLLECTION_TYPE ) && ( Info.Type != WEBDAV_CONTENT_TYPE ) )
1110  return uno::Reference< ucb::XContent >();
1111 
1112  OUString aURL = m_xIdentifier->getContentIdentifier();
1113 
1114  assert( !aURL.isEmpty() && "WebdavContent::createNewContent - empty identifier!" );
1115 
1116  if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
1117  aURL += "/";
1118 
1119  bool isCollection;
1120  if ( Info.Type == WEBDAV_COLLECTION_TYPE )
1121  {
1122  aURL += "New_Collection";
1123  isCollection = true;
1124  }
1125  else
1126  {
1127  aURL += "New_Content";
1128  isCollection = false;
1129  }
1130 
1131  uno::Reference< ucb::XContentIdentifier > xId(
1132  new ::ucbhelper::ContentIdentifier( aURL ) );
1133 
1134  // create the local content
1135  try
1136  {
1137  return new ::webdav_ucp::Content( m_xContext,
1138  m_pProvider,
1139  xId,
1140  m_xResAccess->getSessionFactory(),
1141  isCollection );
1142  }
1143  catch ( ucb::ContentCreationException & )
1144  {
1145  return uno::Reference< ucb::XContent >();
1146  }
1147 }
1148 
1149 
1150 // virtual
1152 {
1153  // <scheme>:// -> ""
1154  // <scheme>://foo -> ""
1155  // <scheme>://foo/ -> ""
1156  // <scheme>://foo/bar -> <scheme>://foo/
1157  // <scheme>://foo/bar/ -> <scheme>://foo/
1158  // <scheme>://foo/bar/abc -> <scheme>://foo/bar/
1159 
1160  OUString aURL = m_xIdentifier->getContentIdentifier();
1161 
1162  sal_Int32 nPos = aURL.lastIndexOf( '/' );
1163  if ( nPos == ( aURL.getLength() - 1 ) )
1164  {
1165  // Trailing slash found. Skip.
1166  nPos = aURL.lastIndexOf( '/', nPos );
1167  }
1168 
1169  sal_Int32 nPos1 = aURL.lastIndexOf( '/', nPos );
1170  if ( nPos1 != -1 )
1171  nPos1 = aURL.lastIndexOf( '/', nPos1 );
1172 
1173  if ( nPos1 == -1 )
1174  return OUString();
1175 
1176  return aURL.copy( 0, nPos + 1 );
1177 }
1178 
1179 
1180 // Non-interface methods.
1181 
1182 
1183 // static
1184 uno::Reference< sdbc::XRow > Content::getPropertyValues(
1185  const uno::Reference< uno::XComponentContext >& rxContext,
1186  const uno::Sequence< beans::Property >& rProperties,
1187  const ContentProperties& rData,
1189  const OUString& rContentId )
1190 {
1191  // Note: Empty sequence means "get values of all supported properties".
1192 
1194  = new ::ucbhelper::PropertyValueSet( rxContext );
1195 
1196  if ( rProperties.hasElements() )
1197  {
1198  uno::Reference< beans::XPropertySet > xAdditionalPropSet;
1199  bool bTriedToGetAdditionalPropSet = false;
1200 
1201  for ( const beans::Property& rProp : rProperties )
1202  {
1203  // Process standard UCB, DAV and HTTP properties.
1204  const uno::Any & rValue = rData.getValue( rProp.Name );
1205  if ( rValue.hasValue() )
1206  {
1207  xRow->appendObject( rProp, rValue );
1208  }
1209  else
1210  {
1211  // Process local Additional Properties.
1212  if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
1213  {
1214  xAdditionalPropSet =
1215  rProvider->getAdditionalPropertySet( rContentId,
1216  false );
1217  bTriedToGetAdditionalPropSet = true;
1218  }
1219 
1220  if ( !xAdditionalPropSet.is() ||
1221  !xRow->appendPropertySetValue(
1222  xAdditionalPropSet, rProp ) )
1223  {
1224  // Append empty entry.
1225  xRow->appendVoid( rProp );
1226  }
1227  }
1228  }
1229  }
1230  else
1231  {
1232  // Append all standard UCB, DAV and HTTP properties.
1233  const std::unique_ptr< PropertyValueMap > & xProps = rData.getProperties();
1234 
1235  ContentProvider * pProvider
1236  = static_cast< ContentProvider * >( rProvider.get() );
1237  beans::Property aProp;
1238 
1239  for ( const auto& rProp : *xProps )
1240  {
1241  pProvider->getProperty( rProp.first, aProp );
1242  xRow->appendObject( aProp, rProp.second.value() );
1243  }
1244 
1245  // Append all local Additional Properties.
1246  uno::Reference< beans::XPropertySet > xSet =
1247  rProvider->getAdditionalPropertySet( rContentId, false );
1248  xRow->appendPropertySet( xSet );
1249  }
1250 
1251  return uno::Reference< sdbc::XRow >( xRow.get() );
1252 }
1253 
1254 namespace {
1255 void GetPropsUsingHeadRequest(DAVResource& resource,
1256  const std::unique_ptr< DAVResourceAccess >& xResAccess,
1257  const std::vector< OUString >& aHTTPNames,
1258  const uno::Reference< ucb::XCommandEnvironment >& xEnv)
1259 {
1260  if (!aHTTPNames.empty())
1261  {
1262  DAVOptions aDAVOptions;
1263  OUString aTargetURL = xResAccess->getURL();
1264  // retrieve the cached options if any
1265  aStaticDAVOptionsCache.getDAVOptions(aTargetURL, aDAVOptions);
1266 
1267  // clean cached value of PROPFIND property names
1268  // PROPPATCH can change them
1270  // test if HEAD allowed, if not, throw, should be caught immediately
1271  // SC_GONE used internally by us, see comment in Content::getPropertyValues
1272  // in the catch scope
1273  if (aDAVOptions.getHttpResponseStatusCode() != SC_GONE &&
1274  !aDAVOptions.isHeadAllowed())
1275  {
1276  throw DAVException(DAVException::DAV_HTTP_ERROR, "405 Not Implemented", SC_METHOD_NOT_ALLOWED);
1277  }
1278  // if HEAD is enabled on this site
1279  // check if there is a relevant HTTP response status code cached
1280  if (aDAVOptions.getHttpResponseStatusCode() != SC_NONE)
1281  {
1282  // throws exception as if there was a server error, a DAV exception
1284  aDAVOptions.getHttpResponseStatusText(),
1285  aDAVOptions.getHttpResponseStatusCode());
1286  // Unreachable
1287  }
1288 
1289  xResAccess->HEAD(aHTTPNames, resource, xEnv);
1290  }
1291 }
1292 }
1293 
1294 uno::Reference< sdbc::XRow > Content::getPropertyValues(
1295  const uno::Sequence< beans::Property >& rProperties,
1296  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1297 {
1298  std::unique_ptr< ContentProperties > xProps;
1299  std::unique_ptr< ContentProperties > xCachedProps;
1300  std::unique_ptr< DAVResourceAccess > xResAccess;
1301  OUString aUnescapedTitle;
1302  bool bHasAll = false;
1303  uno::Reference< ucb::XContentIdentifier > xIdentifier;
1305 
1306  {
1307  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1308 
1309  aUnescapedTitle = NeonUri::unescape( m_aEscapedTitle );
1310  xIdentifier.set( m_xIdentifier );
1311  xProvider.set( m_xProvider.get() );
1312  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
1313 
1314  // First, ask cache...
1315  if (m_xCachedProps)
1316  {
1317  xCachedProps.reset(new ContentProperties(*m_xCachedProps));
1318 
1319  std::vector< OUString > aMissingProps;
1320  if ( xCachedProps->containsAllNames( rProperties, aMissingProps ) )
1321  {
1322  // All properties are already in cache! No server access needed.
1323  bHasAll = true;
1324  }
1325 
1326  // use the cached ContentProperties instance
1327  xProps.reset(new ContentProperties(*xCachedProps));
1328  }
1329  }
1330 
1331  if ( !m_bTransient && !bHasAll )
1332  {
1333 
1334  // Obtain values from server...
1335 
1336 
1337  // First, identify whether resource is DAV or not
1338  bool bNetworkAccessAllowed = true;
1339  ResourceType eType = getResourceType(
1340  xEnv, xResAccess, &bNetworkAccessAllowed );
1341 
1342  if ( eType == DAV )
1343  {
1344  // cache lookup... getResourceType may fill the props cache via
1345  // PROPFIND!
1346  if (m_xCachedProps)
1347  {
1348  xCachedProps.reset(new ContentProperties(*m_xCachedProps));
1349 
1350  std::vector< OUString > aMissingProps;
1351  if ( xCachedProps->containsAllNames(
1352  rProperties, aMissingProps ) )
1353  {
1354  // All properties are already in cache! No server access
1355  // needed.
1356  bHasAll = true;
1357  }
1358 
1359  // use the cached ContentProperties instance
1360  xProps.reset(new ContentProperties(*xCachedProps));
1361  }
1362 
1363  if ( !bHasAll )
1364  {
1365  // Only DAV resources support PROPFIND
1366  std::vector< OUString > aPropNames;
1367 
1368  uno::Sequence< beans::Property > aProperties(
1369  rProperties.getLength() );
1370 
1371  if ( !m_aFailedPropNames.empty() )
1372  {
1373  sal_Int32 nProps = rProperties.getLength();
1374  std::copy(rProperties.begin(), rProperties.end(), aProperties.begin());
1375 
1376  aProperties.realloc( nProps );
1377  }
1378  else
1379  {
1380  aProperties = rProperties;
1381  }
1382 
1383  if ( aProperties.hasElements() )
1385  aProperties, aPropNames );
1386 
1387  if ( !aPropNames.empty() )
1388  {
1389  std::vector< DAVResource > resources;
1390  try
1391  {
1392  xResAccess->PROPFIND(
1393  DAVZERO, aPropNames, resources, xEnv );
1394 
1395  if ( 1 == resources.size() )
1396  {
1397 #if defined SAL_LOG_INFO
1398  {//debug
1399  // print received resources
1400  for ( const auto& rProp : resources[0].properties )
1401  {
1402  OUString aPropValue;
1403  bool bValue;
1404  uno::Sequence< ucb::LockEntry > aSupportedLocks;
1405  if( rProp.Value >>= aPropValue )
1406  SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << rProp.Name << ":" << aPropValue );
1407  else if( rProp.Value >>= bValue )
1408  SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << rProp.Name << ":" <<
1409  ( bValue ? "true" : "false" ) );
1410  else if( rProp.Value >>= aSupportedLocks )
1411  {
1412  SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << rProp.Name << ":" );
1413  for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
1414  {
1415  SAL_INFO( "ucb.ucp.webdav"," scope: "
1416  << ( aSupportedLocks[ n ].Scope == css::ucb::LockScope_SHARED ? "shared" : "exclusive" )
1417  << ", type: "
1418  << ( aSupportedLocks[ n ].Type != css::ucb::LockType_WRITE ? "" : "write" ) );
1419  }
1420  }
1421  }
1422  }
1423 #endif
1424  if (xProps)
1425  xProps->addProperties(
1426  aPropNames,
1427  ContentProperties( resources[ 0 ] ));
1428  else
1429  xProps.reset(
1430  new ContentProperties( resources[ 0 ] ) );
1431  }
1432  }
1433  catch ( DAVException const & e )
1434  {
1435  bNetworkAccessAllowed = bNetworkAccessAllowed
1437 
1438  if ( !bNetworkAccessAllowed )
1439  {
1440  cancelCommandExecution( e, xEnv );
1441  // unreachable
1442  }
1443  }
1444  }
1445  }
1446  }
1447 
1448  if ( bNetworkAccessAllowed )
1449  {
1450  // All properties obtained already?
1451  std::vector< OUString > aMissingProps;
1452  if ( !( xProps.get()
1453  && xProps->containsAllNames(
1454  rProperties, aMissingProps ) )
1455  && !m_bDidGetOrHead )
1456  {
1457  // Possibly the missing props can be obtained using a HEAD
1458  // request.
1459 
1460  std::vector< OUString > aHeaderNames;
1462  rProperties,
1463  aHeaderNames );
1464 
1465  if( eType != DAV )
1466  {
1467  // in case of not DAV PROFIND (previously in program flow) failed
1468  // so we need to add the only prop that's common
1469  // to DAV and NON_DAV: MediaType, that maps to Content-Type
1470  aHeaderNames.emplace_back("Content-Type" );
1471  }
1472 
1473  if (!aHeaderNames.empty()) try
1474  {
1475  DAVResource resource;
1476  GetPropsUsingHeadRequest(resource, xResAccess, aHeaderNames, xEnv);
1477  m_bDidGetOrHead = true;
1478 
1479  if (xProps)
1480  xProps->addProperties(
1481  aMissingProps,
1482  ContentProperties(resource));
1483  else
1484  xProps.reset(new ContentProperties(resource));
1485 
1486  if (m_eResourceType == NON_DAV)
1487  xProps->addProperties(aMissingProps,
1489  aUnescapedTitle,
1490  false));
1491  }
1492  catch ( DAVException const & e )
1493  {
1494  // non "general-purpose servers" may not support HEAD requests
1495  // see http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1
1496  // In this case, perform a partial GET only to get the header info
1497  // vid. http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
1498  // WARNING if the server does not support partial GETs,
1499  // the GET will transfer the whole content
1500  bool bError = true;
1501  DAVException aLastException = e;
1502  OUString aTargetURL = xResAccess->getURL();
1503 
1505  {
1506  // According to the spec. the origin server SHOULD return
1507  // * 405 (Method Not Allowed):
1508  // the method is known but not allowed for the requested resource
1509  // * 501 (Not Implemented):
1510  // the method is unrecognized or not implemented
1511  // * 404 (SC_NOT_FOUND)
1512  // is for google-code server and for MS IIS 10.0 Web server
1513  // when only GET is enabled
1514  if ( aLastException.getStatus() == SC_NOT_IMPLEMENTED ||
1515  aLastException.getStatus() == SC_METHOD_NOT_ALLOWED ||
1516  aLastException.getStatus() == SC_NOT_FOUND )
1517  {
1518  SAL_WARN( "ucb.ucp.webdav", "HEAD probably not implemented: fall back to a partial GET" );
1519  aStaticDAVOptionsCache.setHeadAllowed( aTargetURL, false );
1520  lcl_sendPartialGETRequest( bError,
1521  aLastException,
1522  aMissingProps,
1523  aHeaderNames,
1524  xResAccess,
1525  xProps,
1526  xEnv );
1527  m_bDidGetOrHead = !bError;
1528  }
1529  }
1530 
1531  if ( bError )
1532  {
1533  DAVOptions aDAVOptionsException;
1534 
1535  aDAVOptionsException.setURL( aTargetURL );
1536  // check if the error was SC_NOT_FOUND, meaning that the
1537  // GET fall back didn't succeeded and the element is really missing
1538  // we will consider the resource SC_GONE (410) for some time
1539  // we use SC_GONE because has the same meaning of SC_NOT_FOUND (404)
1540  // see:
1541  // <https://tools.ietf.org/html/rfc7231#section-6.5.9> (retrieved 2016-10-09)
1542  // apparently it's not used to mark the missing HEAD method (so far...)
1543  sal_uInt16 ResponseStatusCode =
1544  ( aLastException.getStatus() == SC_NOT_FOUND ) ?
1545  SC_GONE :
1546  aLastException.getStatus();
1547  aDAVOptionsException.setHttpResponseStatusCode( ResponseStatusCode );
1548  aDAVOptionsException.setHttpResponseStatusText( aLastException.getData() );
1549  aStaticDAVOptionsCache.addDAVOptions( aDAVOptionsException,
1551 
1552  if ( !shouldAccessNetworkAfterException( aLastException ) )
1553  {
1554  cancelCommandExecution( aLastException, xEnv );
1555  // unreachable
1556  }
1557  }
1558  }
1559  }
1560  }
1561 
1562  // might trigger HTTP redirect.
1563  // Therefore, title must be updated here.
1564  NeonUri aUri( xResAccess->getURL() );
1565  aUnescapedTitle = aUri.GetPathBaseNameUnescaped();
1566 
1567  if ( eType == UNKNOWN )
1568  {
1569  xProps.reset( new ContentProperties( aUnescapedTitle ) );
1570  }
1571 
1572  // For DAV resources we only know the Title, for non-DAV
1573  // resources we additionally know that it is a document.
1574 
1575  if ( eType == DAV )
1576  {
1577  if (!xProps)
1578  xProps.reset(new ContentProperties(aUnescapedTitle));
1579  else
1580  xProps->addProperty("Title", uno::makeAny(aUnescapedTitle), true);
1581  }
1582  else
1583  {
1584  if (!xProps)
1585  xProps.reset( new ContentProperties( aUnescapedTitle, false ) );
1586  else
1587  xProps->addProperty(
1588  "Title",
1589  uno::makeAny( aUnescapedTitle ),
1590  true );
1591 
1592  xProps->addProperty(
1593  "IsFolder",
1594  uno::makeAny( false ),
1595  true );
1596  xProps->addProperty(
1597  "IsDocument",
1598  uno::makeAny( true ),
1599  true );
1600  }
1601  }
1602  else
1603  {
1604  // No server access for just created (not yet committed) objects.
1605  // Only a minimal set of properties supported at this stage.
1606  if (m_bTransient)
1607  xProps.reset( new ContentProperties( aUnescapedTitle,
1608  m_bCollection ) );
1609  }
1610 
1611  // Add a default for the properties requested but not found.
1612  // Determine still missing properties, add a default.
1613  // Some client function doesn't expect a void uno::Any,
1614  // but instead wants some sort of default.
1615  std::vector< OUString > aMissingProps;
1616  if ( !xProps->containsAllNames(
1617  rProperties, aMissingProps ) )
1618  {
1619  //
1620  for ( const auto& rProp : aMissingProps )
1621  {
1622  // For the time being only a couple of properties need to be added
1623  if ( rProp == "DateModified" || rProp == "DateCreated" )
1624  {
1625  util::DateTime aDate;
1626  xProps->addProperty(
1627  rProp,
1628  uno::makeAny( aDate ),
1629  true );
1630  }
1631  // If WebDAV didn't return the resource type, assume default
1632  // This happens e.g. for lists exported by SharePoint
1633  else if ( rProp == "IsFolder" )
1634  {
1635  xProps->addProperty(
1636  rProp,
1637  uno::makeAny( false ),
1638  true );
1639  }
1640  else if ( rProp == "IsDocument" )
1641  {
1642  xProps->addProperty(
1643  rProp,
1644  uno::makeAny( true ),
1645  true );
1646  }
1647  }
1648  }
1649 
1650  for ( const auto& rProperty : rProperties )
1651  {
1652  const OUString rName = rProperty.Name;
1653  if ( rName == "BaseURI" )
1654  {
1655  // Add BaseURI property, if requested.
1656  xProps->addProperty(
1657  "BaseURI",
1658  uno::makeAny( getBaseURI( xResAccess ) ),
1659  true );
1660  }
1661  else if ( rName == "CreatableContentsInfo" )
1662  {
1663  // Add CreatableContentsInfo property, if requested.
1664  bool bFolder = false;
1665  xProps->getValue(
1666  "IsFolder" )
1667  >>= bFolder;
1668  xProps->addProperty(
1669  "CreatableContentsInfo",
1670  uno::makeAny( bFolder
1672  : uno::Sequence< ucb::ContentInfo >() ),
1673  true );
1674  }
1675  }
1676 
1677  uno::Reference< sdbc::XRow > xResultRow
1679  rProperties,
1680  *xProps,
1681  xProvider,
1682  xIdentifier->getContentIdentifier() );
1683 
1684  {
1685  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1686 
1687  if (!m_xCachedProps)
1688  m_xCachedProps.reset(new CachableContentProperties(*xProps));
1689  else
1690  m_xCachedProps->addProperties(*xProps);
1691 
1692  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
1693  m_aEscapedTitle = NeonUri::escapeSegment( aUnescapedTitle );
1694  }
1695 
1696  return xResultRow;
1697 }
1698 
1699 
1700 uno::Sequence< uno::Any > Content::setPropertyValues(
1701  const uno::Sequence< beans::PropertyValue >& rValues,
1702  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1703 {
1704  uno::Reference< ucb::XContentIdentifier > xIdentifier;
1706  bool bTransient;
1707  std::unique_ptr< DAVResourceAccess > xResAccess;
1708 
1709  {
1710  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1711 
1712  xProvider.set( m_pProvider );
1713  xIdentifier.set( m_xIdentifier );
1714  bTransient = m_bTransient;
1715  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
1716  }
1717 
1718  uno::Sequence< uno::Any > aRet( rValues.getLength() );
1719  uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
1720  sal_Int32 nChanged = 0;
1721 
1722  beans::PropertyChangeEvent aEvent;
1723  aEvent.Source = static_cast< cppu::OWeakObject * >( this );
1724  aEvent.Further = false;
1725  // aEvent.PropertyName =
1726  aEvent.PropertyHandle = -1;
1727  // aEvent.OldValue =
1728  // aEvent.NewValue =
1729 
1730  std::vector< ProppatchValue > aProppatchValues;
1731 
1732  uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet;
1733  bool bTriedToGetAdditionalPropSet = false;
1734 
1735  bool bExchange = false;
1736  OUString aNewTitle;
1737  OUString aOldTitle;
1738  sal_Int32 nTitlePos = -1;
1739 
1740  uno::Reference< beans::XPropertySetInfo > xInfo;
1741 
1742  const beans::PropertyValue* pValues = rValues.getConstArray();
1743  sal_Int32 nCount = rValues.getLength();
1744  for ( sal_Int32 n = 0; n < nCount; ++n )
1745  {
1746  const beans::PropertyValue& rValue = pValues[ n ];
1747  const OUString & rName = rValue.Name;
1748 
1749  beans::Property aTmpProp;
1750  xProvider->getProperty( rName, aTmpProp );
1751 
1752  if ( aTmpProp.Attributes & beans::PropertyAttribute::READONLY )
1753  {
1754  // Read-only property!
1755  aRet[ n ] <<= lang::IllegalAccessException(
1756  "Property is read-only!",
1757  static_cast< cppu::OWeakObject * >( this ) );
1758  continue;
1759  }
1760 
1761 
1762  // Mandatory props.
1763 
1764 
1765  if ( rName == "ContentType" )
1766  {
1767  // Read-only property!
1768  aRet[ n ] <<= lang::IllegalAccessException(
1769  "Property is read-only!",
1770  static_cast< cppu::OWeakObject * >( this ) );
1771  }
1772  else if ( rName == "IsDocument" )
1773  {
1774  // Read-only property!
1775  aRet[ n ] <<= lang::IllegalAccessException(
1776  "Property is read-only!",
1777  static_cast< cppu::OWeakObject * >( this ) );
1778  }
1779  else if ( rName == "IsFolder" )
1780  {
1781  // Read-only property!
1782  aRet[ n ] <<= lang::IllegalAccessException(
1783  "Property is read-only!",
1784  static_cast< cppu::OWeakObject * >( this ) );
1785  }
1786  else if ( rName == "Title" )
1787  {
1788  OUString aNewValue;
1789  if ( rValue.Value >>= aNewValue )
1790  {
1791  // No empty titles!
1792  if ( !aNewValue.isEmpty() )
1793  {
1794  try
1795  {
1796  NeonUri aURI( xIdentifier->getContentIdentifier() );
1797  aOldTitle = aURI.GetPathBaseNameUnescaped();
1798 
1799  if ( aNewValue != aOldTitle )
1800  {
1801  // modified title -> modified URL -> exchange !
1802  if ( !bTransient )
1803  bExchange = true;
1804 
1805  // new value will be set later...
1806  aNewTitle = aNewValue;
1807 
1808  // remember position within sequence of values (for
1809  // error handling).
1810  nTitlePos = n;
1811  }
1812  }
1813  catch ( DAVException const & )
1814  {
1815  aRet[ n ] <<= lang::IllegalArgumentException(
1816  "Invalid content identifier!",
1817  static_cast< cppu::OWeakObject * >( this ),
1818  -1 );
1819  }
1820  }
1821  else
1822  {
1823  aRet[ n ] <<= lang::IllegalArgumentException(
1824  "Empty title not allowed!",
1825  static_cast< cppu::OWeakObject * >( this ),
1826  -1 );
1827  }
1828  }
1829  else
1830  {
1831  aRet[ n ] <<= beans::IllegalTypeException(
1832  "Property value has wrong type!",
1833  static_cast< cppu::OWeakObject * >( this ) );
1834  }
1835  }
1836  else
1837  {
1838 
1839  // Optional props.
1840 
1841 
1842  OUString aSpecialName;
1843  bool bIsSpecial = DAVProperties::isUCBSpecialProperty(
1844  rName, aSpecialName );
1845 
1846  if ( !xInfo.is() )
1847  xInfo = getPropertySetInfo( xEnv,
1848  false /* don't cache data */ );
1849 
1850  if ( !xInfo->hasPropertyByName(
1851  bIsSpecial ? aSpecialName : rName ) )
1852  {
1853  // Check, whether property exists. Skip otherwise.
1854  // PROPPATCH::set would add the property automatically, which
1855  // is not allowed for "setPropertyValues" command!
1856  aRet[ n ] <<= beans::UnknownPropertyException(
1857  "Property is unknown!",
1858  static_cast< cppu::OWeakObject * >( this ) );
1859  continue;
1860  }
1861 
1862  if ( rName == "Size" )
1863  {
1864  // Read-only property!
1865  aRet[ n ] <<= lang::IllegalAccessException(
1866  "Property is read-only!",
1867  static_cast< cppu::OWeakObject * >( this ) );
1868  }
1869  else if ( rName == "DateCreated" )
1870  {
1871  // Read-only property!
1872  aRet[ n ] <<= lang::IllegalAccessException(
1873  "Property is read-only!",
1874  static_cast< cppu::OWeakObject * >( this ) );
1875  }
1876  else if ( rName == "DateModified" )
1877  {
1878  // Read-only property!
1879  aRet[ n ] <<= lang::IllegalAccessException(
1880  "Property is read-only!",
1881  static_cast< cppu::OWeakObject * >( this ) );
1882  }
1883  else if ( rName == "MediaType" )
1884  {
1885  // Read-only property!
1886  // (but could be writable, if 'getcontenttype' would be)
1887  aRet[ n ] <<= lang::IllegalAccessException(
1888  "Property is read-only!",
1889  static_cast< cppu::OWeakObject * >( this ) );
1890  }
1891  if ( rName == "CreatableContentsInfo" )
1892  {
1893  // Read-only property!
1894  aRet[ n ] <<= lang::IllegalAccessException(
1895  "Property is read-only!",
1896  static_cast< cppu::OWeakObject * >( this ) );
1897  }
1898  else
1899  {
1900  if ( getResourceType( xEnv, xResAccess ) == DAV )
1901  {
1902  // Property value will be set on server.
1903  ProppatchValue aValue( PROPSET, rName, rValue.Value );
1904  aProppatchValues.push_back( aValue );
1905  }
1906  else
1907  {
1908  // Property value will be stored in local property store.
1909  if ( !bTriedToGetAdditionalPropSet &&
1910  !xAdditionalPropSet.is() )
1911  {
1912  xAdditionalPropSet
1913  = getAdditionalPropertySet( false );
1914  bTriedToGetAdditionalPropSet = true;
1915  }
1916 
1917  if ( xAdditionalPropSet.is() )
1918  {
1919  try
1920  {
1921  uno::Any aOldValue
1922  = xAdditionalPropSet->getPropertyValue( rName );
1923  if ( aOldValue != rValue.Value )
1924  {
1925  xAdditionalPropSet->setPropertyValue(
1926  rName, rValue.Value );
1927 
1928  aEvent.PropertyName = rName;
1929  aEvent.OldValue = aOldValue;
1930  aEvent.NewValue = rValue.Value;
1931 
1932  aChanges.getArray()[ nChanged ] = aEvent;
1933  nChanged++;
1934  }
1935  }
1936  catch ( beans::UnknownPropertyException const & e )
1937  {
1938  aRet[ n ] <<= e;
1939  }
1940  catch ( lang::WrappedTargetException const & e )
1941  {
1942  aRet[ n ] <<= e;
1943  }
1944  catch ( beans::PropertyVetoException const & e )
1945  {
1946  aRet[ n ] <<= e;
1947  }
1948  catch ( lang::IllegalArgumentException const & e )
1949  {
1950  aRet[ n ] <<= e;
1951  }
1952  }
1953  else
1954  {
1955  aRet[ n ] <<= uno::Exception(
1956  "No property set for storing the value!",
1957  static_cast< cppu::OWeakObject * >( this ) );
1958  }
1959  }
1960  }
1961  }
1962  } // for
1963 
1964  if ( !bTransient && !aProppatchValues.empty() )
1965  {
1966  try
1967  {
1968  // clean cached value of PROPFIND property names
1969  // PROPPATCH can change them
1970  removeCachedPropertyNames( xResAccess->getURL() );
1971  // Set property values at server.
1972  aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
1973  xResAccess->PROPPATCH( aProppatchValues, xEnv );
1974 
1975  for ( const auto& rProppatchValue : aProppatchValues )
1976  {
1977  aEvent.PropertyName = rProppatchValue.name;
1978  aEvent.OldValue = uno::Any(); // @@@ too expensive to obtain!
1979  aEvent.NewValue = rProppatchValue.value;
1980 
1981  aChanges.getArray()[ nChanged ] = aEvent;
1982  nChanged++;
1983  }
1984  }
1985  catch ( DAVException const & e )
1986  {
1987  SAL_WARN( "ucb.ucp.webdav", "Content::setPropertyValues - PROPPATCH failed!" );
1988  cancelCommandExecution( e, xEnv );
1989  // unreachable
1990  }
1991  }
1992 
1993  if ( bExchange )
1994  {
1995  // Assemble new content identifier...
1996 
1997  OUString aNewURL = getParentURL();
1998  if ( aNewURL.lastIndexOf( '/' ) != ( aNewURL.getLength() - 1 ) )
1999  aNewURL += "/";
2000 
2001  aNewURL += NeonUri::escapeSegment( aNewTitle );
2002 
2003  uno::Reference< ucb::XContentIdentifier > xNewId
2004  = new ::ucbhelper::ContentIdentifier( aNewURL );
2005 
2006  NeonUri sourceURI( xIdentifier->getContentIdentifier() );
2007  NeonUri targetURI( xNewId->getContentIdentifier() );
2008 
2009  try
2010  {
2011  targetURI.SetScheme( sourceURI.GetScheme() );
2012 
2013  // clean cached value of PROPFIND property names
2014  removeCachedPropertyNames( sourceURI.GetURI() );
2015  removeCachedPropertyNames( targetURI.GetURI() );
2016  aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
2017  aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
2018  xResAccess->MOVE(
2019  sourceURI.GetPath(), targetURI.GetURI(), false, xEnv );
2020 
2021  // @@@ Should check for resources that could not be moved
2022  // (due to source access or target overwrite) and send
2023  // this information through the interaction handler.
2024 
2025  // @@@ Existing content should be checked to see if it needs
2026  // to be deleted at the source
2027 
2028  // @@@ Existing content should be checked to see if it has
2029  // been overwritten at the target
2030 
2031  if ( exchangeIdentity( xNewId ) )
2032  {
2033  xResAccess->setURL( aNewURL );
2034 
2035 // DAV resources store all additional props on server!
2036 // // Adapt Additional Core Properties.
2037 // renameAdditionalPropertySet( xOldId->getContentIdentifier(),
2038 // xNewId->getContentIdentifier(),
2039 // sal_True );
2040  }
2041  else
2042  {
2043  // Do not set new title!
2044  aNewTitle.clear();
2045 
2046  // Set error .
2047  aRet[ nTitlePos ] <<= uno::Exception(
2048  "Exchange failed!",
2049  static_cast< cppu::OWeakObject * >( this ) );
2050  }
2051  }
2052  catch ( DAVException const & e )
2053  {
2054  // Do not set new title!
2055  aNewTitle.clear();
2056 
2057  // Set error .
2058  aRet[ nTitlePos ] = MapDAVException( e, true );
2059  }
2060  }
2061 
2062  if ( !aNewTitle.isEmpty() )
2063  {
2064  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2065 
2066  aEvent.PropertyName = "Title";
2067  aEvent.OldValue <<= aOldTitle;
2068  aEvent.NewValue <<= aNewTitle;
2069 
2070  m_aEscapedTitle = NeonUri::escapeSegment( aNewTitle );
2071 
2072  aChanges.getArray()[ nChanged ] = aEvent;
2073  nChanged++;
2074  }
2075 
2076  if ( nChanged > 0 )
2077  {
2078  aChanges.realloc( nChanged );
2079  notifyPropertiesChange( aChanges );
2080  }
2081 
2082  {
2083  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2084  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
2085  }
2086 
2087  return aRet;
2088 }
2089 
2090 
2092  const ucb::OpenCommandArgument3 & rArg,
2093  const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2094 {
2095  uno::Any aRet;
2096 
2097  bool bOpenFolder = ( ( rArg.Mode == ucb::OpenMode::ALL ) ||
2098  ( rArg.Mode == ucb::OpenMode::FOLDERS ) ||
2099  ( rArg.Mode == ucb::OpenMode::DOCUMENTS ) );
2100  if ( bOpenFolder )
2101  {
2102  if ( isFolder( xEnv ) )
2103  {
2104  // Open collection.
2105 
2106  uno::Reference< ucb::XDynamicResultSet > xSet
2107  = new DynamicResultSet( m_xContext, this, rArg, xEnv );
2108  aRet <<= xSet;
2109  }
2110  else
2111  {
2112  // Error: Not a folder!
2113 
2114  OUStringBuffer aMsg;
2115  if ( getResourceType( xEnv ) == FTP )
2116  {
2117  aMsg.append( "FTP over HTTP proxy: resource cannot "
2118  "be opened as folder! Wrong Open Mode!" );
2119  }
2120  else
2121  {
2122  aMsg.append( "Non-folder resource cannot be "
2123  "opened as folder! Wrong Open Mode!" );
2124  }
2125 
2127  uno::makeAny(
2128  lang::IllegalArgumentException(
2129  aMsg.makeStringAndClear(),
2130  static_cast< cppu::OWeakObject * >( this ),
2131  -1 ) ),
2132  xEnv );
2133  // Unreachable
2134  }
2135  }
2136 
2137  if ( rArg.Sink.is() )
2138  {
2139  // Open document.
2140 
2141  if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
2142  ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
2143  {
2144  // Currently(?) unsupported.
2146  uno::makeAny(
2147  ucb::UnsupportedOpenModeException(
2148  OUString(),
2149  static_cast< cppu::OWeakObject * >( this ),
2150  sal_Int16( rArg.Mode ) ) ),
2151  xEnv );
2152  // Unreachable
2153  }
2154 
2155  uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY );
2156  if ( xOut.is() )
2157  {
2158  // PUSH: write data
2159  try
2160  {
2161  std::unique_ptr< DAVResourceAccess > xResAccess;
2162 
2163  {
2164  osl::MutexGuard aGuard( m_aMutex );
2165 
2166  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
2167  }
2168 
2169  xResAccess->setFlags( rArg.OpeningFlags );
2170  DAVResource aResource;
2171  std::vector< OUString > aHeaders;
2172 
2173  removeCachedPropertyNames( xResAccess->getURL() );
2174  xResAccess->GET( xOut, aHeaders, aResource, xEnv );
2175  m_bDidGetOrHead = true;
2176 
2177  {
2178  osl::MutexGuard aGuard( m_aMutex );
2179 
2180  // cache headers.
2181  if (!m_xCachedProps)
2182  m_xCachedProps.reset(
2183  new CachableContentProperties( ContentProperties( aResource ) ) );
2184  else
2185  m_xCachedProps->addProperties( ContentProperties( aResource ) );
2186 
2187  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
2188  }
2189  }
2190  catch ( DAVException const & e )
2191  {
2192  cancelCommandExecution( e, xEnv );
2193  // Unreachable
2194  }
2195  }
2196  else
2197  {
2198  uno::Reference< io::XActiveDataSink > xDataSink( rArg.Sink, uno::UNO_QUERY );
2199  if ( xDataSink.is() )
2200  {
2201  // PULL: wait for client read
2202  OUString aTargetURL = m_xIdentifier->getContentIdentifier();
2203  try
2204  {
2205  std::unique_ptr< DAVResourceAccess > xResAccess;
2206  {
2207  osl::MutexGuard aGuard( m_aMutex );
2208 
2209  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
2210  }
2211  xResAccess->setFlags( rArg.OpeningFlags );
2212 
2213  // fill inputstream sync; return if all data present
2214  DAVResource aResource;
2215  std::vector< OUString > aHeaders;
2216 
2217  aTargetURL = xResAccess->getURL();
2218  removeCachedPropertyNames( aTargetURL );
2219  // check if the resource was present on the server
2220  // first update it, if necessary
2221  // if the open is called directly, without the default open sequence,
2222  // e.g. the one used when opening a file looking for properties
2223  // first this call will have no effect, since OPTIONS would have already been called
2224  // as a consequence of getPropertyValues()
2225  DAVOptions aDAVOptions;
2226  getResourceOptions( xEnv, aDAVOptions, xResAccess );
2227 
2228  if ( aDAVOptions.getHttpResponseStatusCode() != SC_NONE )
2229  {
2230  // throws exception as if there was a server error, a DAV exception
2232  aDAVOptions.getHttpResponseStatusText(),
2233  aDAVOptions.getHttpResponseStatusCode() );
2234  }
2235  uno::Reference< io::XInputStream > xIn
2236  = xResAccess->GET( aHeaders, aResource, xEnv );
2237  m_bDidGetOrHead = true;
2238 
2239  {
2240  osl::MutexGuard aGuard( m_aMutex );
2241 
2242  // cache headers.
2243  if (!m_xCachedProps)
2244  m_xCachedProps.reset(
2245  new CachableContentProperties( ContentProperties( aResource ) ) );
2246  else
2247  m_xCachedProps->addProperties(
2248  aResource.properties );
2249 
2250  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
2251  }
2252 
2253  xDataSink->setInputStream( xIn );
2254  }
2255  catch ( DAVException const & e )
2256  {
2257  //TODO cache the http error if not yet cached
2258  cancelCommandExecution( e, xEnv );
2259  // Unreachable
2260  }
2261  }
2262  else
2263  {
2264  // Note: aOpenCommand.Sink may contain an XStream
2265  // implementation. Support for this type of
2266  // sink is optional...
2268  uno::makeAny(
2269  ucb::UnsupportedDataSinkException(
2270  OUString(),
2271  static_cast< cppu::OWeakObject * >( this ),
2272  rArg.Sink ) ),
2273  xEnv );
2274  // Unreachable
2275  }
2276  }
2277  }
2278 
2279  return aRet;
2280 }
2281 
2282 void Content::post(
2283  const ucb::PostCommandArgument2 & rArg,
2284  const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2285 {
2286  uno::Reference< io::XActiveDataSink > xSink( rArg.Sink, uno::UNO_QUERY );
2287  if ( xSink.is() )
2288  {
2289  try
2290  {
2291  std::unique_ptr< DAVResourceAccess > xResAccess;
2292  {
2293  osl::MutexGuard aGuard( m_aMutex );
2294  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
2295  }
2296 
2297  removeCachedPropertyNames( xResAccess->getURL() );
2298  uno::Reference< io::XInputStream > xResult
2299  = xResAccess->POST( rArg.MediaType,
2300  rArg.Referer,
2301  rArg.Source,
2302  xEnv );
2303 
2304  {
2305  osl::MutexGuard aGuard( m_aMutex );
2306  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
2307  }
2308 
2309  xSink->setInputStream( xResult );
2310  }
2311  catch ( DAVException const & e )
2312  {
2313  cancelCommandExecution( e, xEnv, true );
2314  // Unreachable
2315  }
2316  }
2317  else
2318  {
2319  uno::Reference< io::XOutputStream > xResult( rArg.Sink, uno::UNO_QUERY );
2320  if ( xResult.is() )
2321  {
2322  try
2323  {
2324  std::unique_ptr< DAVResourceAccess > xResAccess;
2325  {
2326  osl::MutexGuard aGuard( m_aMutex );
2327  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
2328  }
2329 
2330  removeCachedPropertyNames( xResAccess->getURL() );
2331  xResAccess->POST( rArg.MediaType,
2332  rArg.Referer,
2333  rArg.Source,
2334  xResult,
2335  xEnv );
2336 
2337  {
2338  osl::MutexGuard aGuard( m_aMutex );
2339  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
2340  }
2341  }
2342  catch ( DAVException const & e )
2343  {
2344  cancelCommandExecution( e, xEnv, true );
2345  // Unreachable
2346  }
2347  }
2348  else
2349  {
2351  uno::makeAny(
2352  ucb::UnsupportedDataSinkException(
2353  OUString(),
2354  static_cast< cppu::OWeakObject * >( this ),
2355  rArg.Sink ) ),
2356  xEnv );
2357  // Unreachable
2358  }
2359  }
2360 }
2361 
2362 
2364 {
2365  // Obtain a list with a snapshot of all currently instantiated contents
2366  // from provider and extract the contents which are direct children
2367  // of this content.
2368 
2369  ::ucbhelper::ContentRefList aAllContents;
2370  m_xProvider->queryExistingContents( aAllContents );
2371 
2372  OUString aURL = m_xIdentifier->getContentIdentifier();
2373  sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
2374 
2375  if ( nURLPos != ( aURL.getLength() - 1 ) )
2376  {
2377  // No trailing slash found. Append.
2378  aURL += "/";
2379  }
2380 
2381  sal_Int32 nLen = aURL.getLength();
2382 
2383  for ( const auto& rChild : aAllContents )
2384  {
2385  ::ucbhelper::ContentImplHelperRef xChild = rChild;
2386  OUString aChildURL
2387  = xChild->getIdentifier()->getContentIdentifier();
2388 
2389  // Is aURL a prefix of aChildURL?
2390  if ( ( aChildURL.getLength() > nLen ) &&
2391  ( aChildURL.startsWith( aURL ) ) )
2392  {
2393  sal_Int32 nPos = aChildURL.indexOf( '/', nLen );
2394 
2395  if ( ( nPos == -1 ) ||
2396  ( nPos == ( aChildURL.getLength() - 1 ) ) )
2397  {
2398  // No further slashes / only a final slash. It's a child!
2399  rChildren.emplace_back(
2400  static_cast< ::webdav_ucp::Content * >(
2401  xChild.get() ) );
2402  }
2403  }
2404  }
2405 }
2406 
2407 
2408 void Content::insert(
2409  const uno::Reference< io::XInputStream > & xInputStream,
2410  bool bReplaceExisting,
2411  const uno::Reference< ucb::XCommandEnvironment >& Environment )
2412 {
2413  bool bTransient, bCollection;
2414  OUString aEscapedTitle;
2415  std::unique_ptr< DAVResourceAccess > xResAccess;
2416 
2417  {
2418  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2419 
2420  bTransient = m_bTransient;
2421  bCollection = m_bCollection;
2422  aEscapedTitle = m_aEscapedTitle;
2423  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
2424  }
2425 
2426  // Check, if all required properties are present.
2427 
2428  if ( aEscapedTitle.isEmpty() )
2429  {
2430  SAL_WARN( "ucb.ucp.webdav", "Content::insert - Title missing!" );
2431 
2432  uno::Sequence<OUString> aProps { "Title" };
2434  uno::makeAny( ucb::MissingPropertiesException(
2435  OUString(),
2436  static_cast< cppu::OWeakObject * >( this ),
2437  aProps ) ),
2438  Environment );
2439  // Unreachable
2440  }
2441 
2442  if ( !bReplaceExisting )
2443  {
2444  /* [RFC 2616] - HTTP
2445 
2446  The PUT method requests that the enclosed entity be stored under the
2447  supplied Request-URI. If the Request-URI refers to an already
2448  existing resource, the enclosed entity SHOULD be considered as a
2449  modified version of the one residing on the origin server.
2450  */
2451 
2452  /* [RFC 2518] - WebDAV
2453 
2454  MKCOL creates a new collection resource at the location specified by
2455  the Request-URI. If the resource identified by the Request-URI is
2456  non-null then the MKCOL MUST fail.
2457  */
2458 
2459  // ==> Complain on PUT, continue on MKCOL.
2460  if ( !bTransient || !bCollection )
2461  {
2462  ucb::UnsupportedNameClashException aEx(
2463  "Unable to write without overwrite!",
2464  static_cast< cppu::OWeakObject * >( this ),
2465  ucb::NameClash::ERROR );
2466 
2467  uno::Reference< task::XInteractionHandler > xIH;
2468 
2469  if ( Environment.is() )
2470  xIH = Environment->getInteractionHandler();
2471 
2472  if ( !xIH.is() )
2473  {
2474  // No IH; throw.
2475  throw aEx;
2476  }
2477 
2478  uno::Any aExAsAny( uno::makeAny( aEx ) );
2479 
2482  aExAsAny,
2483  ContinuationFlags::Approve | ContinuationFlags::Disapprove );
2484  xIH->handle( xRequest.get() );
2485 
2486  const ContinuationFlags nResp = xRequest->getResponse();
2487 
2488  switch ( nResp )
2489  {
2490  case ContinuationFlags::NONE:
2491  // Not handled; throw.
2492  throw aEx;
2493 // break;
2494 
2495  case ContinuationFlags::Approve:
2496  // Continue -> Overwrite.
2497  bReplaceExisting = true;
2498  break;
2499 
2500  case ContinuationFlags::Disapprove:
2501  // Abort.
2502  throw ucb::CommandFailedException(
2503  OUString(),
2504  uno::Reference< uno::XInterface >(),
2505  aExAsAny );
2506 // break;
2507 
2508  default:
2509  SAL_WARN( "ucb.ucp.webdav", "Content::insert - "
2510  "Unknown interaction selection!" );
2511  throw ucb::CommandFailedException(
2512  "Unknown interaction selection!",
2513  uno::Reference< uno::XInterface >(),
2514  aExAsAny );
2515 // break;
2516  }
2517 
2518  }
2519  }
2520 
2521  if ( bTransient )
2522  {
2523  // Assemble new content identifier...
2524  OUString aURL = getParentURL();
2525  if ( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ) )
2526  aURL += "/";
2527 
2528  aURL += aEscapedTitle;
2529 
2530  try
2531  {
2532  xResAccess->setURL( aURL );
2533 
2534  if ( bCollection )
2535  {
2536  aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
2537  removeCachedPropertyNames( xResAccess->getURL() );
2538  xResAccess->MKCOL( Environment );
2539  }
2540  else
2541  {
2542  // remove options from cache, PUT may change it
2543  // it will be refreshed when needed
2544  aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
2545  removeCachedPropertyNames( xResAccess->getURL() );
2546  xResAccess->PUT( xInputStream, Environment );
2547  // clean cached value of PROPFIND properties names
2548  }
2549  // no error , set the resourcetype to unknown type
2550  // the resource may have transitioned from NOT FOUND or UNKNOWN to something else
2551  // depending on the server behaviour
2552  // this will force a recheck of the resource type
2555  }
2556  catch ( DAVException const & except )
2557  {
2558  if ( bCollection )
2559  {
2560  if ( except.getStatus() == SC_METHOD_NOT_ALLOWED )
2561  {
2562  // [RFC 2518] - WebDAV
2563  // 405 (Method Not Allowed) - MKCOL can only be
2564  // executed on a deleted/non-existent resource.
2565 
2566  if ( bReplaceExisting )
2567  {
2568  // Destroy old resource.
2569  try
2570  {
2571  removeCachedPropertyNames( xResAccess->getURL() );
2572  xResAccess->DESTROY( Environment );
2573  }
2574  catch ( DAVException const & e )
2575  {
2576  cancelCommandExecution( e, Environment, true );
2577  // Unreachable
2578  }
2579 
2580  // Insert (recursion!).
2581  insert( xInputStream, bReplaceExisting, Environment );
2582 
2583  {
2584  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2585  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
2586  }
2587 
2588  // Success!
2589  return;
2590  }
2591  else
2592  {
2593  OUString aTitle;
2594  try
2595  {
2596  NeonUri aURI( aURL );
2597  aTitle = aURI.GetPathBaseNameUnescaped();
2598  }
2599  catch ( DAVException const & )
2600  {
2601  }
2602 
2604  uno::makeAny(
2605  ucb::NameClashException(
2606  OUString(),
2607  static_cast< cppu::OWeakObject * >( this ),
2608  task::InteractionClassification_ERROR,
2609  aTitle ) ),
2610  Environment );
2611  // Unreachable
2612  }
2613  }
2614  }
2615 
2616  cancelCommandExecution( except, Environment, true );
2617  // Unreachable
2618  }
2619 
2620  {
2621  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2622  m_xIdentifier = new ::ucbhelper::ContentIdentifier( aURL );
2623  }
2624 
2625  inserted();
2626 
2627  {
2628  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2629  m_bTransient = false;
2630  }
2631  }
2632  else
2633  {
2634  if ( !xInputStream.is() )
2635  {
2637  uno::makeAny(
2638  ucb::MissingInputStreamException(
2639  OUString(),
2640  static_cast< cppu::OWeakObject * >( this ) ) ),
2641  Environment );
2642  // Unreachable
2643  }
2644 
2645  // save the URL since it may change due to redirection
2646  OUString aTargetUrl = xResAccess->getURL();
2647  try
2648  {
2649  removeCachedPropertyNames( xResAccess->getURL() );
2650  // remove options from cache, PUT may change it
2651  // it will be refreshed when needed
2652  aStaticDAVOptionsCache.removeDAVOptions( aTargetUrl );
2653  xResAccess->PUT( xInputStream, Environment );
2654  }
2655  catch ( DAVException const & e )
2656  {
2657  cancelCommandExecution( e, Environment, true );
2658  // Unreachable
2659  }
2660  }
2661 
2662  {
2663  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2664  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
2665  }
2666 }
2667 
2668 
2669 void Content::transfer(
2670  const ucb::TransferInfo & rArgs,
2671  const uno::Reference< ucb::XCommandEnvironment >& Environment )
2672 {
2673  uno::Reference< ucb::XContentIdentifier > xIdentifier;
2674  uno::Reference< ucb::XContentProvider > xProvider;
2675  std::unique_ptr< DAVResourceAccess > xResAccess;
2676 
2677  {
2678  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2679 
2680  xIdentifier.set( m_xIdentifier );
2681  xProvider.set( m_xProvider.get() );
2682  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
2683  }
2684 
2685  NeonUri sourceURI( rArgs.SourceURL );
2686  NeonUri targetURI( xIdentifier->getContentIdentifier() );
2687 
2688  OUString aTargetURI;
2689  try
2690  {
2691  aTargetURI = targetURI.GetPathBaseNameUnescaped();
2692 
2693  // Check source's and target's URL scheme
2694 
2695  OUString aScheme = sourceURI.GetScheme().toAsciiLowerCase();
2696  if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
2697  {
2698  sourceURI.SetScheme( HTTP_URL_SCHEME );
2699  }
2700  else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
2701  {
2702  sourceURI.SetScheme( HTTPS_URL_SCHEME );
2703  }
2704  else if ( aScheme == DAV_URL_SCHEME )
2705  {
2706  sourceURI.SetScheme( HTTP_URL_SCHEME );
2707  }
2708  else if ( aScheme == DAVS_URL_SCHEME )
2709  {
2710  sourceURI.SetScheme( HTTPS_URL_SCHEME );
2711  }
2712  else if ( aScheme == WEBDAV_URL_SCHEME )
2713  {
2714  sourceURI.SetScheme( HTTP_URL_SCHEME );
2715  }
2716  else if ( aScheme == WEBDAVS_URL_SCHEME )
2717  {
2718  sourceURI.SetScheme( HTTPS_URL_SCHEME );
2719  }
2720  else
2721  {
2722  if ( aScheme != HTTP_URL_SCHEME && aScheme != HTTPS_URL_SCHEME )
2723  {
2725  uno::makeAny(
2726  ucb::InteractiveBadTransferURLException(
2727  "Unsupported URL scheme!",
2728  static_cast< cppu::OWeakObject * >( this ) ) ),
2729  Environment );
2730  // Unreachable
2731  }
2732  }
2733 
2734  aScheme = targetURI.GetScheme().toAsciiLowerCase();
2735  if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
2736  targetURI.SetScheme( HTTP_URL_SCHEME );
2737  else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
2738  targetURI.SetScheme( HTTPS_URL_SCHEME );
2739  else if ( aScheme == DAV_URL_SCHEME )
2740  targetURI.SetScheme( HTTP_URL_SCHEME );
2741  else if ( aScheme == DAVS_URL_SCHEME )
2742  targetURI.SetScheme( HTTPS_URL_SCHEME );
2743  else if ( aScheme == WEBDAV_URL_SCHEME )
2744  targetURI.SetScheme( HTTP_URL_SCHEME );
2745  else if ( aScheme == WEBDAVS_URL_SCHEME )
2746  targetURI.SetScheme( HTTPS_URL_SCHEME );
2747 
2748  // @@@ This implementation of 'transfer' only works
2749  // if the source and target are located at same host.
2750  // (Neon does not support cross-server copy/move)
2751 
2752  // Check for same host
2753 
2754  if ( !sourceURI.GetHost().isEmpty() &&
2755  ( sourceURI.GetHost() != targetURI.GetHost() ) )
2756  {
2758  uno::makeAny( ucb::InteractiveBadTransferURLException(
2759  "Different hosts!",
2760  static_cast< cppu::OWeakObject * >( this ) ) ),
2761  Environment );
2762  // Unreachable
2763  }
2764 
2765  OUString aTitle = rArgs.NewTitle;
2766 
2767  if ( aTitle.isEmpty() )
2768  aTitle = sourceURI.GetPathBaseNameUnescaped();
2769 
2770  if ( aTitle == "/" )
2771  {
2772  // kso: ???
2773  aTitle.clear();
2774  }
2775 
2776  targetURI.AppendPath( aTitle );
2777 
2778  OUString aTargetURL = xIdentifier->getContentIdentifier();
2779  if ( ( aTargetURL.lastIndexOf( '/' ) + 1 )
2780  != aTargetURL.getLength() )
2781  aTargetURL += "/";
2782 
2783  aTargetURL += aTitle;
2784 
2785  uno::Reference< ucb::XContentIdentifier > xTargetId
2786  = new ::ucbhelper::ContentIdentifier( aTargetURL );
2787 
2788  DAVResourceAccess aSourceAccess( m_xContext,
2789  xResAccess->getSessionFactory(),
2790  sourceURI.GetURI() );
2791 
2792  if ( rArgs.MoveData )
2793  {
2794  uno::Reference< ucb::XContentIdentifier > xId
2795  = new ::ucbhelper::ContentIdentifier( rArgs.SourceURL );
2796 
2797  // Note: The static cast is okay here, because its sure that
2798  // xProvider is always the WebDAVContentProvider.
2800  = static_cast< Content * >(
2801  xProvider->queryContent( xId ).get() );
2802 
2803  // [RFC 2518] - WebDAV
2804  // If a resource exists at the destination and the Overwrite
2805  // header is "T" then prior to performing the move the server
2806  // MUST perform a DELETE with "Depth: infinity" on the
2807  // destination resource. If the Overwrite header is set to
2808  // "F" then the operation will fail.
2809 
2810  aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
2811  aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
2812  aSourceAccess.MOVE( sourceURI.GetPath(),
2813  targetURI.GetURI(),
2814  rArgs.NameClash
2815  == ucb::NameClash::OVERWRITE,
2816  Environment );
2817 
2818  if ( xSource.is() )
2819  {
2820  // Propagate destruction to listeners.
2821  xSource->destroy( true );
2822  }
2823 
2824 // DAV resources store all additional props on server!
2825 // // Rename own and all children's Additional Core Properties.
2826 // renameAdditionalPropertySet( xId->getContentIdentifier(),
2827 // xTargetId->getContentIdentifier(),
2828 // sal_True );
2829  }
2830  else
2831  {
2832  // [RFC 2518] - WebDAV
2833  // If a resource exists at the destination and the Overwrite
2834  // header is "T" then prior to performing the copy the server
2835  // MUST perform a DELETE with "Depth: infinity" on the
2836  // destination resource. If the Overwrite header is set to
2837  // "F" then the operation will fail.
2838 
2839  aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
2840  aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
2841  aSourceAccess.COPY( sourceURI.GetPath(),
2842  targetURI.GetURI(),
2843  rArgs.NameClash
2844  == ucb::NameClash::OVERWRITE,
2845  Environment );
2846 
2847 // DAV resources store all additional props on server!
2848 // // Copy own and all children's Additional Core Properties.
2849 // copyAdditionalPropertySet( xId->getContentIdentifier(),
2850 // xTargetId->getContentIdentifier(),
2851 // sal_True );
2852  }
2853 
2854  // Note: The static cast is okay here, because its sure that
2855  // xProvider is always the WebDAVContentProvider.
2857  = static_cast< Content * >(
2858  xProvider->queryContent( xTargetId ).get() );
2859 
2860  // Announce transferred content in its new folder.
2861  xTarget->inserted();
2862  }
2863  catch ( ucb::IllegalIdentifierException const & )
2864  {
2865  // queryContent
2866  }
2867  catch ( DAVException const & e )
2868  {
2869  // [RFC 2518] - WebDAV
2870  // 412 (Precondition Failed) - The server was unable to maintain
2871  // the liveness of the properties listed in the propertybehavior
2872  // XML element or the Overwrite header is "F" and the state of
2873  // the destination resource is non-null.
2874 
2875  if ( e.getStatus() == SC_PRECONDITION_FAILED )
2876  {
2877  switch ( rArgs.NameClash )
2878  {
2879  case ucb::NameClash::ERROR:
2880  {
2882  uno::makeAny(
2883  ucb::NameClashException(
2884  OUString(),
2885  static_cast< cppu::OWeakObject * >( this ),
2886  task::InteractionClassification_ERROR,
2887  aTargetURI ) ),
2888  Environment );
2889  [[fallthrough]]; // Unreachable
2890  }
2891 
2892  case ucb::NameClash::OVERWRITE:
2893  break;
2894 
2895  case ucb::NameClash::KEEP: // deprecated
2896  case ucb::NameClash::RENAME:
2897  case ucb::NameClash::ASK:
2898  default:
2899  {
2901  uno::makeAny(
2902  ucb::UnsupportedNameClashException(
2903  OUString(),
2904  static_cast< cppu::OWeakObject * >( this ),
2905  rArgs.NameClash ) ),
2906  Environment );
2907  // Unreachable
2908  }
2909  }
2910  }
2911 
2912  cancelCommandExecution( e, Environment, true );
2913  // Unreachable
2914  }
2915 
2916  {
2917  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2918  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
2919  }
2920 }
2921 
2922 
2923 void Content::destroy( bool bDeletePhysical )
2924 {
2925  // @@@ take care about bDeletePhysical -> trashcan support
2926  uno::Reference< ucb::XContent > xThis = this;
2927 
2928  deleted();
2929 
2930  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2931 
2932  // Process instantiated children...
2933 
2935  queryChildren( aChildren );
2936 
2937  for ( auto& rChild : aChildren )
2938  {
2939  rChild->destroy( bDeletePhysical );
2940  }
2941 }
2942 
2943 // returns the resource type, to be checked for locks
2945  const uno::Reference< ucb::XCommandEnvironment >& Environment,
2946  const std::unique_ptr< DAVResourceAccess > & rResAccess)
2947 {
2948  ResourceType eResourceTypeForLocks = UNKNOWN;
2949  {
2950  osl::MutexGuard g(m_aMutex);
2951  //check if cache contains what we need, usually the first PROPFIND on the URI has supported lock
2952  std::unique_ptr< ContentProperties > xProps;
2953  if (m_xCachedProps)
2954  {
2955  uno::Sequence< ucb::LockEntry > aSupportedLocks;
2957  >>= aSupportedLocks ) //get the cached value for supportedlock
2958  {
2959  for ( const auto& rSupportedLock : std::as_const(aSupportedLocks) )
2960  {
2961  if ( rSupportedLock.Scope
2962  == ucb::LockScope_EXCLUSIVE &&
2963  rSupportedLock.Type
2964  == ucb::LockType_WRITE )
2965  eResourceTypeForLocks = DAV;
2966  }
2967  }
2968  }
2969  }
2970 
2971  const OUString & rURL = m_xIdentifier->getContentIdentifier();
2972 
2973  if ( eResourceTypeForLocks == UNKNOWN )
2974  {
2975  // resource type for lock/unlock operations still unknown, need to ask the server
2976 
2977  const OUString aScheme(
2978  rURL.copy( 0, rURL.indexOf( ':' ) ).toAsciiLowerCase() );
2979 
2980  if ( aScheme == FTP_URL_SCHEME )
2981  {
2982  eResourceTypeForLocks = FTP;
2983  }
2984  else
2985  {
2986  DAVOptions aDAVOptions;
2987  getResourceOptions( Environment, aDAVOptions, rResAccess );
2988  if( aDAVOptions.isClass1() ||
2989  aDAVOptions.isClass2() ||
2990  aDAVOptions.isClass3() )
2991  {
2992  // this is at least a DAV, lock to be confirmed
2993  // class 2 is needed for full lock support
2994  // see
2995  // <https://tools.ietf.org/html/rfc4918#section-18.2>
2996  eResourceTypeForLocks = DAV_NOLOCK;
2997  if( aDAVOptions.isClass2() )
2998  {
2999  // ok, possible lock, check for it
3000  try
3001  {
3002  // we need only DAV:supportedlock
3003  std::vector< DAVResource > resources;
3004  std::vector< OUString > aPropNames;
3005  uno::Sequence< beans::Property > aProperties( 1 );
3006  aProperties[ 0 ].Name = DAVProperties::SUPPORTEDLOCK;
3007 
3008  ContentProperties::UCBNamesToDAVNames( aProperties, aPropNames );
3009  rResAccess->PROPFIND( DAVZERO, aPropNames, resources, Environment );
3010 
3011  bool wasSupportedlockFound = false;
3012 
3013  // only one resource should be returned
3014  if ( resources.size() == 1 )
3015  {
3016  // we may have received a bunch of other properties
3017  // (some servers seems to do so)
3018  // but we need only supported lock for this check
3019  // all returned properties are in
3020  // resources.properties[n].Name/.Value
3021 
3022  for ( const auto& rProp : resources[0].properties )
3023  {
3024  if ( rProp.Name == DAVProperties::SUPPORTEDLOCK )
3025  {
3026  wasSupportedlockFound = true;
3027  uno::Sequence< ucb::LockEntry > aSupportedLocks;
3028  if ( rProp.Value >>= aSupportedLocks )
3029  {
3030  bool isSupported = std::any_of(aSupportedLocks.begin(), aSupportedLocks.end(),
3031  [](const ucb::LockEntry& rLock) {
3032  // TODO: if the lock type is changed from 'exclusive write' to 'shared write'
3033  // e.g. to implement 'Calc shared file feature', the ucb::LockScope_EXCLUSIVE
3034  // value should be checked as well, adaptation the code may be needed
3035  return rLock.Scope == ucb::LockScope_EXCLUSIVE
3036  && rLock.Type == ucb::LockType_WRITE;
3037  });
3038  if (isSupported)
3039  {
3040  // requested locking mode is supported
3041  eResourceTypeForLocks = DAV;
3042  SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks - URL: <"
3043  << m_xIdentifier->getContentIdentifier() << ">, DAV lock/unlock supported");
3044  }
3045  break;
3046  }
3047  }
3048  }
3049  }
3050  else
3051  {
3052  // PROPFIND failed; check if HEAD contains Content-Disposition: attachment (RFC1806, HTTP/1.1 19.5.1),
3053  // which supposedly means no lock for the resource (happens e.g. with SharePoint exported lists)
3054  OUString sContentDisposition;
3055  // First, check cached properties
3056  if (m_xCachedProps)
3057  {
3058  if ((m_xCachedProps->getValue("Content-Disposition") >>= sContentDisposition)
3059  && sContentDisposition.startsWithIgnoreAsciiCase("attachment"))
3060  {
3061  eResourceTypeForLocks = DAV_NOLOCK;
3062  wasSupportedlockFound = true;
3063  }
3064  }
3065  // If no data in cache, try HEAD request
3066  if (sContentDisposition.isEmpty() && !m_bDidGetOrHead) try
3067  {
3068  DAVResource resource;
3069  GetPropsUsingHeadRequest(resource, rResAccess, {"Content-Disposition"}, Environment);
3070  m_bDidGetOrHead = true;
3071  for (const auto& it : resource.properties)
3072  {
3073  if (it.Name.equalsIgnoreAsciiCase("Content-Disposition"))
3074  {
3075  if ((it.Value >>= sContentDisposition) && sContentDisposition.equalsIgnoreAsciiCase("attachment"))
3076  {
3077  eResourceTypeForLocks = DAV_NOLOCK;
3078  wasSupportedlockFound = true;
3079  }
3080  break;
3081  }
3082  }
3083  }
3084  catch (...){}
3085  }
3086  // check if this is still only a DAV_NOLOCK
3087  // a fallback for resources that do not have DAVProperties::SUPPORTEDLOCK property
3088  // we check for the returned OPTION if LOCK is allowed on the resource
3089  if ( !wasSupportedlockFound && eResourceTypeForLocks == DAV_NOLOCK )
3090  {
3091  SAL_INFO( "ucb.ucp.webdav", "This WebDAV server has no supportedlock property, check for allowed LOCK method in OPTIONS" );
3092  // ATTENTION: if the lock type is changed from 'exclusive write' to 'shared write'
3093  // e.g. to implement 'Calc shared file feature' on WebDAV directly, and we arrive to this fallback
3094  // and the LOCK is allowed, we should assume that only exclusive write lock is available
3095  // this is just a reminder...
3096  if ( aDAVOptions.isLockAllowed() )
3097  eResourceTypeForLocks = DAV;
3098  }
3099  }
3100  catch ( DAVException const & e )
3101  {
3102  rResAccess->resetUri();
3103  //grab the error code
3104  switch( e.getStatus() )
3105  {
3106  case SC_NOT_FOUND:
3107  SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
3108  << m_xIdentifier->getContentIdentifier() << "> was not found. ");
3109  eResourceTypeForLocks = NOT_FOUND;
3110  break;
3111  // some servers returns SC_FORBIDDEN, instead
3112  // the meaning of SC_FORBIDDEN is, according to <http://tools.ietf.org/html/rfc7231#section-6.5.3>:
3113  // The 403 (Forbidden) status code indicates that the server understood
3114  // the request but refuses to authorize it
3115  case SC_FORBIDDEN:
3116  // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3117  // part of base http 1.1 RFCs
3118  case SC_NOT_IMPLEMENTED: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3119  case SC_METHOD_NOT_ALLOWED: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3120  // they all mean the resource is NON_DAV
3121  SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException (SC_FORBIDDEN, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3122  << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3123  eResourceTypeForLocks = NON_DAV;
3124  break;
3125  default:
3126  //fallthrough
3127  SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException - URL: <"
3128  << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3129  eResourceTypeForLocks = UNKNOWN;
3130  }
3131  }
3132  }
3133  }
3134  else
3135  eResourceTypeForLocks = NON_DAV;
3136 
3137  }
3138  }
3139  osl::MutexGuard g(m_aMutex);
3141  {
3142  m_eResourceTypeForLocks = eResourceTypeForLocks;
3143  }
3144  else
3145  {
3146  SAL_WARN_IF(
3147  eResourceTypeForLocks != m_eResourceTypeForLocks, "ucb.ucp.webdav",
3148  "different resource types for <" << rURL << ">: "
3149  << +eResourceTypeForLocks << " vs. " << +m_eResourceTypeForLocks);
3150  }
3151  SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
3152  << m_xIdentifier->getContentIdentifier() << ">, m_eResourceTypeForLocks: " << m_eResourceTypeForLocks );
3153  return m_eResourceTypeForLocks;
3154 }
3155 
3157  const uno::Reference< ucb::XCommandEnvironment >& Environment )
3158 {
3159  std::unique_ptr< DAVResourceAccess > xResAccess;
3160  {
3161  osl::MutexGuard aGuard( m_aMutex );
3162  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
3163  }
3164  Content::ResourceType ret = resourceTypeForLocks( Environment, xResAccess );
3165  {
3166  osl::Guard< osl::Mutex > aGuard( m_aMutex );
3167  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
3168  }
3169  return ret;
3170 }
3171 
3172 void Content::lock(
3173  const uno::Reference< ucb::XCommandEnvironment >& Environment )
3174 {
3175 // prepare aURL to be used in exception, see below
3176  OUString aURL;
3177  if ( m_bTransient )
3178  {
3179  aURL = getParentURL();
3180  if ( aURL.lastIndexOf('/') != ( aURL.getLength() - 1 ) )
3181  aURL += "/";
3182 
3183  aURL += m_aEscapedTitle;
3184  }
3185  else
3186  {
3187  aURL = m_xIdentifier->getContentIdentifier();
3188  }
3189 
3190  try
3191  {
3192  std::unique_ptr< DAVResourceAccess > xResAccess;
3193  {
3194  osl::Guard< osl::Mutex > aGuard( m_aMutex );
3195  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
3196  }
3197 
3198  uno::Any aOwnerAny;
3199  aOwnerAny
3200  <<= OUString("LibreOffice - http://www.libreoffice.org/");
3201 
3202  ucb::Lock aLock(
3203  ucb::LockScope_EXCLUSIVE,
3204  ucb::LockType_WRITE,
3205  ucb::LockDepth_ZERO,
3206  aOwnerAny,
3207  180, // lock timeout in secs
3208  //-1, // infinite lock
3209  uno::Sequence< OUString >() );
3210 
3211  // OPTIONS may change as a consequence of the lock operation
3212  aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
3213  removeCachedPropertyNames( xResAccess->getURL() );
3214  xResAccess->LOCK( aLock, Environment );
3215 
3216  {
3217  osl::Guard< osl::Mutex > aGuard( m_aMutex );
3218  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
3219  }
3220  }
3221  catch ( DAVException const & e )
3222  {
3223  // check if the exception thrown is 'already locked'
3224  // this exception is mapped directly to the ucb correct one, without
3225  // going into the cancelCommandExecution() user interaction
3226  // this exception should be managed by the issuer of 'lock' command
3227  switch( e.getError() )
3228  {
3230  {
3231  SAL_WARN( "ucb.ucp.webdav", "lock() resource already locked - URL: <"
3232  << m_xIdentifier->getContentIdentifier() << ">");
3233  throw
3234  ucb::InteractiveLockingLockedException(
3235  "Locked!",
3236  static_cast< cppu::OWeakObject * >( this ),
3237  task::InteractionClassification_ERROR,
3238  aURL,
3239  false );
3240  }
3241  break;
3243  {
3244  SAL_WARN( "ucb.ucp.webdav", "lock() DAVException Authentication error - URL: <"
3245  << m_xIdentifier->getContentIdentifier() << ">" );
3246  // DAVException::DAV_HTTP_AUTH exception can mean:
3247  // - interaction handler for credential management not present (happens, depending
3248  // on the LO framework processing)
3249  // - the remote site is a WebDAV with special configuration: read/only for read operations
3250  // and read/write for write operations, the user is not allowed to lock/write and
3251  // she cancelled the credentials request.
3252  // this is not actually an error, but the exception is sent directly from here, avoiding the automatic
3253  // management that takes part in cancelCommandExecution() below
3254  // Unfortunately there is no InteractiveNetwork*Exception available to signal this
3255  // since it mostly happens on read/only part of webdav, this appears to be the most correct exception available
3256  throw
3257  ucb::InteractiveNetworkWriteException(
3258  "Authentication error while trying to lock! Write only WebDAV perhaps?",
3259  static_cast< cppu::OWeakObject * >( this ),
3260  task::InteractionClassification_ERROR,
3261  e.getData() );
3262  }
3263  break;
3265  //grab the error code
3266  switch( e.getStatus() )
3267  {
3268  // The 'case SC_NOT_FOUND' just below tries to solve a problem in eXo Platform
3269  // WebDAV connector which apparently fail on resource first creation
3270  // rfc4918 section-7.3 (see link below)
3271  case SC_NOT_FOUND: // <http://tools.ietf.org/html/rfc7231#section-6.5.4>
3272  // The 'case SC_PRECONDITION_FAILED' just below tries to solve a problem
3273  // in SharePoint when locking the resource on first creation fails due to this:
3274  // <https://msdn.microsoft.com/en-us/library/jj575265%28v=office.12%29.aspx#id15>
3275  // (retrieved on 2015-08-14)
3276  case SC_PRECONDITION_FAILED: // <http://tools.ietf.org/html/rfc7232#section-4.2>
3277  // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3278  // part of base http 1.1 RFCs
3279  case SC_NOT_IMPLEMENTED: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3280  case SC_METHOD_NOT_ALLOWED: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3281  SAL_WARN( "ucb.ucp.webdav", "lock() DAVException (SC_NOT_FOUND, SC_PRECONDITION_FAILED, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3282  << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3283  // act as nothing happened
3284  // that's because when a resource is first created
3285  // the lock is sent before the put, so the resource
3286  // is actually created by LOCK, locking it before
3287  // the first PUT, but if LOCK is not supported
3288  // (simple web or DAV with lock disabled) we end with one of these http
3289  // errors.
3290  // These same errors may be reported when the LOCK on an unmapped
3291  // (i.e. non existent) resource is not implemented.
3292  // Detailed specification in:
3293  // <http://tools.ietf.org/html/rfc4918#section-7.3>
3294  return;
3295  break;
3296  default:
3297  //fallthrough
3298  ;
3299  }
3300  break;
3302  // we already hold the lock and it is in our internal lockstore
3303  // just return as if the lock was successful
3304  return;
3305  break;
3306  default:
3307  //fallthrough
3308  ;
3309  }
3310 
3311  SAL_WARN( "ucb.ucp.webdav","lock() DAVException - URL: <"
3312  << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3313  cancelCommandExecution( e, Environment );
3314  // Unreachable
3315  }
3316 }
3317 
3318 
3319 void Content::unlock(
3320  const uno::Reference< ucb::XCommandEnvironment >& Environment )
3321 {
3322 
3323  try
3324  {
3325  std::unique_ptr< DAVResourceAccess > xResAccess;
3326  {
3327  osl::Guard< osl::Mutex > aGuard( m_aMutex );
3328  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
3329  }
3330 
3331  // check if the target URL is a Class1 DAV
3332  DAVOptions aDAVOptions;
3333  getResourceOptions( Environment, aDAVOptions, xResAccess );
3334 
3335  // at least class one is needed
3336  if( aDAVOptions.isClass1() )
3337  {
3338  // remove options from cache, unlock may change it
3339  // it will be refreshed when needed
3340  aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
3341  // clean cached value of PROPFIND properties names
3342  removeCachedPropertyNames( xResAccess->getURL() );
3343  xResAccess->UNLOCK( Environment );
3344  }
3345 
3346  {
3347  osl::Guard< osl::Mutex > aGuard( m_aMutex );
3348  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
3349  }
3350  }
3351  catch ( DAVException const & e )
3352  {
3353  switch( e.getError() )
3354  {
3356  SAL_WARN( "ucb.ucp.webdav", "unlock() DAVException::DAV_NOT_LOCKED - URL: <"
3357  << m_xIdentifier->getContentIdentifier() << ">");
3358  // means that we don't own any lock on this resource
3359  // intercepted here to remove a confusing indication to the user
3360  // unfortunately this happens in some WebDAV server configuration
3361  // acting as WebDAV and having lock/unlock enabled only
3362  // for authorized user.
3363  return;
3364  break;
3366  //grab the error code
3367  switch( e.getStatus() )
3368  {
3369  // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3370  // part of base http 1.1 RFCs
3371  case SC_NOT_IMPLEMENTED: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3372  case SC_METHOD_NOT_ALLOWED: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3373  SAL_WARN( "ucb.ucp.webdav", "unlock() DAVException (SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3374  << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3375  return;
3376  break;
3377  default:
3378  //fallthrough
3379  ;
3380  }
3381  break;
3382  default:
3383  //fallthrough
3384  ;
3385  }
3386  SAL_WARN( "ucb.ucp.webdav","unlock() DAVException - URL: <"
3387  << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3388  cancelCommandExecution( e, Environment );
3389  // Unreachable
3390  }
3391 }
3392 
3393 
3395  const uno::Reference< ucb::XContentIdentifier >& xNewId )
3396 {
3397  if ( !xNewId.is() )
3398  return false;
3399 
3400  osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
3401 
3402  uno::Reference< ucb::XContent > xThis = this;
3403 
3404  // Already persistent?
3405  if ( m_bTransient )
3406  {
3407  SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - Not persistent!" );
3408  return false;
3409  }
3410 
3411  // Exchange own identitity.
3412 
3413  // Fail, if a content with given id already exists.
3414 // if ( !hasData( xNewId ) )
3415  {
3416  OUString aOldURL = m_xIdentifier->getContentIdentifier();
3417 
3418  aGuard.clear();
3419  if ( exchange( xNewId ) )
3420  {
3421  // Process instantiated children...
3422 
3423  ContentRefList aChildren;
3424  queryChildren( aChildren );
3425 
3426  for ( const auto& rChild : aChildren )
3427  {
3428  ContentRef xChild = rChild;
3429 
3430  // Create new content identifier for the child...
3431  uno::Reference< ucb::XContentIdentifier >
3432  xOldChildId = xChild->getIdentifier();
3433  OUString aOldChildURL
3434  = xOldChildId->getContentIdentifier();
3435  OUString aNewChildURL
3436  = aOldChildURL.replaceAt(
3437  0,
3438  aOldURL.getLength(),
3439  xNewId->getContentIdentifier() );
3440  uno::Reference< ucb::XContentIdentifier > xNewChildId
3441  = new ::ucbhelper::ContentIdentifier( aNewChildURL );
3442 
3443  if ( !xChild->exchangeIdentity( xNewChildId ) )
3444  return false;
3445  }
3446  return true;
3447  }
3448  }
3449 
3450  SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - "
3451  "Panic! Cannot exchange identity!" );
3452  return false;
3453 }
3454 
3455 
3456 bool Content::isFolder(
3457  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
3458 {
3459  {
3460  osl::MutexGuard aGuard( m_aMutex );
3461 
3462  if ( m_bTransient )
3463  return m_bCollection;
3464  }
3465 
3466  uno::Sequence< beans::Property > aProperties( 1 );
3467  aProperties[ 0 ].Name = "IsFolder";
3468  aProperties[ 0 ].Handle = -1;
3469  uno::Reference< sdbc::XRow > xRow( getPropertyValues( aProperties, xEnv ) );
3470  if ( xRow.is() )
3471  {
3472  try
3473  {
3474  return xRow->getBoolean( 1 );
3475  }
3476  catch ( sdbc::SQLException const & )
3477  {
3478  }
3479  }
3480 
3481  return false;
3482 }
3483 
3484 
3486 {
3487  // Map DAVException...
3488  uno::Any aException;
3489 
3490  OUString aURL;
3491  if ( m_bTransient )
3492  {
3493  aURL = getParentURL();
3494  if ( aURL.lastIndexOf('/') != ( aURL.getLength() - 1 ) )
3495  aURL += "/";
3496 
3497  aURL += m_aEscapedTitle;
3498  }
3499  else
3500  {
3501  aURL = m_xIdentifier->getContentIdentifier();
3502  }
3503 
3504  switch ( e.getStatus() )
3505  {
3506  case SC_NOT_FOUND:
3507  {
3508  uno::Sequence< uno::Any > aArgs( 1 );
3509  aArgs[ 0 ] <<= beans::PropertyValue(
3510  "Uri", -1,
3511  uno::makeAny(aURL),
3512  beans::PropertyState_DIRECT_VALUE);
3513 
3514  aException <<=
3515  ucb::InteractiveAugmentedIOException(
3516  "Not found!",
3517  static_cast< cppu::OWeakObject * >( this ),
3518  task::InteractionClassification_ERROR,
3519  ucb::IOErrorCode_NOT_EXISTING,
3520  aArgs );
3521  return aException;
3522  }
3523  default:
3524  break;
3525  }
3526 
3527  switch ( e.getError() )
3528  {
3530  {
3531  if ( bWrite )
3532  aException <<=
3533  ucb::InteractiveNetworkWriteException(
3534  e.getData(),
3535  static_cast< cppu::OWeakObject * >( this ),
3536  task::InteractionClassification_ERROR,
3537  e.getData() );
3538  else
3539  aException <<=
3540  ucb::InteractiveNetworkReadException(
3541  e.getData(),
3542  static_cast< cppu::OWeakObject * >( this ),
3543  task::InteractionClassification_ERROR,
3544  e.getData() );
3545  break;
3546  }
3547 
3549  aException <<=
3550  ucb::InteractiveNetworkResolveNameException(
3551  OUString(),
3552  static_cast< cppu::OWeakObject * >( this ),
3553  task::InteractionClassification_ERROR,
3554  e.getData() );
3555  break;
3556 
3557 // @@@ No matching InteractiveNetwork*Exception
3558 // case DAVException::DAV_HTTP_AUTH:
3559 // break;
3560 
3561 // @@@ No matching InteractiveNetwork*Exception
3562 // case DAVException::DAV_HTTP_AUTHPROXY:
3563 // break;
3564 
3567  aException <<=
3568  ucb::InteractiveNetworkConnectException(
3569  OUString(),
3570  static_cast< cppu::OWeakObject * >( this ),
3571  task::InteractionClassification_ERROR,
3572  e.getData() );
3573  break;
3574 
3575 // @@@ No matching InteractiveNetwork*Exception
3576 // case DAVException::DAV_HTTP_REDIRECT:
3577 // break;
3578 
3579 // @@@ No matching InteractiveNetwork*Exception
3580 // case DAVException::DAV_SESSION_CREATE:
3581 // break;
3582 
3584  aException <<=
3585  lang::IllegalArgumentException(
3586  OUString(),
3587  static_cast< cppu::OWeakObject * >( this ),
3588  -1 );
3589  break;
3590 
3592  aException <<=
3593  ucb::InteractiveLockingLockedException(
3594  "Locked!",
3595  static_cast< cppu::OWeakObject * >( this ),
3596  task::InteractionClassification_ERROR,
3597  aURL,
3598  false ); // not SelfOwned
3599  break;
3600 
3602  aException <<=
3603  ucb::InteractiveLockingLockedException(
3604  "Locked (self!)",
3605  static_cast< cppu::OWeakObject * >( this ),
3606  task::InteractionClassification_ERROR,
3607  aURL,
3608  true ); // SelfOwned
3609  break;
3610 
3612  aException <<=
3613  ucb::InteractiveLockingNotLockedException(
3614  "Not locked!",
3615  static_cast< cppu::OWeakObject * >( this ),
3616  task::InteractionClassification_ERROR,
3617  aURL );
3618  break;
3619 
3621  aException <<=
3622  ucb::InteractiveLockingLockExpiredException(
3623  "Lock expired!",
3624  static_cast< cppu::OWeakObject * >( this ),
3625  task::InteractionClassification_ERROR,
3626  aURL );
3627  break;
3628 
3629  default:
3630  aException <<=
3631  ucb::InteractiveNetworkGeneralException(
3632  OUString(),
3633  static_cast< cppu::OWeakObject * >( this ),
3634  task::InteractionClassification_ERROR );
3635  break;
3636  }
3637 
3638  return aException;
3639 }
3640 
3641 
3642 // static
3644 {
3645  return !(( e.getStatus() == SC_NOT_FOUND ) ||
3646  ( e.getStatus() == SC_GONE ) ||
3652 }
3653 
3654 
3656  const DAVException & e,
3657  const uno::Reference< ucb::XCommandEnvironment > & xEnv,
3658  bool bWrite /* = sal_False */ )
3659 {
3660  ucbhelper::cancelCommandExecution( MapDAVException( e, bWrite ), xEnv );
3661  // Unreachable
3662 }
3663 
3664 
3665 OUString
3666 Content::getBaseURI( const std::unique_ptr< DAVResourceAccess > & rResAccess )
3667 {
3668  osl::Guard< osl::Mutex > aGuard( m_aMutex );
3669 
3670  // First, try to obtain value of response header "Content-Location".
3671  if (m_xCachedProps)
3672  {
3673  OUString aLocation;
3674  m_xCachedProps->getValue( "Content-Location" ) >>= aLocation;
3675  if ( !aLocation.isEmpty() )
3676  {
3677  try
3678  {
3679  // Do not use m_xIdentifier->getContentIdentifier() because it
3680  // for example does not reflect redirects applied to requests
3681  // done using the original URI but m_xResAccess' URI does.
3682  return rtl::Uri::convertRelToAbs( rResAccess->getURL(),
3683  aLocation );
3684  }
3685  catch ( rtl::MalformedUriException const & )
3686  {
3687  }
3688  }
3689  }
3690 
3691  return rResAccess->getURL();
3692 }
3693 
3694 // resource type is the type of the WebDAV resource
3696  const uno::Reference< ucb::XCommandEnvironment >& xEnv,
3697  const std::unique_ptr< DAVResourceAccess > & rResAccess,
3698  bool * networkAccessAllowed)
3699 {
3700  {
3701  osl::MutexGuard g(m_aMutex);
3702  if (m_eResourceType != UNKNOWN) {
3703  return m_eResourceType;
3704  }
3705  }
3706 
3707  ResourceType eResourceType = UNKNOWN;
3708  DAVOptions aDAVOptions;
3709 
3710  const OUString & rURL = rResAccess->getURL();
3711  const OUString aScheme(
3712  rURL.copy( 0, rURL.indexOf( ':' ) ).toAsciiLowerCase() );
3713 
3714  if ( aScheme == FTP_URL_SCHEME )
3715  {
3716  eResourceType = FTP;
3717  }
3718  else
3719  {
3720  getResourceOptions( xEnv, aDAVOptions, rResAccess, networkAccessAllowed );
3721 
3722  // at least class one is needed
3723  if( aDAVOptions.isClass1() )
3724  {
3725  try
3726  {
3727  // Try to fetch some frequently used property value, e.g. those
3728  // used when loading documents... along with identifying whether
3729  // this is a DAV resource.
3730  std::vector< DAVResource > resources;
3731  std::vector< OUString > aPropNames;
3732  uno::Sequence< beans::Property > aProperties( 5 );
3733  aProperties[ 0 ].Name = "IsFolder";
3734  aProperties[ 1 ].Name = "IsDocument";
3735  aProperties[ 2 ].Name = "IsReadOnly";
3736  aProperties[ 3 ].Name = "MediaType";
3737  aProperties[ 4 ].Name = DAVProperties::SUPPORTEDLOCK;
3738 
3739  ContentProperties::UCBNamesToDAVNames( aProperties, aPropNames );
3740 
3741  rResAccess->PROPFIND( DAVZERO, aPropNames, resources, xEnv );
3742 
3743  if ( resources.size() == 1 )
3744  {
3745 #if defined SAL_LOG_INFO
3746  {//debug
3747  // print received resources
3748  for ( const auto& rProp : resources[0].properties )
3749  {
3750  OUString aPropValue;
3751  bool bValue;
3752  uno::Sequence< ucb::LockEntry > aSupportedLocks;
3753  if(rProp.Value >>= aPropValue )
3754  SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << rProp.Name << ":" << aPropValue );
3755  else if( rProp.Value >>= bValue )
3756  SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << rProp.Name << ":" <<
3757  ( bValue ? "true" : "false" ) );
3758  else if( rProp.Value >>= aSupportedLocks )
3759  {
3760  SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << rProp.Name << ":" );
3761  for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
3762  {
3763  SAL_INFO( "ucb.ucp.webdav","PROPFIND (getResourceType) - supportedlock[" << n <<"]: scope: "
3764  << ( aSupportedLocks[ n ].Scope == css::ucb::LockScope_SHARED ? "shared" : "exclusive" )
3765  << ", type: "
3766  << ( aSupportedLocks[ n ].Type != css::ucb::LockType_WRITE ? "" : "write" ) );
3767  }
3768  }
3769  }
3770  }
3771 #endif
3772  osl::MutexGuard g(m_aMutex);
3773  m_xCachedProps.reset(
3774  new CachableContentProperties( ContentProperties( resources[ 0 ] ) ) );
3775  m_xCachedProps->containsAllNames(
3776  aProperties, m_aFailedPropNames );
3777  }
3778  eResourceType = DAV;
3779  }
3780  catch ( DAVException const & e )
3781  {
3782  rResAccess->resetUri();
3783 
3784  SAL_WARN( "ucb.ucp.webdav", "Content::getResourceType returned errors, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3785 
3786  if ( e.getStatus() == SC_METHOD_NOT_ALLOWED )
3787  {
3788  // Status SC_METHOD_NOT_ALLOWED is a safe indicator that the
3789  // resource is NON_DAV
3790  eResourceType = NON_DAV;
3791  }
3792  else if (networkAccessAllowed != nullptr)
3793  {
3794  *networkAccessAllowed = *networkAccessAllowed
3796  }
3797  if ( e.getStatus() == SC_NOT_FOUND )
3798  {
3799  // arrives here if OPTIONS is still cached for a resource previously available
3800  // operate on the OPTIONS cache:
3801  // if OPTIONS was not found, do nothing
3802  // else OPTIONS returned on a resource not existent (example a server that allows lock on null resource) set
3803  // not found and adjust lifetime accordingly
3804  DAVOptions aDAVOptionsInner;
3805  if( aStaticDAVOptionsCache.getDAVOptions( rURL, aDAVOptionsInner ) )
3806  {
3807  // TODO? get redirected url
3808  aDAVOptionsInner.setHttpResponseStatusCode( e.getStatus() );
3809  aDAVOptionsInner.setHttpResponseStatusText( e.getData() );
3810  aStaticDAVOptionsCache.addDAVOptions( aDAVOptionsInner,
3812  }
3813  }
3814  // if the two net events below happen, something
3815  // is going on to the connection so break the command flow
3816  if ( ( e.getError() == DAVException::DAV_HTTP_TIMEOUT ) ||
3818  {
3819  cancelCommandExecution( e, xEnv );
3820  // unreachable
3821  }
3822  }
3823  }
3824  else
3825  {
3826  rResAccess->resetUri();
3827 
3828  // first check if the cached error can be mapped to DAVException::DAV_HTTP_TIMEOUT or mapped to DAVException::DAV_HTTP_CONNECT
3829  if ( aDAVOptions.getHttpResponseStatusCode() == USC_CONNECTION_TIMED_OUT )
3830  {
3831  // behave same as DAVException::DAV_HTTP_TIMEOUT or DAVException::DAV_HTTP_CONNECT was thrown
3832  try
3833  {
3834  // extract host name and connection port
3835  NeonUri theUri( rURL );
3836  const OUString& aHostName = theUri.GetHost();
3837  sal_Int32 nPort = theUri.GetPort();
3840  nPort ) );
3841  }
3842  catch ( DAVException& exp )
3843  {
3844  cancelCommandExecution( exp, xEnv );
3845  }
3846  }
3847 
3848  if ( aDAVOptions.getHttpResponseStatusCode() != SC_NOT_FOUND &&
3849  aDAVOptions.getHttpResponseStatusCode() != SC_GONE ) // the cached OPTIONS can have SC_GONE
3850  {
3851  eResourceType = NON_DAV;
3852  }
3853  else
3854  {
3855  //resource doesn't exist
3856  if ( networkAccessAllowed != nullptr )
3857  *networkAccessAllowed = false; }
3858  }
3859  }
3860 
3861  osl::MutexGuard g(m_aMutex);
3862  if (m_eResourceType == UNKNOWN) {
3863  m_eResourceType = eResourceType;
3864  } else {
3865  SAL_WARN_IF(
3866  eResourceType != m_eResourceType, "ucb.ucp.webdav",
3867  "different resource types for <" << rURL << ">: "
3868  << +eResourceType << " vs. " << +m_eResourceType);
3869  }
3870  SAL_INFO( "ucb.ucp.webdav", "m_eResourceType for <"<<rURL<<">: " << m_eResourceType );
3871  return m_eResourceType;
3872 }
3873 
3874 
3876  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
3877 {
3878  std::unique_ptr< DAVResourceAccess > xResAccess;
3879  {
3880  osl::MutexGuard aGuard( m_aMutex );
3881  xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
3882  }
3883  Content::ResourceType const ret = getResourceType( xEnv, xResAccess );
3884  {
3885  osl::Guard< osl::Mutex > aGuard( m_aMutex );
3886  m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
3887  }
3888  return ret;
3889 }
3890 
3891 
3893 {
3894  // see description in
3895  // officecfg/registry/schema/org/openoffice/Inet.xcs
3896  // for use of these field values.
3897  sal_uInt32 nAtime;
3898  nAtime = officecfg::Inet::Settings::OptsCacheLifeImplWeb::get( m_xContext );
3899  m_nOptsCacheLifeImplWeb = std::max( sal_uInt32( 0 ),
3900  std::min( nAtime, sal_uInt32( 3600 ) ) );
3901 
3902  nAtime = officecfg::Inet::Settings::OptsCacheLifeDAV::get( m_xContext );
3903  m_nOptsCacheLifeDAV = std::max( sal_uInt32( 0 ),
3904  std::min( nAtime, sal_uInt32( 3600 ) ) );
3905 
3906  nAtime = officecfg::Inet::Settings::OptsCacheLifeDAVLocked::get( m_xContext );
3907  m_nOptsCacheLifeDAVLocked = std::max( sal_uInt32( 0 ),
3908  std::min( nAtime, sal_uInt32( 3600 ) ) );
3909 
3910  nAtime = officecfg::Inet::Settings::OptsCacheLifeNotImpl::get( m_xContext );
3911  m_nOptsCacheLifeNotImpl = std::max( sal_uInt32( 0 ),
3912  std::min( nAtime, sal_uInt32( 43200 ) ) );
3913 
3914  nAtime = officecfg::Inet::Settings::OptsCacheLifeNotFound::get( m_xContext );
3915  m_nOptsCacheLifeNotFound = std::max( sal_uInt32( 0 ),
3916  std::min( nAtime, sal_uInt32( 30 ) ) );
3917 }
3918 
3919 
3921  const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
3922  DAVOptions& rDAVOptions,
3923  const std::unique_ptr< DAVResourceAccess > & rResAccess,
3924  bool * networkAccessAllowed )
3925 {
3926  OUString aRedirURL;
3927  OUString aTargetURL = rResAccess->getURL();
3928  DAVOptions aDAVOptions;
3929  // first check if in cache, if not, then send method to server
3930  if ( !aStaticDAVOptionsCache.getDAVOptions( aTargetURL, aDAVOptions ) )
3931  {
3932  try
3933  {
3934  rResAccess->OPTIONS( aDAVOptions, xEnv );
3935  // IMPORTANT:the correctly implemented server will answer without errors, even if the resource is not present
3936  sal_uInt32 nLifeTime = ( aDAVOptions.isClass1() ||
3937  aDAVOptions.isClass2() ||
3938  aDAVOptions.isClass3() ) ?
3939  m_nOptsCacheLifeDAV : // a WebDAV site
3940  m_nOptsCacheLifeImplWeb; // a site implementing OPTIONS but
3941  // it's not DAV
3942  // if resource is locked, will use a
3943  // different lifetime
3944  if( aDAVOptions.isLocked() )
3945  nLifeTime = m_nOptsCacheLifeDAVLocked;
3946 
3947  // check if redirected
3948  aRedirURL = rResAccess->getURL();
3949  if( aRedirURL == aTargetURL)
3950  { // no redirection
3951  aRedirURL.clear();
3952  }
3953  // cache this URL's option
3954  aDAVOptions.setURL( aTargetURL );
3955  aDAVOptions.setRedirectedURL( aRedirURL );
3956  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
3957  nLifeTime );
3958  }
3959  catch ( DAVException const & e )
3960  {
3961  // first, remove from cache, will be added if needed, depending on the error received
3962  aStaticDAVOptionsCache.removeDAVOptions( aTargetURL );
3963  rResAccess->resetUri();
3964 
3965  aDAVOptions.setURL( aTargetURL );
3966  aDAVOptions.setRedirectedURL( aRedirURL );
3967  switch( e.getError() )
3968  {
3971  {
3972  // something bad happened to the connection
3973  // not same as not found, this instead happens when the server doesn't exist or doesn't answer at all
3974  // probably a new bit stating 'timed out' should be added to opts var?
3975  // in any case abort the command
3976  SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_TIMEOUT or DAV_HTTP_CONNECT for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
3977  // cache the internal unofficial status code
3978 
3980  // used only internally, so the text doesn't really matter...
3981  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
3983  if ( networkAccessAllowed != nullptr )
3984  {
3985  *networkAccessAllowed = *networkAccessAllowed
3987  }
3988  }
3989  break;
3991  {
3992  SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_LOOKUP for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
3994  // used only internally, so the text doesn't really matter...
3995  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
3997  if ( networkAccessAllowed != nullptr )
3998  {
3999  *networkAccessAllowed = *networkAccessAllowed
4001  }
4002  }
4003  break;
4005  {
4006  SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTH for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
4007  // - the remote site is a WebDAV with special configuration: read/only for read operations
4008  // and read/write for write operations, the user is not allowed to lock/write and
4009  // she cancelled the credentials request.
4010  // this is not actually an error, it means only that for current user this is a standard web,
4011  // though possibly DAV enabled
4013  // used only internally, so the text doesn't really matter...
4014  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
4016  if ( networkAccessAllowed != nullptr )
4017  {
4018  *networkAccessAllowed = *networkAccessAllowed
4020  }
4021  }
4022  break;
4024  {
4025  SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTHPROXY for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
4027  // used only internally, so the text doesn't really matter...
4028  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
4030  if ( networkAccessAllowed != nullptr )
4031  {
4032  *networkAccessAllowed = *networkAccessAllowed
4034  }
4035  }
4036  break;
4038  {
4039  switch( e.getStatus() )
4040  {
4041  case SC_FORBIDDEN:
4042  {
4043  SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_FORBIDDEN for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
4044  // cache it, so OPTIONS won't be called again, this URL does not support it
4045  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
4047  }
4048  break;
4049  case SC_BAD_REQUEST:
4051  {
4052  SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_BAD_REQUEST or SC_INTERNAL_SERVER_ERROR for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
4053  << ", '" << e.getData() << "'" );
4054  // cache it, so OPTIONS won't be called again, this URL detect some problem while answering the method
4055  aDAVOptions.setHttpResponseStatusCode( e.getStatus() );
4056  aDAVOptions.setHttpResponseStatusText( e.getData() );
4057  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
4059  }
4060  break;
4061  case SC_NOT_IMPLEMENTED:
4062  case SC_METHOD_NOT_ALLOWED:
4063  {
4064  // OPTIONS method must be implemented in DAV
4065  // resource is NON_DAV, or not advertising it
4066  SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
4067  << ", '" << e.getData() << "'" );
4068  // cache it, so OPTIONS won't be called again, this URL does not support it
4069  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
4071  }
4072  break;
4073  case SC_NOT_FOUND:
4074  {
4075  // Apparently on IIS 10.0, if you disabled OPTIONS method, this error is the one reported,
4076  // instead of SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED.
4077  // So check if this is an available resource, or a real 'Not Found' event.
4078  sal_uInt32 nLifeTime = m_nOptsCacheLifeNotFound;
4079  if( isResourceAvailable( xEnv, rResAccess, aDAVOptions ) )
4080  {
4081  SAL_WARN( "ucb.ucp.webdav", "OPTIONS - Got an SC_NOT_FOUND, but the URL <" << m_xIdentifier->getContentIdentifier() << "> resource exists" );
4082  nLifeTime = m_nOptsCacheLifeNotImpl;
4083  }
4084  else
4085  {
4086  SAL_WARN( "ucb.ucp.webdav", "OPTIONS - SC_NOT_FOUND for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
4087  if ( networkAccessAllowed != nullptr )
4088  {
4089  *networkAccessAllowed = *networkAccessAllowed
4091  }
4092  }
4093  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
4094  nLifeTime );
4095  }
4096  break;
4097  default:
4098  {
4099  SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAV_HTTP_ERROR, for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
4100  << ", '" << e.getData() << "'" );
4101  aDAVOptions.setHttpResponseStatusCode( e.getStatus() );
4102  aDAVOptions.setHttpResponseStatusText( e.getData() );
4103  // cache it, so OPTIONS won't be called again, this URL does not support it
4104  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
4106  }
4107  break;
4108  }
4109  }
4110  break;
4111  // The 'DAVException::DAV_HTTP_REDIRECT' means we reached the maximum
4112  // number of redirections, consider the resource type as UNKNOWN
4113  // possibly a normal web site, not DAV
4115  default:
4116  {
4117  SAL_WARN( "ucb.ucp.webdav","OPTIONS - General DAVException (or max DAV_HTTP_REDIRECT reached) for URL <" << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: "
4118  << e.getError() << ", HTTP error: "<< e.getStatus() );
4119  aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
4121  }
4122  break;
4123  }
4124  }
4125  }
4126  else
4127  {
4128  // check current response status code, perhaps we need to set networkAccessAllowed
4129  sal_uInt16 CachedResponseStatusCode = aDAVOptions.getHttpResponseStatusCode();
4130  if ( networkAccessAllowed != nullptr &&
4131  ( ( CachedResponseStatusCode == SC_NOT_FOUND ) ||
4132  ( CachedResponseStatusCode == SC_GONE ) ||
4133  ( CachedResponseStatusCode == USC_CONNECTION_TIMED_OUT ) ||
4134  ( CachedResponseStatusCode == USC_LOOKUP_FAILED ) ||
4135  ( CachedResponseStatusCode == USC_AUTH_FAILED ) ||
4136  ( CachedResponseStatusCode == USC_AUTHPROXY_FAILED )
4137  )
4138  )
4139  {
4140  *networkAccessAllowed = false;
4141  }
4142  }
4143  rDAVOptions = aDAVOptions;
4144 }
4145 
4146 
4147 //static
4148 bool Content::isResourceAvailable( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
4149  const std::unique_ptr< DAVResourceAccess > & rResAccess,
4150  DAVOptions& rDAVOptions )
4151 {
4152  std::vector< OUString > aHeaderNames;
4153  DAVResource aResource;
4154 
4155  try
4156  {
4157  // To check for the physical URL resource availability, first
4158  // try using a simple HEAD command
4159  // if HEAD is successful, set element found,
4160  rResAccess->HEAD( aHeaderNames, aResource, xEnv );
4161  rDAVOptions.setHttpResponseStatusCode( 0 );
4162  rDAVOptions.setHttpResponseStatusText( OUString() );
4163  return true;
4164  }
4165  catch ( DAVException const & e )
4166  {
4168  {
4169  if ( e.getStatus() == SC_NOT_IMPLEMENTED ||
4171  e.getStatus() == SC_NOT_FOUND )
4172  {
4173  SAL_WARN( "ucb.ucp.webdav", "HEAD probably not implemented: fall back to a partial GET" );
4174  // set in cached OPTIONS "HEAD not implemented"
4175  // so it won't be used again on this resource
4176  rDAVOptions.setHeadAllowed( false );
4177  try
4178  {
4179  // do a GET with a payload of 0, the server does not
4180  // support HEAD (or has HEAD disabled)
4181  DAVRequestHeaders aPartialGet;
4182  aPartialGet.emplace_back(
4183  OUString( "Range" ),
4184  OUString( "bytes=0-0" ));
4185 
4186  rResAccess->GET0( aPartialGet,
4187  aHeaderNames,
4188  aResource,
4189  xEnv );
4190  return true;
4191  }
4192  catch ( DAVException const & ex )
4193  {
4194  if ( ex.getError() == DAVException::DAV_HTTP_ERROR )
4195  {
4196  rDAVOptions.setHttpResponseStatusCode( ex.getStatus() );
4197  rDAVOptions.setHttpResponseStatusText( ex.getData() );
4198  }
4199  }
4200  }
4201  else
4202  {
4203  rDAVOptions.setHttpResponseStatusCode( e.getStatus() );
4204  rDAVOptions.setHttpResponseStatusText( e.getData() );
4205  }
4206  }
4207  return false;
4208  }
4209  catch ( ... )
4210  {
4211  }
4212  // set SC_NOT_IMPLEMENTED since at a minimum GET must be implemented in a basic Web server
4214  rDAVOptions.setHttpResponseStatusText( OUString() );
4215  return false;
4216 }
4217 
4218 
4219 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define WEBDAV_COLLECTION_TYPE
Type
css::uno::Reference< css::uno::XComponentContext > m_xContext
const sal_uInt16 USC_AUTHPROXY_FAILED
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
void transfer(const css::ucb::TransferInfo &rArgs, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
void setURL(const OUString &sURL)
bool hasValue()
std::vector< ContentImplHelperRef > ContentRefList
static OUString unescape(const OUString &string)
Definition: NeonUri.cxx:257
void getResourceOptions(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, DAVOptions &rDAVOptions, const std::unique_ptr< DAVResourceAccess > &rResAccess, bool *networkAccessAllowed=nullptr)
const sal_uInt16 SC_BAD_REQUEST
OUString GetPathBaseNameUnescaped() const
Definition: NeonUri.cxx:233
void removeDAVOptions(const OUString &rURL)
Definition: DAVTypes.cxx:130
ResourceType m_eResourceTypeForLocks
bool isLockAllowed() const
std::vector< DAVRequestHeader > DAVRequestHeaders
bool exchangeIdentity(const css::uno::Reference< css::ucb::XContentIdentifier > &xNewId)
void addDAVOptions(DAVOptions &rDAVOptions, const sal_uInt32 nLifeTime)
Definition: DAVTypes.cxx:143
static void removeCachedPropertyNames(const OUString &rURL)
void setHeadAllowed(const OUString &rURL, bool HeadAllowed=true)
Definition: DAVTypes.cxx:171
void insert(const css::uno::Reference< css::io::XInputStream > &xInputStream, bool bReplaceExisting, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
sal_uInt32 m_nOptsCacheLifeDAVLocked
static void UCBNamesToHTTPNames(const css::uno::Sequence< css::beans::Property > &rProps, std::vector< OUString > &resources)
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
void setHttpResponseStatusCode(const sal_uInt16 nHttpResponseStatusCode)
OUString GetPathBaseName() const
Definition: NeonUri.cxx:199
virtual OUString getParentURL() override
virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType) override
#define DAVS_URL_SCHEME
std::vector< ContentRef > ContentRefList
#define WEBDAV_CONTENT_SERVICE_NAME
const ExceptionCode & getError() const
std::vector< OUString > m_aFailedPropNames
void setHeadAllowed(bool HeadAllowed=true)
#define WEBDAV_CONTENT_TYPE
css::uno::Reference< css::ucb::XPersistentPropertySet > getAdditionalPropertySet(bool bCreate)
void post(const css::ucb::PostCommandArgument2 &rArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
#define WEBDAV_URL_SCHEME
#define WEBDAVS_URL_SCHEME
void removeProperty(const OUString &Name, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
bool getDAVOptions(const OUString &rURL, DAVOptions &rDAVOptions)
Definition: DAVTypes.cxx:103
#define VNDSUNSTARWEBDAV_URL_SCHEME
const OUString & getHttpResponseStatusText() const
css::uno::Any open(const css::ucb::OpenCommandArgument3 &rArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
void setRedirectedURL(const OUString &sRedirectedURL)
UNKNOWN
void notifyPropertiesChange(const css::uno::Sequence< css::beans::PropertyChangeEvent > &evt) const
const OUString & getData() const
virtual css::uno::Any SAL_CALL execute(const css::ucb::Command &aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment) override
const std::unique_ptr< PropertyValueMap > & getProperties() const
ContentProvider * m_pProvider
void cancelCommandExecution(const uno::Any &rException, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
const sal_uInt16 SC_METHOD_NOT_ALLOWED
css::uno::Any MapDAVException(const DAVException &e, bool bWrite)
const sal_uInt16 SC_NOT_IMPLEMENTED
void lock(const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
#define DAV_URL_SCHEME
ResourceType
sal_uInt16 getStatus() const
css::uno::Reference< css::sdbc::XRow > getPropertyValues(const css::uno::Sequence< css::beans::Property > &rProperties, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
css::uno::Reference< css::ucb::XContentIdentifier > m_xIdentifier
static bool isResourceAvailable(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, const std::unique_ptr< DAVResourceAccess > &rResAccess, DAVOptions &rDAVOptions)
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL createNewContent(const css::ucb::ContentInfo &Info) override
css::uno::Reference< css::beans::XPropertySetInfo > getPropertySetInfo(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bCache=true)
#define VNDSUNSTARWEBDAVS_URL_SCHEME
Object Value
css::uno::Reference< css::ucb::XCommandInfo > getCommandInfo(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bCache=true)
void getProperty(const OUString &rPropName, css::beans::Property &rProp)
sal_uInt16 getHttpResponseStatusCode() const
virtual css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL queryCreatableContentsInfo() override
void cancelCommandExecution(const DAVException &e, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bWrite=false)
XTYPEPROVIDER_COMMON_IMPL(Content)
SQLUSMALLINT SQLCHAR SQLSMALLINT SQLCHAR SQLSMALLINT SQLCHAR SQLSMALLINT SQLUSMALLINT Scope
const sal_uInt16 SC_GONE
ResourceType resourceTypeForLocks(const css::uno::Reference< css::ucb::XCommandEnvironment > &rEnvironment, const std::unique_ptr< DAVResourceAccess > &rResAccess)
DocumentType const eType
const sal_uInt16 SC_FORBIDDEN
const sal_uInt16 SC_NONE
static bool shouldAccessNetworkAfterException(const DAVException &e)
static bool supportsType(const css::uno::Type &rType)
static bool isUCBSpecialProperty(const OUString &rFullName, OUString &rParsedName)
std::unique_ptr< DAVResourceAccess > m_xResAccess
const sal_uInt16 USC_LOOKUP_FAILED
virtual void SAL_CALL abort(sal_Int32 CommandId) override
rtl::Reference< ContentProviderImplHelper > m_xProvider
virtual OUString SAL_CALL getImplementationName() override
#define HTTP_URL_SCHEME
ContinuationFlags
const css::uno::Any & getValue(const OUString &rName) const
std::vector< DAVPropertyValue > properties
void notifyPropertySetInfoChange(const css::beans::PropertySetInfoChangeEvent &evt) const
void SetScheme(const OUString &scheme)
Definition: NeonUri.hxx:84
#define CPPU_TYPE_REF(T)
#define SAL_WARN_IF(condition, area, stream)
bool isFolder(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
void destroy(bool bDeletePhysical)
std::unique_ptr< CachableContentProperties > m_xCachedProps
OUString getBaseURI(const std::unique_ptr< DAVResourceAccess > &rResAccess)
const sal_uInt16 USC_CONNECTION_TIMED_OUT
#define FTP_URL_SCHEME
static OUString escapeSegment(const OUString &segment)
Definition: NeonUri.cxx:248
void setHttpResponseStatusText(const OUString &rHttpResponseStatusText)
#define SAL_INFO(area, stream)
void unlock(const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
static const OUString SUPPORTEDLOCK
css::uno::Sequence< css::uno::Type > SAL_CALL getTypes()
const sal_uInt16 USC_AUTH_FAILED
const OUString & getURL() const
const sal_uInt16 SC_NOT_FOUND
ResourceType getResourceType(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
void addProperty(const css::ucb::PropertyCommandArgument &aCmdArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
virtual OUString SAL_CALL getContentType() override
#define SAL_WARN(area, stream)
const sal_uInt16 SC_PRECONDITION_FAILED
bool exchange(const css::uno::Reference< css::ucb::XContentIdentifier > &rNewId)
void queryChildren(ContentRefList &rChildren)
static DAVOptionsCache aStaticDAVOptionsCache
virtual void SAL_CALL release() override
bool isHeadAllowed() const
sal_Int32 nPos
#define HTTPS_URL_SCHEME
css::uno::Sequence< css::uno::Any > setPropertyValues(const css::uno::Sequence< css::beans::PropertyValue > &rValues, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType, Interface1 *p1)
rtl::Reference< Content > ContentRef
static void UCBNamesToDAVNames(const css::uno::Sequence< css::beans::Property > &rProps, std::vector< OUString > &resources)
const sal_uInt16 SC_INTERNAL_SERVER_ERROR
virtual void SAL_CALL acquire() override
static OUString makeConnectionEndPointString(const OUString &rHostName, int nPort)
Definition: NeonUri.cxx:265