LibreOffice Module ucb (master)  1
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  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 
23 #include <rtl/uri.hxx>
24 #include <rtl/ustrbuf.hxx>
25 #include <sal/log.hxx>
27 #include <ucbhelper/macros.hxx>
31 
32 #include <com/sun/star/beans/IllegalTypeException.hpp>
33 #include <com/sun/star/beans/NotRemoveableException.hpp>
34 #include <com/sun/star/beans/PropertyAttribute.hpp>
35 #include <com/sun/star/beans/PropertyExistException.hpp>
36 #include <com/sun/star/beans/PropertySetInfoChange.hpp>
37 #include <com/sun/star/beans/PropertySetInfoChangeEvent.hpp>
38 #include <com/sun/star/beans/PropertyValue.hpp>
39 #include <com/sun/star/io/XActiveDataSink.hpp>
40 #include <com/sun/star/io/XOutputStream.hpp>
41 #include <com/sun/star/lang/IllegalAccessException.hpp>
42 #include <com/sun/star/lang/IllegalArgumentException.hpp>
43 #include <com/sun/star/sdbc/SQLException.hpp>
44 #include <com/sun/star/task/PasswordContainerInteractionHandler.hpp>
45 #include <com/sun/star/ucb/CommandEnvironment.hpp>
46 #include <com/sun/star/ucb/CommandFailedException.hpp>
47 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
48 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
49 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
50 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
51 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
52 #include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
53 #include <com/sun/star/ucb/InteractiveLockingLockExpiredException.hpp>
54 #include <com/sun/star/ucb/InteractiveLockingNotLockedException.hpp>
55 #include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
56 #include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
57 #include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
58 #include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
59 #include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
60 #include <com/sun/star/ucb/MissingInputStreamException.hpp>
61 #include <com/sun/star/ucb/MissingPropertiesException.hpp>
62 #include <com/sun/star/ucb/NameClash.hpp>
63 #include <com/sun/star/ucb/NameClashException.hpp>
64 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
65 #include <com/sun/star/ucb/OpenMode.hpp>
66 #include <com/sun/star/ucb/PostCommandArgument2.hpp>
67 #include <com/sun/star/ucb/PropertyCommandArgument.hpp>
68 #include <com/sun/star/ucb/TransferInfo.hpp>
69 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
70 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
71 #include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
72 #include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
73 #include <com/sun/star/ucb/XCommandInfo.hpp>
74 #include <com/sun/star/ucb/XPersistentPropertySet.hpp>
75 #include <com/sun/star/uno/XComponentContext.hpp>
76 
77 #include "webdavcontent.hxx"
78 #include "webdavprovider.hxx"
79 #include "webdavresultset.hxx"
80 #include "ContentProperties.hxx"
81 #include "SerfUri.hxx"
82 #include "UCBDeadPropertyValue.hxx"
83 #include "DAVException.hxx"
84 #include "DAVProperties.hxx"
85 
86 using namespace com::sun::star;
87 using namespace http_dav_ucp;
88 
89 namespace
90 {
91 void lcl_sendPartialGETRequest( bool &bError,
92  DAVException &aLastException,
93  const std::vector< OUString >& rProps,
94  std::vector< OUString > &aHeaderNames,
95  const std::unique_ptr< DAVResourceAccess > &xResAccess,
96  std::unique_ptr< ContentProperties > &xProps,
97  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
98 {
99  DAVResource aResource;
100  DAVRequestHeaders aPartialGet;
101  aPartialGet.push_back(
103  OUString( "Range" ),
104  OUString( "bytes=0-0" )));
105 
106  bool bIsRequestSize = std::any_of(aHeaderNames.begin(), aHeaderNames.end(),
107  [](const OUString& rHeaderName) { return rHeaderName == "Content-Length"; });
108 
109  if ( bIsRequestSize )
110  {
111  // we need to know if the server accepts range requests for a resource
112  // and the range unit it uses
113  aHeaderNames.push_back( OUString( "Accept-Ranges" ) );
114  aHeaderNames.push_back( OUString( "Content-Range" ) );
115  }
116  try
117  {
118  uno::Reference< io::XInputStream > xIn = xResAccess->GET( aPartialGet,
119  aHeaderNames,
120  aResource,
121  xEnv );
122  bError = false;
123 
124  if ( bIsRequestSize )
125  {
126  // the ContentProperties maps "Content-Length" to the UCB "Size" property
127  // This would have an unrealistic value of 1 byte because we did only a partial GET
128  // Solution: if "Content-Range" is present, map it with UCB "Size" property
129  OUString aAcceptRanges, aContentRange, aContentLength;
130  std::vector< DAVPropertyValue > &aResponseProps = aResource.properties;
131  for ( const auto& rResponseProp : aResponseProps )
132  {
133  if ( rResponseProp.Name == "Accept-Ranges" )
134  rResponseProp.Value >>= aAcceptRanges;
135  else if ( rResponseProp.Name == "Content-Range" )
136  rResponseProp.Value >>= aContentRange;
137  else if ( rResponseProp.Name == "Content-Length" )
138  rResponseProp.Value >>= aContentLength;
139  }
140 
141  sal_Int64 nSize = 1;
142  if ( aContentLength.getLength() )
143  {
144  nSize = aContentLength.toInt64();
145  }
146 
147  // according to http://tools.ietf.org/html/rfc2616#section-3.12
148  // the only range unit defined is "bytes" and implementations
149  // MAY ignore ranges specified using other units.
150  if ( nSize == 1 &&
151  aContentRange.getLength() &&
152  aAcceptRanges == "bytes" )
153  {
154  // Parse the Content-Range to get the size
155  // vid. http://tools.ietf.org/html/rfc2616#section-14.16
156  // Content-Range: <range unit> <bytes range>/<size>
157  sal_Int32 nSlash = aContentRange.lastIndexOf( '/' );
158  if ( nSlash != -1 )
159  {
160  OUString aSize = aContentRange.copy( nSlash + 1 );
161  // "*" means that the instance-length is unknown at the time when the response was generated
162  if ( aSize != "*" )
163  {
164  auto it = std::find_if(aResponseProps.begin(), aResponseProps.end(),
165  [](const DAVPropertyValue& rProp) { return rProp.Name == "Content-Length"; });
166  if (it != aResponseProps.end())
167  {
168  it->Value <<= aSize;
169  }
170  }
171  }
172  }
173  }
174 
175  if ( xProps.get() )
176  xProps->addProperties(
177  rProps,
178  ContentProperties( aResource ) );
179  else
180  xProps.reset ( new ContentProperties( aResource ) );
181  }
182  catch ( DAVException const & ex )
183  {
184  aLastException = ex;
185  }
186 }
187 }
188 
189 
190 // Content Implementation.
191 
192 
193 // ctr for content on an existing webdav resource
194 Content::Content(
195  const uno::Reference< uno::XComponentContext >& rxContext,
196  ContentProvider* pProvider,
197  const uno::Reference< ucb::XContentIdentifier >& Identifier,
198  rtl::Reference< DAVSessionFactory > const & rSessionFactory )
199 : ContentImplHelper( rxContext, pProvider, Identifier ),
200  m_eResourceType( UNKNOWN ),
201  m_pProvider( pProvider ),
202  m_bTransient( false ),
203  m_bLocked( false ),
204  m_bCollection( false ),
205  m_bDidGetOrHead( false )
206 {
207  try
208  {
209  m_xResAccess.reset( new DAVResourceAccess(
210  rxContext,
211  rSessionFactory,
212  Identifier->getContentIdentifier() ) );
213 
214  SerfUri aURI( Identifier->getContentIdentifier() );
215  m_aEscapedTitle = aURI.GetPathBaseName();
216  }
217  catch ( DAVException const & )
218  {
219  throw ucb::ContentCreationException();
220  }
221 }
222 
223 
224 // ctr for content on a non-existing webdav resource
225 Content::Content(
226  const uno::Reference< uno::XComponentContext >& rxContext,
227  ContentProvider* pProvider,
228  const uno::Reference< ucb::XContentIdentifier >& Identifier,
229  rtl::Reference< DAVSessionFactory > const & rSessionFactory,
230  bool isCollection )
231 : ContentImplHelper( rxContext, pProvider, Identifier ),
232  m_eResourceType( UNKNOWN ),
233  m_pProvider( pProvider ),
234  m_bTransient( true ),
235  m_bLocked( false ),
236  m_bCollection( isCollection ),
237  m_bDidGetOrHead( false )
238 {
239  try
240  {
241  m_xResAccess.reset( new DAVResourceAccess(
242  rxContext, rSessionFactory, Identifier->getContentIdentifier() ) );
243  }
244  catch ( DAVException const & )
245  {
246  throw ucb::ContentCreationException();
247  }
248 
249  // Do not set m_aEscapedTitle here! Content::insert relays on this!!!
250 }
251 
252 
253 // virtual
254 Content::~Content()
255 {
256  if (m_bLocked)
257  unlock(uno::Reference< ucb::XCommandEnvironment >());
258 }
259 
260 
261 // XInterface methods.
262 
263 
264 // virtual
265 void SAL_CALL Content::acquire()
266  throw( )
267 {
268  ContentImplHelper::acquire();
269 }
270 
271 
272 // virtual
273 void SAL_CALL Content::release()
274  throw( )
275 {
276  ContentImplHelper::release();
277 }
278 
279 
280 // virtual
281 uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
282 {
283  // Note: isFolder may require network activities! So call it only
284  // if it is really necessary!!!
286  rType,
287  static_cast< ucb::XContentCreator * >( this ) );
288  if ( aRet.hasValue() )
289  {
290  try
291  {
292  uno::Reference< beans::XPropertySet > const xProps(
293  m_xContext, uno::UNO_QUERY_THROW );
294  uno::Reference< uno::XComponentContext > xCtx;
295  xCtx.set( xProps->getPropertyValue( "DefaultContext" ),
296  uno::UNO_QUERY_THROW );
297 
298  uno::Reference< task::XInteractionHandler > xIH(
299  task::PasswordContainerInteractionHandler::create( xCtx ) );
300 
301  // Supply a command env to isFolder() that contains an interaction
302  // handler that uses the password container service to obtain
303  // credentials without displaying a password gui.
304 
305  uno::Reference< ucb::XCommandEnvironment > xCmdEnv(
306  ucb::CommandEnvironment::create(
307  xCtx,
308  xIH,
309  uno::Reference< ucb::XProgressHandler >() ) );
310 
311  return isFolder( xCmdEnv ) ? aRet : uno::Any();
312  }
313  catch ( uno::RuntimeException const & )
314  {
315  throw;
316  }
317  catch ( uno::Exception const & )
318  {
319  return uno::Any();
320  }
321  }
322  return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType );
323 }
324 
325 
326 // XTypeProvider methods.
327 
328 
330 
331 
332 // virtual
333 uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
334 {
335  bool bFolder = false;
336  try
337  {
338  bFolder
339  = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
340  }
341  catch ( uno::RuntimeException const & )
342  {
343  throw;
344  }
345  catch ( uno::Exception const & )
346  {
347  }
348 
349  if ( bFolder )
350  {
351  static cppu::OTypeCollection s_aFolderTypes(
352  CPPU_TYPE_REF( lang::XTypeProvider ),
353  CPPU_TYPE_REF( lang::XServiceInfo ),
354  CPPU_TYPE_REF( lang::XComponent ),
355  CPPU_TYPE_REF( ucb::XContent ),
356  CPPU_TYPE_REF( ucb::XCommandProcessor ),
357  CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
358  CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
359  CPPU_TYPE_REF( beans::XPropertyContainer ),
360  CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
361  CPPU_TYPE_REF( container::XChild ),
362  CPPU_TYPE_REF( ucb::XContentCreator ) );
363 
364  return s_aFolderTypes.getTypes();
365  }
366  else
367  {
368  static cppu::OTypeCollection s_aDocumentTypes(
369  CPPU_TYPE_REF( lang::XTypeProvider ),
370  CPPU_TYPE_REF( lang::XServiceInfo ),
371  CPPU_TYPE_REF( lang::XComponent ),
372  CPPU_TYPE_REF( ucb::XContent ),
373  CPPU_TYPE_REF( ucb::XCommandProcessor ),
374  CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
375  CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
376  CPPU_TYPE_REF( beans::XPropertyContainer ),
377  CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
378  CPPU_TYPE_REF( container::XChild ) );
379 
380  return s_aDocumentTypes.getTypes();
381  }
382 }
383 
384 
385 // XServiceInfo methods.
386 
387 
388 // virtual
389 OUString SAL_CALL Content::getImplementationName()
390 {
391  return OUString( "com.sun.star.comp.ucb.WebDAVContent" );
392 }
393 
394 
395 // virtual
396 uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
397 {
398  uno::Sequence<OUString> aSNS { WEBDAV_CONTENT_SERVICE_NAME };
399  return aSNS;
400 }
401 
402 
403 // XContent methods.
404 
405 
406 // virtual
407 OUString SAL_CALL Content::getContentType()
408 {
409  bool bFolder = false;
410  try
411  {
412  bFolder
413  = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
414  }
415  catch ( uno::RuntimeException const & )
416  {
417  throw;
418  }
419  catch ( uno::Exception const & )
420  {
421  }
422 
423  if ( bFolder )
424  return OUString( WEBDAV_COLLECTION_TYPE );
425 
426  return OUString( WEBDAV_CONTENT_TYPE );
427 }
428 
429 
430 // XCommandProcessor methods.
431 
432 
433 // virtual
434 uno::Any SAL_CALL Content::execute(
435  const ucb::Command& aCommand,
436  sal_Int32 /*CommandId*/,
437  const uno::Reference< ucb::XCommandEnvironment >& Environment )
438 {
439  SAL_INFO("ucb.ucp.webdav", ">>>>> Content::execute: start: command: " << aCommand.Name
440  << ", env: " << (Environment.is() ? "present" : "missing") );
441 
442  uno::Any aRet;
443 
444  if ( aCommand.Name == "getPropertyValues" )
445  {
446 
447  // getPropertyValues
448 
449 
450  uno::Sequence< beans::Property > Properties;
451  if ( !( aCommand.Argument >>= Properties ) )
452  {
454  uno::makeAny( lang::IllegalArgumentException(
455  "Wrong argument type!",
456  static_cast< cppu::OWeakObject * >( this ),
457  -1 ) ),
458  Environment );
459  // Unreachable
460  }
461 
462  aRet <<= getPropertyValues( Properties, Environment );
463  }
464  else if ( aCommand.Name == "setPropertyValues" )
465  {
466 
467  // setPropertyValues
468 
469 
470  uno::Sequence< beans::PropertyValue > aProperties;
471  if ( !( aCommand.Argument >>= aProperties ) )
472  {
474  uno::makeAny( lang::IllegalArgumentException(
475  "Wrong argument type!",
476  static_cast< cppu::OWeakObject * >( this ),
477  -1 ) ),
478  Environment );
479  // Unreachable
480  }
481 
482  if ( !aProperties.getLength() )
483  {
485  uno::makeAny( lang::IllegalArgumentException(
486  "No properties!",
487  static_cast< cppu::OWeakObject * >( this ),
488  -1 ) ),
489  Environment );
490  // Unreachable
491  }
492 
493  aRet <<= setPropertyValues( aProperties, Environment );
494  }
495  else if ( aCommand.Name == "getPropertySetInfo" )
496  {
497 
498  // getPropertySetInfo
499 
500 
501  // Note: Implemented by base class.
502  aRet <<= getPropertySetInfo( Environment,
503  false /* don't cache data */ );
504  }
505  else if ( aCommand.Name == "getCommandInfo" )
506  {
507 
508  // getCommandInfo
509 
510 
511  // Note: Implemented by base class.
512  aRet <<= getCommandInfo( Environment, false );
513  }
514  else if ( aCommand.Name == "open" )
515  {
516 
517  // open
518 
519 
520  ucb::OpenCommandArgument2 aOpenCommand;
521  if ( !( aCommand.Argument >>= aOpenCommand ) )
522  {
524  uno::makeAny( lang::IllegalArgumentException(
525  "Wrong argument type!",
526  static_cast< cppu::OWeakObject * >( this ),
527  -1 ) ),
528  Environment );
529  // Unreachable
530  }
531 
532  aRet = open( aOpenCommand, Environment );
533 
534  if ( (aOpenCommand.Mode == ucb::OpenMode::DOCUMENT ||
535  aOpenCommand.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE) &&
536  supportsExclusiveWriteLock( Environment ) )
537  lock( Environment );
538  }
539  else if ( aCommand.Name == "insert" )
540  {
541 
542  // insert
543 
544 
545  ucb::InsertCommandArgument arg;
546  if ( !( aCommand.Argument >>= arg ) )
547  {
549  uno::makeAny( lang::IllegalArgumentException(
550  "Wrong argument type!",
551  static_cast< cppu::OWeakObject * >( this ),
552  -1 ) ),
553  Environment );
554  // Unreachable
555  }
556 
557  insert( arg.Data, arg.ReplaceExisting, Environment );
558  }
559  else if ( aCommand.Name == "delete" )
560  {
561 
562  // delete
563 
564 
565  bool bDeletePhysical = false;
566  aCommand.Argument >>= bDeletePhysical;
567 
568 // KSO: Ignore parameter and destroy the content, if you don't support
569 // putting objects into trashcan. ( Since we do not have a trash can
570 // service yet (src603), you actually have no other choice. )
571 // if ( bDeletePhysical )
572 // {
573  try
574  {
575  std::unique_ptr< DAVResourceAccess > xResAccess;
576  {
577  osl::Guard< osl::Mutex > aGuard( m_aMutex );
578  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
579  }
580  xResAccess->DESTROY( Environment );
581  {
582  osl::Guard< osl::Mutex > aGuard( m_aMutex );
583  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
584  }
585  }
586  catch ( DAVException const & e )
587  {
588  cancelCommandExecution( e, Environment, true );
589  // Unreachable
590  }
591 // }
592 
593  // Propagate destruction.
594  destroy( bDeletePhysical );
595 
596  // Remove own and all children's Additional Core Properties.
597  removeAdditionalPropertySet();
598  }
599  else if ( aCommand.Name == "transfer" && isFolder( Environment ) )
600  {
601 
602  // transfer
603  // ( Not available at documents )
604 
605 
606  ucb::TransferInfo transferArgs;
607  if ( !( aCommand.Argument >>= transferArgs ) )
608  {
610  uno::makeAny( lang::IllegalArgumentException(
611  "Wrong argument type!",
612  static_cast< cppu::OWeakObject * >( this ),
613  -1 ) ),
614  Environment );
615  // Unreachable
616  }
617 
618  transfer( transferArgs, Environment );
619  }
620  else if ( aCommand.Name == "post" )
621  {
622 
623  // post
624 
625 
626  ucb::PostCommandArgument2 aArg;
627  if ( !( aCommand.Argument >>= aArg ) )
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  post( aArg, Environment );
639  }
640  else if ( aCommand.Name == "lock" &&
641  supportsExclusiveWriteLock( Environment ) )
642  {
643 
644  // lock
645 
646 
647  lock( Environment );
648  }
649  else if ( aCommand.Name == "unlock" &&
650  supportsExclusiveWriteLock( Environment ) )
651  {
652 
653  // unlock
654 
655 
656  unlock( Environment );
657  }
658  else if ( aCommand.Name == "createNewContent" &&
659  isFolder( Environment ) )
660  {
661 
662  // createNewContent
663 
664 
665  ucb::ContentInfo aArg;
666  if ( !( aCommand.Argument >>= aArg ) )
667  {
669  uno::makeAny( lang::IllegalArgumentException(
670  "Wrong argument type!",
671  static_cast< cppu::OWeakObject * >( this ),
672  -1 ) ),
673  Environment );
674  // Unreachable
675  }
676 
677  aRet <<= createNewContent( aArg );
678  }
679  else if ( aCommand.Name == "addProperty" )
680  {
681  ucb::PropertyCommandArgument aPropArg;
682  if ( !( aCommand.Argument >>= aPropArg ))
683  {
685  uno::makeAny( lang::IllegalArgumentException(
686  "Wrong argument type!",
687  static_cast< cppu::OWeakObject * >( this ),
688  -1 ) ),
689  Environment );
690  }
691 
692  // TODO when/if XPropertyContainer is removed,
693  // the command execution can be canceled in addProperty
694  try
695  {
696  addProperty( aPropArg, Environment );
697  }
698  catch ( const beans::PropertyExistException &e )
699  {
700  ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
701  }
702  catch ( const beans::IllegalTypeException&e )
703  {
704  ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
705  }
706  catch ( const lang::IllegalArgumentException&e )
707  {
708  ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
709  }
710  }
711  else if ( aCommand.Name == "removeProperty" )
712  {
713  OUString sPropName;
714  if ( !( aCommand.Argument >>= sPropName ) )
715  {
717  uno::makeAny( lang::IllegalArgumentException(
718  "Wrong argument type!",
719  static_cast< cppu::OWeakObject * >( this ),
720  -1 ) ),
721  Environment );
722  }
723 
724  // TODO when/if XPropertyContainer is removed,
725  // the command execution can be canceled in removeProperty
726  try
727  {
728  removeProperty( sPropName, Environment );
729  }
730  catch( const beans::UnknownPropertyException &e )
731  {
732  ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
733  }
734  catch( const beans::NotRemoveableException &e )
735  {
736  ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
737  }
738  }
739  else
740  {
741 
742  // Unsupported command
743 
744 
746  uno::makeAny( ucb::UnsupportedCommandException(
747  aCommand.Name,
748  static_cast< cppu::OWeakObject * >( this ) ) ),
749  Environment );
750  // Unreachable
751  }
752 
753  SAL_INFO("ucb.ucp.webdav", "<<<<< Content::execute: end: command: " << aCommand.Name);
754  return aRet;
755 }
756 
757 
758 // virtual
759 void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
760 {
761  try
762  {
763  std::unique_ptr< DAVResourceAccess > xResAccess;
764  {
765  osl::MutexGuard aGuard( m_aMutex );
766  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
767  }
768  DAVResourceAccess::abort();
769  {
770  osl::Guard< osl::Mutex > aGuard( m_aMutex );
771  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
772  }
773  }
774  catch ( DAVException const & )
775  {
776  // abort failed!
777  }
778 }
779 
780 
781 // XPropertyContainer methods.
782 
783 
784 void Content::addProperty( const css::ucb::PropertyCommandArgument &aCmdArg,
785  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
786 {
787 // if ( m_bTransient )
788 // @@@ ???
789  const beans::Property aProperty = aCmdArg.Property;
790  const uno::Any aDefaultValue = aCmdArg.DefaultValue;
791 
792  // check property Name
793  if ( !aProperty.Name.getLength() )
794  throw lang::IllegalArgumentException(
795  "\"addProperty\" with empty Property.Name",
796  static_cast< ::cppu::OWeakObject * >( this ),
797  -1 );
798 
799  // Check property type.
800  if ( !UCBDeadPropertyValue::supportsType( aProperty.Type ) )
801  throw beans::IllegalTypeException(
802  "\"addProperty\" unsupported Property.Type",
803  static_cast< ::cppu::OWeakObject * >( this ) );
804 
805  // check default value
806  if ( aDefaultValue.hasValue() && aDefaultValue.getValueType() != aProperty.Type )
807  throw beans::IllegalTypeException(
808  "\"addProperty\" DefaultValue does not match Property.Type",
809  static_cast< ::cppu::OWeakObject * >( this ) );
810 
811 
812  // Make sure a property with the requested name does not already
813  // exist in dynamic and static(!) properties.
814 
815 
816  // Take into account special properties with custom namespace
817  // using <prop:the_propname xmlns:prop="the_namespace">
818  OUString aSpecialName;
819  bool bIsSpecial = DAVProperties::isUCBSpecialProperty( aProperty.Name, aSpecialName );
820 
821  // Note: This requires network access!
822  if ( getPropertySetInfo( xEnv, false /* don't cache data */ )
823  ->hasPropertyByName( bIsSpecial ? aSpecialName : aProperty.Name ) )
824  {
825  // Property does already exist.
826  throw beans::PropertyExistException();
827  }
828 
829 
830  // Add a new dynamic property.
831 
832 
833  ProppatchValue aValue( PROPSET, aProperty.Name, aDefaultValue );
834 
835  std::vector< ProppatchValue > aProppatchValues;
836  aProppatchValues.push_back( aValue );
837 
838  try
839  {
840  // Set property value at server.
841  std::unique_ptr< DAVResourceAccess > xResAccess;
842  {
843  osl::Guard< osl::Mutex > aGuard( m_aMutex );
844  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
845  }
846  xResAccess->PROPPATCH( aProppatchValues, xEnv );
847  {
848  osl::Guard< osl::Mutex > aGuard( m_aMutex );
849  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
850  }
851 
852  // Notify propertyset info change listeners.
853  beans::PropertySetInfoChangeEvent evt(
854  static_cast< cppu::OWeakObject * >( this ),
855  bIsSpecial ? aSpecialName : aProperty.Name,
856  -1, // No handle available
857  beans::PropertySetInfoChange::PROPERTY_INSERTED );
858  notifyPropertySetInfoChange( evt );
859  }
860  catch ( DAVException const & e )
861  {
862  if ( e.getStatus() == SC_FORBIDDEN )
863  {
864  // Support for setting arbitrary dead properties is optional!
865 
866  // Store property locally.
867  ContentImplHelper::addProperty( bIsSpecial ? aSpecialName : aProperty.Name,
868  aProperty.Attributes,
869  aDefaultValue );
870  }
871  else
872  {
873  if ( shouldAccessNetworkAfterException( e ) )
874  {
875  try
876  {
877  const ResourceType & rType = getResourceType( xEnv );
878  switch ( rType )
879  {
880  case UNKNOWN:
881  case DAV:
882  throw lang::IllegalArgumentException();
883 
884  case NON_DAV:
885  // Store property locally.
886  ContentImplHelper::addProperty( bIsSpecial ? aSpecialName : aProperty.Name,
887  aProperty.Attributes,
888  aDefaultValue );
889  break;
890 
891  default:
892  SAL_WARN( "ucb.ucp.webdav",
893  "Content::addProperty - "
894  "Unsupported resource type!" );
895  break;
896  }
897  }
898  catch ( uno::Exception const & )
899  {
900  SAL_WARN( "ucb.ucp.webdav",
901  "Content::addProperty - "
902  "Unable to determine resource type!" );
903  }
904  }
905  else
906  {
907  SAL_WARN( "ucb.ucp.webdav",
908  "Content::addProperty - "
909  "Unable to determine resource type!" );
910  }
911  }
912  }
913 }
914 
915 void Content::removeProperty( const OUString& Name,
916  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
917 {
918 #if 0
919  // @@@ REMOVABLE at the moment not properly set in the PropSetInfo
920  try
921  {
922  beans::Property aProp
923  = getPropertySetInfo( xEnv, false /* don't cache data */ )
924  ->getPropertyByName( Name );
925 
926  if ( !( aProp.Attributes & beans::PropertyAttribute::REMOVABLE ) )
927  {
928  // Not removable!
929  throw beans::NotRemoveableException();
930  }
931  }
932  catch ( beans::UnknownPropertyException const & )
933  {
934  //SAL_WARN( "ucb.ucp.webdav", "removeProperty - Unknown property!" );
935  throw;
936  }
937 #endif
938 
939  // Try to remove property from server.
940  try
941  {
942  std::vector< ProppatchValue > aProppatchValues;
943  ProppatchValue aValue( PROPREMOVE, Name, uno::Any() );
944  aProppatchValues.push_back( aValue );
945 
946  // Remove property value from server.
947  std::unique_ptr< DAVResourceAccess > xResAccess;
948  {
949  osl::Guard< osl::Mutex > aGuard( m_aMutex );
950  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
951  }
952  xResAccess->PROPPATCH( aProppatchValues, xEnv );
953  {
954  osl::Guard< osl::Mutex > aGuard( m_aMutex );
955  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
956  }
957 
958  // Notify propertyset info change listeners.
959  beans::PropertySetInfoChangeEvent evt(
960  static_cast< cppu::OWeakObject * >( this ),
961  Name,
962  -1, // No handle available
963  beans::PropertySetInfoChange::PROPERTY_REMOVED );
964  notifyPropertySetInfoChange( evt );
965  }
966  catch ( DAVException const & e )
967  {
968  if ( e.getStatus() == SC_FORBIDDEN )
969  {
970  // Support for setting arbitrary dead properties is optional!
971 
972  // Try to remove property from local store.
973  ContentImplHelper::removeProperty( Name );
974  }
975  else
976  {
977  if ( shouldAccessNetworkAfterException( e ) )
978  {
979  try
980  {
981  const ResourceType & rType = getResourceType( xEnv );
982  switch ( rType )
983  {
984  case UNKNOWN:
985  case DAV:
986  throw beans::UnknownPropertyException(Name);
987 
988  case NON_DAV:
989  // Try to remove property from local store.
990  ContentImplHelper::removeProperty( Name );
991  break;
992 
993  default:
994  SAL_WARN( "ucb.ucp.webdav",
995  "Content::removeProperty - "
996  "Unsupported resource type!" );
997  break;
998  }
999  }
1000  catch ( uno::Exception const & )
1001  {
1002  SAL_WARN( "ucb.ucp.webdav",
1003  "Content::removeProperty - "
1004  "Unable to determine resource type!" );
1005  }
1006  }
1007  else
1008  {
1009  SAL_WARN( "ucb.ucp.webdav",
1010  "Content::removeProperty - "
1011  "Unable to determine resource type!" );
1012 // throw beans::UnknownPropertyException();
1013  }
1014  }
1015  }
1016 }
1017 
1018 // virtual
1019 void SAL_CALL Content::addProperty( const OUString& Name,
1020  sal_Int16 Attributes,
1021  const uno::Any& DefaultValue )
1022 {
1023  beans::Property aProperty;
1024  aProperty.Name = Name;
1025  aProperty.Type = DefaultValue.getValueType();
1026  aProperty.Attributes = Attributes;
1027  aProperty.Handle = -1;
1028 
1029  addProperty( ucb::PropertyCommandArgument( aProperty, DefaultValue ),
1030  uno::Reference< ucb::XCommandEnvironment >());
1031 }
1032 
1033 // virtual
1034 void SAL_CALL Content::removeProperty( const OUString& Name )
1035 {
1036  removeProperty( Name,
1037  uno::Reference< ucb::XCommandEnvironment >() );
1038 }
1039 
1040 
1041 // XContentCreator methods.
1042 
1043 
1044 // virtual
1045 uno::Sequence< ucb::ContentInfo > SAL_CALL
1046 Content::queryCreatableContentsInfo()
1047 {
1048  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1049 
1050  uno::Sequence< ucb::ContentInfo > aSeq( 2 );
1051 
1052  // document.
1053  aSeq.getArray()[ 0 ].Type = WEBDAV_CONTENT_TYPE;
1054  aSeq.getArray()[ 0 ].Attributes
1055  = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
1056  | ucb::ContentInfoAttribute::KIND_DOCUMENT;
1057 
1058  beans::Property aProp;
1059  m_pProvider->getProperty( "Title", aProp );
1060 
1061  uno::Sequence< beans::Property > aDocProps( 1 );
1062  aDocProps.getArray()[ 0 ] = aProp;
1063  aSeq.getArray()[ 0 ].Properties = aDocProps;
1064 
1065  // folder.
1066  aSeq.getArray()[ 1 ].Type = WEBDAV_COLLECTION_TYPE;
1067  aSeq.getArray()[ 1 ].Attributes
1068  = ucb::ContentInfoAttribute::KIND_FOLDER;
1069 
1070  uno::Sequence< beans::Property > aFolderProps( 1 );
1071  aFolderProps.getArray()[ 0 ] = aProp;
1072  aSeq.getArray()[ 1 ].Properties = aFolderProps;
1073  return aSeq;
1074 }
1075 
1076 
1077 // virtual
1078 uno::Reference< ucb::XContent > SAL_CALL
1079 Content::createNewContent( const ucb::ContentInfo& Info )
1080 {
1081  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1082 
1083  if ( !Info.Type.getLength() )
1084  return uno::Reference< ucb::XContent >();
1085 
1086  if ( ( Info.Type != WEBDAV_COLLECTION_TYPE )
1087  &&
1088  ( Info.Type != WEBDAV_CONTENT_TYPE ) )
1089  return uno::Reference< ucb::XContent >();
1090 
1091  OUString aURL = m_xIdentifier->getContentIdentifier();
1092 
1093  SAL_WARN_IF( aURL.isEmpty(), "ucb.ucp.webdav",
1094  "WebdavContent::createNewContent - empty identifier!" );
1095 
1096  if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
1097  aURL += "/";
1098 
1099  bool isCollection;
1100  if ( Info.Type == WEBDAV_COLLECTION_TYPE )
1101  {
1102  aURL += "New_Collection";
1103  isCollection = true;
1104  }
1105  else
1106  {
1107  aURL += "New_Content";
1108  isCollection = false;
1109  }
1110 
1111  uno::Reference< ucb::XContentIdentifier > xId(
1112  new ::ucbhelper::ContentIdentifier( aURL ) );
1113 
1114  // create the local content
1115  try
1116  {
1117  return new ::http_dav_ucp::Content( m_xContext,
1118  m_pProvider,
1119  xId,
1120  m_xResAccess->getSessionFactory(),
1121  isCollection );
1122  }
1123  catch ( ucb::ContentCreationException & )
1124  {
1125  return uno::Reference< ucb::XContent >();
1126  }
1127 }
1128 
1129 
1130 // virtual
1131 OUString Content::getParentURL()
1132 {
1133  // <scheme>:// -> ""
1134  // <scheme>://foo -> ""
1135  // <scheme>://foo/ -> ""
1136  // <scheme>://foo/bar -> <scheme>://foo/
1137  // <scheme>://foo/bar/ -> <scheme>://foo/
1138  // <scheme>://foo/bar/abc -> <scheme>://foo/bar/
1139 
1140  OUString aURL = m_xIdentifier->getContentIdentifier();
1141 
1142  sal_Int32 nPos = aURL.lastIndexOf( '/' );
1143  if ( nPos == ( aURL.getLength() - 1 ) )
1144  {
1145  // Trailing slash found. Skip.
1146  nPos = aURL.lastIndexOf( '/', nPos );
1147  }
1148 
1149  sal_Int32 nPos1 = aURL.lastIndexOf( '/', nPos );
1150  if ( nPos1 != -1 )
1151  nPos1 = aURL.lastIndexOf( '/', nPos1 );
1152 
1153  if ( nPos1 == -1 )
1154  return OUString();
1155 
1156  return aURL.copy( 0, nPos + 1 );
1157 }
1158 
1159 
1160 // Non-interface methods.
1161 
1162 
1163 // static
1164 uno::Reference< sdbc::XRow > Content::getPropertyValues(
1165  const uno::Reference< uno::XComponentContext >& rxContext,
1166  const uno::Sequence< beans::Property >& rProperties,
1167  const ContentProperties& rData,
1169  const OUString& rContentId )
1170 {
1171  // Note: Empty sequence means "get values of all supported properties".
1172 
1174  = new ::ucbhelper::PropertyValueSet( rxContext );
1175 
1176  sal_Int32 nCount = rProperties.getLength();
1177  if ( nCount )
1178  {
1179  uno::Reference< beans::XPropertySet > xAdditionalPropSet;
1180  bool bTriedToGetAdditionalPropSet = false;
1181 
1182  const beans::Property* pProps = rProperties.getConstArray();
1183  for ( sal_Int32 n = 0; n < nCount; ++n )
1184  {
1185  const beans::Property& rProp = pProps[ n ];
1186 
1187  // Process standard UCB, DAV and HTTP properties.
1188  const uno::Any & rValue = rData.getValue( rProp.Name );
1189  if ( rValue.hasValue() )
1190  {
1191  xRow->appendObject( rProp, rValue );
1192  }
1193  else
1194  {
1195  // Process local Additional Properties.
1196  if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
1197  {
1198  xAdditionalPropSet.set(
1199  rProvider->getAdditionalPropertySet( rContentId,
1200  false ),
1201  uno::UNO_QUERY );
1202  bTriedToGetAdditionalPropSet = true;
1203  }
1204 
1205  if ( !xAdditionalPropSet.is() ||
1206  !xRow->appendPropertySetValue(
1207  xAdditionalPropSet, rProp ) )
1208  {
1209  // Append empty entry.
1210  xRow->appendVoid( rProp );
1211  }
1212  }
1213  }
1214  }
1215  else
1216  {
1217  // Append all standard UCB, DAV and HTTP properties.
1218 
1219  const std::unique_ptr< PropertyValueMap > & xProps = rData.getProperties();
1220 
1221  ContentProvider * pProvider
1222  = static_cast< ContentProvider * >( rProvider.get() );
1223  beans::Property aProp;
1224 
1225  for ( const auto& rProp : *xProps )
1226  {
1227  if ( pProvider->getProperty( rProp.first, aProp ) )
1228  xRow->appendObject( aProp, rProp.second.value() );
1229  }
1230 
1231  // Append all local Additional Properties.
1232  uno::Reference< beans::XPropertySet > xSet(
1233  rProvider->getAdditionalPropertySet( rContentId, false ),
1234  uno::UNO_QUERY );
1235  xRow->appendPropertySet( xSet );
1236  }
1237 
1238  return uno::Reference< sdbc::XRow >( xRow.get() );
1239 }
1240 
1241 
1242 uno::Reference< sdbc::XRow > Content::getPropertyValues(
1243  const uno::Sequence< beans::Property >& rProperties,
1244  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1245 {
1246  std::unique_ptr< ContentProperties > xProps;
1247  std::unique_ptr< ContentProperties > xCachedProps;
1248  std::unique_ptr< DAVResourceAccess > xResAccess;
1249  OUString aUnescapedTitle;
1250  bool bHasAll = false;
1251  uno::Reference< uno::XComponentContext > xContext;
1252  uno::Reference< ucb::XContentIdentifier > xIdentifier;
1254 
1255  {
1256  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1257 
1258  aUnescapedTitle = SerfUri::unescape( m_aEscapedTitle );
1259  xContext.set( m_xContext );
1260  xIdentifier.set( m_xIdentifier );
1261  xProvider.set( m_xProvider.get() );
1262  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
1263 
1264  // First, ask cache...
1265  if ( m_xCachedProps.get() )
1266  {
1267  xCachedProps.reset( new ContentProperties( *m_xCachedProps.get() ) );
1268 
1269  std::vector< OUString > aMissingProps;
1270  if ( xCachedProps->containsAllNames( rProperties, aMissingProps ) )
1271  {
1272  // All properties are already in cache! No server access needed.
1273  bHasAll = true;
1274  }
1275 
1276  // use the cached ContentProperties instance
1277  xProps.reset( new ContentProperties( *xCachedProps ) );
1278  }
1279  }
1280 
1281  if ( !m_bTransient && !bHasAll )
1282  {
1283  // Obtain values from server...
1284 
1285 
1286  // First, identify whether resource is DAV or not
1287  bool bNetworkAccessAllowed = true;
1288  const ResourceType & rType = getResourceType( xEnv, xResAccess, &bNetworkAccessAllowed );
1289 
1290  if ( DAV == rType )
1291  {
1292  // cache lookup... getResourceType may fill the props cache via
1293  // PROPFIND!
1294  if ( m_xCachedProps.get() )
1295  {
1296  xCachedProps.reset(
1297  new ContentProperties( *m_xCachedProps.get() ) );
1298 
1299  std::vector< OUString > aMissingProps;
1300  if ( xCachedProps->containsAllNames(
1301  rProperties, aMissingProps ) )
1302  {
1303  // All properties are already in cache! No server access
1304  // needed.
1305  bHasAll = true;
1306  }
1307 
1308  // use the cached ContentProperties instance
1309  xProps.reset( new ContentProperties( *xCachedProps ) );
1310  }
1311 
1312  if ( !bHasAll )
1313  {
1314  // Only DAV resources support PROPFIND
1315  std::vector< OUString > aPropNames;
1316 
1317  uno::Sequence< beans::Property > aProperties(
1318  rProperties.getLength() );
1319 
1320  if ( !m_aFailedPropNames.empty() )
1321  {
1322  sal_Int32 nProps = 0;
1323  sal_Int32 nCount = rProperties.getLength();
1324  for ( sal_Int32 n = 0; n < nCount; ++n )
1325  {
1326  const OUString & rName = rProperties[ n ].Name;
1327 
1328  if ( std::none_of(m_aFailedPropNames.begin(), m_aFailedPropNames.end(),
1329  [&rName](const OUString& rPropName) { return rPropName == rName; }) )
1330  {
1331  aProperties[ nProps ] = rProperties[ n ];
1332  nProps++;
1333  }
1334  }
1335 
1336  aProperties.realloc( nProps );
1337  }
1338  else
1339  {
1340  aProperties = rProperties;
1341  }
1342 
1343  if ( aProperties.getLength() > 0 )
1344  ContentProperties::UCBNamesToDAVNames(
1345  aProperties, aPropNames );
1346 
1347  if ( !aPropNames.empty() )
1348  {
1349  std::vector< DAVResource > resources;
1350  try
1351  {
1352  xResAccess->PROPFIND(
1353  DAVZERO, aPropNames, resources, xEnv );
1354 
1355  if ( 1 == resources.size() )
1356  {
1357  if ( xProps.get())
1358  xProps->addProperties(
1359  aPropNames,
1360  ContentProperties( resources[ 0 ] ));
1361  else
1362  xProps.reset(
1363  new ContentProperties( resources[ 0 ] ) );
1364  }
1365  }
1366  catch ( DAVException const & e )
1367  {
1368  bNetworkAccessAllowed = bNetworkAccessAllowed &&
1369  shouldAccessNetworkAfterException( e );
1370 
1371  if ( !bNetworkAccessAllowed )
1372  {
1373  cancelCommandExecution( e, xEnv );
1374  // unreachable
1375  }
1376  }
1377  }
1378  }
1379  }
1380 
1381  if ( bNetworkAccessAllowed )
1382  {
1383  // All properties obtained already?
1384  std::vector< OUString > aMissingProps;
1385  if ( !( xProps.get()
1386  && xProps->containsAllNames(
1387  rProperties, aMissingProps ) )
1388  || !m_bDidGetOrHead )
1389  {
1390  // Possibly the missing props can be obtained using a HEAD
1391  // request.
1392 
1393  std::vector< OUString > aHeaderNames;
1394  ContentProperties::UCBNamesToHTTPNames(
1395  rProperties,
1396  aHeaderNames,
1397  true /* bIncludeUnmatched */ );
1398 
1399  if ( !aHeaderNames.empty() )
1400  {
1401  try
1402  {
1403  DAVResource resource;
1404  xResAccess->HEAD( aHeaderNames, resource, xEnv );
1405  m_bDidGetOrHead = true;
1406 
1407  if ( xProps.get() )
1408  xProps->addProperties(
1409  aMissingProps,
1410  ContentProperties( resource ) );
1411  else
1412  xProps.reset ( new ContentProperties( resource ) );
1413 
1414  if ( m_eResourceType == NON_DAV )
1415  xProps->addProperties( aMissingProps,
1417  aUnescapedTitle,
1418  false ) );
1419  }
1420  catch ( DAVException const & e )
1421  {
1422  // non "general-purpose servers" may not support HEAD requests
1423  // see http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1
1424  // In this case, perform a partial GET only to get the header info
1425  // vid. http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
1426  // WARNING if the server does not support partial GETs,
1427  // the GET will transfer the whole content
1428  bool bError = true;
1429  DAVException aLastException = e;
1430 
1431  // According to the spec. the origin server SHOULD return
1432  // * 405 (Method Not Allowed):
1433  // the method is known but not allowed for the requested resource
1434  // * 501 (Not Implemented):
1435  // the method is unrecognized or not implemented
1436  // TODO SC_NOT_FOUND is only for google-code server
1437  if ( aLastException.getStatus() == SC_NOT_IMPLEMENTED ||
1438  aLastException.getStatus() == SC_METHOD_NOT_ALLOWED ||
1439  aLastException.getStatus() == SC_NOT_FOUND )
1440  {
1441  lcl_sendPartialGETRequest( bError,
1442  aLastException,
1443  aMissingProps,
1444  aHeaderNames,
1445  xResAccess,
1446  xProps,
1447  xEnv );
1448  m_bDidGetOrHead = !bError;
1449  }
1450 
1451  if ( bError )
1452  {
1453  if ( !shouldAccessNetworkAfterException( aLastException ) )
1454  {
1455  cancelCommandExecution( aLastException, xEnv );
1456  // unreachable
1457  }
1458  }
1459  }
1460  }
1461  }
1462  }
1463 
1464  // might trigger HTTP redirect.
1465  // Therefore, title must be updated here.
1466  SerfUri aUri( xResAccess->getURL() );
1467  aUnescapedTitle = aUri.GetPathBaseNameUnescaped();
1468 
1469  if ( rType == UNKNOWN )
1470  {
1471  xProps.reset( new ContentProperties( aUnescapedTitle ) );
1472  }
1473 
1474  // For DAV resources we only know the Title, for non-DAV
1475  // resources we additionally know that it is a document.
1476 
1477  if ( rType == DAV )
1478  {
1479  //xProps.reset(
1480  // new ContentProperties( aUnescapedTitle ) );
1481  xProps->addProperty(
1482  "Title",
1483  uno::makeAny( aUnescapedTitle ),
1484  true );
1485  }
1486  else
1487  {
1488  if ( !xProps.get() )
1489  xProps.reset( new ContentProperties( aUnescapedTitle, false ) );
1490  else
1491  xProps->addProperty(
1492  "Title",
1493  uno::makeAny( aUnescapedTitle ),
1494  true );
1495 
1496  xProps->addProperty(
1497  "IsFolder",
1498  uno::makeAny( false ),
1499  true );
1500  xProps->addProperty(
1501  "IsDocument",
1502  uno::makeAny( true ),
1503  true );
1504  xProps->addProperty(
1505  "ContentType",
1506  uno::makeAny( OUString(WEBDAV_CONTENT_TYPE) ),
1507  true );
1508  }
1509  }
1510  else
1511  {
1512  // No server access for just created (not yet committed) objects.
1513  // Only a minimal set of properties supported at this stage.
1514  if (m_bTransient)
1515  xProps.reset( new ContentProperties( aUnescapedTitle,
1516  m_bCollection ) );
1517  }
1518 
1519  sal_Int32 nCount = rProperties.getLength();
1520  for ( sal_Int32 n = 0; n < nCount; ++n )
1521  {
1522  const OUString rName = rProperties[ n ].Name;
1523  if ( rName == "BaseURI" )
1524  {
1525  // Add BaseURI property, if requested.
1526  xProps->addProperty(
1527  "BaseURI",
1528  uno::makeAny( getBaseURI( xResAccess ) ),
1529  true );
1530  }
1531  else if ( rName == "CreatableContentsInfo" )
1532  {
1533  // Add CreatableContentsInfo property, if requested.
1534  bool bFolder = false;
1535  xProps->getValue( "IsFolder" )
1536  >>= bFolder;
1537  xProps->addProperty(
1538  "CreatableContentsInfo",
1539  uno::makeAny( bFolder
1540  ? queryCreatableContentsInfo()
1541  : uno::Sequence< ucb::ContentInfo >() ),
1542  true );
1543  }
1544  }
1545 
1546  uno::Reference< sdbc::XRow > xResultRow
1547  = getPropertyValues( xContext,
1548  rProperties,
1549  *xProps,
1550  xProvider,
1551  xIdentifier->getContentIdentifier() );
1552 
1553  {
1554  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1555 
1556  if ( !m_xCachedProps.get() )
1557  m_xCachedProps.reset( new CachableContentProperties( *xProps ) );
1558  else
1559  m_xCachedProps->addProperties( *xProps );
1560 
1561  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
1562  m_aEscapedTitle = SerfUri::escapeSegment( aUnescapedTitle );
1563  }
1564 
1565  return xResultRow;
1566 }
1567 
1568 
1569 uno::Sequence< uno::Any > Content::setPropertyValues(
1570  const uno::Sequence< beans::PropertyValue >& rValues,
1571  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1572 {
1573  uno::Reference< ucb::XContentIdentifier > xIdentifier;
1575  bool bTransient;
1576  std::unique_ptr< DAVResourceAccess > xResAccess;
1577 
1578  {
1579  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1580 
1581  xProvider.set( m_pProvider );
1582  xIdentifier.set( m_xIdentifier );
1583  bTransient = m_bTransient;
1584  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
1585  }
1586 
1587  uno::Sequence< uno::Any > aRet( rValues.getLength() );
1588  uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
1589  sal_Int32 nChanged = 0;
1590 
1591  beans::PropertyChangeEvent aEvent;
1592  aEvent.Source = static_cast< cppu::OWeakObject * >( this );
1593  aEvent.Further = false;
1594  // aEvent.PropertyName =
1595  aEvent.PropertyHandle = -1;
1596  // aEvent.OldValue =
1597  // aEvent.NewValue =
1598 
1599  std::vector< ProppatchValue > aProppatchValues;
1600  std::vector< sal_Int32 > aProppatchPropsPositions;
1601 
1602  uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet;
1603  bool bTriedToGetAdditionalPropSet = false;
1604 
1605  bool bExchange = false;
1606  OUString aNewTitle;
1607  OUString aOldTitle;
1608  sal_Int32 nTitlePos = -1;
1609 
1610  uno::Reference< beans::XPropertySetInfo > xInfo;
1611 
1612  const beans::PropertyValue* pValues = rValues.getConstArray();
1613  sal_Int32 nCount = rValues.getLength();
1614  for ( sal_Int32 n = 0; n < nCount; ++n )
1615  {
1616  const beans::PropertyValue& rValue = pValues[ n ];
1617  const OUString & rName = rValue.Name;
1618 
1619  beans::Property aTmpProp;
1620  xProvider->getProperty( rName, aTmpProp );
1621 
1622  if ( aTmpProp.Attributes & beans::PropertyAttribute::READONLY )
1623  {
1624  // Read-only property!
1625  aRet[ n ] <<= lang::IllegalAccessException(
1626  "Property is read-only!",
1627  static_cast< cppu::OWeakObject * >( this ) );
1628  continue;
1629  }
1630 
1631 
1632  // Mandatory props.
1633 
1634 
1635  if ( rName == "ContentType" )
1636  {
1637  // Read-only property!
1638  aRet[ n ] <<= lang::IllegalAccessException(
1639  "Property is read-only!",
1640  static_cast< cppu::OWeakObject * >( this ) );
1641  }
1642  else if ( rName == "IsDocument" )
1643  {
1644  // Read-only property!
1645  aRet[ n ] <<= lang::IllegalAccessException(
1646  "Property is read-only!",
1647  static_cast< cppu::OWeakObject * >( this ) );
1648  }
1649  else if ( rName == "IsFolder" )
1650  {
1651  // Read-only property!
1652  aRet[ n ] <<= lang::IllegalAccessException(
1653  "Property is read-only!",
1654  static_cast< cppu::OWeakObject * >( this ) );
1655  }
1656  else if ( rName == "Title" )
1657  {
1658  OUString aNewValue;
1659  if ( rValue.Value >>= aNewValue )
1660  {
1661  // No empty titles!
1662  if ( aNewValue.getLength() > 0 )
1663  {
1664  try
1665  {
1666  SerfUri aURI( xIdentifier->getContentIdentifier() );
1667  aOldTitle = aURI.GetPathBaseNameUnescaped();
1668 
1669  if ( aNewValue != aOldTitle )
1670  {
1671  // modified title -> modified URL -> exchange !
1672  if ( !bTransient )
1673  bExchange = true;
1674 
1675  // new value will be set later...
1676  aNewTitle = aNewValue;
1677 
1678  // remember position within sequence of values (for
1679  // error handling).
1680  nTitlePos = n;
1681  }
1682  }
1683  catch ( DAVException const & )
1684  {
1685  aRet[ n ] <<= lang::IllegalArgumentException(
1686  "Invalid content identifier!",
1687  static_cast< cppu::OWeakObject * >( this ),
1688  -1 );
1689  }
1690  }
1691  else
1692  {
1693  aRet[ n ] <<= lang::IllegalArgumentException(
1694  "Empty title not allowed!",
1695  static_cast< cppu::OWeakObject * >( this ),
1696  -1 );
1697  }
1698  }
1699  else
1700  {
1701  aRet[ n ] <<= beans::IllegalTypeException(
1702  "Property value has wrong type!",
1703  static_cast< cppu::OWeakObject * >( this ) );
1704  }
1705  }
1706  else
1707  {
1708 
1709  // Optional props.
1710 
1711 
1712  OUString aSpecialName;
1713  bool bIsSpecial = DAVProperties::isUCBSpecialProperty( rName, aSpecialName );
1714 
1715  if ( !xInfo.is() )
1716  xInfo = getPropertySetInfo( xEnv,
1717  false /* don't cache data */ );
1718 
1719  if ( !xInfo->hasPropertyByName( bIsSpecial ? aSpecialName : rName ) )
1720  {
1721  // Check, whether property exists. Skip otherwise.
1722  // PROPPATCH::set would add the property automatically, which
1723  // is not allowed for "setPropertyValues" command!
1724  aRet[ n ] <<= beans::UnknownPropertyException(
1725  "Property is unknown!",
1726  static_cast< cppu::OWeakObject * >( this ) );
1727  continue;
1728  }
1729 
1730  if ( rName == "Size" )
1731  {
1732  // Read-only property!
1733  aRet[ n ] <<= lang::IllegalAccessException(
1734  "Property is read-only!",
1735  static_cast< cppu::OWeakObject * >( this ) );
1736  }
1737  else if ( rName == "DateCreated" )
1738  {
1739  // Read-only property!
1740  aRet[ n ] <<= lang::IllegalAccessException(
1741  "Property is read-only!",
1742  static_cast< cppu::OWeakObject * >( this ) );
1743  }
1744  else if ( rName == "DateModified" )
1745  {
1746  // Read-only property!
1747  aRet[ n ] <<= lang::IllegalAccessException(
1748  "Property is read-only!",
1749  static_cast< cppu::OWeakObject * >( this ) );
1750  }
1751  else if ( rName == "MediaType" )
1752  {
1753  // Read-only property!
1754  // (but could be writable, if 'getcontenttype' would be)
1755  aRet[ n ] <<= lang::IllegalAccessException(
1756  "Property is read-only!",
1757  static_cast< cppu::OWeakObject * >( this ) );
1758  }
1759  if ( rName == "CreatableContentsInfo" )
1760  {
1761  // Read-only property!
1762  aRet[ n ] <<= lang::IllegalAccessException(
1763  "Property is read-only!",
1764  static_cast< cppu::OWeakObject * >( this ) );
1765  }
1766  else
1767  {
1768  if ( getResourceType( xEnv, xResAccess ) == DAV )
1769  {
1770  // Property value will be set on server.
1771  ProppatchValue aValue( PROPSET, rName, rValue.Value );
1772  aProppatchValues.push_back( aValue );
1773 
1774  // remember position within sequence of values (for
1775  // error handling).
1776  aProppatchPropsPositions.push_back( n );
1777  }
1778  else
1779  {
1780  // Property value will be stored in local property store.
1781  if ( !bTriedToGetAdditionalPropSet &&
1782  !xAdditionalPropSet.is() )
1783  {
1784  xAdditionalPropSet
1785  = getAdditionalPropertySet( false );
1786  bTriedToGetAdditionalPropSet = true;
1787  }
1788 
1789  if ( xAdditionalPropSet.is() )
1790  {
1791  try
1792  {
1793  uno::Any aOldValue
1794  = xAdditionalPropSet->getPropertyValue( rName );
1795  if ( aOldValue != rValue.Value )
1796  {
1797  xAdditionalPropSet->setPropertyValue(
1798  rName, rValue.Value );
1799 
1800  aEvent.PropertyName = rName;
1801  aEvent.OldValue = aOldValue;
1802  aEvent.NewValue = rValue.Value;
1803 
1804  aChanges.getArray()[ nChanged ] = aEvent;
1805  nChanged++;
1806  }
1807  }
1808  catch ( beans::UnknownPropertyException const & e )
1809  {
1810  aRet[ n ] <<= e;
1811  }
1812  catch ( lang::WrappedTargetException const & e )
1813  {
1814  aRet[ n ] <<= e;
1815  }
1816  catch ( beans::PropertyVetoException const & e )
1817  {
1818  aRet[ n ] <<= e;
1819  }
1820  catch ( lang::IllegalArgumentException const & e )
1821  {
1822  aRet[ n ] <<= e;
1823  }
1824  }
1825  else
1826  {
1827  aRet[ n ] <<= uno::Exception(
1828  "No property set for storing the value!",
1829  static_cast< cppu::OWeakObject * >( this ) );
1830  }
1831  }
1832  }
1833  }
1834  } // for
1835 
1836  if ( !bTransient && (!aProppatchValues.empty()) )
1837  {
1838  try
1839  {
1840  // Set property values at server.
1841  xResAccess->PROPPATCH( aProppatchValues, xEnv );
1842 
1843  for ( const auto& rProppatchValue : aProppatchValues )
1844  {
1845  aEvent.PropertyName = rProppatchValue.name;
1846  aEvent.OldValue = uno::Any(); // @@@ too expensive to obtain!
1847  aEvent.NewValue = rProppatchValue.value;
1848 
1849  aChanges.getArray()[ nChanged ] = aEvent;
1850  nChanged++;
1851  }
1852  }
1853  catch ( DAVException const & e )
1854  {
1855 // SAL_WARN( "ucb.ucp.webdav",
1856 // "Content::setPropertyValues - PROPPATCH failed!" );
1857 
1858 #if 1
1859  cancelCommandExecution( e, xEnv );
1860  // unreachable
1861 #else
1862  // Note: PROPPATCH either sets ALL property values OR NOTHING.
1863 
1864  std::vector< sal_Int32 >::const_iterator it
1865  = aProppatchPropsPositions.begin();
1866  std::vector< sal_Int32 >::const_iterator end
1867  = aProppatchPropsPositions.end();
1868 
1869  while ( it != end )
1870  {
1871  // Set error.
1872  aRet[ (*it) ] <<= MapDAVException( e, true );
1873  ++it;
1874  }
1875 #endif
1876  }
1877  }
1878 
1879  if ( bExchange )
1880  {
1881  // Assemble new content identifier...
1882 
1883  OUString aNewURL = getParentURL();
1884  if ( aNewURL.lastIndexOf( '/' ) != ( aNewURL.getLength() - 1 ) )
1885  aNewURL += "/";
1886 
1887  aNewURL += SerfUri::escapeSegment( aNewTitle );
1888 
1889  uno::Reference< ucb::XContentIdentifier > xNewId
1890  = new ::ucbhelper::ContentIdentifier( aNewURL );
1891  uno::Reference< ucb::XContentIdentifier > xOldId = xIdentifier;
1892 
1893  try
1894  {
1895  SerfUri sourceURI( xOldId->getContentIdentifier() );
1896  SerfUri targetURI( xNewId->getContentIdentifier() );
1897  targetURI.SetScheme( sourceURI.GetScheme() );
1898 
1899  xResAccess->MOVE(
1900  sourceURI.GetPath(), targetURI.GetURI(), false, xEnv );
1901  // @@@ Should check for resources that could not be moved
1902  // (due to source access or target overwrite) and send
1903  // this information through the interaction handler.
1904 
1905  // @@@ Existing content should be checked to see if it needs
1906  // to be deleted at the source
1907 
1908  // @@@ Existing content should be checked to see if it has
1909  // been overwritten at the target
1910 
1911  if ( exchangeIdentity( xNewId ) )
1912  {
1913  xResAccess->setURL( aNewURL );
1914 
1915 // DAV resources store all additional props on server!
1916 // // Adapt Additional Core Properties.
1917 // renameAdditionalPropertySet( xOldId->getContentIdentifier(),
1918 // xNewId->getContentIdentifier(),
1919 // true );
1920  }
1921  else
1922  {
1923  // Do not set new title!
1924  aNewTitle.clear();
1925 
1926  // Set error .
1927  aRet[ nTitlePos ] <<= uno::Exception(
1928  "Exchange failed!",
1929  static_cast< cppu::OWeakObject * >( this ) );
1930  }
1931  }
1932  catch ( DAVException const & e )
1933  {
1934  // Do not set new title!
1935  aNewTitle.clear();
1936 
1937  // Set error .
1938  aRet[ nTitlePos ] = MapDAVException( e, true );
1939  }
1940  }
1941 
1942  if ( aNewTitle.getLength() )
1943  {
1944  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1945 
1946  aEvent.PropertyName = "Title";
1947  aEvent.OldValue <<= aOldTitle;
1948  aEvent.NewValue <<= aNewTitle;
1949 
1950  m_aEscapedTitle = SerfUri::escapeSegment( aNewTitle );
1951 
1952  aChanges.getArray()[ nChanged ] = aEvent;
1953  nChanged++;
1954  }
1955 
1956  if ( nChanged > 0 )
1957  {
1958  aChanges.realloc( nChanged );
1959  notifyPropertiesChange( aChanges );
1960  }
1961 
1962  {
1963  osl::Guard< osl::Mutex > aGuard( m_aMutex );
1964  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
1965  }
1966 
1967  return aRet;
1968 }
1969 
1970 
1971 uno::Any Content::open(
1972  const ucb::OpenCommandArgument2 & rArg,
1973  const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1974 {
1975  uno::Any aRet;
1976 
1977  bool bOpenFolder = ( ( rArg.Mode == ucb::OpenMode::ALL ) ||
1978  ( rArg.Mode == ucb::OpenMode::FOLDERS ) ||
1979  ( rArg.Mode == ucb::OpenMode::DOCUMENTS ) );
1980  if ( bOpenFolder )
1981  {
1982  if ( isFolder( xEnv ) )
1983  {
1984  // Open collection.
1985 
1986  uno::Reference< ucb::XDynamicResultSet > xSet
1987  = new DynamicResultSet( m_xContext, this, rArg, xEnv );
1988  aRet <<= xSet;
1989  }
1990  else
1991  {
1992  // Error: Not a folder!
1993 
1994  OUStringBuffer aMsg;
1995  aMsg.append( "Non-folder resource cannot be opened as folder! Wrong Open Mode!" );
1996 
1998  uno::makeAny(
1999  lang::IllegalArgumentException(
2000  aMsg.makeStringAndClear(),
2001  static_cast< cppu::OWeakObject * >( this ),
2002  -1 ) ),
2003  xEnv );
2004  // Unreachable
2005  }
2006  }
2007 
2008  if ( rArg.Sink.is() )
2009  {
2010  // Open document.
2011 
2012  if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
2013  ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
2014  {
2015  // Currently(?) unsupported.
2017  uno::makeAny(
2018  ucb::UnsupportedOpenModeException(
2019  OUString(),
2020  static_cast< cppu::OWeakObject * >( this ),
2021  sal_Int16( rArg.Mode ) ) ),
2022  xEnv );
2023  // Unreachable
2024  }
2025 
2026  uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY );
2027  if ( xOut.is() )
2028  {
2029  // PUSH: write data
2030  try
2031  {
2032  std::unique_ptr< DAVResourceAccess > xResAccess;
2033 
2034  {
2035  osl::MutexGuard aGuard( m_aMutex );
2036 
2037  xResAccess.reset(
2038  new DAVResourceAccess( *m_xResAccess.get() ) );
2039  }
2040 
2041  DAVResource aResource;
2042  std::vector< OUString > aHeaders;
2043 
2044  xResAccess->GET( xOut, aHeaders, aResource, xEnv );
2045  m_bDidGetOrHead = true;
2046 
2047  {
2048  osl::MutexGuard aGuard( m_aMutex );
2049 
2050  // cache headers.
2051  if ( !m_xCachedProps.get())
2052  m_xCachedProps.reset(
2053  new CachableContentProperties( ContentProperties( aResource ) ) );
2054  else
2055  m_xCachedProps->addProperties( ContentProperties( aResource ) );
2056 
2057  m_xResAccess.reset(
2058  new DAVResourceAccess( *xResAccess ) );
2059  }
2060  }
2061  catch ( DAVException const & e )
2062  {
2063  cancelCommandExecution( e, xEnv );
2064  // Unreachable
2065  }
2066  }
2067  else
2068  {
2069  uno::Reference< io::XActiveDataSink > xDataSink( rArg.Sink, uno::UNO_QUERY );
2070  if ( xDataSink.is() )
2071  {
2072  // PULL: wait for client read
2073  try
2074  {
2075  std::unique_ptr< DAVResourceAccess > xResAccess;
2076  {
2077  osl::MutexGuard aGuard( m_aMutex );
2078 
2079  xResAccess.reset(
2080  new DAVResourceAccess( *m_xResAccess.get() ) );
2081  }
2082 
2083  // fill inputstream sync; return if all data present
2084  DAVResource aResource;
2085  std::vector< OUString > aHeaders;
2086 
2087  uno::Reference< io::XInputStream > xIn
2088  = xResAccess->GET( aHeaders, aResource, xEnv );
2089  m_bDidGetOrHead = true;
2090 
2091  {
2092  osl::MutexGuard aGuard( m_aMutex );
2093 
2094  // cache headers.
2095  if ( !m_xCachedProps.get())
2096  m_xCachedProps.reset(
2097  new CachableContentProperties( ContentProperties( aResource ) ) );
2098  else
2099  m_xCachedProps->addProperties(
2100  aResource.properties );
2101 
2102  m_xResAccess.reset(
2103  new DAVResourceAccess( *xResAccess ) );
2104  }
2105 
2106  xDataSink->setInputStream( xIn );
2107  }
2108  catch ( DAVException const & e )
2109  {
2110  cancelCommandExecution( e, xEnv );
2111  // Unreachable
2112  }
2113  }
2114  else
2115  {
2116  // Note: aOpenCommand.Sink may contain an XStream
2117  // implementation. Support for this type of
2118  // sink is optional...
2120  uno::makeAny(
2121  ucb::UnsupportedDataSinkException(
2122  OUString(),
2123  static_cast< cppu::OWeakObject * >( this ),
2124  rArg.Sink ) ),
2125  xEnv );
2126  // Unreachable
2127  }
2128  }
2129  }
2130 
2131  return aRet;
2132 }
2133 
2134 
2135 void Content::post(
2136  const ucb::PostCommandArgument2 & rArg,
2137  const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2138 {
2139  uno::Reference< io::XActiveDataSink > xSink( rArg.Sink, uno::UNO_QUERY );
2140  if ( xSink.is() )
2141  {
2142  try
2143  {
2144  std::unique_ptr< DAVResourceAccess > xResAccess;
2145  {
2146  osl::MutexGuard aGuard( m_aMutex );
2147  xResAccess.reset(
2148  new DAVResourceAccess( *m_xResAccess.get() ) );
2149  }
2150 
2151  uno::Reference< io::XInputStream > xResult
2152  = xResAccess->POST( rArg.MediaType,
2153  rArg.Referer,
2154  rArg.Source,
2155  xEnv );
2156 
2157  {
2158  osl::MutexGuard aGuard( m_aMutex );
2159  m_xResAccess.reset(
2160  new DAVResourceAccess( *xResAccess ) );
2161  }
2162 
2163  xSink->setInputStream( xResult );
2164  }
2165  catch ( DAVException const & e )
2166  {
2167  cancelCommandExecution( e, xEnv, true );
2168  // Unreachable
2169  }
2170  }
2171  else
2172  {
2173  uno::Reference< io::XOutputStream > xResult( rArg.Sink, uno::UNO_QUERY );
2174  if ( xResult.is() )
2175  {
2176  try
2177  {
2178  std::unique_ptr< DAVResourceAccess > xResAccess;
2179  {
2180  osl::MutexGuard aGuard( m_aMutex );
2181  xResAccess.reset(
2182  new DAVResourceAccess( *m_xResAccess.get() ) );
2183  }
2184 
2185  xResAccess->POST( rArg.MediaType,
2186  rArg.Referer,
2187  rArg.Source,
2188  xResult,
2189  xEnv );
2190 
2191  {
2192  osl::MutexGuard aGuard( m_aMutex );
2193  m_xResAccess.reset(
2194  new DAVResourceAccess( *xResAccess ) );
2195  }
2196  }
2197  catch ( DAVException const & e )
2198  {
2199  cancelCommandExecution( e, xEnv, true );
2200  // Unreachable
2201  }
2202  }
2203  else
2204  {
2206  uno::makeAny(
2207  ucb::UnsupportedDataSinkException(
2208  OUString(),
2209  static_cast< cppu::OWeakObject * >( this ),
2210  rArg.Sink ) ),
2211  xEnv );
2212  // Unreachable
2213  }
2214  }
2215 }
2216 
2217 
2218 void Content::queryChildren( ContentRefList& rChildren )
2219 {
2220  // Obtain a list with a snapshot of all currently instantiated contents
2221  // from provider and extract the contents which are direct children
2222  // of this content.
2223 
2224  ::ucbhelper::ContentRefList aAllContents;
2225  m_xProvider->queryExistingContents( aAllContents );
2226 
2227  OUString aURL = m_xIdentifier->getContentIdentifier();
2228  sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
2229 
2230  if ( nURLPos != ( aURL.getLength() - 1 ) )
2231  {
2232  // No trailing slash found. Append.
2233  aURL += "/";
2234  }
2235 
2236  sal_Int32 nLen = aURL.getLength();
2237 
2238  for ( const auto& rChild : aAllContents )
2239  {
2240  ::ucbhelper::ContentImplHelperRef xChild = rChild;
2241  OUString aChildURL
2242  = xChild->getIdentifier()->getContentIdentifier();
2243 
2244  // Is aURL a prefix of aChildURL?
2245  if ( ( aChildURL.getLength() > nLen ) &&
2246  ( aChildURL.startsWith( aURL ) ) )
2247  {
2248  sal_Int32 nPos = nLen;
2249  nPos = aChildURL.indexOf( '/', nPos );
2250 
2251  if ( ( nPos == -1 ) ||
2252  ( nPos == ( aChildURL.getLength() - 1 ) ) )
2253  {
2254  // No further slashes / only a final slash. It's a child!
2255  rChildren.push_back(
2257  static_cast< ::http_dav_ucp::Content * >(
2258  xChild.get() ) ) );
2259  }
2260  }
2261  }
2262 }
2263 
2264 
2265 void Content::insert(
2266  const uno::Reference< io::XInputStream > & xInputStream,
2267  bool bReplaceExisting,
2268  const uno::Reference< ucb::XCommandEnvironment >& Environment )
2269 {
2270  bool bTransient, bCollection;
2271  OUString aEscapedTitle;
2272  std::unique_ptr< DAVResourceAccess > xResAccess;
2273 
2274  {
2275  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2276 
2277  bTransient = m_bTransient;
2278  bCollection = m_bCollection;
2279  aEscapedTitle = m_aEscapedTitle;
2280  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
2281  }
2282 
2283  // Check, if all required properties are present.
2284 
2285  if ( aEscapedTitle.isEmpty() )
2286  {
2287  SAL_WARN( "ucb.ucp.webdav", "Content::insert - Title missing!" );
2288 
2289  uno::Sequence<OUString> aProps { "Title" };
2291  uno::makeAny( ucb::MissingPropertiesException(
2292  OUString(),
2293  static_cast< cppu::OWeakObject * >( this ),
2294  aProps ) ),
2295  Environment );
2296  // Unreachable
2297  }
2298 
2299  if ( !bReplaceExisting )
2300  {
2301  /* [RFC 2616] - HTTP
2302 
2303  The PUT method requests that the enclosed entity be stored under the
2304  supplied Request-URI. If the Request-URI refers to an already
2305  existing resource, the enclosed entity SHOULD be considered as a
2306  modified version of the one residing on the origin server.
2307  */
2308 
2309  /* [RFC 2518] - WebDAV
2310 
2311  MKCOL creates a new collection resource at the location specified by
2312  the Request-URI. If the resource identified by the Request-URI is
2313  non-null then the MKCOL MUST fail.
2314  */
2315 
2316  // ==> Complain on PUT, continue on MKCOL.
2317  if ( !bTransient || !bCollection )
2318  {
2319 #undef ERROR
2320  ucb::UnsupportedNameClashException aEx(
2321  "Unable to write without overwrite!",
2322  static_cast< cppu::OWeakObject * >( this ),
2323  ucb::NameClash::ERROR );
2324 
2325  uno::Reference< task::XInteractionHandler > xIH;
2326 
2327  if ( Environment.is() )
2328  xIH = Environment->getInteractionHandler();
2329 
2330  if ( xIH.is() )
2331  {
2332  uno::Any aExAsAny( uno::makeAny( aEx ) );
2333 
2336  aExAsAny,
2337  ContinuationFlags::Approve
2338  | ContinuationFlags::Disapprove );
2339  xIH->handle( xRequest.get() );
2340 
2341  const ContinuationFlags nResp = xRequest->getResponse();
2342 
2343  switch ( nResp )
2344  {
2345  case ContinuationFlags::NONE:
2346  // Not handled; throw.
2347  throw aEx;
2348 // break;
2349 
2350  case ContinuationFlags::Approve:
2351  // Continue -> Overwrite.
2352  bReplaceExisting = true;
2353  break;
2354 
2355  case ContinuationFlags::Disapprove:
2356  // Abort.
2357  throw ucb::CommandFailedException(
2358  OUString(),
2359  uno::Reference< uno::XInterface >(),
2360  aExAsAny );
2361 // break;
2362 
2363  default:
2364  SAL_WARN( "ucb.ucp.webdav",
2365  "Content::insert - "
2366  "Unknown interaction selection!" );
2367  throw ucb::CommandFailedException(
2368  "Unknown interaction selection!",
2369  uno::Reference< uno::XInterface >(),
2370  aExAsAny );
2371 // break;
2372  }
2373  }
2374  else
2375  {
2376  // No IH; throw.
2377  throw aEx;
2378  }
2379  }
2380  }
2381 
2382  if ( bTransient )
2383  {
2384  // Assemble new content identifier...
2385  OUString aURL = getParentURL();
2386  if ( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ) )
2387  aURL += "/";
2388 
2389  aURL += aEscapedTitle;
2390 
2391  try
2392  {
2393  xResAccess->setURL( aURL );
2394 
2395  if ( bCollection )
2396  xResAccess->MKCOL( Environment );
2397  else
2398  xResAccess->PUT( xInputStream, Environment );
2399  }
2400  catch ( DAVException const & except )
2401  {
2402  if ( bCollection )
2403  {
2404  if ( except.getStatus() == SC_METHOD_NOT_ALLOWED )
2405  {
2406  // [RFC 2518] - WebDAV
2407  // 405 (Method Not Allowed) - MKCOL can only be
2408  // executed on a deleted/non-existent resource.
2409 
2410  if ( bReplaceExisting )
2411  {
2412  // Destroy old resource.
2413  try
2414  {
2415  xResAccess->DESTROY( Environment );
2416  }
2417  catch ( DAVException const & e )
2418  {
2419  cancelCommandExecution( e, Environment, true );
2420  // Unreachable
2421  }
2422 
2423  // Insert (recursion!).
2424  insert( xInputStream, bReplaceExisting, Environment );
2425 
2426  {
2427  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2428  m_xResAccess.reset(
2429  new DAVResourceAccess( *xResAccess ) );
2430  }
2431 
2432  // Success!
2433  return;
2434  }
2435  else
2436  {
2437  OUString aTitle;
2438  try
2439  {
2440  SerfUri aURI( aURL );
2441  aTitle = aURI.GetPathBaseNameUnescaped();
2442  }
2443  catch ( DAVException const & )
2444  {
2445  }
2446 
2448  uno::makeAny(
2449  ucb::NameClashException(
2450  OUString(),
2451  static_cast< cppu::OWeakObject * >( this ),
2452  task::InteractionClassification_ERROR,
2453  aTitle ) ),
2454  Environment );
2455  // Unreachable
2456  }
2457  }
2458  }
2459 
2460  cancelCommandExecution( except, Environment, true );
2461  // Unreachable
2462  }
2463 
2464  {
2465  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2466  m_xIdentifier
2467  = new ::ucbhelper::ContentIdentifier( aURL );
2468  }
2469 
2470  inserted();
2471 
2472  {
2473  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2474  m_bTransient = false;
2475  }
2476  }
2477  else
2478  {
2479  if ( !xInputStream.is() )
2480  {
2482  uno::makeAny(
2483  ucb::MissingInputStreamException(
2484  OUString(),
2485  static_cast< cppu::OWeakObject * >( this ) ) ),
2486  Environment );
2487  // Unreachable
2488  }
2489 
2490  try
2491  {
2492  xResAccess->PUT( xInputStream, Environment );
2493  }
2494  catch ( DAVException const & e )
2495  {
2496  cancelCommandExecution( e, Environment, true );
2497  // Unreachable
2498  }
2499  }
2500 
2501  {
2502  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2503  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
2504  }
2505 }
2506 
2507 
2508 void Content::transfer(
2509  const ucb::TransferInfo & rArgs,
2510  const uno::Reference< ucb::XCommandEnvironment >& Environment )
2511 {
2512  uno::Reference< uno::XComponentContext > xContext;
2513  uno::Reference< ucb::XContentIdentifier > xIdentifier;
2514  uno::Reference< ucb::XContentProvider > xProvider;
2515  std::unique_ptr< DAVResourceAccess > xResAccess;
2516 
2517  {
2518  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2519 
2520  xContext.set( m_xContext );
2521  xIdentifier.set( m_xIdentifier );
2522  xProvider.set( m_xProvider.get() );
2523  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
2524  }
2525 
2526  OUString aTargetURI;
2527  try
2528  {
2529  SerfUri sourceURI( rArgs.SourceURL );
2530  SerfUri targetURI( xIdentifier->getContentIdentifier() );
2531  aTargetURI = targetURI.GetPathBaseNameUnescaped();
2532 
2533  // Check source's and target's URL scheme
2534 
2535  OUString aScheme = sourceURI.GetScheme().toAsciiLowerCase();
2536  if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
2537  {
2538  sourceURI.SetScheme( HTTP_URL_SCHEME );
2539  }
2540  else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
2541  {
2542  sourceURI.SetScheme( HTTPS_URL_SCHEME );
2543  }
2544  else if ( aScheme == DAV_URL_SCHEME )
2545  {
2546  sourceURI.SetScheme( HTTP_URL_SCHEME );
2547  }
2548  else if ( aScheme == DAVS_URL_SCHEME )
2549  {
2550  sourceURI.SetScheme( HTTPS_URL_SCHEME );
2551  }
2552  else if (aScheme == WEBDAV_URL_SCHEME)
2553  {
2554  sourceURI.SetScheme(HTTP_URL_SCHEME);
2555  }
2556  else if (aScheme == WEBDAVS_URL_SCHEME)
2557  {
2558  sourceURI.SetScheme(HTTPS_URL_SCHEME);
2559  }
2560  else
2561  {
2562  if ( aScheme != HTTP_URL_SCHEME && aScheme != HTTPS_URL_SCHEME )
2563  {
2565  uno::makeAny(
2566  ucb::InteractiveBadTransferURLException(
2567  "Unsupported URL scheme!",
2568  static_cast< cppu::OWeakObject * >( this ) ) ),
2569  Environment );
2570  // Unreachable
2571  }
2572  }
2573 
2574  aScheme = targetURI.GetScheme().toAsciiLowerCase();
2575  if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
2576  targetURI.SetScheme( HTTP_URL_SCHEME );
2577  else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
2578  targetURI.SetScheme( HTTPS_URL_SCHEME );
2579  else if ( aScheme == DAV_URL_SCHEME )
2580  targetURI.SetScheme( HTTP_URL_SCHEME );
2581  else if ( aScheme == DAVS_URL_SCHEME )
2582  targetURI.SetScheme( HTTPS_URL_SCHEME );
2583  else if (aScheme == WEBDAV_URL_SCHEME)
2584  targetURI.SetScheme(HTTP_URL_SCHEME);
2585  else if (aScheme == WEBDAVS_URL_SCHEME)
2586  targetURI.SetScheme(HTTPS_URL_SCHEME);
2587 
2588  // @@@ This implementation of 'transfer' only works
2589  // if the source and target are located at same host.
2590  // (Neon does not support cross-server copy/move)
2591 
2592  // Check for same host
2593 
2594  if ( sourceURI.GetHost().getLength() &&
2595  ( sourceURI.GetHost() != targetURI.GetHost() ) )
2596  {
2598  uno::makeAny( ucb::InteractiveBadTransferURLException(
2599  "Different hosts!",
2600  static_cast< cppu::OWeakObject * >( this ) ) ),
2601  Environment );
2602  // Unreachable
2603  }
2604 
2605  OUString aTitle = rArgs.NewTitle;
2606 
2607  if ( aTitle.isEmpty() )
2608  aTitle = sourceURI.GetPathBaseNameUnescaped();
2609 
2610  if ( aTitle == "/" )
2611  {
2612  // kso: ???
2613  aTitle.clear();
2614  }
2615 
2616  targetURI.AppendPath( aTitle );
2617 
2618  OUString aTargetURL = xIdentifier->getContentIdentifier();
2619  if ( ( aTargetURL.lastIndexOf( '/' ) + 1 )
2620  != aTargetURL.getLength() )
2621  aTargetURL += "/";
2622 
2623  aTargetURL += aTitle;
2624 
2625  uno::Reference< ucb::XContentIdentifier > xTargetId
2626  = new ::ucbhelper::ContentIdentifier( aTargetURL );
2627 
2628  DAVResourceAccess aSourceAccess( xContext,
2629  xResAccess->getSessionFactory(),
2630  sourceURI.GetURI() );
2631 
2632  if ( rArgs.MoveData )
2633  {
2634  uno::Reference< ucb::XContentIdentifier > xId
2635  = new ::ucbhelper::ContentIdentifier( rArgs.SourceURL );
2636 
2637  // Note: The static cast is okay here, because its sure that
2638  // xProvider is always the WebDAVContentProvider.
2640  = static_cast< Content * >(
2641  xProvider->queryContent( xId ).get() );
2642 
2643  // [RFC 2518] - WebDAV
2644  // If a resource exists at the destination and the Overwrite
2645  // header is "T" then prior to performing the move the server
2646  // MUST perform a DELETE with "Depth: infinity" on the
2647  // destination resource. If the Overwrite header is set to
2648  // "F" then the operation will fail.
2649 
2650  aSourceAccess.MOVE( sourceURI.GetPath(),
2651  targetURI.GetURI(),
2652  rArgs.NameClash
2653  == ucb::NameClash::OVERWRITE,
2654  Environment );
2655 
2656  if ( xSource.is() )
2657  {
2658  // Propagate destruction to listeners.
2659  xSource->destroy( true );
2660  }
2661 
2662 // DAV resources store all additional props on server!
2663 // // Rename own and all children's Additional Core Properties.
2664 // renameAdditionalPropertySet( xId->getContentIdentifier(),
2665 // xTargetId->getContentIdentifier(),
2666 // true );
2667  }
2668  else
2669  {
2670  // [RFC 2518] - WebDAV
2671  // If a resource exists at the destination and the Overwrite
2672  // header is "T" then prior to performing the copy the server
2673  // MUST perform a DELETE with "Depth: infinity" on the
2674  // destination resource. If the Overwrite header is set to
2675  // "F" then the operation will fail.
2676 
2677  aSourceAccess.COPY( sourceURI.GetPath(),
2678  targetURI.GetURI(),
2679  rArgs.NameClash
2680  == ucb::NameClash::OVERWRITE,
2681  Environment );
2682 
2683 // DAV resources store all additional props on server!
2684 // // Copy own and all children's Additional Core Properties.
2685 // copyAdditionalPropertySet( xId->getContentIdentifier(),
2686 // xTargetId->getContentIdentifier(),
2687 // true );
2688  }
2689 
2690  // Note: The static cast is okay here, because its sure that
2691  // xProvider is always the WebDAVContentProvider.
2693  = static_cast< Content * >(
2694  xProvider->queryContent( xTargetId ).get() );
2695 
2696  // Announce transferred content in its new folder.
2697  xTarget->inserted();
2698  }
2699  catch ( ucb::IllegalIdentifierException const & )
2700  {
2701  // queryContent
2702  }
2703  catch ( DAVException const & e )
2704  {
2705  // [RFC 2518] - WebDAV
2706  // 412 (Precondition Failed) - The server was unable to maintain
2707  // the liveness of the properties listed in the propertybehavior
2708  // XML element or the Overwrite header is "F" and the state of
2709  // the destination resource is non-null.
2710 
2711  if ( e.getStatus() == SC_PRECONDITION_FAILED )
2712  {
2713  switch ( rArgs.NameClash )
2714  {
2715  case 0/*ucb::NameClash::ERROR*/:
2716  {
2718  uno::makeAny(
2719  ucb::NameClashException(
2720  OUString(),
2721  static_cast< cppu::OWeakObject * >( this ),
2722  task::InteractionClassification_ERROR,
2723  aTargetURI ) ),
2724  Environment );
2725  // Unreachable
2726  }
2727  [[fallthrough]];
2728 
2729  case ucb::NameClash::OVERWRITE:
2730  break;
2731 
2732  case ucb::NameClash::KEEP: // deprecated
2733  case ucb::NameClash::RENAME:
2734  case ucb::NameClash::ASK:
2735  default:
2736  {
2738  uno::makeAny(
2739  ucb::UnsupportedNameClashException(
2740  OUString(),
2741  static_cast< cppu::OWeakObject * >( this ),
2742  rArgs.NameClash ) ),
2743  Environment );
2744  // Unreachable
2745  }
2746  }
2747  }
2748 
2749  cancelCommandExecution( e, Environment, true );
2750  // Unreachable
2751  }
2752 
2753  {
2754  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2755  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
2756  }
2757 }
2758 
2759 
2760 void Content::destroy( bool bDeletePhysical )
2761 {
2762  // @@@ take care about bDeletePhysical -> trashcan support
2763 
2764  uno::Reference< ucb::XContent > xThis = this;
2765 
2766  deleted();
2767 
2768  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2769 
2770  // Process instantiated children...
2771 
2773  queryChildren( aChildren );
2774 
2775  for ( auto& rChild : aChildren )
2776  {
2777  rChild->destroy( bDeletePhysical );
2778  }
2779 }
2780 
2781 
2782 bool Content::supportsExclusiveWriteLock(
2783  const uno::Reference< ucb::XCommandEnvironment >& Environment )
2784 {
2785  if ( getResourceType( Environment ) == DAV )
2786  {
2787  if ( m_xCachedProps.get() )
2788  {
2789  uno::Sequence< ucb::LockEntry > aSupportedLocks;
2790  if ( m_xCachedProps->getValue( DAVProperties::SUPPORTEDLOCK )
2791  >>= aSupportedLocks )
2792  {
2793  for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
2794  {
2795  if ( aSupportedLocks[ n ].Scope
2796  == ucb::LockScope_EXCLUSIVE &&
2797  aSupportedLocks[ n ].Type
2798  == ucb::LockType_WRITE )
2799  return true;
2800  }
2801  }
2802  }
2803  }
2804  return false;
2805 }
2806 
2807 
2808 void Content::lock(
2809  const uno::Reference< ucb::XCommandEnvironment >& Environment )
2810 {
2811  try
2812  {
2813  std::unique_ptr< DAVResourceAccess > xResAccess;
2814  {
2815  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2816  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
2817  }
2818 
2819  uno::Any aOwnerAny;
2820  aOwnerAny <<= OUString( "http://ucb.openoffice.org" );
2821 
2822  ucb::Lock aLock(
2823  ucb::LockScope_EXCLUSIVE,
2824  ucb::LockType_WRITE,
2825  ucb::LockDepth_ZERO,
2826  aOwnerAny,
2827  180, // lock timeout in secs
2828  //-1, // infinite lock
2829  uno::Sequence< OUString >() );
2830 
2831  xResAccess->LOCK( aLock, Environment );
2832  m_bLocked = true;
2833 
2834  {
2835  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2836  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
2837  }
2838  }
2839  catch ( DAVException const & e )
2840  {
2841  cancelCommandExecution( e, Environment, false );
2842  // Unreachable
2843  }
2844 }
2845 
2846 
2847 void Content::unlock(
2848  const uno::Reference< ucb::XCommandEnvironment >& Environment )
2849 {
2850  try
2851  {
2852  std::unique_ptr< DAVResourceAccess > xResAccess;
2853  {
2854  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2855  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
2856  }
2857 
2858  xResAccess->UNLOCK( Environment );
2859  m_bLocked = false;
2860 
2861  {
2862  osl::Guard< osl::Mutex > aGuard( m_aMutex );
2863  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
2864  }
2865  }
2866  catch ( DAVException const & e )
2867  {
2868  cancelCommandExecution( e, Environment, false );
2869  // Unreachable
2870  }
2871 }
2872 
2873 
2874 bool Content::exchangeIdentity(
2875  const uno::Reference< ucb::XContentIdentifier >& xNewId )
2876 {
2877  if ( !xNewId.is() )
2878  return false;
2879 
2880  osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
2881 
2882  uno::Reference< ucb::XContent > xThis = this;
2883 
2884  // Already persistent?
2885  if ( m_bTransient )
2886  {
2887  SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - Not persistent!" );
2888  return false;
2889  }
2890 
2891  // Exchange own identitity.
2892 
2893  // Fail, if a content with given id already exists.
2894 // if ( !hasData( xNewId ) )
2895  {
2896  OUString aOldURL = m_xIdentifier->getContentIdentifier();
2897 
2898  aGuard.clear();
2899  if ( exchange( xNewId ) )
2900  {
2901  // Process instantiated children...
2902 
2903  ContentRefList aChildren;
2904  queryChildren( aChildren );
2905 
2906  for ( const auto& rChild : aChildren )
2907  {
2908  ContentRef xChild = rChild;
2909 
2910  // Create new content identifier for the child...
2911  uno::Reference< ucb::XContentIdentifier >
2912  xOldChildId = xChild->getIdentifier();
2913  OUString aOldChildURL
2914  = xOldChildId->getContentIdentifier();
2915  OUString aNewChildURL
2916  = aOldChildURL.replaceAt(
2917  0,
2918  aOldURL.getLength(),
2919  xNewId->getContentIdentifier() );
2920  uno::Reference< ucb::XContentIdentifier > xNewChildId
2921  = new ::ucbhelper::ContentIdentifier( aNewChildURL );
2922 
2923  if ( !xChild->exchangeIdentity( xNewChildId ) )
2924  return false;
2925  }
2926  return true;
2927  }
2928  }
2929 
2930  SAL_WARN( "ucb.ucp.webdav",
2931  "Content::exchangeIdentity - "
2932  "Panic! Cannot exchange identity!" );
2933  return false;
2934 }
2935 
2936 
2937 bool Content::isFolder(
2938  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
2939 {
2940  {
2941  osl::MutexGuard aGuard( m_aMutex );
2942 
2943  if ( m_bTransient )
2944  return m_bCollection;
2945  }
2946 
2947  uno::Sequence< beans::Property > aProperties( 1 );
2948  aProperties[ 0 ].Name = "IsFolder";
2949  aProperties[ 0 ].Handle = -1;
2950  uno::Reference< sdbc::XRow > xRow( getPropertyValues( aProperties, xEnv ) );
2951  if ( xRow.is() )
2952  {
2953  try
2954  {
2955  return xRow->getBoolean( 1 );
2956  }
2957  catch ( sdbc::SQLException const & )
2958  {
2959  }
2960  }
2961 
2962  return false;
2963 }
2964 
2965 
2966 uno::Any Content::MapDAVException( const DAVException & e, bool bWrite )
2967 {
2968  // Map DAVException...
2969  uno::Any aException;
2970 
2971  OUString aURL;
2972  if ( m_bTransient )
2973  {
2974  aURL = getParentURL();
2975  if ( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ) )
2976  aURL += "/";
2977 
2978  aURL += m_aEscapedTitle;
2979  }
2980  else
2981  {
2982  aURL = m_xIdentifier->getContentIdentifier();
2983  }
2984 
2985  switch ( e.getStatus() )
2986  {
2987  case SC_NOT_FOUND:
2988  {
2989  uno::Sequence< uno::Any > aArgs( 1 );
2990  aArgs[ 0 ] <<= beans::PropertyValue(
2991  "Uri", -1,
2992  uno::makeAny(aURL),
2993  beans::PropertyState_DIRECT_VALUE);
2994 
2995  aException <<=
2996  ucb::InteractiveAugmentedIOException(
2997  "Not found!",
2998  static_cast< cppu::OWeakObject * >( this ),
2999  task::InteractionClassification_ERROR,
3000  ucb::IOErrorCode_NOT_EXISTING,
3001  aArgs );
3002  return aException;
3003  }
3004  default:
3005  break;
3006  }
3007 
3008  switch ( e.getError() )
3009  {
3010  case DAVException::DAV_HTTP_ERROR:
3011  {
3012  if ( bWrite )
3013  aException <<=
3014  ucb::InteractiveNetworkWriteException(
3015  e.getData(),
3016  static_cast< cppu::OWeakObject * >( this ),
3017  task::InteractionClassification_ERROR,
3018  e.getData() );
3019  else
3020  aException <<=
3021  ucb::InteractiveNetworkReadException(
3022  e.getData(),
3023  static_cast< cppu::OWeakObject * >( this ),
3024  task::InteractionClassification_ERROR,
3025  e.getData() );
3026  break;
3027  }
3028 
3029  case DAVException::DAV_HTTP_LOOKUP:
3030  aException <<=
3031  ucb::InteractiveNetworkResolveNameException(
3032  OUString(),
3033  static_cast< cppu::OWeakObject * >( this ),
3034  task::InteractionClassification_ERROR,
3035  e.getData() );
3036  break;
3037 
3038 // @@@ No matching InteractiveNetwork*Exception
3039 // case DAVException::DAV_HTTP_AUTH:
3040 // break;
3041 
3042 // @@@ No matching InteractiveNetwork*Exception
3043 // case DAVException::DAV_HTTP_AUTHPROXY:
3044 // break;
3045 
3046  case DAVException::DAV_HTTP_CONNECT:
3047  aException <<=
3048  ucb::InteractiveNetworkConnectException(
3049  OUString(),
3050  static_cast< cppu::OWeakObject * >( this ),
3051  task::InteractionClassification_ERROR,
3052  e.getData() );
3053  break;
3054 
3055 // @@@ No matching InteractiveNetwork*Exception
3056 // case DAVException::DAV_HTTP_TIMEOUT:
3057 // break;
3058 
3059 // @@@ No matching InteractiveNetwork*Exception
3060 // case DAVException::DAV_HTTP_REDIRECT:
3061 // break;
3062 
3063 // @@@ No matching InteractiveNetwork*Exception
3064 // case DAVException::DAV_SESSION_CREATE:
3065 // break;
3066 
3067  case DAVException::DAV_INVALID_ARG:
3068  aException <<=
3069  lang::IllegalArgumentException(
3070  OUString(),
3071  static_cast< cppu::OWeakObject * >( this ),
3072  -1 );
3073  break;
3074 
3075  case DAVException::DAV_LOCKED:
3076 #if 1
3077  aException <<=
3078  ucb::InteractiveLockingLockedException(
3079  "Locked!",
3080  static_cast< cppu::OWeakObject * >( this ),
3081  task::InteractionClassification_ERROR,
3082  aURL,
3083  false ); // not SelfOwned
3084 #else
3085  {
3086  uno::Sequence< uno::Any > aArgs( 1 );
3087  aArgs[ 0 ] <<= beans::PropertyValue(
3088  OUString("Uri"), -1,
3089  uno::makeAny(aURL),
3090  beans::PropertyState_DIRECT_VALUE);
3091 
3092  aException <<=
3093  ucb::InteractiveAugmentedIOException(
3094  OUString( "Locked!" ),
3095  static_cast< cppu::OWeakObject * >( this ),
3096  task::InteractionClassification_ERROR,
3097  ucb::IOErrorCode_LOCKING_VIOLATION,
3098  aArgs );
3099  }
3100 #endif
3101  break;
3102 
3103  case DAVException::DAV_LOCKED_SELF:
3104  aException <<=
3105  ucb::InteractiveLockingLockedException(
3106  "Locked (self)!",
3107  static_cast< cppu::OWeakObject * >( this ),
3108  task::InteractionClassification_ERROR,
3109  aURL,
3110  true ); // SelfOwned
3111  break;
3112 
3113  case DAVException::DAV_NOT_LOCKED:
3114  aException <<=
3115  ucb::InteractiveLockingNotLockedException(
3116  "Not locked!",
3117  static_cast< cppu::OWeakObject * >( this ),
3118  task::InteractionClassification_ERROR,
3119  aURL );
3120  break;
3121 
3122  case DAVException::DAV_LOCK_EXPIRED:
3123  aException <<=
3124  ucb::InteractiveLockingLockExpiredException(
3125  "Lock expired!",
3126  static_cast< cppu::OWeakObject * >( this ),
3127  task::InteractionClassification_ERROR,
3128  aURL );
3129  break;
3130 
3131  default:
3132  aException <<=
3133  ucb::InteractiveNetworkGeneralException(
3134  OUString(),
3135  static_cast< cppu::OWeakObject * >( this ),
3136  task::InteractionClassification_ERROR );
3137  break;
3138  }
3139 
3140  return aException;
3141 }
3142 
3143 
3144 // static
3145 bool Content::shouldAccessNetworkAfterException( const DAVException & e )
3146 {
3147  if ( ( e.getStatus() == SC_NOT_FOUND ) ||
3148  ( e.getError() == DAVException::DAV_HTTP_LOOKUP ) ||
3149  ( e.getError() == DAVException::DAV_HTTP_CONNECT ) ||
3150  ( e.getError() == DAVException::DAV_HTTP_AUTH ) ||
3151  ( e.getError() == DAVException::DAV_HTTP_AUTHPROXY ) )
3152  return false;
3153 
3154  return true;
3155 }
3156 
3157 
3158 void Content::cancelCommandExecution(
3159  const DAVException & e,
3160  const uno::Reference< ucb::XCommandEnvironment > & xEnv,
3161  bool bWrite /* = false */ )
3162 {
3163  ucbhelper::cancelCommandExecution( MapDAVException( e, bWrite ), xEnv );
3164  // Unreachable
3165 }
3166 
3167 
3168 const OUString
3169 Content::getBaseURI( const std::unique_ptr< DAVResourceAccess > & rResAccess )
3170 {
3171  osl::Guard< osl::Mutex > aGuard( m_aMutex );
3172 
3173  // First, try to obtain value of response header "Content-Location".
3174  if ( m_xCachedProps.get() )
3175  {
3176  OUString aLocation;
3177  m_xCachedProps->getValue( "Content-Location" ) >>= aLocation;
3178  if ( aLocation.getLength() )
3179  {
3180  try
3181  {
3182  // Do not use m_xIdentifier->getContentIdentifier() because it
3183  // for example does not reflect redirects applied to requests
3184  // done using the original URI but m_xResAccess' URI does.
3185  return rtl::Uri::convertRelToAbs( rResAccess->getURL(),
3186  aLocation );
3187  }
3188  catch ( rtl::MalformedUriException const & )
3189  {
3190  }
3191  }
3192  }
3193 
3194  return rResAccess->getURL();
3195 }
3196 
3197 
3198 Content::ResourceType Content::getResourceType(
3199  const uno::Reference< ucb::XCommandEnvironment >& xEnv,
3200  const std::unique_ptr< DAVResourceAccess > & rResAccess,
3201  bool * networkAccessAllowed )
3202 {
3203  {
3204  osl::MutexGuard g(m_aMutex);
3205  if (m_eResourceType != UNKNOWN) {
3206  return m_eResourceType;
3207  }
3208  }
3209 
3210  ResourceType eResourceType = UNKNOWN;
3211 
3212  try
3213  {
3214  // Try to fetch some frequently used property value, e.g. those
3215  // used when loading documents... along with identifying whether
3216  // this is a DAV resource.
3217  std::vector< DAVResource > resources;
3218  std::vector< OUString > aPropNames;
3219  uno::Sequence< beans::Property > aProperties( 5 );
3220  aProperties[ 0 ].Name = "IsFolder";
3221  aProperties[ 1 ].Name = "IsDocument";
3222  aProperties[ 2 ].Name = "IsReadOnly";
3223  aProperties[ 3 ].Name = "MediaType";
3224  aProperties[ 4 ].Name = DAVProperties::SUPPORTEDLOCK;
3225 
3226  ContentProperties::UCBNamesToDAVNames(
3227  aProperties, aPropNames );
3228 
3229  rResAccess->PROPFIND(
3230  DAVZERO, aPropNames, resources, xEnv );
3231 
3232  // TODO - is this really only one?
3233  if ( resources.size() == 1 )
3234  {
3235  osl::MutexGuard g(m_aMutex);
3236  m_xCachedProps.reset(
3237  new CachableContentProperties( ContentProperties( resources[ 0 ] ) ) );
3238  m_xCachedProps->containsAllNames(
3239  aProperties, m_aFailedPropNames );
3240  }
3241 
3242  eResourceType = DAV;
3243  }
3244  catch ( DAVException const & e )
3245  {
3246  rResAccess->resetUri();
3247 
3248  if ( e.getStatus() == SC_METHOD_NOT_ALLOWED )
3249  {
3250  // Status SC_METHOD_NOT_ALLOWED is a safe indicator that the
3251  // resource is NON_DAV
3252  eResourceType = NON_DAV;
3253  }
3254  else if (networkAccessAllowed != nullptr)
3255  {
3256  *networkAccessAllowed = *networkAccessAllowed
3257  && shouldAccessNetworkAfterException(e);
3258  }
3259 
3260  // cancel command execution is case that no user authentication data has been provided.
3261  if ( e.getError() == DAVException::DAV_HTTP_NOAUTH )
3262  {
3263  cancelCommandExecution( e, uno::Reference< ucb::XCommandEnvironment >() );
3264  }
3265  }
3266 
3267  osl::MutexGuard g(m_aMutex);
3268  if (m_eResourceType == UNKNOWN) {
3269  m_eResourceType = eResourceType;
3270  } else {
3271  SAL_WARN_IF(
3272  eResourceType != m_eResourceType, "ucb.ucp.webdav",
3273  "different resource types for <" << rResAccess->getURL() << ">: "
3274  << +eResourceType << " vs. " << +m_eResourceType);
3275  }
3276  return m_eResourceType;
3277 }
3278 
3279 
3280 Content::ResourceType Content::getResourceType(
3281  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
3282 {
3283  std::unique_ptr< DAVResourceAccess > xResAccess;
3284  {
3285  osl::MutexGuard aGuard( m_aMutex );
3286  xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
3287  }
3288  const Content::ResourceType & ret = getResourceType( xEnv, xResAccess );
3289  {
3290  osl::Guard< osl::Mutex > aGuard( m_aMutex );
3291  m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
3292  }
3293  return ret;
3294 }
3295 
3296 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define WEBDAV_COLLECTION_TYPE
Type
std::vector< DAVRequestHeader > DAVRequestHeaders
OUString GetPathBaseName() const
Definition: SerfUri.cxx:160
::osl::Mutex m_aMutex
bool hasValue()
std::vector< ContentImplHelperRef > ContentRefList
const sal_uInt16 SC_METHOD_NOT_ALLOWED
const ExceptionCode & getError() const
const sal_uInt16 SC_NOT_FOUND
void SetScheme(const OUString &scheme)
Definition: SerfUri.hxx:84
const OUString & getData() const
const sal_uInt16 SC_NOT_IMPLEMENTED
#define DAVS_URL_SCHEME
#define WEBDAV_CONTENT_SERVICE_NAME
#define WEBDAV_CONTENT_TYPE
const css::uno::Any & getValue(const OUString &rName) const
#define WEBDAV_URL_SCHEME
#define WEBDAVS_URL_SCHEME
#define VNDSUNSTARWEBDAV_URL_SCHEME
UNKNOWN
void cancelCommandExecution(const uno::Any &rException, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
#define DAV_URL_SCHEME
ResourceType
DECL_LISTENERMULTIPLEXER_END void SAL_CALL inserted(::sal_Int32 ID) override
XTYPEPROVIDER_COMMON_IMPL(Content)
#define VNDSUNSTARWEBDAVS_URL_SCHEME
Object Value
SQLUSMALLINT SQLCHAR SQLSMALLINT SQLCHAR SQLSMALLINT SQLCHAR SQLSMALLINT SQLUSMALLINT Scope
std::pair< OUString, OUString > DAVRequestHeader
enumrange< T >::Iterator end(enumrange< T >)
#define HTTP_URL_SCHEME
ContinuationFlags
#define CPPU_TYPE_REF(T)
#define SAL_WARN_IF(condition, area, stream)
const sal_uInt16 SC_FORBIDDEN
#define SAL_INFO(area, stream)
std::vector< DAVPropertyValue > properties
Definition: DAVResource.hxx:44
std::vector< ContentRef > ContentRefList
const std::unique_ptr< PropertyValueMap > & getProperties() const
css::uno::Sequence< css::uno::Type > SAL_CALL getTypes()
#define SAL_WARN(area, stream)
OUString GetPathBaseNameUnescaped() const
Definition: SerfUri.cxx:194
sal_uInt16 getStatus() const
bool getProperty(const OUString &rPropName, css::beans::Property &rProp, bool bStrict=false)
sal_Int32 nPos
#define HTTPS_URL_SCHEME
const sal_uInt16 SC_PRECONDITION_FAILED
std::shared_ptr< osl::Mutex > const & lock()
css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType, Interface1 *p1)