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