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