1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
22 #include <cppuhelper/queryinterface.hxx>
23 #include <rtl/uri.hxx>
24 #include <sal/log.hxx>
25 #include <officecfg/Office/Common.hxx>
26 #include <officecfg/Inet.hxx>
27 #include <ucbhelper/contentidentifier.hxx>
28 #include <ucbhelper/macros.hxx>
29 #include <ucbhelper/propertyvalueset.hxx>
30 #include <ucbhelper/simpleinteractionrequest.hxx>
31 #include <ucbhelper/cancelcommandexecution.hxx>
32 #include <svl/lockfilecommon.hxx>
34 #include <com/sun/star/beans/IllegalTypeException.hpp>
35 #include <com/sun/star/beans/NotRemoveableException.hpp>
36 #include <com/sun/star/beans/PropertyAttribute.hpp>
37 #include <com/sun/star/beans/PropertyExistException.hpp>
38 #include <com/sun/star/beans/PropertySetInfoChange.hpp>
39 #include <com/sun/star/beans/PropertySetInfoChangeEvent.hpp>
40 #include <com/sun/star/beans/PropertyValue.hpp>
41 #include <com/sun/star/io/XActiveDataSink.hpp>
42 #include <com/sun/star/io/XOutputStream.hpp>
43 #include <com/sun/star/lang/IllegalAccessException.hpp>
44 #include <com/sun/star/lang/IllegalArgumentException.hpp>
45 #include <com/sun/star/sdbc/SQLException.hpp>
46 #include <com/sun/star/task/PasswordContainerInteractionHandler.hpp>
47 #include <com/sun/star/ucb/CommandEnvironment.hpp>
48 #include <com/sun/star/ucb/CommandFailedException.hpp>
49 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
50 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
51 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
52 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
53 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
54 #include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
55 #include <com/sun/star/ucb/InteractiveLockingLockExpiredException.hpp>
56 #include <com/sun/star/ucb/InteractiveLockingNotLockedException.hpp>
57 #include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
58 #include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
59 #include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
60 #include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
61 #include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
62 #include <com/sun/star/ucb/MissingInputStreamException.hpp>
63 #include <com/sun/star/ucb/MissingPropertiesException.hpp>
64 #include <com/sun/star/ucb/NameClash.hpp>
65 #include <com/sun/star/ucb/NameClashException.hpp>
66 #include <com/sun/star/ucb/OpenCommandArgument3.hpp>
67 #include <com/sun/star/ucb/OpenMode.hpp>
68 #include <com/sun/star/ucb/PostCommandArgument2.hpp>
69 #include <com/sun/star/ucb/PropertyCommandArgument.hpp>
70 #include <com/sun/star/ucb/TransferInfo.hpp>
71 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
72 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
73 #include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
74 #include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
75 #include <com/sun/star/ucb/XCommandInfo.hpp>
76 #include <com/sun/star/ucb/XPersistentPropertySet.hpp>
77 #include <com/sun/star/uno/XComponentContext.hpp>
79 #include "webdavcontent.hxx"
80 #include "webdavprovider.hxx"
81 #include "webdavresultset.hxx"
82 #include "ContentProperties.hxx"
83 #include "CurlUri.hxx"
84 #include "UCBDeadPropertyValue.hxx"
85 #include "DAVException.hxx"
86 #include "DAVProperties.hxx"
88 using namespace com::sun::star
;
89 using namespace http_dav_ucp
;
93 void lcl_sendPartialGETRequest( bool &bError
,
94 DAVException
&aLastException
,
95 const std::vector
< OUString
>& rProps
,
96 std::vector
< OUString
> &aHeaderNames
,
97 const std::unique_ptr
< DAVResourceAccess
> &xResAccess
,
98 std::unique_ptr
< ContentProperties
> &xProps
,
99 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
101 DAVResource aResource
;
102 DAVRequestHeaders aPartialGet
;
103 aPartialGet
.push_back(
105 OUString( "Range" ), // see <https://tools.ietf.org/html/rfc7233#section-3.1>
106 OUString( "bytes=0-0" )));
108 bool bIsRequestSize
= std::any_of(aHeaderNames
.begin(), aHeaderNames
.end(),
109 [](const OUString
& rHeaderName
) { return rHeaderName
== "Content-Length"; });
111 if ( bIsRequestSize
)
113 // we need to know if the server accepts range requests for a resource
114 // and the range unit it uses
115 aHeaderNames
.push_back( OUString( "Accept-Ranges" ) ); // see <https://tools.ietf.org/html/rfc7233#section-2.3>
116 aHeaderNames
.push_back( OUString( "Content-Range" ) ); // see <https://tools.ietf.org/html/rfc7233#section-4.2>
120 xResAccess
->GET0( aPartialGet
, aHeaderNames
, aResource
, xEnv
);
123 if ( bIsRequestSize
)
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
)
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
;
141 if ( aContentLength
.getLength() )
143 nSize
= aContentLength
.toInt64();
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.
150 aContentRange
.getLength() &&
151 aAcceptRanges
== "bytes" )
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( '/' );
159 OUString aSize
= aContentRange
.copy( nSlash
+ 1 );
160 // "*" means that the instance-length is unknown at the time when the response was generated
163 auto it
= std::find_if(aResponseProps
.begin(), aResponseProps
.end(),
164 [](const DAVPropertyValue
& rProp
) { return rProp
.Name
== "Content-Length"; });
165 if (it
!= aResponseProps
.end())
175 xProps
->addProperties(
177 ContentProperties( aResource
) );
179 xProps
.reset ( new ContentProperties( aResource
) );
181 catch ( DAVException
const & ex
)
188 // Static value, to manage a simple OPTIONS cache
189 // Key is the URL, element is the DAVOptions resulting from an OPTIONS call.
190 // Cached DAVOptions have a lifetime that depends on the errors received or not received
191 // and on the value of received options.
192 static DAVOptionsCache aStaticDAVOptionsCache
;
195 // Content Implementation.
198 // ctr for content on an existing webdav resource
200 const uno::Reference
< uno::XComponentContext
>& rxContext
,
201 ContentProvider
* pProvider
,
202 const uno::Reference
< ucb::XContentIdentifier
>& Identifier
,
203 rtl::Reference
< DAVSessionFactory
> const & rSessionFactory
)
204 : ContentImplHelper( rxContext
, pProvider
, Identifier
),
205 m_eResourceType( UNKNOWN
),
206 m_eResourceTypeForLocks( UNKNOWN
),
207 m_pProvider( pProvider
),
208 m_bTransient( false ),
209 m_bCollection( false ),
210 m_bDidGetOrHead( false )
214 initOptsCacheLifeTime();
215 m_xResAccess
.reset( new DAVResourceAccess(
218 Identifier
->getContentIdentifier() ) );
220 CurlUri
const aURI( Identifier
->getContentIdentifier() );
221 m_aEscapedTitle
= aURI
.GetPathBaseName();
223 catch ( DAVException
const & )
225 throw ucb::ContentCreationException();
230 // ctr for content on a non-existing webdav resource
232 const uno::Reference
< uno::XComponentContext
>& rxContext
,
233 ContentProvider
* pProvider
,
234 const uno::Reference
< ucb::XContentIdentifier
>& Identifier
,
235 rtl::Reference
< DAVSessionFactory
> const & rSessionFactory
,
237 : ContentImplHelper( rxContext
, pProvider
, Identifier
),
238 m_eResourceType( UNKNOWN
),
239 m_eResourceTypeForLocks( UNKNOWN
),
240 m_pProvider( pProvider
),
241 m_bTransient( true ),
242 m_bCollection( isCollection
),
243 m_bDidGetOrHead( false )
247 initOptsCacheLifeTime();
248 m_xResAccess
.reset( new DAVResourceAccess(
249 rxContext
, rSessionFactory
, Identifier
->getContentIdentifier() ) );
251 catch ( DAVException
const & )
253 throw ucb::ContentCreationException();
256 // Do not set m_aEscapedTitle here! Content::insert relays on this!!!
266 // XInterface methods.
270 void SAL_CALL
Content::acquire() noexcept
272 ContentImplHelper::acquire();
277 void SAL_CALL
Content::release() noexcept
279 ContentImplHelper::release();
284 uno::Any SAL_CALL
Content::queryInterface( const uno::Type
& rType
)
286 // Note: isFolder may require network activities! So call it only
287 // if it is really necessary!!!
288 uno::Any aRet
= cppu::queryInterface(
290 static_cast< ucb::XContentCreator
* >( this ) );
291 if ( aRet
.hasValue() )
295 uno::Reference
< task::XInteractionHandler
> xIH(
296 task::PasswordContainerInteractionHandler::create(m_xContext
) );
298 // Supply a command env to isFolder() that contains an interaction
299 // handler that uses the password container service to obtain
300 // credentials without displaying a password gui.
302 uno::Reference
< ucb::XCommandEnvironment
> xCmdEnv(
303 ucb::CommandEnvironment::create(
306 uno::Reference
< ucb::XProgressHandler
>() ) );
308 return isFolder( xCmdEnv
) ? aRet
: uno::Any();
310 catch ( uno::RuntimeException
const & )
314 catch ( uno::Exception
const & )
319 return aRet
.hasValue() ? aRet
: ContentImplHelper::queryInterface( rType
);
323 // XTypeProvider methods.
326 XTYPEPROVIDER_COMMON_IMPL( Content
);
330 uno::Sequence
< uno::Type
> SAL_CALL
Content::getTypes()
332 bool bFolder
= false;
336 = isFolder( uno::Reference
< ucb::XCommandEnvironment
>() );
338 catch ( uno::RuntimeException
const & )
342 catch ( uno::Exception
const & )
348 static cppu::OTypeCollection
s_aFolderTypes(
349 CPPU_TYPE_REF( lang::XTypeProvider
),
350 CPPU_TYPE_REF( lang::XServiceInfo
),
351 CPPU_TYPE_REF( lang::XComponent
),
352 CPPU_TYPE_REF( ucb::XContent
),
353 CPPU_TYPE_REF( ucb::XCommandProcessor
),
354 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier
),
355 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier
),
356 CPPU_TYPE_REF( beans::XPropertyContainer
),
357 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier
),
358 CPPU_TYPE_REF( container::XChild
),
359 CPPU_TYPE_REF( ucb::XContentCreator
) );
361 return s_aFolderTypes
.getTypes();
365 static cppu::OTypeCollection
s_aDocumentTypes(
366 CPPU_TYPE_REF( lang::XTypeProvider
),
367 CPPU_TYPE_REF( lang::XServiceInfo
),
368 CPPU_TYPE_REF( lang::XComponent
),
369 CPPU_TYPE_REF( ucb::XContent
),
370 CPPU_TYPE_REF( ucb::XCommandProcessor
),
371 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier
),
372 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier
),
373 CPPU_TYPE_REF( beans::XPropertyContainer
),
374 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier
),
375 CPPU_TYPE_REF( container::XChild
) );
377 return s_aDocumentTypes
.getTypes();
382 // XServiceInfo methods.
386 OUString SAL_CALL
Content::getImplementationName()
388 return "com.sun.star.comp.ucb.WebDAVContent";
393 uno::Sequence
< OUString
> SAL_CALL
Content::getSupportedServiceNames()
395 uno::Sequence
<OUString
> aSNS
{ WEBDAV_CONTENT_SERVICE_NAME
};
404 OUString SAL_CALL
Content::getContentType()
406 bool bFolder
= false;
410 = isFolder( uno::Reference
< ucb::XCommandEnvironment
>() );
412 catch ( uno::RuntimeException
const & )
416 catch ( uno::Exception
const & )
421 return WEBDAV_COLLECTION_TYPE
;
423 return WEBDAV_CONTENT_TYPE
;
427 // XCommandProcessor methods.
431 uno::Any SAL_CALL
Content::execute(
432 const ucb::Command
& aCommand
,
433 sal_Int32
/*CommandId*/,
434 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
436 SAL_INFO("ucb.ucp.webdav", ">>>>> Content::execute: start: command: " << aCommand
.Name
437 << ", env: " << (Environment
.is() ? "present" : "missing") );
441 if ( aCommand
.Name
== "getPropertyValues" )
447 uno::Sequence
< beans::Property
> Properties
;
448 if ( !( aCommand
.Argument
>>= Properties
) )
450 ucbhelper::cancelCommandExecution(
451 uno::Any( lang::IllegalArgumentException(
452 "Wrong argument type!",
453 static_cast< cppu::OWeakObject
* >( this ),
459 aRet
<<= getPropertyValues( Properties
, Environment
);
461 else if ( aCommand
.Name
== "setPropertyValues" )
467 uno::Sequence
< beans::PropertyValue
> aProperties
;
468 if ( !( aCommand
.Argument
>>= aProperties
) )
470 ucbhelper::cancelCommandExecution(
471 uno::Any( lang::IllegalArgumentException(
472 "Wrong argument type!",
473 static_cast< cppu::OWeakObject
* >( this ),
479 if ( !aProperties
.getLength() )
481 ucbhelper::cancelCommandExecution(
482 uno::Any( lang::IllegalArgumentException(
484 static_cast< cppu::OWeakObject
* >( this ),
490 aRet
<<= setPropertyValues( aProperties
, Environment
);
492 else if ( aCommand
.Name
== "getPropertySetInfo" )
495 // getPropertySetInfo
498 // Note: Implemented by base class.
499 aRet
<<= getPropertySetInfo( Environment
,
500 false /* don't cache data */ );
502 else if ( aCommand
.Name
== "getCommandInfo" )
508 // Note: Implemented by base class.
509 aRet
<<= getCommandInfo( Environment
, false );
511 else if ( aCommand
.Name
== "open" )
517 ucb::OpenCommandArgument3 aOpenCommand
;
518 ucb::OpenCommandArgument2 aTmp
;
519 if ( !( aCommand
.Argument
>>= aTmp
) )
521 ucbhelper::cancelCommandExecution(
522 uno::Any( lang::IllegalArgumentException(
523 "Wrong argument type!",
524 static_cast< cppu::OWeakObject
* >( this ),
529 if ( !( aCommand
.Argument
>>= aOpenCommand
) )
531 // compat mode, extract Arg2 info into newer structure
532 aOpenCommand
.Mode
= aTmp
.Mode
;
533 aOpenCommand
.Priority
= aTmp
.Priority
;
534 aOpenCommand
.Sink
= aTmp
.Sink
;
535 aOpenCommand
.Properties
= aTmp
.Properties
;
536 aOpenCommand
.SortingInfo
= aTmp
.SortingInfo
;
539 aRet
= open( aOpenCommand
, Environment
);
542 else if ( aCommand
.Name
== "insert" )
548 ucb::InsertCommandArgument arg
;
549 if ( !( aCommand
.Argument
>>= arg
) )
551 ucbhelper::cancelCommandExecution(
552 uno::Any( lang::IllegalArgumentException(
553 "Wrong argument type!",
554 static_cast< cppu::OWeakObject
* >( this ),
560 insert( arg
.Data
, arg
.ReplaceExisting
, Environment
);
562 else if ( aCommand
.Name
== "delete" )
568 bool bDeletePhysical
= false;
569 aCommand
.Argument
>>= bDeletePhysical
;
571 // KSO: Ignore parameter and destroy the content, if you don't support
572 // putting objects into trashcan. ( Since we do not have a trash can
573 // service yet (src603), you actually have no other choice. )
574 // if ( bDeletePhysical )
578 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
580 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
581 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
583 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
584 // clean cached value of PROPFIND property names
585 removeCachedPropertyNames( xResAccess
->getURL() );
586 xResAccess
->DESTROY( Environment
);
588 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
589 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
592 catch ( DAVException
const & e
)
594 cancelCommandExecution( e
, Environment
, true );
599 // Propagate destruction.
600 destroy( bDeletePhysical
);
602 // Remove own and all children's Additional Core Properties.
603 removeAdditionalPropertySet();
605 else if ( aCommand
.Name
== "transfer" && isFolder( Environment
) )
609 // ( Not available at documents )
612 ucb::TransferInfo transferArgs
;
613 if ( !( aCommand
.Argument
>>= transferArgs
) )
615 ucbhelper::cancelCommandExecution(
616 uno::Any( lang::IllegalArgumentException(
617 "Wrong argument type!",
618 static_cast< cppu::OWeakObject
* >( this ),
624 transfer( transferArgs
, Environment
);
626 else if ( aCommand
.Name
== "post" )
632 ucb::PostCommandArgument2 aArg
;
633 if ( !( aCommand
.Argument
>>= aArg
) )
635 ucbhelper::cancelCommandExecution(
636 uno::Any( lang::IllegalArgumentException(
637 "Wrong argument type!",
638 static_cast< cppu::OWeakObject
* >( this ),
644 post( aArg
, Environment
);
646 else if ( aCommand
.Name
== "lock" )
651 ResourceType eType
= resourceTypeForLocks( Environment
);
652 // when the resource is not yet present the lock is used to create it
653 // see: http://tools.ietf.org/html/rfc4918#section-7.3
654 // If the resource doesn't exists and the lock is not enabled (DAV with
655 // no lock or a simple web) the error will be dealt with inside lock() method
656 if ( eType
== NOT_FOUND
||
660 if ( eType
== NOT_FOUND
)
662 m_eResourceType
= UNKNOWN
; // lock may have created it, need to check again
663 m_eResourceTypeForLocks
= UNKNOWN
;
667 else if ( aCommand
.Name
== "unlock" )
671 // do not check for a DAV resource
672 // the lock store will be checked before sending
673 unlock( Environment
);
675 else if ( aCommand
.Name
== "createNewContent" &&
676 isFolder( Environment
) )
682 ucb::ContentInfo aArg
;
683 if ( !( aCommand
.Argument
>>= aArg
) )
685 ucbhelper::cancelCommandExecution(
686 uno::Any( lang::IllegalArgumentException(
687 "Wrong argument type!",
688 static_cast< cppu::OWeakObject
* >( this ),
694 aRet
<<= createNewContent( aArg
);
696 else if ( aCommand
.Name
== "addProperty" )
698 ucb::PropertyCommandArgument aPropArg
;
699 if ( !( aCommand
.Argument
>>= aPropArg
))
701 ucbhelper::cancelCommandExecution(
702 uno::Any( lang::IllegalArgumentException(
703 "Wrong argument type!",
704 static_cast< cppu::OWeakObject
* >( this ),
709 // TODO when/if XPropertyContainer is removed,
710 // the command execution can be canceled in addProperty
713 addProperty( aPropArg
, Environment
);
715 catch ( const beans::PropertyExistException
&e
)
717 ucbhelper::cancelCommandExecution( uno::Any( e
), Environment
);
719 catch ( const beans::IllegalTypeException
&e
)
721 ucbhelper::cancelCommandExecution( uno::Any( e
), Environment
);
723 catch ( const lang::IllegalArgumentException
&e
)
725 ucbhelper::cancelCommandExecution( uno::Any( e
), Environment
);
728 else if ( aCommand
.Name
== "removeProperty" )
731 if ( !( aCommand
.Argument
>>= sPropName
) )
733 ucbhelper::cancelCommandExecution(
734 uno::Any( lang::IllegalArgumentException(
735 "Wrong argument type!",
736 static_cast< cppu::OWeakObject
* >( this ),
741 // TODO when/if XPropertyContainer is removed,
742 // the command execution can be canceled in removeProperty
745 removeProperty( sPropName
, Environment
);
747 catch( const beans::UnknownPropertyException
&e
)
749 ucbhelper::cancelCommandExecution( uno::Any( e
), Environment
);
751 catch( const beans::NotRemoveableException
&e
)
753 ucbhelper::cancelCommandExecution( uno::Any( e
), Environment
);
759 // Unsupported command
762 ucbhelper::cancelCommandExecution(
763 uno::Any( ucb::UnsupportedCommandException(
765 static_cast< cppu::OWeakObject
* >( this ) ) ),
770 SAL_INFO("ucb.ucp.webdav", "<<<<< Content::execute: end: command: " << aCommand
.Name
);
777 void SAL_CALL
Content::abort( sal_Int32
/*CommandId*/ )
781 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
783 osl::MutexGuard
aGuard( m_aMutex
);
784 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
788 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
789 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
792 catch ( DAVException
const & )
799 // XPropertyContainer methods.
802 void Content::addProperty( const css::ucb::PropertyCommandArgument
&aCmdArg
,
803 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
805 // if ( m_bTransient )
807 const beans::Property aProperty
= aCmdArg
.Property
;
808 const uno::Any aDefaultValue
= aCmdArg
.DefaultValue
;
810 // check property Name
811 if ( !aProperty
.Name
.getLength() )
812 throw lang::IllegalArgumentException(
813 "\"addProperty\" with empty Property.Name",
814 static_cast< ::cppu::OWeakObject
* >( this ),
817 // Check property type.
818 if ( !UCBDeadPropertyValue::supportsType( aProperty
.Type
) )
819 throw beans::IllegalTypeException(
820 "\"addProperty\" unsupported Property.Type",
821 static_cast< ::cppu::OWeakObject
* >( this ) );
823 // check default value
824 if ( aDefaultValue
.hasValue() && aDefaultValue
.getValueType() != aProperty
.Type
)
825 throw beans::IllegalTypeException(
826 "\"addProperty\" DefaultValue does not match Property.Type",
827 static_cast< ::cppu::OWeakObject
* >( this ) );
830 // Make sure a property with the requested name does not already
831 // exist in dynamic and static(!) properties.
834 // Take into account special properties with custom namespace
835 // using <prop:the_propname xmlns:prop="the_namespace">
836 OUString aSpecialName
;
837 bool bIsSpecial
= DAVProperties::isUCBSpecialProperty( aProperty
.Name
, aSpecialName
);
839 // Note: This requires network access!
840 if ( getPropertySetInfo( xEnv
, false /* don't cache data */ )
841 ->hasPropertyByName( bIsSpecial
? aSpecialName
: aProperty
.Name
) )
843 // Property does already exist.
844 throw beans::PropertyExistException();
848 // Add a new dynamic property.
851 ProppatchValue
aValue( PROPSET
, aProperty
.Name
, aDefaultValue
);
853 std::vector
< ProppatchValue
> aProppatchValues
;
854 aProppatchValues
.push_back( aValue
);
858 // Set property value at server.
859 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
861 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
862 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
864 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
865 // clean cached value of PROPFIND property names
866 // PROPPATCH can change them
867 removeCachedPropertyNames( xResAccess
->getURL() );
868 xResAccess
->PROPPATCH( aProppatchValues
, xEnv
);
870 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
871 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
874 // Notify propertyset info change listeners.
875 beans::PropertySetInfoChangeEvent
evt(
876 static_cast< cppu::OWeakObject
* >( this ),
877 bIsSpecial
? aSpecialName
: aProperty
.Name
,
878 -1, // No handle available
879 beans::PropertySetInfoChange::PROPERTY_INSERTED
);
880 notifyPropertySetInfoChange( evt
);
882 catch ( DAVException
const & e
)
884 if ( e
.getStatus() == SC_FORBIDDEN
)
886 // Support for setting arbitrary dead properties is optional!
888 // Store property locally.
889 ContentImplHelper::addProperty( bIsSpecial
? aSpecialName
: aProperty
.Name
,
890 aProperty
.Attributes
,
895 if ( shouldAccessNetworkAfterException( e
) )
899 const ResourceType eType
= getResourceType( xEnv
);
904 throw lang::IllegalArgumentException();
907 // Store property locally.
908 ContentImplHelper::addProperty( bIsSpecial
? aSpecialName
: aProperty
.Name
,
909 aProperty
.Attributes
,
914 SAL_WARN( "ucb.ucp.webdav",
915 "Content::addProperty - "
916 "Unsupported resource type!" );
920 catch ( uno::Exception
const & )
922 SAL_WARN( "ucb.ucp.webdav",
923 "Content::addProperty - "
924 "Unable to determine resource type!" );
929 SAL_WARN( "ucb.ucp.webdav",
930 "Content::addProperty - "
931 "Unable to determine resource type!" );
937 void Content::removeProperty( const OUString
& Name
,
938 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
941 // @@@ REMOVABLE at the moment not properly set in the PropSetInfo
944 beans::Property aProp
945 = getPropertySetInfo( xEnv
, false /* don't cache data */ )
946 ->getPropertyByName( Name
);
948 if ( !( aProp
.Attributes
& beans::PropertyAttribute::REMOVABLE
) )
951 throw beans::NotRemoveableException();
954 catch ( beans::UnknownPropertyException
const & )
956 //SAL_WARN( "ucb.ucp.webdav", "removeProperty - Unknown property!" );
961 // Try to remove property from server.
964 std::vector
< ProppatchValue
> aProppatchValues
;
965 ProppatchValue
aValue( PROPREMOVE
, Name
, uno::Any() );
966 aProppatchValues
.push_back( aValue
);
968 // Remove property value from server.
969 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
971 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
972 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
974 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
975 // clean cached value of PROPFIND property names
976 // PROPPATCH can change them
977 removeCachedPropertyNames( xResAccess
->getURL() );
978 xResAccess
->PROPPATCH( aProppatchValues
, xEnv
);
980 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
981 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
984 // Notify propertyset info change listeners.
985 beans::PropertySetInfoChangeEvent
evt(
986 static_cast< cppu::OWeakObject
* >( this ),
988 -1, // No handle available
989 beans::PropertySetInfoChange::PROPERTY_REMOVED
);
990 notifyPropertySetInfoChange( evt
);
992 catch ( DAVException
const & e
)
994 if ( e
.getStatus() == SC_FORBIDDEN
)
996 // Support for setting arbitrary dead properties is optional!
998 // Try to remove property from local store.
999 ContentImplHelper::removeProperty( Name
);
1003 if ( shouldAccessNetworkAfterException( e
) )
1007 const ResourceType eType
= getResourceType( xEnv
);
1012 throw beans::UnknownPropertyException(Name
);
1015 // Try to remove property from local store.
1016 ContentImplHelper::removeProperty( Name
);
1020 SAL_WARN( "ucb.ucp.webdav",
1021 "Content::removeProperty - "
1022 "Unsupported resource type!" );
1026 catch ( uno::Exception
const & )
1028 SAL_WARN( "ucb.ucp.webdav",
1029 "Content::removeProperty - "
1030 "Unable to determine resource type!" );
1035 SAL_WARN( "ucb.ucp.webdav",
1036 "Content::removeProperty - "
1037 "Unable to determine resource type!" );
1038 // throw beans::UnknownPropertyException();
1045 void SAL_CALL
Content::addProperty( const OUString
& Name
,
1046 sal_Int16 Attributes
,
1047 const uno::Any
& DefaultValue
)
1049 beans::Property aProperty
;
1050 aProperty
.Name
= Name
;
1051 aProperty
.Type
= DefaultValue
.getValueType();
1052 aProperty
.Attributes
= Attributes
;
1053 aProperty
.Handle
= -1;
1055 addProperty( ucb::PropertyCommandArgument( aProperty
, DefaultValue
),
1056 uno::Reference
< ucb::XCommandEnvironment
>());
1060 void SAL_CALL
Content::removeProperty( const OUString
& Name
)
1062 removeProperty( Name
,
1063 uno::Reference
< ucb::XCommandEnvironment
>() );
1067 // XContentCreator methods.
1071 uno::Sequence
< ucb::ContentInfo
> SAL_CALL
1072 Content::queryCreatableContentsInfo()
1074 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
1076 uno::Sequence
< ucb::ContentInfo
> aSeq( 2 );
1079 aSeq
.getArray()[ 0 ].Type
= WEBDAV_CONTENT_TYPE
;
1080 aSeq
.getArray()[ 0 ].Attributes
1081 = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
1082 | ucb::ContentInfoAttribute::KIND_DOCUMENT
;
1084 beans::Property aProp
;
1085 m_pProvider
->getProperty( "Title", aProp
);
1087 uno::Sequence
< beans::Property
> aDocProps( 1 );
1088 aDocProps
.getArray()[ 0 ] = aProp
;
1089 aSeq
.getArray()[ 0 ].Properties
= aDocProps
;
1092 aSeq
.getArray()[ 1 ].Type
= WEBDAV_COLLECTION_TYPE
;
1093 aSeq
.getArray()[ 1 ].Attributes
1094 = ucb::ContentInfoAttribute::KIND_FOLDER
;
1096 uno::Sequence
< beans::Property
> aFolderProps( 1 );
1097 aFolderProps
.getArray()[ 0 ] = aProp
;
1098 aSeq
.getArray()[ 1 ].Properties
= aFolderProps
;
1104 uno::Reference
< ucb::XContent
> SAL_CALL
1105 Content::createNewContent( const ucb::ContentInfo
& Info
)
1107 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
1109 if ( !Info
.Type
.getLength() )
1110 return uno::Reference
< ucb::XContent
>();
1112 if ( ( Info
.Type
!= WEBDAV_COLLECTION_TYPE
)
1114 ( Info
.Type
!= WEBDAV_CONTENT_TYPE
) )
1115 return uno::Reference
< ucb::XContent
>();
1117 OUString aURL
= m_xIdentifier
->getContentIdentifier();
1119 SAL_WARN_IF( aURL
.isEmpty(), "ucb.ucp.webdav",
1120 "WebdavContent::createNewContent - empty identifier!" );
1122 if ( ( aURL
.lastIndexOf( '/' ) + 1 ) != aURL
.getLength() )
1126 if ( Info
.Type
== WEBDAV_COLLECTION_TYPE
)
1128 aURL
+= "New_Collection";
1129 isCollection
= true;
1133 aURL
+= "New_Content";
1134 isCollection
= false;
1137 uno::Reference
< ucb::XContentIdentifier
> xId(
1138 new ::ucbhelper::ContentIdentifier( aURL
) );
1140 // create the local content
1143 return new ::http_dav_ucp::Content( m_xContext
,
1146 m_xResAccess
->getSessionFactory(),
1149 catch ( ucb::ContentCreationException
& )
1151 return uno::Reference
< ucb::XContent
>();
1157 OUString
Content::getParentURL()
1159 // <scheme>:// -> ""
1160 // <scheme>://foo -> ""
1161 // <scheme>://foo/ -> ""
1162 // <scheme>://foo/bar -> <scheme>://foo/
1163 // <scheme>://foo/bar/ -> <scheme>://foo/
1164 // <scheme>://foo/bar/abc -> <scheme>://foo/bar/
1166 OUString aURL
= m_xIdentifier
->getContentIdentifier();
1168 sal_Int32 nPos
= aURL
.lastIndexOf( '/' );
1169 if ( nPos
== ( aURL
.getLength() - 1 ) )
1171 // Trailing slash found. Skip.
1172 nPos
= aURL
.lastIndexOf( '/', nPos
);
1175 sal_Int32 nPos1
= aURL
.lastIndexOf( '/', nPos
);
1177 nPos1
= aURL
.lastIndexOf( '/', nPos1
);
1182 return aURL
.copy( 0, nPos
+ 1 );
1186 // Non-interface methods.
1190 uno::Reference
< sdbc::XRow
> Content::getPropertyValues(
1191 const uno::Reference
< uno::XComponentContext
>& rxContext
,
1192 const uno::Sequence
< beans::Property
>& rProperties
,
1193 const ContentProperties
& rData
,
1194 const rtl::Reference
< ::ucbhelper::ContentProviderImplHelper
>& rProvider
,
1195 const OUString
& rContentId
)
1197 // Note: Empty sequence means "get values of all supported properties".
1199 rtl::Reference
< ::ucbhelper::PropertyValueSet
> xRow
1200 = new ::ucbhelper::PropertyValueSet( rxContext
);
1202 sal_Int32 nCount
= rProperties
.getLength();
1205 uno::Reference
< beans::XPropertySet
> xAdditionalPropSet
;
1206 bool bTriedToGetAdditionalPropSet
= false;
1208 const beans::Property
* pProps
= rProperties
.getConstArray();
1209 for ( sal_Int32 n
= 0; n
< nCount
; ++n
)
1211 const beans::Property
& rProp
= pProps
[ n
];
1213 // Process standard UCB, DAV and HTTP properties.
1214 const uno::Any
& rValue
= rData
.getValue( rProp
.Name
);
1215 if ( rValue
.hasValue() )
1217 xRow
->appendObject( rProp
, rValue
);
1221 // Process local Additional Properties.
1222 if ( !bTriedToGetAdditionalPropSet
&& !xAdditionalPropSet
.is() )
1224 xAdditionalPropSet
=
1225 rProvider
->getAdditionalPropertySet( rContentId
,
1227 bTriedToGetAdditionalPropSet
= true;
1230 if ( !xAdditionalPropSet
.is() ||
1231 !xRow
->appendPropertySetValue(
1232 xAdditionalPropSet
, rProp
) )
1234 // Append empty entry.
1235 xRow
->appendVoid( rProp
);
1242 // Append all standard UCB, DAV and HTTP properties.
1244 const std::unique_ptr
< PropertyValueMap
> & xProps
= rData
.getProperties();
1246 ContentProvider
* pProvider
1247 = static_cast< ContentProvider
* >( rProvider
.get() );
1248 beans::Property aProp
;
1250 for ( const auto& rProp
: *xProps
)
1252 if ( pProvider
->getProperty( rProp
.first
, aProp
) )
1253 xRow
->appendObject( aProp
, rProp
.second
.value() );
1256 // Append all local Additional Properties.
1257 uno::Reference
< beans::XPropertySet
> xSet
=
1258 rProvider
->getAdditionalPropertySet( rContentId
, false );
1259 xRow
->appendPropertySet( xSet
);
1262 return uno::Reference
<sdbc::XRow
>(xRow
);
1266 void GetPropsUsingHeadRequest(DAVResource
& resource
,
1267 const std::unique_ptr
< DAVResourceAccess
>& xResAccess
,
1268 const std::vector
< OUString
>& aHTTPNames
,
1269 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
1271 if (!aHTTPNames
.empty())
1273 DAVOptions aDAVOptions
;
1274 OUString aTargetURL
= xResAccess
->getURL();
1275 // retrieve the cached options if any
1276 aStaticDAVOptionsCache
.getDAVOptions(aTargetURL
, aDAVOptions
);
1278 // clean cached value of PROPFIND property names
1279 // PROPPATCH can change them
1280 Content::removeCachedPropertyNames(aTargetURL
);
1281 // test if HEAD allowed, if not, throw, should be caught immediately
1282 // SC_GONE used internally by us, see comment in Content::getPropertyValues
1283 // in the catch scope
1284 if (aDAVOptions
.getHttpResponseStatusCode() != SC_GONE
&&
1285 !aDAVOptions
.isHeadAllowed())
1287 throw DAVException(DAVException::DAV_HTTP_ERROR
, "405 Not Implemented", SC_METHOD_NOT_ALLOWED
);
1289 // if HEAD is enabled on this site
1290 // check if there is a relevant HTTP response status code cached
1291 if (aDAVOptions
.getHttpResponseStatusCode() != SC_NONE
)
1293 // throws exception as if there was a server error, a DAV exception
1294 throw DAVException(DAVException::DAV_HTTP_ERROR
,
1295 aDAVOptions
.getHttpResponseStatusText(),
1296 aDAVOptions
.getHttpResponseStatusCode());
1300 xResAccess
->HEAD(aHTTPNames
, resource
, xEnv
);
1305 uno::Reference
< sdbc::XRow
> Content::getPropertyValues(
1306 const uno::Sequence
< beans::Property
>& rProperties
,
1307 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
1309 std::unique_ptr
< ContentProperties
> xProps
;
1310 std::unique_ptr
< ContentProperties
> xCachedProps
;
1311 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
1312 OUString aUnescapedTitle
;
1313 bool bHasAll
= false;
1314 uno::Reference
< uno::XComponentContext
> xContext
;
1315 uno::Reference
< ucb::XContentIdentifier
> xIdentifier
;
1316 rtl::Reference
< ::ucbhelper::ContentProviderImplHelper
> xProvider
;
1319 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
1321 aUnescapedTitle
= DecodeURI(m_aEscapedTitle
);
1322 xContext
.set( m_xContext
);
1323 xIdentifier
.set( m_xIdentifier
);
1324 xProvider
= m_xProvider
;
1325 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
1327 // First, ask cache...
1330 xCachedProps
.reset( new ContentProperties( *m_xCachedProps
) );
1332 std::vector
< OUString
> aMissingProps
;
1333 if ( xCachedProps
->containsAllNames( rProperties
, aMissingProps
) )
1335 // All properties are already in cache! No server access needed.
1339 // use the cached ContentProperties instance
1340 xProps
.reset( new ContentProperties( *xCachedProps
) );
1344 bool bNetworkAccessAllowed
= true;
1346 if ( !m_bTransient
&& !bHasAll
)
1348 // Obtain values from server...
1351 // First, identify whether resource is DAV or not
1352 const ResourceType eType
= getResourceType(
1353 xEnv
, xResAccess
, &bNetworkAccessAllowed
);
1357 // cache lookup... getResourceType may fill the props cache via
1362 new ContentProperties( *m_xCachedProps
) );
1364 std::vector
< OUString
> aMissingProps
;
1365 if ( xCachedProps
->containsAllNames(
1366 rProperties
, aMissingProps
) )
1368 // All properties are already in cache! No server access
1373 // use the cached ContentProperties instance
1374 xProps
.reset( new ContentProperties( *xCachedProps
) );
1379 // Only DAV resources support PROPFIND
1380 std::vector
< OUString
> aPropNames
;
1382 uno::Sequence
< beans::Property
> aProperties(rProperties
);
1384 if ( aProperties
.getLength() > 0 )
1385 ContentProperties::UCBNamesToDAVNames(
1386 aProperties
, aPropNames
);
1388 if ( !aPropNames
.empty() )
1390 std::vector
< DAVResource
> resources
;
1393 xResAccess
->PROPFIND(
1394 DAVZERO
, aPropNames
, resources
, xEnv
);
1396 if ( 1 == resources
.size() )
1398 #if defined SAL_LOG_INFO
1400 // print received resources
1401 std::vector
< DAVPropertyValue
>::const_iterator it
= resources
[0].properties
.begin();
1402 std::vector
< DAVPropertyValue
>::const_iterator end
= resources
[0].properties
.end();
1405 OUString aPropValue
;
1407 uno::Sequence
< ucb::LockEntry
> aSupportedLocks
;
1408 if( (*it
).Value
>>= aPropValue
)
1409 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << (*it
).Name
<< ":" << aPropValue
);
1410 else if( (*it
).Value
>>= bValue
)
1411 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << (*it
).Name
<< ":" <<
1412 ( bValue
? "true" : "false" ) );
1413 else if( (*it
).Value
>>= aSupportedLocks
)
1415 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << (*it
).Name
<< ":" );
1416 for ( sal_Int32 n
= 0; n
< aSupportedLocks
.getLength(); ++n
)
1418 SAL_INFO( "ucb.ucp.webdav"," scope: "
1419 << (aSupportedLocks
[n
].Scope
== ucb::LockScope_SHARED
? "shared" : "exclusive")
1421 << (aSupportedLocks
[n
].Type
!= ucb::LockType_WRITE
? "" : "write") );
1429 xProps
->addProperties(
1431 ContentProperties( resources
[ 0 ] ));
1434 new ContentProperties( resources
[ 0 ] ) );
1437 catch ( DAVException
const & e
)
1439 bNetworkAccessAllowed
= bNetworkAccessAllowed
1440 && shouldAccessNetworkAfterException( e
);
1442 if ( !bNetworkAccessAllowed
)
1444 cancelCommandExecution( e
, xEnv
);
1452 if ( bNetworkAccessAllowed
)
1454 // All properties obtained already?
1455 std::vector
< OUString
> aMissingProps
;
1457 && xProps
->containsAllNames(rProperties
, aMissingProps
))
1458 // i#121922 for non-DAV, uncacheable properties must be fetched
1459 // regardless of m_bDidGetOrHead.
1460 // But SharePoint may do weird things on HEAD so for DAV
1461 // only do this if required.
1462 && (eType
!= DAV
|| !m_bDidGetOrHead
))
1464 // Possibly the missing props can be obtained using a HEAD
1467 std::vector
< OUString
> aHeaderNames
;
1468 ContentProperties::UCBNamesToHTTPNames(
1474 // in case of not DAV PROFIND (previously in program flow) failed
1475 // so we need to add the only prop that's common
1476 // to DAV and NON_DAV: MediaType, that maps to Content-Type
1477 aHeaderNames
.push_back( "Content-Type" );
1480 if (!aHeaderNames
.empty()) try
1482 DAVResource resource
;
1483 GetPropsUsingHeadRequest(resource
, xResAccess
, aHeaderNames
, xEnv
);
1484 m_bDidGetOrHead
= true;
1487 xProps
->addProperties(
1489 ContentProperties( resource
) );
1491 xProps
.reset ( new ContentProperties( resource
) );
1493 if (m_eResourceType
== NON_DAV
)
1494 xProps
->addProperties(aMissingProps
,
1499 catch ( DAVException
const & e
)
1501 // non "general-purpose servers" may not support HEAD requests
1502 // see http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1
1503 // In this case, perform a partial GET only to get the header info
1504 // vid. http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
1505 // WARNING if the server does not support partial GETs,
1506 // the GET will transfer the whole content
1508 DAVException aLastException
= e
;
1509 OUString aTargetURL
= xResAccess
->getURL();
1511 if ( e
.getError() == DAVException::DAV_HTTP_ERROR
)
1513 // According to the spec. the origin server SHOULD return
1514 // * 405 (Method Not Allowed):
1515 // the method is known but not allowed for the requested resource
1516 // * 501 (Not Implemented):
1517 // the method is unrecognized or not implemented
1518 // * 404 (SC_NOT_FOUND)
1519 // is for google-code server and for MS IIS 10.0 Web server
1520 // when only GET is enabled
1521 if ( aLastException
.getStatus() == SC_NOT_IMPLEMENTED
||
1522 aLastException
.getStatus() == SC_METHOD_NOT_ALLOWED
||
1523 aLastException
.getStatus() == SC_NOT_FOUND
)
1525 SAL_WARN( "ucb.ucp.webdav", "HEAD probably not implemented: fall back to a partial GET" );
1526 aStaticDAVOptionsCache
.setHeadAllowed( aTargetURL
, false );
1527 lcl_sendPartialGETRequest( bError
,
1534 m_bDidGetOrHead
= !bError
;
1540 DAVOptions aDAVOptionsException
;
1542 aDAVOptionsException
.setURL( aTargetURL
);
1543 // check if the error was SC_NOT_FOUND, meaning that the
1544 // GET fall back didn't succeeded and the element is really missing
1545 // we will consider the resource SC_GONE (410) for some time
1546 // we use SC_GONE because has the same meaning of SC_NOT_FOUND (404)
1548 // <https://tools.ietf.org/html/rfc7231#section-6.5.9> (retrieved 2016-10-09)
1549 // apparently it's not used to mark the missing HEAD method (so far...)
1550 sal_uInt16 ResponseStatusCode
=
1551 ( aLastException
.getStatus() == SC_NOT_FOUND
) ?
1553 aLastException
.getStatus();
1554 aDAVOptionsException
.setHttpResponseStatusCode( ResponseStatusCode
);
1555 aDAVOptionsException
.setHttpResponseStatusText( aLastException
.getData() );
1556 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptionsException
,
1557 m_nOptsCacheLifeNotFound
);
1559 if ( !shouldAccessNetworkAfterException( aLastException
) )
1561 cancelCommandExecution( aLastException
, xEnv
);
1569 // might trigger HTTP redirect.
1570 // Therefore, title must be updated here.
1571 CurlUri
const aUri( xResAccess
->getURL() );
1572 aUnescapedTitle
= aUri
.GetPathBaseNameUnescaped();
1574 if ( eType
== UNKNOWN
)
1576 xProps
.reset( new ContentProperties( aUnescapedTitle
) );
1579 // For DAV resources we only know the Title, for non-DAV
1580 // resources we additionally know that it is a document.
1582 else if ( eType
== DAV
)
1585 xProps
.reset(new ContentProperties(aUnescapedTitle
));
1587 xProps
->addProperty("Title", uno::Any(aUnescapedTitle
), true);
1592 xProps
.reset( new ContentProperties( aUnescapedTitle
, false ) );
1594 xProps
->addProperty(
1596 uno::Any( aUnescapedTitle
),
1599 xProps
->addProperty(
1603 xProps
->addProperty(
1607 xProps
->addProperty(
1609 uno::Any( OUString(WEBDAV_CONTENT_TYPE
) ),
1615 // No server access for just created (not yet committed) objects.
1616 // Only a minimal set of properties supported at this stage.
1618 xProps
.reset( new ContentProperties( aUnescapedTitle
,
1622 // Add a default for the properties requested but not found.
1623 // Determine still missing properties, add a default.
1624 // Some client function doesn't expect a void uno::Any,
1625 // but instead wants some sort of default.
1626 std::vector
< OUString
> aMissingProps
;
1627 if ( !xProps
->containsAllNames(
1628 rProperties
, aMissingProps
) )
1631 for ( std::vector
< rtl::OUString
>::const_iterator it
= aMissingProps
.begin();
1632 it
!= aMissingProps
.end(); ++it
)
1634 // For the time being only a couple of properties need to be added
1635 if ( (*it
) == "DateModified" || (*it
) == "DateCreated" )
1637 util::DateTime aDate
;
1638 xProps
->addProperty(
1643 else if (bNetworkAccessAllowed
) // don't set these if connection failed
1645 // If WebDAV didn't return the resource type, assume default
1646 // This happens e.g. for lists exported by SharePoint
1647 if ((*it
) == "IsFolder")
1649 xProps
->addProperty(
1654 else if ((*it
) == "IsDocument")
1656 xProps
->addProperty(
1665 sal_Int32 nCount
= rProperties
.getLength();
1666 for ( sal_Int32 n
= 0; n
< nCount
; ++n
)
1668 const OUString rName
= rProperties
[ n
].Name
;
1669 if ( rName
== "BaseURI" )
1671 // Add BaseURI property, if requested.
1672 xProps
->addProperty(
1674 uno::Any( getBaseURI( xResAccess
) ),
1677 else if ( rName
== "CreatableContentsInfo" )
1679 // Add CreatableContentsInfo property, if requested.
1680 bool bFolder
= false;
1681 xProps
->getValue( "IsFolder" )
1683 xProps
->addProperty(
1684 "CreatableContentsInfo",
1686 ? queryCreatableContentsInfo()
1687 : uno::Sequence
< ucb::ContentInfo
>() ),
1692 uno::Reference
< sdbc::XRow
> xResultRow
1693 = getPropertyValues( xContext
,
1697 xIdentifier
->getContentIdentifier() );
1700 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
1702 if (!m_xCachedProps
)
1703 m_xCachedProps
.reset( new CachableContentProperties( *xProps
) );
1705 m_xCachedProps
->addProperties( *xProps
);
1707 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
1708 m_aEscapedTitle
= EncodeSegment(aUnescapedTitle
);
1715 uno::Sequence
< uno::Any
> Content::setPropertyValues(
1716 const uno::Sequence
< beans::PropertyValue
>& rValues
,
1717 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
1719 uno::Reference
< ucb::XContentIdentifier
> xIdentifier
;
1720 rtl::Reference
< ContentProvider
> xProvider
;
1722 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
1725 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
1727 xProvider
.set( m_pProvider
);
1728 xIdentifier
.set( m_xIdentifier
);
1729 bTransient
= m_bTransient
;
1730 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
1733 uno::Sequence
< uno::Any
> aRet( rValues
.getLength() );
1734 auto aRetRange
= asNonConstRange(aRet
);
1735 uno::Sequence
< beans::PropertyChangeEvent
> aChanges( rValues
.getLength() );
1736 sal_Int32 nChanged
= 0;
1738 beans::PropertyChangeEvent aEvent
;
1739 aEvent
.Source
= static_cast< cppu::OWeakObject
* >( this );
1740 aEvent
.Further
= false;
1741 // aEvent.PropertyName =
1742 aEvent
.PropertyHandle
= -1;
1743 // aEvent.OldValue =
1744 // aEvent.NewValue =
1746 std::vector
< ProppatchValue
> aProppatchValues
;
1747 std::vector
< sal_Int32
> aProppatchPropsPositions
;
1749 uno::Reference
< ucb::XPersistentPropertySet
> xAdditionalPropSet
;
1750 bool bTriedToGetAdditionalPropSet
= false;
1752 bool bExchange
= false;
1755 sal_Int32 nTitlePos
= -1;
1757 uno::Reference
< beans::XPropertySetInfo
> xInfo
;
1759 const beans::PropertyValue
* pValues
= rValues
.getConstArray();
1760 sal_Int32 nCount
= rValues
.getLength();
1761 for ( sal_Int32 n
= 0; n
< nCount
; ++n
)
1763 const beans::PropertyValue
& rValue
= pValues
[ n
];
1764 const OUString
& rName
= rValue
.Name
;
1766 beans::Property aTmpProp
;
1767 xProvider
->getProperty( rName
, aTmpProp
);
1769 if ( aTmpProp
.Attributes
& beans::PropertyAttribute::READONLY
)
1771 // Read-only property!
1772 aRetRange
[ n
] <<= lang::IllegalAccessException(
1773 "Property is read-only!",
1774 static_cast< cppu::OWeakObject
* >( this ) );
1782 if ( rName
== "ContentType" )
1784 // Read-only property!
1785 aRetRange
[ n
] <<= lang::IllegalAccessException(
1786 "Property is read-only!",
1787 static_cast< cppu::OWeakObject
* >( this ) );
1789 else if ( rName
== "IsDocument" )
1791 // Read-only property!
1792 aRetRange
[ n
] <<= lang::IllegalAccessException(
1793 "Property is read-only!",
1794 static_cast< cppu::OWeakObject
* >( this ) );
1796 else if ( rName
== "IsFolder" )
1798 // Read-only property!
1799 aRetRange
[ n
] <<= lang::IllegalAccessException(
1800 "Property is read-only!",
1801 static_cast< cppu::OWeakObject
* >( this ) );
1803 else if ( rName
== "Title" )
1806 if ( rValue
.Value
>>= aNewValue
)
1809 if ( aNewValue
.getLength() > 0 )
1813 CurlUri
const aURI(xIdentifier
->getContentIdentifier());
1814 aOldTitle
= aURI
.GetPathBaseNameUnescaped();
1816 if ( aNewValue
!= aOldTitle
)
1818 // modified title -> modified URL -> exchange !
1822 // new value will be set later...
1823 aNewTitle
= aNewValue
;
1825 // remember position within sequence of values (for
1830 catch ( DAVException
const & )
1832 aRetRange
[ n
] <<= lang::IllegalArgumentException(
1833 "Invalid content identifier!",
1834 static_cast< cppu::OWeakObject
* >( this ),
1840 aRetRange
[ n
] <<= lang::IllegalArgumentException(
1841 "Empty title not allowed!",
1842 static_cast< cppu::OWeakObject
* >( this ),
1848 aRetRange
[ n
] <<= beans::IllegalTypeException(
1849 "Property value has wrong type!",
1850 static_cast< cppu::OWeakObject
* >( this ) );
1859 OUString aSpecialName
;
1860 bool bIsSpecial
= DAVProperties::isUCBSpecialProperty( rName
, aSpecialName
);
1863 xInfo
= getPropertySetInfo( xEnv
,
1864 false /* don't cache data */ );
1866 if ( !xInfo
->hasPropertyByName( bIsSpecial
? aSpecialName
: rName
) )
1868 // Check, whether property exists. Skip otherwise.
1869 // PROPPATCH::set would add the property automatically, which
1870 // is not allowed for "setPropertyValues" command!
1871 aRetRange
[ n
] <<= beans::UnknownPropertyException(
1872 "Property is unknown!",
1873 static_cast< cppu::OWeakObject
* >( this ) );
1877 if ( rName
== "Size" )
1879 // Read-only property!
1880 aRetRange
[ n
] <<= lang::IllegalAccessException(
1881 "Property is read-only!",
1882 static_cast< cppu::OWeakObject
* >( this ) );
1884 else if ( rName
== "DateCreated" )
1886 // Read-only property!
1887 aRetRange
[ n
] <<= lang::IllegalAccessException(
1888 "Property is read-only!",
1889 static_cast< cppu::OWeakObject
* >( this ) );
1891 else if ( rName
== "DateModified" )
1893 // Read-only property!
1894 aRetRange
[ n
] <<= lang::IllegalAccessException(
1895 "Property is read-only!",
1896 static_cast< cppu::OWeakObject
* >( this ) );
1898 else if ( rName
== "MediaType" )
1900 // Read-only property!
1901 // (but could be writable, if 'getcontenttype' would be)
1902 aRetRange
[ n
] <<= lang::IllegalAccessException(
1903 "Property is read-only!",
1904 static_cast< cppu::OWeakObject
* >( this ) );
1906 if ( rName
== "CreatableContentsInfo" )
1908 // Read-only property!
1909 aRetRange
[ n
] <<= lang::IllegalAccessException(
1910 "Property is read-only!",
1911 static_cast< cppu::OWeakObject
* >( this ) );
1915 if ( getResourceType( xEnv
, xResAccess
) == DAV
)
1917 // Property value will be set on server.
1918 ProppatchValue
aValue( PROPSET
, rName
, rValue
.Value
);
1919 aProppatchValues
.push_back( aValue
);
1921 // remember position within sequence of values (for
1923 aProppatchPropsPositions
.push_back( n
);
1927 // Property value will be stored in local property store.
1928 if ( !bTriedToGetAdditionalPropSet
&&
1929 !xAdditionalPropSet
.is() )
1932 = getAdditionalPropertySet( false );
1933 bTriedToGetAdditionalPropSet
= true;
1936 if ( xAdditionalPropSet
.is() )
1941 = xAdditionalPropSet
->getPropertyValue( rName
);
1942 if ( aOldValue
!= rValue
.Value
)
1944 xAdditionalPropSet
->setPropertyValue(
1945 rName
, rValue
.Value
);
1947 aEvent
.PropertyName
= rName
;
1948 aEvent
.OldValue
= aOldValue
;
1949 aEvent
.NewValue
= rValue
.Value
;
1951 aChanges
.getArray()[ nChanged
] = aEvent
;
1955 catch ( beans::UnknownPropertyException
const & e
)
1957 aRetRange
[ n
] <<= e
;
1959 catch ( lang::WrappedTargetException
const & e
)
1961 aRetRange
[ n
] <<= e
;
1963 catch ( beans::PropertyVetoException
const & e
)
1965 aRetRange
[ n
] <<= e
;
1967 catch ( lang::IllegalArgumentException
const & e
)
1969 aRetRange
[ n
] <<= e
;
1974 aRetRange
[ n
] <<= uno::Exception(
1975 "No property set for storing the value!",
1976 static_cast< cppu::OWeakObject
* >( this ) );
1983 if ( !bTransient
&& (!aProppatchValues
.empty()) )
1987 // clean cached value of PROPFIND property names
1988 // PROPPATCH can change them
1989 removeCachedPropertyNames( xResAccess
->getURL() );
1990 // Set property values at server.
1991 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
1992 xResAccess
->PROPPATCH( aProppatchValues
, xEnv
);
1994 for ( const auto& rProppatchValue
: aProppatchValues
)
1996 aEvent
.PropertyName
= rProppatchValue
.name
;
1997 aEvent
.OldValue
= uno::Any(); // @@@ too expensive to obtain!
1998 aEvent
.NewValue
= rProppatchValue
.value
;
2000 aChanges
.getArray()[ nChanged
] = aEvent
;
2004 catch ( DAVException
const & e
)
2006 // SAL_WARN( "ucb.ucp.webdav",
2007 // "Content::setPropertyValues - PROPPATCH failed!" );
2010 cancelCommandExecution( e
, xEnv
);
2013 // Note: PROPPATCH either sets ALL property values OR NOTHING.
2015 std::vector
< sal_Int32
>::const_iterator it
2016 = aProppatchPropsPositions
.begin();
2017 std::vector
< sal_Int32
>::const_iterator end
2018 = aProppatchPropsPositions
.end();
2023 aRetRange
[ (*it
) ] <<= MapDAVException( e
, true );
2032 // Assemble new content identifier...
2034 OUString aNewURL
= getParentURL();
2035 if ( aNewURL
.lastIndexOf( '/' ) != ( aNewURL
.getLength() - 1 ) )
2038 aNewURL
+= EncodeSegment(aNewTitle
);
2040 uno::Reference
< ucb::XContentIdentifier
> xNewId
2041 = new ::ucbhelper::ContentIdentifier( aNewURL
);
2042 uno::Reference
< ucb::XContentIdentifier
> xOldId
= xIdentifier
;
2046 CurlUri
const sourceURI( xOldId
->getContentIdentifier() );
2047 CurlUri
targetURI( xNewId
->getContentIdentifier() );
2049 targetURI
.SetScheme( sourceURI
.GetScheme() );
2051 // clean cached value of PROPFIND property names
2052 removeCachedPropertyNames( sourceURI
.GetURI() );
2053 removeCachedPropertyNames( targetURI
.GetURI() );
2054 aStaticDAVOptionsCache
.removeDAVOptions( sourceURI
.GetURI() );
2055 aStaticDAVOptionsCache
.removeDAVOptions( targetURI
.GetURI() );
2057 sourceURI
.GetRelativeReference(), targetURI
.GetURI(), false, xEnv
);
2059 // @@@ Should check for resources that could not be moved
2060 // (due to source access or target overwrite) and send
2061 // this information through the interaction handler.
2063 // @@@ Existing content should be checked to see if it needs
2064 // to be deleted at the source
2066 // @@@ Existing content should be checked to see if it has
2067 // been overwritten at the target
2069 if ( exchangeIdentity( xNewId
) )
2071 xResAccess
->setURL( aNewURL
);
2073 // DAV resources store all additional props on server!
2074 // // Adapt Additional Core Properties.
2075 // renameAdditionalPropertySet( xOldId->getContentIdentifier(),
2076 // xNewId->getContentIdentifier(),
2081 // Do not set new title!
2085 aRetRange
[ nTitlePos
] <<= uno::Exception(
2087 static_cast< cppu::OWeakObject
* >( this ) );
2090 catch ( DAVException
const & e
)
2092 // Do not set new title!
2096 aRetRange
[ nTitlePos
] = MapDAVException( e
, true );
2100 if ( aNewTitle
.getLength() )
2102 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2104 aEvent
.PropertyName
= "Title";
2105 aEvent
.OldValue
<<= aOldTitle
;
2106 aEvent
.NewValue
<<= aNewTitle
;
2108 m_aEscapedTitle
= EncodeSegment(aNewTitle
);
2110 aChanges
.getArray()[ nChanged
] = aEvent
;
2116 aChanges
.realloc( nChanged
);
2117 notifyPropertiesChange( aChanges
);
2121 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2122 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
2129 uno::Any
Content::open(
2130 const ucb::OpenCommandArgument3
& rArg
,
2131 const uno::Reference
< ucb::XCommandEnvironment
> & xEnv
)
2135 bool bOpenFolder
= ( ( rArg
.Mode
== ucb::OpenMode::ALL
) ||
2136 ( rArg
.Mode
== ucb::OpenMode::FOLDERS
) ||
2137 ( rArg
.Mode
== ucb::OpenMode::DOCUMENTS
) );
2140 if ( isFolder( xEnv
) )
2144 uno::Reference
< ucb::XDynamicResultSet
> xSet
2145 = new DynamicResultSet( m_xContext
, this, rArg
, xEnv
);
2150 // Error: Not a folder!
2152 ucbhelper::cancelCommandExecution(
2154 lang::IllegalArgumentException(
2155 "Non-folder resource cannot be opened as folder! Wrong Open Mode!",
2156 static_cast< cppu::OWeakObject
* >( this ),
2163 if ( rArg
.Sink
.is() )
2167 if ( ( rArg
.Mode
== ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE
) ||
2168 ( rArg
.Mode
== ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE
) )
2170 // Currently(?) unsupported.
2171 ucbhelper::cancelCommandExecution(
2173 ucb::UnsupportedOpenModeException(
2175 static_cast< cppu::OWeakObject
* >( this ),
2176 sal_Int16( rArg
.Mode
) ) ),
2181 uno::Reference
< io::XOutputStream
> xOut( rArg
.Sink
, uno::UNO_QUERY
);
2187 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2190 osl::MutexGuard
aGuard( m_aMutex
);
2193 new DAVResourceAccess( *m_xResAccess
) );
2196 xResAccess
->setFlags( rArg
.OpeningFlags
);
2197 DAVResource aResource
;
2198 std::vector
< OUString
> aHeaders
;
2200 removeCachedPropertyNames( xResAccess
->getURL() );
2201 xResAccess
->GET( xOut
, aHeaders
, aResource
, xEnv
);
2202 m_bDidGetOrHead
= true;
2205 osl::MutexGuard
aGuard( m_aMutex
);
2208 if (!m_xCachedProps
)
2209 m_xCachedProps
.reset(
2210 new CachableContentProperties( ContentProperties( aResource
) ) );
2212 m_xCachedProps
->addProperties( ContentProperties( aResource
) );
2215 new DAVResourceAccess( *xResAccess
) );
2218 catch ( DAVException
const & e
)
2220 cancelCommandExecution( e
, xEnv
);
2226 uno::Reference
< io::XActiveDataSink
> xDataSink( rArg
.Sink
, uno::UNO_QUERY
);
2227 if ( xDataSink
.is() )
2229 // PULL: wait for client read
2230 OUString aTargetURL
= m_xIdentifier
->getContentIdentifier();
2233 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2235 osl::MutexGuard
aGuard( m_aMutex
);
2238 new DAVResourceAccess( *m_xResAccess
) );
2240 xResAccess
->setFlags( rArg
.OpeningFlags
);
2242 // fill inputstream sync; return if all data present
2243 DAVResource aResource
;
2244 std::vector
< OUString
> aHeaders
;
2246 aTargetURL
= xResAccess
->getURL();
2247 removeCachedPropertyNames( aTargetURL
);
2248 // check if the resource was present on the server
2249 // first update it, if necessary
2250 // if the open is called directly, without the default open sequence,
2251 // e.g. the one used when opening a file looking for properties
2252 // first this call will have no effect, since OPTIONS would have already been called
2253 // as a consequence of getPropertyValues()
2254 DAVOptions aDAVOptions
;
2255 getResourceOptions( xEnv
, aDAVOptions
, xResAccess
);
2257 if (aDAVOptions
.getHttpResponseStatusCode() != SC_NONE
2258 // tdf#148426 fall back to GET in case of 500
2259 && aDAVOptions
.getHttpResponseStatusCode() != SC_INTERNAL_SERVER_ERROR
)
2261 // throws exception as if there was a server error, a DAV exception
2262 throw DAVException( DAVException::DAV_HTTP_ERROR
,
2263 aDAVOptions
.getHttpResponseStatusText(),
2264 aDAVOptions
.getHttpResponseStatusCode() );
2266 uno::Reference
< io::XInputStream
> xIn
2267 = xResAccess
->GET( aHeaders
, aResource
, xEnv
);
2268 m_bDidGetOrHead
= true;
2271 osl::MutexGuard
aGuard( m_aMutex
);
2274 if (!m_xCachedProps
)
2275 m_xCachedProps
.reset(
2276 new CachableContentProperties( ContentProperties( aResource
) ) );
2278 m_xCachedProps
->addProperties(
2279 aResource
.properties
);
2282 new DAVResourceAccess( *xResAccess
) );
2285 xDataSink
->setInputStream( xIn
);
2287 catch ( DAVException
const & e
)
2289 //TODO cache the http error if not yet cached
2290 cancelCommandExecution( e
, xEnv
);
2296 // Note: aOpenCommand.Sink may contain an XStream
2297 // implementation. Support for this type of
2298 // sink is optional...
2299 ucbhelper::cancelCommandExecution(
2301 ucb::UnsupportedDataSinkException(
2303 static_cast< cppu::OWeakObject
* >( this ),
2316 const ucb::PostCommandArgument2
& rArg
,
2317 const uno::Reference
< ucb::XCommandEnvironment
> & xEnv
)
2319 uno::Reference
< io::XActiveDataSink
> xSink( rArg
.Sink
, uno::UNO_QUERY
);
2324 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2326 osl::MutexGuard
aGuard( m_aMutex
);
2328 new DAVResourceAccess( *m_xResAccess
) );
2331 removeCachedPropertyNames( xResAccess
->getURL() );
2332 uno::Reference
< io::XInputStream
> xResult
2333 = xResAccess
->POST( rArg
.MediaType
,
2339 osl::MutexGuard
aGuard( m_aMutex
);
2341 new DAVResourceAccess( *xResAccess
) );
2344 xSink
->setInputStream( xResult
);
2346 catch ( DAVException
const & e
)
2348 cancelCommandExecution( e
, xEnv
, true );
2354 uno::Reference
< io::XOutputStream
> xResult( rArg
.Sink
, uno::UNO_QUERY
);
2359 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2361 osl::MutexGuard
aGuard( m_aMutex
);
2363 new DAVResourceAccess( *m_xResAccess
) );
2366 removeCachedPropertyNames( xResAccess
->getURL() );
2367 xResAccess
->POST( rArg
.MediaType
,
2374 osl::MutexGuard
aGuard( m_aMutex
);
2376 new DAVResourceAccess( *xResAccess
) );
2379 catch ( DAVException
const & e
)
2381 cancelCommandExecution( e
, xEnv
, true );
2387 ucbhelper::cancelCommandExecution(
2389 ucb::UnsupportedDataSinkException(
2391 static_cast< cppu::OWeakObject
* >( this ),
2400 void Content::queryChildren( ContentRefList
& rChildren
)
2402 // Obtain a list with a snapshot of all currently instantiated contents
2403 // from provider and extract the contents which are direct children
2406 ::ucbhelper::ContentRefList aAllContents
;
2407 m_xProvider
->queryExistingContents( aAllContents
);
2409 OUString aURL
= m_xIdentifier
->getContentIdentifier();
2410 sal_Int32 nURLPos
= aURL
.lastIndexOf( '/' );
2412 if ( nURLPos
!= ( aURL
.getLength() - 1 ) )
2414 // No trailing slash found. Append.
2418 sal_Int32 nLen
= aURL
.getLength();
2420 for ( const auto& rChild
: aAllContents
)
2422 ::ucbhelper::ContentImplHelperRef xChild
= rChild
;
2424 = xChild
->getIdentifier()->getContentIdentifier();
2426 // Is aURL a prefix of aChildURL?
2427 if ( ( aChildURL
.getLength() > nLen
) &&
2428 ( aChildURL
.startsWith( aURL
) ) )
2430 sal_Int32 nPos
= nLen
;
2431 nPos
= aChildURL
.indexOf( '/', nPos
);
2433 if ( ( nPos
== -1 ) ||
2434 ( nPos
== ( aChildURL
.getLength() - 1 ) ) )
2436 // No further slashes / only a final slash. It's a child!
2437 rChildren
.push_back(
2438 ::http_dav_ucp::Content::ContentRef(
2439 static_cast< ::http_dav_ucp::Content
* >(
2447 void Content::insert(
2448 const uno::Reference
< io::XInputStream
> & xInputStream
,
2449 bool bReplaceExisting
,
2450 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
2452 bool bTransient
, bCollection
;
2453 OUString aEscapedTitle
;
2454 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2457 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2459 bTransient
= m_bTransient
;
2460 bCollection
= m_bCollection
;
2461 aEscapedTitle
= m_aEscapedTitle
;
2462 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
2465 // Check, if all required properties are present.
2467 if ( aEscapedTitle
.isEmpty() )
2469 SAL_WARN( "ucb.ucp.webdav", "Content::insert - Title missing!" );
2471 uno::Sequence
<OUString
> aProps
{ "Title" };
2472 ucbhelper::cancelCommandExecution(
2473 uno::Any( ucb::MissingPropertiesException(
2475 static_cast< cppu::OWeakObject
* >( this ),
2481 if ( !bReplaceExisting
)
2483 /* [RFC 2616] - HTTP
2485 The PUT method requests that the enclosed entity be stored under the
2486 supplied Request-URI. If the Request-URI refers to an already
2487 existing resource, the enclosed entity SHOULD be considered as a
2488 modified version of the one residing on the origin server.
2491 /* [RFC 2518] - WebDAV
2493 MKCOL creates a new collection resource at the location specified by
2494 the Request-URI. If the resource identified by the Request-URI is
2495 non-null then the MKCOL MUST fail.
2498 // ==> Complain on PUT, continue on MKCOL.
2499 if ( !bTransient
|| !bCollection
)
2502 ucb::UnsupportedNameClashException
aEx(
2503 "Unable to write without overwrite!",
2504 static_cast< cppu::OWeakObject
* >( this ),
2505 ucb::NameClash::ERROR
);
2507 uno::Reference
< task::XInteractionHandler
> xIH
;
2509 if ( Environment
.is() )
2510 xIH
= Environment
->getInteractionHandler();
2514 uno::Any
aExAsAny( aEx
);
2516 rtl::Reference
< ucbhelper::SimpleInteractionRequest
> xRequest
2517 = new ucbhelper::SimpleInteractionRequest(
2519 ContinuationFlags::Approve
2520 | ContinuationFlags::Disapprove
);
2521 xIH
->handle( xRequest
);
2523 const ContinuationFlags nResp
= xRequest
->getResponse();
2527 case ContinuationFlags::NONE
:
2528 // Not handled; throw.
2532 case ContinuationFlags::Approve
:
2533 // Continue -> Overwrite.
2534 bReplaceExisting
= true;
2537 case ContinuationFlags::Disapprove
:
2539 throw ucb::CommandFailedException(
2541 uno::Reference
< uno::XInterface
>(),
2546 SAL_WARN( "ucb.ucp.webdav",
2547 "Content::insert - "
2548 "Unknown interaction selection!" );
2549 throw ucb::CommandFailedException(
2550 "Unknown interaction selection!",
2551 uno::Reference
< uno::XInterface
>(),
2566 // Assemble new content identifier...
2567 OUString aURL
= getParentURL();
2568 if ( aURL
.lastIndexOf( '/' ) != ( aURL
.getLength() - 1 ) )
2571 aURL
+= aEscapedTitle
;
2575 xResAccess
->setURL( aURL
);
2579 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
2580 removeCachedPropertyNames( xResAccess
->getURL() );
2581 xResAccess
->MKCOL( Environment
);
2585 // remove options from cache, PUT may change it
2586 // it will be refreshed when needed
2587 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
2588 removeCachedPropertyNames( xResAccess
->getURL() );
2589 xResAccess
->PUT( xInputStream
, Environment
);
2590 // clean cached value of PROPFIND properties names
2592 // no error , set the resourcetype to unknown type
2593 // the resource may have transitioned from NOT FOUND or UNKNOWN to something else
2594 // depending on the server behaviour
2595 // this will force a recheck of the resource type
2596 m_eResourceType
= UNKNOWN
;
2597 m_eResourceTypeForLocks
= UNKNOWN
;
2599 catch ( DAVException
const & except
)
2603 if ( except
.getStatus() == SC_METHOD_NOT_ALLOWED
)
2605 // [RFC 2518] - WebDAV
2606 // 405 (Method Not Allowed) - MKCOL can only be
2607 // executed on a deleted/non-existent resource.
2609 if ( bReplaceExisting
)
2611 // Destroy old resource.
2614 removeCachedPropertyNames( xResAccess
->getURL() );
2615 xResAccess
->DESTROY( Environment
);
2617 catch ( DAVException
const & e
)
2619 cancelCommandExecution( e
, Environment
, true );
2623 // Insert (recursion!).
2624 insert( xInputStream
, bReplaceExisting
, Environment
);
2627 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2629 new DAVResourceAccess( *xResAccess
) );
2640 CurlUri
const aURI( aURL
);
2641 aTitle
= aURI
.GetPathBaseNameUnescaped();
2643 catch ( DAVException
const & )
2647 ucbhelper::cancelCommandExecution(
2649 ucb::NameClashException(
2651 static_cast< cppu::OWeakObject
* >( this ),
2652 task::InteractionClassification_ERROR
,
2660 cancelCommandExecution( except
, Environment
, true );
2665 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2667 = new ::ucbhelper::ContentIdentifier( aURL
);
2673 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2674 m_bTransient
= false;
2679 if ( !xInputStream
.is() )
2681 ucbhelper::cancelCommandExecution(
2683 ucb::MissingInputStreamException(
2685 static_cast< cppu::OWeakObject
* >( this ) ) ),
2690 // save the URL since it may change due to redirection
2691 OUString aTargetUrl
= xResAccess
->getURL();
2694 removeCachedPropertyNames( xResAccess
->getURL() );
2695 // remove options from cache, PUT may change it
2696 // it will be refreshed when needed
2697 aStaticDAVOptionsCache
.removeDAVOptions( aTargetUrl
);
2698 xResAccess
->PUT( xInputStream
, Environment
);
2700 catch ( DAVException
const & e
)
2702 cancelCommandExecution( e
, Environment
, true );
2708 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2709 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
2714 void Content::transfer(
2715 const ucb::TransferInfo
& rArgs
,
2716 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
2718 uno::Reference
< uno::XComponentContext
> xContext
;
2719 uno::Reference
< ucb::XContentIdentifier
> xIdentifier
;
2720 uno::Reference
< ucb::XContentProvider
> xProvider
;
2721 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2724 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2726 xContext
.set( m_xContext
);
2727 xIdentifier
.set( m_xIdentifier
);
2728 xProvider
.set( m_xProvider
);
2729 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
2732 OUString aTargetURI
;
2735 CurlUri
sourceURI( rArgs
.SourceURL
);
2736 CurlUri
targetURI( xIdentifier
->getContentIdentifier() );
2738 aTargetURI
= targetURI
.GetPathBaseNameUnescaped();
2740 // Check source's and target's URL scheme
2742 OUString aScheme
= sourceURI
.GetScheme().toAsciiLowerCase();
2743 if ( aScheme
== VNDSUNSTARWEBDAV_URL_SCHEME
)
2745 sourceURI
.SetScheme( HTTP_URL_SCHEME
);
2747 else if ( aScheme
== VNDSUNSTARWEBDAVS_URL_SCHEME
)
2749 sourceURI
.SetScheme( HTTPS_URL_SCHEME
);
2751 else if ( aScheme
== DAV_URL_SCHEME
)
2753 sourceURI
.SetScheme( HTTP_URL_SCHEME
);
2755 else if ( aScheme
== DAVS_URL_SCHEME
)
2757 sourceURI
.SetScheme( HTTPS_URL_SCHEME
);
2759 else if (aScheme
== WEBDAV_URL_SCHEME
)
2761 sourceURI
.SetScheme(HTTP_URL_SCHEME
);
2763 else if (aScheme
== WEBDAVS_URL_SCHEME
)
2765 sourceURI
.SetScheme(HTTPS_URL_SCHEME
);
2769 if ( aScheme
!= HTTP_URL_SCHEME
&& aScheme
!= HTTPS_URL_SCHEME
)
2771 ucbhelper::cancelCommandExecution(
2773 ucb::InteractiveBadTransferURLException(
2774 "Unsupported URL scheme!",
2775 static_cast< cppu::OWeakObject
* >( this ) ) ),
2781 aScheme
= targetURI
.GetScheme().toAsciiLowerCase();
2782 if ( aScheme
== VNDSUNSTARWEBDAV_URL_SCHEME
)
2783 targetURI
.SetScheme( HTTP_URL_SCHEME
);
2784 else if ( aScheme
== VNDSUNSTARWEBDAVS_URL_SCHEME
)
2785 targetURI
.SetScheme( HTTPS_URL_SCHEME
);
2786 else if ( aScheme
== DAV_URL_SCHEME
)
2787 targetURI
.SetScheme( HTTP_URL_SCHEME
);
2788 else if ( aScheme
== DAVS_URL_SCHEME
)
2789 targetURI
.SetScheme( HTTPS_URL_SCHEME
);
2790 else if (aScheme
== WEBDAV_URL_SCHEME
)
2791 targetURI
.SetScheme(HTTP_URL_SCHEME
);
2792 else if (aScheme
== WEBDAVS_URL_SCHEME
)
2793 targetURI
.SetScheme(HTTPS_URL_SCHEME
);
2795 // @@@ This implementation of 'transfer' only works
2796 // if the source and target are located at same host.
2797 // (Neon does not support cross-server copy/move)
2799 // Check for same host
2801 if ( sourceURI
.GetHost().getLength() &&
2802 ( sourceURI
.GetHost() != targetURI
.GetHost() ) )
2804 ucbhelper::cancelCommandExecution(
2805 uno::Any( ucb::InteractiveBadTransferURLException(
2807 static_cast< cppu::OWeakObject
* >( this ) ) ),
2812 OUString aTitle
= rArgs
.NewTitle
;
2814 if ( aTitle
.isEmpty() )
2815 aTitle
= sourceURI
.GetPathBaseNameUnescaped();
2817 if ( aTitle
== "/" )
2823 targetURI
.AppendPath( aTitle
);
2825 OUString aTargetURL
= xIdentifier
->getContentIdentifier();
2826 if ( ( aTargetURL
.lastIndexOf( '/' ) + 1 )
2827 != aTargetURL
.getLength() )
2830 aTargetURL
+= aTitle
;
2832 uno::Reference
< ucb::XContentIdentifier
> xTargetId
2833 = new ::ucbhelper::ContentIdentifier( aTargetURL
);
2835 DAVResourceAccess
aSourceAccess( xContext
,
2836 xResAccess
->getSessionFactory(),
2837 sourceURI
.GetURI() );
2839 if ( rArgs
.MoveData
)
2841 uno::Reference
< ucb::XContentIdentifier
> xId
2842 = new ::ucbhelper::ContentIdentifier( rArgs
.SourceURL
);
2844 // Note: The static cast is okay here, because its sure that
2845 // xProvider is always the WebDAVContentProvider.
2846 rtl::Reference
< Content
> xSource
2847 = static_cast< Content
* >(
2848 xProvider
->queryContent( xId
).get() );
2850 // [RFC 2518] - WebDAV
2851 // If a resource exists at the destination and the Overwrite
2852 // header is "T" then prior to performing the move the server
2853 // MUST perform a DELETE with "Depth: infinity" on the
2854 // destination resource. If the Overwrite header is set to
2855 // "F" then the operation will fail.
2857 aStaticDAVOptionsCache
.removeDAVOptions( sourceURI
.GetURI() );
2858 aStaticDAVOptionsCache
.removeDAVOptions( targetURI
.GetURI() );
2859 aSourceAccess
.MOVE( sourceURI
.GetRelativeReference(),
2862 == ucb::NameClash::OVERWRITE
,
2867 // Propagate destruction to listeners.
2868 xSource
->destroy( true );
2871 // DAV resources store all additional props on server!
2872 // // Rename own and all children's Additional Core Properties.
2873 // renameAdditionalPropertySet( xId->getContentIdentifier(),
2874 // xTargetId->getContentIdentifier(),
2879 // [RFC 2518] - WebDAV
2880 // If a resource exists at the destination and the Overwrite
2881 // header is "T" then prior to performing the copy the server
2882 // MUST perform a DELETE with "Depth: infinity" on the
2883 // destination resource. If the Overwrite header is set to
2884 // "F" then the operation will fail.
2886 aStaticDAVOptionsCache
.removeDAVOptions( sourceURI
.GetURI() );
2887 aStaticDAVOptionsCache
.removeDAVOptions( targetURI
.GetURI() );
2888 aSourceAccess
.COPY( sourceURI
.GetRelativeReference(),
2891 == ucb::NameClash::OVERWRITE
,
2894 // DAV resources store all additional props on server!
2895 // // Copy own and all children's Additional Core Properties.
2896 // copyAdditionalPropertySet( xId->getContentIdentifier(),
2897 // xTargetId->getContentIdentifier(),
2901 // Note: The static cast is okay here, because its sure that
2902 // xProvider is always the WebDAVContentProvider.
2903 rtl::Reference
< Content
> xTarget
2904 = static_cast< Content
* >(
2905 xProvider
->queryContent( xTargetId
).get() );
2907 // Announce transferred content in its new folder.
2908 xTarget
->inserted();
2910 catch ( ucb::IllegalIdentifierException
const & )
2914 catch ( DAVException
const & e
)
2916 // [RFC 2518] - WebDAV
2917 // 412 (Precondition Failed) - The server was unable to maintain
2918 // the liveness of the properties listed in the propertybehavior
2919 // XML element or the Overwrite header is "F" and the state of
2920 // the destination resource is non-null.
2922 if ( e
.getStatus() == SC_PRECONDITION_FAILED
)
2924 switch ( rArgs
.NameClash
)
2926 case 0/*ucb::NameClash::ERROR*/:
2928 ucbhelper::cancelCommandExecution(
2930 ucb::NameClashException(
2932 static_cast< cppu::OWeakObject
* >( this ),
2933 task::InteractionClassification_ERROR
,
2940 case ucb::NameClash::OVERWRITE
:
2943 case ucb::NameClash::KEEP
: // deprecated
2944 case ucb::NameClash::RENAME
:
2945 case ucb::NameClash::ASK
:
2948 ucbhelper::cancelCommandExecution(
2950 ucb::UnsupportedNameClashException(
2952 static_cast< cppu::OWeakObject
* >( this ),
2953 rArgs
.NameClash
) ),
2960 cancelCommandExecution( e
, Environment
, true );
2965 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2966 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
2971 void Content::destroy( bool bDeletePhysical
)
2973 // @@@ take care about bDeletePhysical -> trashcan support
2975 uno::Reference
< ucb::XContent
> xThis
= this;
2979 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2981 // Process instantiated children...
2983 ::http_dav_ucp::Content::ContentRefList aChildren
;
2984 queryChildren( aChildren
);
2986 for ( auto& rChild
: aChildren
)
2988 rChild
->destroy( bDeletePhysical
);
2992 // returns the resource type, to be checked for locks
2993 Content::ResourceType
Content::resourceTypeForLocks(
2994 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
,
2995 const std::unique_ptr
< DAVResourceAccess
> & rResAccess
)
2997 ResourceType eResourceTypeForLocks
= UNKNOWN
;
2999 osl::MutexGuard
g(m_aMutex
);
3000 //check if cache contains what we need, usually the first PROPFIND on the URI has supported lock
3003 uno::Sequence
< ucb::LockEntry
> aSupportedLocks
;
3004 if ( m_xCachedProps
->getValue( DAVProperties::SUPPORTEDLOCK
)
3005 >>= aSupportedLocks
) //get the cached value for supportedlock
3007 for ( sal_Int32 n
= 0; n
< aSupportedLocks
.getLength(); ++n
)
3009 if ( aSupportedLocks
[ n
].Scope
3010 == ucb::LockScope_EXCLUSIVE
&&
3011 aSupportedLocks
[ n
].Type
3012 == ucb::LockType_WRITE
)
3013 eResourceTypeForLocks
= DAV
;
3019 const OUString
& rURL
= m_xIdentifier
->getContentIdentifier();
3021 if ( eResourceTypeForLocks
== UNKNOWN
)
3023 // resource type for lock/unlock operations still unknown, need to ask the server
3026 DAVOptions aDAVOptions
;
3027 getResourceOptions( Environment
, aDAVOptions
, rResAccess
);
3028 if( aDAVOptions
.isClass1() ||
3029 aDAVOptions
.isClass2() ||
3030 aDAVOptions
.isClass3() )
3032 // this is at least a DAV, lock to be confirmed
3033 // class 2 is needed for full lock support
3035 // <https://tools.ietf.org/html/rfc4918#section-18.2>
3036 eResourceTypeForLocks
= DAV_NOLOCK
;
3037 if( aDAVOptions
.isClass2() )
3039 // ok, possible lock, check for it
3042 // we need only DAV:supportedlock
3043 std::vector
< DAVResource
> resources
;
3044 std::vector
< OUString
> aPropNames
;
3045 uno::Sequence
< beans::Property
> aProperties( 1 );
3046 aProperties
.getArray()[ 0 ].Name
= DAVProperties::SUPPORTEDLOCK
;
3048 ContentProperties::UCBNamesToDAVNames( aProperties
, aPropNames
);
3049 rResAccess
->PROPFIND( DAVZERO
, aPropNames
, resources
, Environment
);
3051 bool wasSupportedlockFound
= false;
3053 // only one resource should be returned
3054 if ( resources
.size() == 1 )
3056 // we may have received a bunch of other properties
3057 // (some servers seems to do so)
3058 // but we need only supported lock for this check
3059 // all returned properties are in
3060 // resources.properties[n].Name/.Value
3062 std::vector
< DAVPropertyValue
>::iterator it
;
3064 for ( it
= resources
[0].properties
.begin();
3065 it
!= resources
[0].properties
.end(); ++it
)
3067 if ( (*it
).Name
== DAVProperties::SUPPORTEDLOCK
)
3069 wasSupportedlockFound
= true;
3070 uno::Sequence
< ucb::LockEntry
> aSupportedLocks
;
3071 if ( (*it
).Value
>>= aSupportedLocks
)
3073 for ( sal_Int32 n
= 0; n
< aSupportedLocks
.getLength(); ++n
)
3075 // TODO: if the lock type is changed from 'exclusive write' to 'shared write'
3076 // e.g. to implement 'Calc shared file feature', the ucb::LockScope_EXCLUSIVE
3077 // value should be checked as well, adaptation the code may be needed
3078 if ( aSupportedLocks
[ n
].Scope
== ucb::LockScope_EXCLUSIVE
&&
3079 aSupportedLocks
[ n
].Type
== ucb::LockType_WRITE
)
3081 // requested locking mode is supported
3082 eResourceTypeForLocks
= DAV
;
3083 SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks - URL: <"
3084 << m_xIdentifier
->getContentIdentifier() << ">, DAV lock/unlock supported");
3095 // PROPFIND failed; check if HEAD contains Content-Disposition: attachment (RFC1806, HTTP/1.1 19.5.1),
3096 // which supposedly means no lock for the resource (happens e.g. with SharePoint exported lists)
3097 OUString sContentDisposition
;
3098 // First, check cached properties
3101 if ((m_xCachedProps
->getValue("Content-Disposition") >>= sContentDisposition
)
3102 && sContentDisposition
.startsWithIgnoreAsciiCase("attachment"))
3104 eResourceTypeForLocks
= DAV_NOLOCK
;
3105 wasSupportedlockFound
= true;
3108 // If no data in cache, try HEAD request
3109 if (sContentDisposition
.isEmpty() && !m_bDidGetOrHead
) try
3111 DAVResource resource
;
3112 GetPropsUsingHeadRequest(resource
, rResAccess
, {"Content-Disposition"}, Environment
);
3113 m_bDidGetOrHead
= true;
3114 for (const auto& it
: resource
.properties
)
3116 if (it
.Name
.equalsIgnoreAsciiCase("Content-Disposition"))
3118 if ((it
.Value
>>= sContentDisposition
) && sContentDisposition
.equalsIgnoreAsciiCase("attachment"))
3120 eResourceTypeForLocks
= DAV_NOLOCK
;
3121 wasSupportedlockFound
= true;
3129 // check if this is still only a DAV_NOLOCK
3130 // a fallback for resources that do not have DAVProperties::SUPPORTEDLOCK property
3131 // we check for the returned OPTION if LOCK is allowed on the resource
3132 if ( !wasSupportedlockFound
&& eResourceTypeForLocks
== DAV_NOLOCK
)
3134 SAL_INFO( "ucb.ucp.webdav", "This WebDAV server has no supportedlock property, check for allowed LOCK method in OPTIONS" );
3135 // ATTENTION: if the lock type is changed from 'exclusive write' to 'shared write'
3136 // e.g. to implement 'Calc shared file feature' on WebDAV directly, and we arrive to this fallback
3137 // and the LOCK is allowed, we should assume that only exclusive write lock is available
3138 // this is just a reminder...
3139 if ( aDAVOptions
.isLockAllowed() )
3140 eResourceTypeForLocks
= DAV
;
3143 catch ( DAVException
const & e
)
3145 rResAccess
->resetUri();
3146 //grab the error code
3147 switch( e
.getStatus() )
3150 SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
3151 << m_xIdentifier
->getContentIdentifier() << "> was not found. ");
3152 eResourceTypeForLocks
= NOT_FOUND
;
3154 // some servers returns SC_FORBIDDEN, instead
3155 // the meaning of SC_FORBIDDEN is, according to <http://tools.ietf.org/html/rfc7231#section-6.5.3>:
3156 // The 403 (Forbidden) status code indicates that the server understood
3157 // the request but refuses to authorize it
3159 // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3160 // part of base http 1.1 RFCs
3161 case SC_NOT_IMPLEMENTED
: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3162 case SC_METHOD_NOT_ALLOWED
: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3163 // they all mean the resource is NON_DAV
3164 SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException (SC_FORBIDDEN, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3165 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3166 eResourceTypeForLocks
= NON_DAV
;
3170 SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException - URL: <"
3171 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3172 eResourceTypeForLocks
= UNKNOWN
;
3178 eResourceTypeForLocks
= NON_DAV
;
3182 osl::MutexGuard
g(m_aMutex
);
3183 if (m_eResourceTypeForLocks
== UNKNOWN
)
3185 m_eResourceTypeForLocks
= eResourceTypeForLocks
;
3190 eResourceTypeForLocks
!= m_eResourceTypeForLocks
, "ucb.ucp.webdav",
3191 "different resource types for <" << rURL
<< ">: "
3192 << +eResourceTypeForLocks
<< " vs. " << +m_eResourceTypeForLocks
);
3194 SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
3195 << m_xIdentifier
->getContentIdentifier() << ">, m_eResourceTypeForLocks: " << m_eResourceTypeForLocks
);
3196 return m_eResourceTypeForLocks
;
3199 Content::ResourceType
Content::resourceTypeForLocks(
3200 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
3202 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
3204 osl::MutexGuard
aGuard( m_aMutex
);
3205 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
3207 Content::ResourceType ret
= resourceTypeForLocks( Environment
, xResAccess
);
3209 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3210 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
3216 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
3218 // prepare aURL to be used in exception, see below
3222 aURL
= getParentURL();
3223 if ( aURL
.lastIndexOf('/') != ( aURL
.getLength() - 1 ) )
3226 aURL
+= m_aEscapedTitle
;
3230 aURL
= m_xIdentifier
->getContentIdentifier();
3235 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
3237 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3238 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
3242 OUString
const user(officecfg::Office::Common::Save::Document::UseUserData::get()
3243 ? " - " + ::svt::LockFileCommon::GetOOOUserName()
3245 aOwnerAny
<<= OUString("LibreOffice" + user
);
3248 ucb::LockScope_EXCLUSIVE
,
3249 ucb::LockType_WRITE
,
3250 ucb::LockDepth_ZERO
,
3252 180, // lock timeout in secs
3253 //-1, // infinite lock
3254 uno::Sequence
< OUString
>() );
3256 // OPTIONS may change as a consequence of the lock operation
3257 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
3258 removeCachedPropertyNames( xResAccess
->getURL() );
3259 xResAccess
->LOCK( aLock
, Environment
);
3262 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3263 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
3266 catch ( DAVException
const & e
)
3268 // check if the exception thrown is 'already locked'
3269 // this exception is mapped directly to the ucb correct one, without
3270 // going into the cancelCommandExecution() user interaction
3271 // this exception should be managed by the issuer of 'lock' command
3272 switch( e
.getError() )
3274 case DAVException::DAV_LOCKED
:
3276 SAL_WARN( "ucb.ucp.webdav", "lock(): resource already locked - URL: <"
3277 << m_xIdentifier
->getContentIdentifier() << ">");
3279 ucb::InteractiveLockingLockedException(
3281 static_cast< cppu::OWeakObject
* >( this ),
3282 task::InteractionClassification_ERROR
,
3287 case DAVException::DAV_HTTP_NOAUTH
:
3288 case DAVException::DAV_HTTP_AUTH
:
3290 SAL_WARN( "ucb.ucp.webdav", "lock(): DAVException Authentication error - URL: <"
3291 << m_xIdentifier
->getContentIdentifier() << ">" );
3292 // DAVException::DAV_HTTP_AUTH exception can mean:
3293 // - interaction handler for credential management not present (happens, depending
3294 // on the LO framework processing)
3295 // - the remote site is a WebDAV with special configuration: read/only for read operations
3296 // and read/write for write operations, the user is not allowed to lock/write and
3297 // she cancelled the credentials request.
3298 // this is not actually an error, but the exception is sent directly from here, avoiding the automatic
3299 // management that takes part in cancelCommandExecution() below
3300 // Unfortunately there is no InteractiveNetwork*Exception available to signal this
3301 // since it mostly happens on read/only part of webdav, this appears to be the most correct exception available
3303 ucb::InteractiveNetworkWriteException(
3304 "Authentication error while trying to lock! Write only WebDAV perhaps?",
3305 static_cast< cppu::OWeakObject
* >( this ),
3306 task::InteractionClassification_ERROR
,
3310 case DAVException::DAV_HTTP_ERROR
:
3311 //grab the error code
3312 switch( e
.getStatus() )
3314 // The 'case SC_NOT_FOUND' just below tries to solve a problem in eXo Platform
3315 // WebDAV connector which apparently fail on resource first creation
3316 // rfc4918 section-7.3 (see link below)
3317 case SC_NOT_FOUND
: // <http://tools.ietf.org/html/rfc7231#section-6.5.4>
3318 // The 'case SC_PRECONDITION_FAILED' just below tries to solve a problem
3319 // in SharePoint when locking the resource on first creation fails due to this:
3320 // <https://msdn.microsoft.com/en-us/library/jj575265%28v=office.12%29.aspx#id15>
3321 // (retrieved on 2015-08-14)
3322 case SC_PRECONDITION_FAILED
: // <http://tools.ietf.org/html/rfc7232#section-4.2>
3323 // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3324 // part of base http 1.1 RFCs
3325 case SC_NOT_IMPLEMENTED
: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3326 case SC_METHOD_NOT_ALLOWED
: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3327 SAL_WARN( "ucb.ucp.webdav", "lock() DAVException (SC_NOT_FOUND, SC_PRECONDITION_FAILED, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3328 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3329 // act as nothing happened
3330 // that's because when a resource is first created
3331 // the lock is sent before the put, so the resource
3332 // is actually created by LOCK, locking it before
3333 // the first PUT, but if LOCK is not supported
3334 // (simple web or DAV with lock disabled) we end with one of these http
3336 // These same errors may be reported when the LOCK on an unmapped
3337 // (i.e. non existent) resource is not implemented.
3338 // Detailed specification in:
3339 // <http://tools.ietf.org/html/rfc4918#section-7.3>
3346 case DAVException::DAV_LOCKED_SELF
:
3347 // we already hold the lock and it is in our internal lockstore
3348 // just return as if the lock was successful
3355 SAL_WARN( "ucb.ucp.webdav","lock() DAVException - URL: <"
3356 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3357 cancelCommandExecution( e
, Environment
, false );
3363 void Content::unlock(
3364 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
3369 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
3371 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3372 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
3375 // check if the target URL is a Class1 DAV
3376 DAVOptions aDAVOptions
;
3377 getResourceOptions( Environment
, aDAVOptions
, xResAccess
);
3379 // at least class one is needed
3380 if( aDAVOptions
.isClass1() )
3382 // remove options from cache, unlock may change it
3383 // it will be refreshed when needed
3384 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
3385 // clean cached value of PROPFIND properties names
3386 removeCachedPropertyNames( xResAccess
->getURL() );
3387 xResAccess
->UNLOCK( Environment
);
3391 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3392 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
3395 catch ( DAVException
const & e
)
3397 switch( e
.getError() )
3399 case DAVException::DAV_NOT_LOCKED
:
3400 SAL_WARN( "ucb.ucp.webdav", "unlock(): DAVException::DAV_NOT_LOCKED - URL: <"
3401 << m_xIdentifier
->getContentIdentifier() << ">");
3402 // means that we don't own any lock on this resource
3403 // intercepted here to remove a confusing indication to the user
3404 // unfortunately this happens in some WebDAV server configuration
3405 // acting as WebDAV and having lock/unlock enabled only
3406 // for authorized user.
3408 case DAVException::DAV_HTTP_ERROR
:
3409 //grab the error code
3410 switch( e
.getStatus() )
3412 // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3413 // part of base http 1.1 RFCs
3414 case SC_NOT_IMPLEMENTED
: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3415 case SC_METHOD_NOT_ALLOWED
: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3416 SAL_WARN( "ucb.ucp.webdav", "unlock() DAVException (SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3417 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3428 SAL_WARN( "ucb.ucp.webdav","unlock() DAVException - URL: <"
3429 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3430 cancelCommandExecution( e
, Environment
, false );
3436 bool Content::exchangeIdentity(
3437 const uno::Reference
< ucb::XContentIdentifier
>& xNewId
)
3442 osl::ClearableGuard
< osl::Mutex
> aGuard( m_aMutex
);
3444 uno::Reference
< ucb::XContent
> xThis
= this;
3446 // Already persistent?
3449 SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - Not persistent!" );
3453 // Exchange own identity.
3455 // Fail, if a content with given id already exists.
3456 // if ( !hasData( xNewId ) )
3458 OUString aOldURL
= m_xIdentifier
->getContentIdentifier();
3461 if ( exchange( xNewId
) )
3463 // Process instantiated children...
3465 ContentRefList aChildren
;
3466 queryChildren( aChildren
);
3468 for ( const auto& rChild
: aChildren
)
3470 ContentRef xChild
= rChild
;
3472 // Create new content identifier for the child...
3473 uno::Reference
< ucb::XContentIdentifier
>
3474 xOldChildId
= xChild
->getIdentifier();
3475 OUString aOldChildURL
3476 = xOldChildId
->getContentIdentifier();
3477 OUString aNewChildURL
3478 = aOldChildURL
.replaceAt(
3480 aOldURL
.getLength(),
3481 xNewId
->getContentIdentifier() );
3482 uno::Reference
< ucb::XContentIdentifier
> xNewChildId
3483 = new ::ucbhelper::ContentIdentifier( aNewChildURL
);
3485 if ( !xChild
->exchangeIdentity( xNewChildId
) )
3492 SAL_WARN( "ucb.ucp.webdav",
3493 "Content::exchangeIdentity - "
3494 "Panic! Cannot exchange identity!" );
3499 bool Content::isFolder(
3500 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
3503 osl::MutexGuard
aGuard( m_aMutex
);
3506 return m_bCollection
;
3509 uno::Sequence
< beans::Property
> aProperties( 1 );
3510 auto pProperties
= aProperties
.getArray();
3511 pProperties
[ 0 ].Name
= "IsFolder";
3512 pProperties
[ 0 ].Handle
= -1;
3513 uno::Reference
< sdbc::XRow
> xRow( getPropertyValues( aProperties
, xEnv
) );
3518 return xRow
->getBoolean( 1 );
3520 catch ( sdbc::SQLException
const & )
3529 uno::Any
Content::MapDAVException( const DAVException
& e
, bool bWrite
)
3531 // Map DAVException...
3532 uno::Any aException
;
3537 aURL
= getParentURL();
3538 if ( aURL
.lastIndexOf( '/' ) != ( aURL
.getLength() - 1 ) )
3541 aURL
+= m_aEscapedTitle
;
3545 aURL
= m_xIdentifier
->getContentIdentifier();
3548 switch ( e
.getStatus() )
3552 uno::Sequence
<uno::Any
> aArgs
{ uno::Any(beans::PropertyValue(
3553 "Uri", -1, uno::Any(aURL
), beans::PropertyState_DIRECT_VALUE
)) };
3556 ucb::InteractiveAugmentedIOException(
3558 static_cast< cppu::OWeakObject
* >( this ),
3559 task::InteractionClassification_ERROR
,
3560 ucb::IOErrorCode_NOT_EXISTING
,
3568 switch ( e
.getError() )
3570 case DAVException::DAV_HTTP_ERROR
:
3574 ucb::InteractiveNetworkWriteException(
3576 static_cast< cppu::OWeakObject
* >( this ),
3577 task::InteractionClassification_ERROR
,
3581 ucb::InteractiveNetworkReadException(
3583 static_cast< cppu::OWeakObject
* >( this ),
3584 task::InteractionClassification_ERROR
,
3589 case DAVException::DAV_HTTP_LOOKUP
:
3591 ucb::InteractiveNetworkResolveNameException(
3593 static_cast< cppu::OWeakObject
* >( this ),
3594 task::InteractionClassification_ERROR
,
3598 // @@@ No matching InteractiveNetwork*Exception
3599 // case DAVException::DAV_HTTP_AUTH:
3602 // @@@ No matching InteractiveNetwork*Exception
3603 // case DAVException::DAV_HTTP_AUTHPROXY:
3606 case DAVException::DAV_HTTP_TIMEOUT
:
3607 case DAVException::DAV_HTTP_CONNECT
:
3609 ucb::InteractiveNetworkConnectException(
3611 static_cast< cppu::OWeakObject
* >( this ),
3612 task::InteractionClassification_ERROR
,
3616 // @@@ No matching InteractiveNetwork*Exception
3617 // case DAVException::DAV_HTTP_REDIRECT:
3620 // @@@ No matching InteractiveNetwork*Exception
3621 // case DAVException::DAV_SESSION_CREATE:
3624 case DAVException::DAV_INVALID_ARG
:
3626 lang::IllegalArgumentException(
3628 static_cast< cppu::OWeakObject
* >( this ),
3632 case DAVException::DAV_LOCKED
:
3635 ucb::InteractiveLockingLockedException(
3637 static_cast< cppu::OWeakObject
* >( this ),
3638 task::InteractionClassification_ERROR
,
3640 false ); // not SelfOwned
3643 uno::Sequence
< uno::Any
> aArgs( 1 );
3644 aArgs
[ 0 ] <<= beans::PropertyValue(
3645 OUString("Uri"), -1,
3647 beans::PropertyState_DIRECT_VALUE
);
3650 ucb::InteractiveAugmentedIOException(
3651 OUString( "Locked!" ),
3652 static_cast< cppu::OWeakObject
* >( this ),
3653 task::InteractionClassification_ERROR
,
3654 ucb::IOErrorCode_LOCKING_VIOLATION
,
3660 case DAVException::DAV_LOCKED_SELF
:
3662 ucb::InteractiveLockingLockedException(
3664 static_cast< cppu::OWeakObject
* >( this ),
3665 task::InteractionClassification_ERROR
,
3667 true ); // SelfOwned
3670 case DAVException::DAV_NOT_LOCKED
:
3672 ucb::InteractiveLockingNotLockedException(
3674 static_cast< cppu::OWeakObject
* >( this ),
3675 task::InteractionClassification_ERROR
,
3679 case DAVException::DAV_LOCK_EXPIRED
:
3681 ucb::InteractiveLockingLockExpiredException(
3683 static_cast< cppu::OWeakObject
* >( this ),
3684 task::InteractionClassification_ERROR
,
3690 ucb::InteractiveNetworkGeneralException(
3692 static_cast< cppu::OWeakObject
* >( this ),
3693 task::InteractionClassification_ERROR
);
3702 bool Content::shouldAccessNetworkAfterException( const DAVException
& e
)
3704 if ( ( e
.getStatus() == SC_NOT_FOUND
) ||
3705 ( e
.getStatus() == SC_GONE
) ||
3706 ( e
.getError() == DAVException::DAV_HTTP_TIMEOUT
) ||
3707 ( e
.getError() == DAVException::DAV_HTTP_LOOKUP
) ||
3708 ( e
.getError() == DAVException::DAV_HTTP_CONNECT
) ||
3709 ( e
.getError() == DAVException::DAV_HTTP_NOAUTH
) ||
3710 ( e
.getError() == DAVException::DAV_HTTP_AUTH
) ||
3711 ( e
.getError() == DAVException::DAV_HTTP_AUTHPROXY
) )
3718 void Content::cancelCommandExecution(
3719 const DAVException
& e
,
3720 const uno::Reference
< ucb::XCommandEnvironment
> & xEnv
,
3721 bool bWrite
/* = false */ )
3723 ucbhelper::cancelCommandExecution( MapDAVException( e
, bWrite
), xEnv
);
3729 Content::getBaseURI( const std::unique_ptr
< DAVResourceAccess
> & rResAccess
)
3731 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3733 // First, try to obtain value of response header "Content-Location".
3737 m_xCachedProps
->getValue( "Content-Location" ) >>= aLocation
;
3738 if ( aLocation
.getLength() )
3742 // Do not use m_xIdentifier->getContentIdentifier() because it
3743 // for example does not reflect redirects applied to requests
3744 // done using the original URI but m_xResAccess' URI does.
3745 return rtl::Uri::convertRelToAbs( rResAccess
->getURL(),
3748 catch ( rtl::MalformedUriException
const & )
3754 return rResAccess
->getURL();
3757 // resource type is the type of the WebDAV resource
3758 Content::ResourceType
Content::getResourceType(
3759 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
,
3760 const std::unique_ptr
< DAVResourceAccess
> & rResAccess
,
3761 bool * networkAccessAllowed
)
3764 osl::MutexGuard
g(m_aMutex
);
3765 if (m_eResourceType
!= UNKNOWN
) {
3766 return m_eResourceType
;
3770 ResourceType eResourceType
= UNKNOWN
;
3771 DAVOptions aDAVOptions
;
3774 getResourceOptions( xEnv
, aDAVOptions
, rResAccess
, networkAccessAllowed
);
3776 // at least class one is needed
3777 if( aDAVOptions
.isClass1() )
3781 // Try to fetch some frequently used property value, e.g. those
3782 // used when loading documents... along with identifying whether
3783 // this is a DAV resource.
3784 std::vector
< DAVResource
> resources
;
3785 std::vector
< OUString
> aPropNames
;
3786 uno::Sequence
< beans::Property
> aProperties( 5 );
3787 auto pProperties
= aProperties
.getArray();
3788 pProperties
[ 0 ].Name
= "IsFolder";
3789 pProperties
[ 1 ].Name
= "IsDocument";
3790 pProperties
[ 2 ].Name
= "IsReadOnly";
3791 pProperties
[ 3 ].Name
= "MediaType";
3792 pProperties
[ 4 ].Name
= DAVProperties::SUPPORTEDLOCK
;
3794 ContentProperties::UCBNamesToDAVNames( aProperties
, aPropNames
);
3796 rResAccess
->PROPFIND( DAVZERO
, aPropNames
, resources
, xEnv
);
3798 if ( resources
.size() == 1 )
3800 #if defined SAL_LOG_INFO
3802 // print received resources
3803 std::vector
< DAVPropertyValue
>::const_iterator it
= resources
[0].properties
.begin();
3804 std::vector
< DAVPropertyValue
>::const_iterator end
= resources
[0].properties
.end();
3807 OUString aPropValue
;
3809 uno::Sequence
< ucb::LockEntry
> aSupportedLocks
;
3810 if((*it
).Value
>>= aPropValue
)
3811 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << (*it
).Name
<< ":" << aPropValue
);
3812 else if( (*it
).Value
>>= bValue
)
3813 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << (*it
).Name
<< ":" <<
3814 ( bValue
? "true" : "false" ) );
3815 else if( (*it
).Value
>>= aSupportedLocks
)
3817 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << (*it
).Name
<< ":" );
3818 for ( sal_Int32 n
= 0; n
< aSupportedLocks
.getLength(); ++n
)
3820 SAL_INFO( "ucb.ucp.webdav","PROPFIND (getResourceType) - supportedlock[" << n
<<"]: scope: "
3821 << (aSupportedLocks
[n
].Scope
== ucb::LockScope_SHARED
? "shared" : "exclusive")
3823 << (aSupportedLocks
[n
].Type
!= ucb::LockType_WRITE
? "" : "write") );
3830 osl::MutexGuard
g(m_aMutex
);
3831 m_xCachedProps
.reset(
3832 new CachableContentProperties( ContentProperties( resources
[ 0 ] ) ) );
3833 m_xCachedProps
->containsAllNames(
3834 aProperties
, m_aFailedPropNames
);
3836 eResourceType
= DAV
;
3838 catch ( DAVException
const & e
)
3840 rResAccess
->resetUri();
3842 SAL_WARN( "ucb.ucp.webdav", "Content::getResourceType returned errors, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3844 if ( e
.getStatus() == SC_METHOD_NOT_ALLOWED
)
3846 // Status SC_METHOD_NOT_ALLOWED is a safe indicator that the
3847 // resource is NON_DAV
3848 eResourceType
= NON_DAV
;
3850 else if (networkAccessAllowed
!= nullptr)
3852 *networkAccessAllowed
= *networkAccessAllowed
3853 && shouldAccessNetworkAfterException(e
);
3855 if ( e
.getStatus() == SC_NOT_FOUND
)
3857 // arrives here if OPTIONS is still cached for a resource previously available
3858 // operate on the OPTIONS cache:
3859 // if OPTIONS was not found, do nothing
3860 // else OPTIONS returned on a resource not existent (example a server that allows lock on null resource) set
3861 // not found and adjust lifetime accordingly
3862 DAVOptions aDAVOptionsInner
;
3863 if (aStaticDAVOptionsCache
.getDAVOptions(rResAccess
->getURL(), aDAVOptionsInner
))
3865 // TODO? get redirected url
3866 aDAVOptionsInner
.setHttpResponseStatusCode( e
.getStatus() );
3867 aDAVOptionsInner
.setHttpResponseStatusText( e
.getData() );
3868 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptionsInner
,
3869 m_nOptsCacheLifeNotFound
);
3872 // if the two net events below happen, something
3873 // is going on to the connection so break the command flow
3874 if ( ( e
.getError() == DAVException::DAV_HTTP_TIMEOUT
) ||
3875 ( e
.getError() == DAVException::DAV_HTTP_CONNECT
) )
3877 cancelCommandExecution( e
, xEnv
);
3881 // cancel command execution is case that no user authentication data has been provided.
3882 if ( e
.getError() == DAVException::DAV_HTTP_NOAUTH
)
3884 cancelCommandExecution( e
, uno::Reference
< ucb::XCommandEnvironment
>() );
3890 rResAccess
->resetUri();
3892 // first check if the cached error can be mapped to DAVException::DAV_HTTP_TIMEOUT or mapped to DAVException::DAV_HTTP_CONNECT
3893 if (aDAVOptions
.getHttpResponseStatusCode() == USC_CONNECTION_TIMED_OUT
3894 // can't get any reliable info without auth => cancel request
3895 || aDAVOptions
.getHttpResponseStatusCode() == USC_AUTH_FAILED
3896 || aDAVOptions
.getHttpResponseStatusCode() == USC_AUTHPROXY_FAILED
)
3898 // behave same as DAVException::DAV_HTTP_TIMEOUT or DAVException::DAV_HTTP_CONNECT was thrown
3901 // extract host name and connection port
3902 CurlUri
theUri( rResAccess
->getURL() );
3903 OUString aHostName
= theUri
.GetHost();
3904 sal_Int32 nPort
= theUri
.GetPort();
3905 DAVException::ExceptionCode e
{};
3906 switch (aDAVOptions
.getHttpResponseStatusCode())
3908 case USC_CONNECTION_TIMED_OUT
:
3909 e
= DAVException::DAV_HTTP_TIMEOUT
;
3911 case USC_AUTH_FAILED
:
3912 e
= DAVException::DAV_HTTP_AUTH
;
3914 case USC_AUTHPROXY_FAILED
:
3915 e
= DAVException::DAV_HTTP_AUTHPROXY
;
3920 throw DAVException( e
,
3921 ConnectionEndPointString(aHostName
, nPort
) );
3923 catch ( DAVException
& exp
)
3925 cancelCommandExecution( exp
, xEnv
);
3929 if ( aDAVOptions
.getHttpResponseStatusCode() != SC_NOT_FOUND
&&
3930 aDAVOptions
.getHttpResponseStatusCode() != SC_GONE
) // the cached OPTIONS can have SC_GONE
3932 eResourceType
= NON_DAV
;
3936 //resource doesn't exist
3937 if ( networkAccessAllowed
!= nullptr )
3938 *networkAccessAllowed
= false;
3943 osl::MutexGuard
g(m_aMutex
);
3944 if (m_eResourceType
== UNKNOWN
) {
3945 m_eResourceType
= eResourceType
;
3948 eResourceType
!= m_eResourceType
, "ucb.ucp.webdav",
3949 "different resource types for <" << rResAccess
->getURL() << ">: "
3950 << +eResourceType
<< " vs. " << +m_eResourceType
);
3952 SAL_INFO( "ucb.ucp.webdav", "m_eResourceType for <" << rResAccess
->getURL() << ">: " << m_eResourceType
);
3953 return m_eResourceType
;
3957 Content::ResourceType
Content::getResourceType(
3958 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
3960 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
3962 osl::MutexGuard
aGuard( m_aMutex
);
3963 xResAccess
.reset( new DAVResourceAccess( *m_xResAccess
) );
3965 const Content::ResourceType
& ret
= getResourceType( xEnv
, xResAccess
);
3967 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3968 m_xResAccess
.reset( new DAVResourceAccess( *xResAccess
) );
3974 void Content::initOptsCacheLifeTime()
3976 // see description in
3977 // officecfg/registry/schema/org/openoffice/Inet.xcs
3978 // for use of these field values.
3980 nAtime
= officecfg::Inet::Settings::OptsCacheLifeImplWeb::get();
3981 m_nOptsCacheLifeImplWeb
= std::max( sal_uInt32( 0 ),
3982 std::min( nAtime
, sal_uInt32( 3600 ) ) );
3984 nAtime
= officecfg::Inet::Settings::OptsCacheLifeDAV::get();
3985 m_nOptsCacheLifeDAV
= std::max( sal_uInt32( 0 ),
3986 std::min( nAtime
, sal_uInt32( 3600 ) ) );
3988 nAtime
= officecfg::Inet::Settings::OptsCacheLifeDAVLocked::get();
3989 m_nOptsCacheLifeDAVLocked
= std::max( sal_uInt32( 0 ),
3990 std::min( nAtime
, sal_uInt32( 3600 ) ) );
3992 nAtime
= officecfg::Inet::Settings::OptsCacheLifeNotImpl::get();
3993 m_nOptsCacheLifeNotImpl
= std::max( sal_uInt32( 0 ),
3994 std::min( nAtime
, sal_uInt32( 43200 ) ) );
3996 nAtime
= officecfg::Inet::Settings::OptsCacheLifeNotFound::get();
3997 m_nOptsCacheLifeNotFound
= std::max( sal_uInt32( 0 ),
3998 std::min( nAtime
, sal_uInt32( 30 ) ) );
4002 void Content::getResourceOptions(
4003 const css::uno::Reference
< css::ucb::XCommandEnvironment
>& xEnv
,
4004 DAVOptions
& rDAVOptions
,
4005 const std::unique_ptr
< DAVResourceAccess
> & rResAccess
,
4006 bool * networkAccessAllowed
)
4009 OUString aTargetURL
= rResAccess
->getURL();
4010 DAVOptions aDAVOptions
;
4011 // first check if in cache, if not, then send method to server
4012 if ( !aStaticDAVOptionsCache
.getDAVOptions( aTargetURL
, aDAVOptions
) )
4016 rResAccess
->OPTIONS( aDAVOptions
, xEnv
);
4017 // IMPORTANT:the correctly implemented server will answer without errors, even if the resource is not present
4018 sal_uInt32 nLifeTime
= ( aDAVOptions
.isClass1() ||
4019 aDAVOptions
.isClass2() ||
4020 aDAVOptions
.isClass3() ) ?
4021 m_nOptsCacheLifeDAV
: // a WebDAV site
4022 m_nOptsCacheLifeImplWeb
; // a site implementing OPTIONS but
4024 // if resource is locked, will use a
4025 // different lifetime
4026 if( aDAVOptions
.isLocked() )
4027 nLifeTime
= m_nOptsCacheLifeDAVLocked
;
4029 // check if redirected
4030 aRedirURL
= rResAccess
->getURL();
4031 if( aRedirURL
== aTargetURL
)
4035 // cache this URL's option
4036 aDAVOptions
.setURL( aTargetURL
);
4037 aDAVOptions
.setRedirectedURL( aRedirURL
);
4038 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4041 catch ( DAVException
const & e
)
4043 // first, remove from cache, will be added if needed, depending on the error received
4044 aStaticDAVOptionsCache
.removeDAVOptions( aTargetURL
);
4045 rResAccess
->resetUri();
4047 aDAVOptions
.setURL( aTargetURL
);
4048 aDAVOptions
.setRedirectedURL( aRedirURL
);
4049 switch( e
.getError() )
4051 case DAVException::DAV_HTTP_TIMEOUT
:
4052 case DAVException::DAV_HTTP_CONNECT
:
4054 // something bad happened to the connection
4055 // not same as not found, this instead happens when the server doesn't exist or doesn't answer at all
4056 // probably a new bit stating 'timed out' should be added to opts var?
4057 // in any case abort the command
4058 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_TIMEOUT or DAV_HTTP_CONNECT for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
4059 // cache the internal unofficial status code
4061 aDAVOptions
.setHttpResponseStatusCode( USC_CONNECTION_TIMED_OUT
);
4062 // used only internally, so the text doesn't really matter..
4063 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4064 m_nOptsCacheLifeNotFound
);
4065 if ( networkAccessAllowed
!= nullptr )
4067 *networkAccessAllowed
= *networkAccessAllowed
4068 && shouldAccessNetworkAfterException(e
);
4072 case DAVException::DAV_HTTP_LOOKUP
:
4074 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_LOOKUP for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
4075 aDAVOptions
.setHttpResponseStatusCode( USC_LOOKUP_FAILED
);
4076 // used only internally, so the text doesn't really matter..
4077 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4078 m_nOptsCacheLifeNotFound
);
4079 if ( networkAccessAllowed
!= nullptr )
4081 *networkAccessAllowed
= *networkAccessAllowed
4082 && shouldAccessNetworkAfterException(e
);
4086 case DAVException::DAV_HTTP_NOAUTH
:
4087 case DAVException::DAV_HTTP_AUTH
:
4089 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTH for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
4090 // - the remote site is a WebDAV with special configuration: read/only for read operations
4091 // and read/write for write operations, the user is not allowed to lock/write and
4092 // she cancelled the credentials request.
4093 // this is not actually an error, it means only that for current user this is a standard web,
4094 // though possibly DAV enabled
4095 aDAVOptions
.setHttpResponseStatusCode( USC_AUTH_FAILED
);
4096 // used only internally, so the text doesn't really matter..
4097 if (xEnv
&& xEnv
->getInteractionHandler())
4098 { // only cache if there actually was a chance to request auth
4099 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4100 m_nOptsCacheLifeNotFound
);
4102 if ( networkAccessAllowed
!= nullptr )
4104 *networkAccessAllowed
= *networkAccessAllowed
4105 && shouldAccessNetworkAfterException(e
);
4109 case DAVException::DAV_HTTP_AUTHPROXY
:
4111 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTHPROXY for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
4112 aDAVOptions
.setHttpResponseStatusCode( USC_AUTHPROXY_FAILED
);
4113 // used only internally, so the text doesn't really matter..
4114 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4115 m_nOptsCacheLifeNotFound
);
4116 if ( networkAccessAllowed
!= nullptr )
4118 *networkAccessAllowed
= *networkAccessAllowed
4119 && shouldAccessNetworkAfterException(e
);
4123 case DAVException::DAV_HTTP_ERROR
:
4125 switch( e
.getStatus() )
4129 SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_FORBIDDEN for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
4130 // cache it, so OPTIONS won't be called again, this URL does not support it
4131 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4132 m_nOptsCacheLifeNotImpl
);
4135 case SC_BAD_REQUEST
:
4136 case SC_INTERNAL_SERVER_ERROR
:
4138 SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_BAD_REQUEST or SC_INTERNAL_SERVER_ERROR for URL <" << m_xIdentifier
->getContentIdentifier() << ">, HTTP error: "<< e
.getStatus()
4139 << ", '" << e
.getData() << "'" );
4140 // cache it, so OPTIONS won't be called again, this URL detect some problem while answering the method
4141 aDAVOptions
.setHttpResponseStatusCode( e
.getStatus() );
4142 aDAVOptions
.setHttpResponseStatusText( e
.getData() );
4143 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4144 m_nOptsCacheLifeNotFound
);
4147 case SC_NOT_IMPLEMENTED
:
4148 case SC_METHOD_NOT_ALLOWED
:
4150 // OPTIONS method must be implemented in DAV
4151 // resource is NON_DAV, or not advertising it
4152 SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED for URL <" << m_xIdentifier
->getContentIdentifier() << ">, HTTP error: "<< e
.getStatus()
4153 << ", '" << e
.getData() << "'" );
4154 // cache it, so OPTIONS won't be called again, this URL does not support it
4155 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4156 m_nOptsCacheLifeNotImpl
);
4161 // Apparently on IIS 10.0, if you disabled OPTIONS method, this error is the one reported,
4162 // instead of SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED.
4163 // So check if this is an available resource, or a real 'Not Found' event.
4164 sal_uInt32 nLifeTime
= m_nOptsCacheLifeNotFound
;
4165 if( isResourceAvailable( xEnv
, rResAccess
, aDAVOptions
) )
4167 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - Got an SC_NOT_FOUND, but the URL <" << m_xIdentifier
->getContentIdentifier() << "> resource exists" );
4168 nLifeTime
= m_nOptsCacheLifeNotImpl
;
4172 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - SC_NOT_FOUND for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
4173 if ( networkAccessAllowed
!= nullptr )
4175 *networkAccessAllowed
= *networkAccessAllowed
4176 && shouldAccessNetworkAfterException(e
);
4179 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4185 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAV_HTTP_ERROR, for URL <" << m_xIdentifier
->getContentIdentifier() << ">, HTTP error: "<< e
.getStatus()
4186 << ", '" << e
.getData() << "'" );
4187 aDAVOptions
.setHttpResponseStatusCode( e
.getStatus() );
4188 aDAVOptions
.setHttpResponseStatusText( e
.getData() );
4189 // cache it, so OPTIONS won't be called again, this URL does not support it
4190 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4191 m_nOptsCacheLifeNotImpl
);
4197 // The 'DAVException::DAV_HTTP_REDIRECT' means we reached the maximum
4198 // number of redirections, consider the resource type as UNKNOWN
4199 // possibly a normal web site, not DAV
4200 case DAVException::DAV_HTTP_REDIRECT
:
4203 SAL_WARN( "ucb.ucp.webdav","OPTIONS - General DAVException (or max DAV_HTTP_REDIRECT reached) for URL <" << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: "
4204 << e
.getError() << ", HTTP error: "<< e
.getStatus() );
4205 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4206 m_nOptsCacheLifeNotImpl
);
4214 // check current response status code, perhaps we need to set networkAccessAllowed
4215 sal_uInt16 CachedResponseStatusCode
= aDAVOptions
.getHttpResponseStatusCode();
4216 if ( networkAccessAllowed
!= nullptr &&
4217 ( ( CachedResponseStatusCode
== SC_NOT_FOUND
) ||
4218 ( CachedResponseStatusCode
== SC_GONE
) ||
4219 ( CachedResponseStatusCode
== USC_CONNECTION_TIMED_OUT
) ||
4220 ( CachedResponseStatusCode
== USC_LOOKUP_FAILED
) ||
4221 ( CachedResponseStatusCode
== USC_AUTH_FAILED
) ||
4222 ( CachedResponseStatusCode
== USC_AUTHPROXY_FAILED
)
4226 *networkAccessAllowed
= *networkAccessAllowed
&& false;
4229 rDAVOptions
= aDAVOptions
;
4233 bool Content::isResourceAvailable( const css::uno::Reference
< css::ucb::XCommandEnvironment
>& xEnv
,
4234 const std::unique_ptr
< DAVResourceAccess
> & rResAccess
,
4235 DAVOptions
& rDAVOptions
)
4237 std::vector
< rtl::OUString
> aHeaderNames
;
4238 DAVResource aResource
;
4242 // To check for the physical URL resource availability, first
4243 // try using a simple HEAD command
4244 // if HEAD is successful, set element found.
4245 rResAccess
->HEAD( aHeaderNames
, aResource
, xEnv
);
4246 rDAVOptions
.setHttpResponseStatusCode( 0 );
4247 rDAVOptions
.setHttpResponseStatusText("");
4250 catch ( DAVException
const & e
)
4252 if ( e
.getError() == DAVException::DAV_HTTP_ERROR
)
4254 if ( e
.getStatus() == SC_NOT_IMPLEMENTED
||
4255 e
.getStatus() == SC_METHOD_NOT_ALLOWED
||
4256 e
.getStatus() == SC_NOT_FOUND
)
4258 SAL_WARN( "ucb.ucp.webdav", "HEAD probably not implemented: fall back to a partial GET" );
4259 // set in cached OPTIONS "HEAD not implemented"
4260 // so it won't be used again on this resource
4261 rDAVOptions
.setHeadAllowed( false );
4264 // do a GET with a payload of 0, the server does not
4265 // support HEAD (or has HEAD disabled)
4266 DAVRequestHeaders aPartialGet
;
4267 aPartialGet
.push_back(
4269 OUString( "Range" ),
4270 OUString( "bytes=0-0" )));
4272 rResAccess
->GET0( aPartialGet
,
4278 catch ( DAVException
const & ex
)
4280 if ( ex
.getError() == DAVException::DAV_HTTP_ERROR
)
4282 rDAVOptions
.setHttpResponseStatusCode( ex
.getStatus() );
4283 rDAVOptions
.setHttpResponseStatusText( ex
.getData() );
4289 rDAVOptions
.setHttpResponseStatusCode( e
.getStatus() );
4290 rDAVOptions
.setHttpResponseStatusText( e
.getData() );
4298 // set SC_NOT_IMPLEMENTED since at a minimum GET must be implemented in a basic Web server
4299 rDAVOptions
.setHttpResponseStatusCode( SC_NOT_IMPLEMENTED
);
4300 rDAVOptions
.setHttpResponseStatusText("");
4305 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */