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