1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 /**************************************************************************
32 **************************************************************************
34 *************************************************************************/
37 #include <rtl/uri.hxx>
38 #include <rtl/ustrbuf.hxx>
39 #include <sal/log.hxx>
40 #include <cppuhelper/queryinterface.hxx>
41 #include <officecfg/Inet.hxx>
42 #include <ucbhelper/contentidentifier.hxx>
43 #include <ucbhelper/propertyvalueset.hxx>
44 #include <ucbhelper/simpleinteractionrequest.hxx>
45 #include <ucbhelper/cancelcommandexecution.hxx>
46 #include <com/sun/star/beans/IllegalTypeException.hpp>
47 #include <com/sun/star/beans/NotRemoveableException.hpp>
48 #include <com/sun/star/beans/PropertyAttribute.hpp>
49 #include <com/sun/star/beans/PropertyExistException.hpp>
50 #include <com/sun/star/beans/PropertySetInfoChange.hpp>
51 #include <com/sun/star/beans/PropertySetInfoChangeEvent.hpp>
52 #include <com/sun/star/beans/PropertyValue.hpp>
53 #include <com/sun/star/io/XActiveDataSink.hpp>
54 #include <com/sun/star/io/XOutputStream.hpp>
55 #include <com/sun/star/lang/IllegalAccessException.hpp>
56 #include <com/sun/star/sdbc/SQLException.hpp>
57 #include <com/sun/star/task/PasswordContainerInteractionHandler.hpp>
58 #include <com/sun/star/ucb/CommandEnvironment.hpp>
59 #include <com/sun/star/ucb/CommandFailedException.hpp>
60 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
61 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
62 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
63 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
64 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
65 #include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
66 #include <com/sun/star/ucb/InteractiveLockingLockExpiredException.hpp>
67 #include <com/sun/star/ucb/InteractiveLockingNotLockedException.hpp>
68 #include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
69 #include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
70 #include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
71 #include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
72 #include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
73 #include <com/sun/star/ucb/MissingInputStreamException.hpp>
74 #include <com/sun/star/ucb/MissingPropertiesException.hpp>
75 #include <com/sun/star/ucb/NameClash.hpp>
76 #include <com/sun/star/ucb/NameClashException.hpp>
77 #include <com/sun/star/ucb/OpenCommandArgument3.hpp>
78 #include <com/sun/star/ucb/OpenMode.hpp>
79 #include <com/sun/star/ucb/PostCommandArgument2.hpp>
80 #include <com/sun/star/ucb/PropertyCommandArgument.hpp>
81 #include <com/sun/star/ucb/TransferInfo.hpp>
82 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
83 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
84 #include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
85 #include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
86 #include <com/sun/star/ucb/XCommandInfo.hpp>
87 #include <com/sun/star/ucb/XPersistentPropertySet.hpp>
88 #include <ucbhelper/macros.hxx>
90 #include "webdavcontent.hxx"
91 #include "webdavprovider.hxx"
92 #include "webdavresultset.hxx"
93 #include "ContentProperties.hxx"
94 #include "NeonUri.hxx"
95 #include "UCBDeadPropertyValue.hxx"
97 using namespace com::sun::star
;
98 using namespace webdav_ucp
;
102 // implement a GET to substitute HEAD, when HEAD not available
103 void lcl_sendPartialGETRequest( bool &bError
,
104 DAVException
&aLastException
,
105 const std::vector
< OUString
>& rProps
,
106 std::vector
< OUString
> &aHeaderNames
,
107 const std::unique_ptr
< DAVResourceAccess
> &xResAccess
,
108 std::unique_ptr
< ContentProperties
> &xProps
,
109 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
111 DAVResource aResource
;
112 DAVRequestHeaders aPartialGet
;
113 aPartialGet
.emplace_back( OUString( "Range" ), // see <https://tools.ietf.org/html/rfc7233#section-3.1>
114 OUString( "bytes=0-0" ) );
116 bool bIsRequestSize
= std::any_of(aHeaderNames
.begin(), aHeaderNames
.end(),
117 [](const OUString
& rHeaderName
) { return rHeaderName
== "Content-Length"; });
119 if ( bIsRequestSize
)
121 // we need to know if the server accepts range requests for a resource
122 // and the range unit it uses
123 aHeaderNames
.emplace_back( "Accept-Ranges" ); // see <https://tools.ietf.org/html/rfc7233#section-2.3>
124 aHeaderNames
.emplace_back( "Content-Range" ); // see <https://tools.ietf.org/html/rfc7233#section-4.2>
128 xResAccess
->GET0( aPartialGet
, aHeaderNames
, aResource
, xEnv
);
131 if ( bIsRequestSize
)
133 // the ContentProperties maps "Content-Length" to the UCB "Size" property
134 // This would have an unrealistic value of 1 byte because we did only a partial GET
135 // Solution: if "Content-Range" is present, map it with UCB "Size" property
136 OUString aAcceptRanges
, aContentRange
, aContentLength
;
137 std::vector
< DAVPropertyValue
> &aResponseProps
= aResource
.properties
;
138 for ( const auto& rResponseProp
: aResponseProps
)
140 if ( rResponseProp
.Name
== "Accept-Ranges" )
141 rResponseProp
.Value
>>= aAcceptRanges
;
142 else if ( rResponseProp
.Name
== "Content-Range" )
143 rResponseProp
.Value
>>= aContentRange
;
144 else if ( rResponseProp
.Name
== "Content-Length" )
145 rResponseProp
.Value
>>= aContentLength
;
149 if ( aContentLength
.getLength() )
151 nSize
= aContentLength
.toInt64();
154 // according to <> http://tools.ietf.org/html/rfc2616#section-3.12
155 // <https://tools.ietf.org/html/rfc7233#section-2>
156 // needs some explanation for this
157 // probably some changes?
158 // the only range unit defined is "bytes" and implementations
159 // MAY ignore ranges specified using other units.
161 aContentRange
.getLength() &&
162 aAcceptRanges
== "bytes" )
164 // Parse the Content-Range to get the size
165 // vid. http://tools.ietf.org/html/rfc2616#section-14.16
166 // Content-Range: <range unit> <bytes range>/<size>
167 sal_Int32 nSlash
= aContentRange
.lastIndexOf( '/' );
170 OUString aSize
= aContentRange
.copy( nSlash
+ 1 );
171 // "*" means that the instance-length is unknown at the time when the response was generated
174 auto it
= std::find_if(aResponseProps
.begin(), aResponseProps
.end(),
175 [](const DAVPropertyValue
& rProp
) { return rProp
.Name
== "Content-Length"; });
176 if (it
!= aResponseProps
.end())
186 xProps
->addProperties(
188 ContentProperties( aResource
) );
190 xProps
.reset ( new ContentProperties( aResource
) );
192 catch ( DAVException
const & ex
)
200 // Static value, to manage a simple OPTIONS cache
201 // Key is the URL, element is the DAVOptions resulting from an OPTIONS call.
202 // Cached DAVOptions have a lifetime that depends on the errors received or not received
203 // and on the value of received options.
204 static DAVOptionsCache aStaticDAVOptionsCache
;
207 // Content Implementation.
210 // ctr for content on an existing webdav resource
212 const uno::Reference
< uno::XComponentContext
>& rxContext
,
213 ContentProvider
* pProvider
,
214 const uno::Reference
< ucb::XContentIdentifier
>& Identifier
,
215 rtl::Reference
< DAVSessionFactory
> const & rSessionFactory
)
216 : ContentImplHelper( rxContext
, pProvider
, Identifier
),
217 m_eResourceType( UNKNOWN
),
218 m_eResourceTypeForLocks( UNKNOWN
),
219 m_pProvider( pProvider
),
220 m_bTransient( false ),
221 m_bCollection( false ),
222 m_bDidGetOrHead( false )
226 initOptsCacheLifeTime();
227 m_xResAccess
.reset( new DAVResourceAccess(
230 Identifier
->getContentIdentifier() ) );
232 NeonUri
aURI( Identifier
->getContentIdentifier() );
233 m_aEscapedTitle
= aURI
.GetPathBaseName();
235 catch ( DAVException
const & )
237 throw ucb::ContentCreationException();
242 // ctr for content on a non-existing webdav resource
244 const uno::Reference
< uno::XComponentContext
>& rxContext
,
245 ContentProvider
* pProvider
,
246 const uno::Reference
< ucb::XContentIdentifier
>& Identifier
,
247 rtl::Reference
< DAVSessionFactory
> const & rSessionFactory
,
249 : ContentImplHelper( rxContext
, pProvider
, Identifier
),
250 m_eResourceType( UNKNOWN
),
251 m_eResourceTypeForLocks( UNKNOWN
),
252 m_pProvider( pProvider
),
253 m_bTransient( true ),
254 m_bCollection( isCollection
),
255 m_bDidGetOrHead( false )
259 initOptsCacheLifeTime();
260 m_xResAccess
.reset( new DAVResourceAccess(
261 rxContext
, rSessionFactory
, Identifier
->getContentIdentifier() ) );
263 catch ( DAVException
const & )
265 throw ucb::ContentCreationException();
268 // Do not set m_aEscapedTitle here! Content::insert relays on this!!!
278 // XInterface methods.
282 void SAL_CALL
Content::acquire()
285 ContentImplHelper::acquire();
290 void SAL_CALL
Content::release()
293 ContentImplHelper::release();
298 uno::Any SAL_CALL
Content::queryInterface( const uno::Type
& rType
)
300 // Note: isFolder may require network activities! So call it only
301 // if it is really necessary!!!
302 uno::Any aRet
= cppu::queryInterface(
304 static_cast< ucb::XContentCreator
* >( this ) );
305 if ( aRet
.hasValue() )
309 uno::Reference
< task::XInteractionHandler
> xIH(
310 task::PasswordContainerInteractionHandler::create( m_xContext
) );
312 // Supply a command env to isFolder() that contains an interaction
313 // handler that uses the password container service to obtain
314 // credentials without displaying a password gui.
316 uno::Reference
< ucb::XCommandEnvironment
> xCmdEnv(
317 ucb::CommandEnvironment::create(
320 uno::Reference
< ucb::XProgressHandler
>() ) );
322 return isFolder( xCmdEnv
) ? aRet
: uno::Any();
324 catch ( uno::RuntimeException
const & )
328 catch ( uno::Exception
const & )
333 return aRet
.hasValue() ? aRet
: ContentImplHelper::queryInterface( rType
);
337 // XTypeProvider methods.
340 XTYPEPROVIDER_COMMON_IMPL( Content
);
344 uno::Sequence
< uno::Type
> SAL_CALL
Content::getTypes()
346 bool bFolder
= false;
350 = isFolder( uno::Reference
< ucb::XCommandEnvironment
>() );
352 catch ( uno::RuntimeException
const & )
356 catch ( uno::Exception
const & )
362 static cppu::OTypeCollection
s_aFolderTypes(
363 CPPU_TYPE_REF( lang::XTypeProvider
),
364 CPPU_TYPE_REF( lang::XServiceInfo
),
365 CPPU_TYPE_REF( lang::XComponent
),
366 CPPU_TYPE_REF( ucb::XContent
),
367 CPPU_TYPE_REF( ucb::XCommandProcessor
),
368 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier
),
369 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier
),
370 CPPU_TYPE_REF( beans::XPropertyContainer
),
371 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier
),
372 CPPU_TYPE_REF( container::XChild
),
373 CPPU_TYPE_REF( ucb::XContentCreator
) );
375 return s_aFolderTypes
.getTypes();
379 static cppu::OTypeCollection
s_aDocumentTypes(
380 CPPU_TYPE_REF( lang::XTypeProvider
),
381 CPPU_TYPE_REF( lang::XServiceInfo
),
382 CPPU_TYPE_REF( lang::XComponent
),
383 CPPU_TYPE_REF( ucb::XContent
),
384 CPPU_TYPE_REF( ucb::XCommandProcessor
),
385 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier
),
386 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier
),
387 CPPU_TYPE_REF( beans::XPropertyContainer
),
388 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier
),
389 CPPU_TYPE_REF( container::XChild
) );
391 return s_aDocumentTypes
.getTypes();
396 // XServiceInfo methods.
400 OUString SAL_CALL
Content::getImplementationName()
402 return "com.sun.star.comp.ucb.WebDAVContent";
407 uno::Sequence
< OUString
> SAL_CALL
Content::getSupportedServiceNames()
409 uno::Sequence
<OUString
> aSNS
{ WEBDAV_CONTENT_SERVICE_NAME
};
418 OUString SAL_CALL
Content::getContentType()
420 bool bFolder
= false;
424 = isFolder( uno::Reference
< ucb::XCommandEnvironment
>() );
426 catch ( uno::RuntimeException
const & )
430 catch ( uno::Exception
const & )
435 return WEBDAV_COLLECTION_TYPE
;
437 return WEBDAV_CONTENT_TYPE
;
441 // XCommandProcessor methods.
445 uno::Any SAL_CALL
Content::execute(
446 const ucb::Command
& aCommand
,
447 sal_Int32
/*CommandId*/,
448 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
450 SAL_INFO( "ucb.ucp.webdav", "Content::execute: start: command: " <<
451 aCommand
.Name
<< ", env: " <<
452 (Environment
.is() ? "present" : "missing") );
456 if ( aCommand
.Name
== "getPropertyValues" )
462 uno::Sequence
< beans::Property
> Properties
;
463 if ( !( aCommand
.Argument
>>= Properties
) )
465 ucbhelper::cancelCommandExecution(
466 uno::makeAny( lang::IllegalArgumentException(
467 "Wrong argument type!",
468 static_cast< cppu::OWeakObject
* >( this ),
474 aRet
<<= getPropertyValues( Properties
, Environment
);
476 else if ( aCommand
.Name
== "setPropertyValues" )
482 uno::Sequence
< beans::PropertyValue
> aProperties
;
483 if ( !( aCommand
.Argument
>>= aProperties
) )
485 ucbhelper::cancelCommandExecution(
486 uno::makeAny( lang::IllegalArgumentException(
487 "Wrong argument type!",
488 static_cast< cppu::OWeakObject
* >( this ),
494 if ( !aProperties
.hasElements() )
496 ucbhelper::cancelCommandExecution(
497 uno::makeAny( lang::IllegalArgumentException(
499 static_cast< cppu::OWeakObject
* >( this ),
505 aRet
<<= setPropertyValues( aProperties
, Environment
);
507 else if ( aCommand
.Name
== "getPropertySetInfo" )
510 // getPropertySetInfo
513 // Note: Implemented by base class.
514 aRet
<<= getPropertySetInfo( Environment
,
515 false /* don't cache data */ );
517 else if ( aCommand
.Name
== "getCommandInfo" )
523 // Note: Implemented by base class.
524 aRet
<<= getCommandInfo( Environment
, false );
526 else if ( aCommand
.Name
== "open" )
532 ucb::OpenCommandArgument3 aOpenCommand
;
533 ucb::OpenCommandArgument2 aTmp
;
534 if ( !( aCommand
.Argument
>>= aTmp
) )
536 ucbhelper::cancelCommandExecution(
537 uno::makeAny( lang::IllegalArgumentException(
538 "Wrong argument type!",
539 static_cast< cppu::OWeakObject
* >( this ),
544 if ( !( aCommand
.Argument
>>= aOpenCommand
) )
546 // compat mode, extract Arg2 info into newer structure
547 aOpenCommand
.Mode
= aTmp
.Mode
;
548 aOpenCommand
.Priority
= aTmp
.Priority
;
549 aOpenCommand
.Sink
= aTmp
.Sink
;
550 aOpenCommand
.Properties
= aTmp
.Properties
;
551 aOpenCommand
.SortingInfo
= aTmp
.SortingInfo
;
554 aRet
= open( aOpenCommand
, Environment
);
557 else if ( aCommand
.Name
== "insert" )
563 ucb::InsertCommandArgument arg
;
564 if ( !( aCommand
.Argument
>>= arg
) )
566 ucbhelper::cancelCommandExecution(
567 uno::makeAny( lang::IllegalArgumentException(
568 "Wrong argument type!",
569 static_cast< cppu::OWeakObject
* >( this ),
575 insert( arg
.Data
, arg
.ReplaceExisting
, Environment
);
577 else if ( aCommand
.Name
== "delete" )
583 bool bDeletePhysical
= false;
584 aCommand
.Argument
>>= bDeletePhysical
;
586 // KSO: Ignore parameter and destroy the content, if you don't support
587 // putting objects into trashcan. ( Since we do not have a trash can
588 // service yet (src603), you actually have no other choice. )
589 // if ( bDeletePhysical )
593 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
595 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
596 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
598 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
599 // clean cached value of PROPFIND property names
600 removeCachedPropertyNames( xResAccess
->getURL() );
601 xResAccess
->DESTROY( Environment
);
603 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
604 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
607 catch ( DAVException
const & e
)
609 cancelCommandExecution( e
, Environment
, true );
614 // Propagate destruction.
615 destroy( bDeletePhysical
);
617 // Remove own and all children's Additional Core Properties.
618 removeAdditionalPropertySet();
620 else if ( aCommand
.Name
== "transfer" && isFolder( Environment
) )
624 // ( Not available at documents )
627 ucb::TransferInfo transferArgs
;
628 if ( !( aCommand
.Argument
>>= transferArgs
) )
630 ucbhelper::cancelCommandExecution(
631 uno::makeAny( lang::IllegalArgumentException(
632 "Wrong argument type!",
633 static_cast< cppu::OWeakObject
* >( this ),
639 transfer( transferArgs
, Environment
);
641 else if ( aCommand
.Name
== "post" )
647 ucb::PostCommandArgument2 aArg
;
648 if ( !( aCommand
.Argument
>>= aArg
) )
650 ucbhelper::cancelCommandExecution(
651 uno::makeAny( lang::IllegalArgumentException(
652 "Wrong argument type!",
653 static_cast< cppu::OWeakObject
* >( this ),
659 post( aArg
, Environment
);
661 else if ( aCommand
.Name
== "lock" )
666 ResourceType eType
= resourceTypeForLocks( Environment
);
667 // when the resource is not yet present the lock is used to create it
668 // see: http://tools.ietf.org/html/rfc4918#section-7.3
669 // If the resource doesn't exists and the lock is not enabled (DAV with
670 // no lock or a simple web) the error will be dealt with inside lock() method
671 if ( eType
== NOT_FOUND
||
675 if ( eType
== NOT_FOUND
)
677 m_eResourceType
= UNKNOWN
; // lock may have created it, need to check again
678 m_eResourceTypeForLocks
= UNKNOWN
;
682 else if ( aCommand
.Name
== "unlock" )
686 // do not check for a DAV resource
687 // the lock store will be checked before sending
688 unlock( Environment
);
690 else if ( aCommand
.Name
== "createNewContent" && isFolder( Environment
) )
696 ucb::ContentInfo aArg
;
697 if ( !( aCommand
.Argument
>>= aArg
) )
699 ucbhelper::cancelCommandExecution(
700 uno::makeAny( lang::IllegalArgumentException(
701 "Wrong argument type!",
702 static_cast< cppu::OWeakObject
* >( this ),
708 aRet
<<= createNewContent( aArg
);
710 else if ( aCommand
.Name
== "addProperty" )
712 ucb::PropertyCommandArgument aPropArg
;
713 if ( !( aCommand
.Argument
>>= aPropArg
))
715 ucbhelper::cancelCommandExecution(
716 uno::makeAny( lang::IllegalArgumentException(
717 "Wrong argument type!",
718 static_cast< cppu::OWeakObject
* >( this ),
723 // TODO when/if XPropertyContainer is removed,
724 // the command execution can be canceled in addProperty
727 addProperty( aPropArg
, Environment
);
729 catch ( const beans::PropertyExistException
&e
)
731 ucbhelper::cancelCommandExecution( uno::makeAny( e
), Environment
);
733 catch ( const beans::IllegalTypeException
&e
)
735 ucbhelper::cancelCommandExecution( uno::makeAny( e
), Environment
);
737 catch ( const lang::IllegalArgumentException
&e
)
739 ucbhelper::cancelCommandExecution( uno::makeAny( e
), Environment
);
742 else if ( aCommand
.Name
== "removeProperty" )
745 if ( !( aCommand
.Argument
>>= sPropName
) )
747 ucbhelper::cancelCommandExecution(
748 uno::makeAny( lang::IllegalArgumentException(
749 "Wrong argument type!",
750 static_cast< cppu::OWeakObject
* >( this ),
755 // TODO when/if XPropertyContainer is removed,
756 // the command execution can be canceled in removeProperty
759 removeProperty( sPropName
, Environment
);
761 catch( const beans::UnknownPropertyException
&e
)
763 ucbhelper::cancelCommandExecution( uno::makeAny( e
), Environment
);
765 catch( const beans::NotRemoveableException
&e
)
767 ucbhelper::cancelCommandExecution( uno::makeAny( e
), Environment
);
773 // Unsupported command
776 ucbhelper::cancelCommandExecution(
777 uno::makeAny( ucb::UnsupportedCommandException(
779 static_cast< cppu::OWeakObject
* >( this ) ) ),
784 SAL_INFO( "ucb.ucp.webdav", "Content::execute: end: command: " << aCommand
.Name
);
791 void SAL_CALL
Content::abort( sal_Int32
/*CommandId*/ )
795 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
797 osl::MutexGuard
aGuard( m_aMutex
);
798 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
802 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
803 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
806 catch ( DAVException
const & )
813 // XPropertyContainer methods.
816 void Content::addProperty( const ucb::PropertyCommandArgument
& aCmdArg
,
817 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
819 // if ( m_bTransient )
822 if ( aCmdArg
.Property
.Name
.isEmpty() )
823 throw lang::IllegalArgumentException(
824 "\"addProperty\" with empty Property.Name",
825 static_cast< cppu::OWeakObject
* >( this ),
828 // Check property type.
829 if ( !UCBDeadPropertyValue::supportsType( aCmdArg
.Property
.Type
) )
831 throw beans::IllegalTypeException(
832 "\"addProperty\" unsupported Property.Type",
833 static_cast< cppu::OWeakObject
* >( this ) );
836 if ( aCmdArg
.DefaultValue
.hasValue()
837 && aCmdArg
.DefaultValue
.getValueType() != aCmdArg
.Property
.Type
)
839 throw beans::IllegalTypeException(
840 "\"addProperty\" DefaultValue does not match Property.Type",
841 static_cast< ::cppu::OWeakObject
* >( this ) );
845 // Make sure a property with the requested name does not already
846 // exist in dynamic and static(!) properties.
849 // Take into account special properties with custom namespace
850 // using <prop:the_propname xmlns:prop="the_namespace">
851 OUString aSpecialName
;
852 bool bIsSpecial
= DAVProperties::isUCBSpecialProperty(
853 aCmdArg
.Property
.Name
, aSpecialName
);
855 // Note: This requires network access!
856 if ( getPropertySetInfo( xEnv
, false /* don't cache data */ )
858 bIsSpecial
? aSpecialName
: aCmdArg
.Property
.Name
) )
860 // Property does already exist.
861 throw beans::PropertyExistException();
865 // Add a new dynamic property.
868 ProppatchValue
aValue(
869 PROPSET
, aCmdArg
.Property
.Name
, aCmdArg
.DefaultValue
);
871 std::vector
< ProppatchValue
> aProppatchValues
;
872 aProppatchValues
.push_back( aValue
);
876 // Set property value at server.
877 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
879 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
880 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
882 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
883 // clean cached value of PROPFIND property names
884 // PROPPATCH can change them
885 removeCachedPropertyNames( xResAccess
->getURL() );
886 xResAccess
->PROPPATCH( aProppatchValues
, xEnv
);
888 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
889 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
892 // Notify propertyset info change listeners.
893 beans::PropertySetInfoChangeEvent
evt(
894 static_cast< cppu::OWeakObject
* >( this ),
895 bIsSpecial
? aSpecialName
: aCmdArg
.Property
.Name
,
896 -1, // No handle available
897 beans::PropertySetInfoChange::PROPERTY_INSERTED
);
898 notifyPropertySetInfoChange( evt
);
900 catch ( DAVException
const & e
)
902 if ( e
.getStatus() == SC_FORBIDDEN
)
904 // Support for setting arbitrary dead properties is optional!
906 // Store property locally.
907 ContentImplHelper::addProperty(
908 bIsSpecial
? aSpecialName
: aCmdArg
.Property
.Name
,
909 aCmdArg
.Property
.Attributes
, aCmdArg
.DefaultValue
);
913 if ( shouldAccessNetworkAfterException( e
) )
917 ResourceType eType
= getResourceType( xEnv
);
922 throw lang::IllegalArgumentException();
926 // Store property locally.
927 ContentImplHelper::addProperty(
928 bIsSpecial
? aSpecialName
: aCmdArg
.Property
.Name
,
929 aCmdArg
.Property
.Attributes
, aCmdArg
.DefaultValue
);
933 SAL_WARN( "ucb.ucp.webdav", "Content::addProperty - "
934 "Unsupported resource type!" );
938 catch ( uno::Exception
const & )
940 SAL_WARN( "ucb.ucp.webdav", "Content::addProperty - "
941 "Unable to determine resource type!" );
946 SAL_WARN( "ucb.ucp.webdav", "Content::addProperty - "
947 "Unable to determine resource type!" );
953 void Content::removeProperty( const OUString
& Name
,
954 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
957 // Try to remove property from server.
962 std::vector
< ProppatchValue
> aProppatchValues
;
963 ProppatchValue
aValue( PROPREMOVE
, Name
, uno::Any() );
964 aProppatchValues
.push_back( aValue
);
966 // Remove property value from server.
967 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
969 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
970 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
972 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
973 // clean cached value of PROPFIND property names
974 // PROPPATCH can change them
975 removeCachedPropertyNames( xResAccess
->getURL() );
976 xResAccess
->PROPPATCH( aProppatchValues
, xEnv
);
978 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
979 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
982 // Notify propertyset info change listeners.
983 beans::PropertySetInfoChangeEvent
evt(
984 static_cast< cppu::OWeakObject
* >( this ),
986 -1, // No handle available
987 beans::PropertySetInfoChange::PROPERTY_REMOVED
);
988 notifyPropertySetInfoChange( evt
);
990 catch ( DAVException
const & e
)
992 if ( e
.getStatus() == SC_FORBIDDEN
)
994 // Support for setting arbitrary dead properties is optional!
996 // Try to remove property from local store.
997 ContentImplHelper::removeProperty( Name
);
1001 if ( shouldAccessNetworkAfterException( e
) )
1005 ResourceType eType
= getResourceType( xEnv
);
1010 throw beans::UnknownPropertyException(Name
);
1014 // Try to remove property from local store.
1015 ContentImplHelper::removeProperty( Name
);
1019 SAL_WARN( "ucb.ucp.webdav", "Content::removeProperty - "
1020 "Unsupported resource type!" );
1024 catch ( uno::Exception
const & )
1026 SAL_WARN( "ucb.ucp.webdav", "Content::removeProperty - "
1027 "Unable to determine resource type!" );
1032 SAL_WARN( "ucb.ucp.webdav", "Content::removeProperty - "
1033 "Unable to determine resource type!" );
1034 // throw beans::UnknownPropertyException();
1041 void SAL_CALL
Content::addProperty( const OUString
& Name
,
1042 sal_Int16 Attributes
,
1043 const uno::Any
& DefaultValue
)
1045 beans::Property aProperty
;
1046 aProperty
.Name
= Name
;
1047 aProperty
.Type
= DefaultValue
.getValueType();
1048 aProperty
.Attributes
= Attributes
;
1049 aProperty
.Handle
= -1;
1051 addProperty( ucb::PropertyCommandArgument( aProperty
, DefaultValue
),
1052 uno::Reference
< ucb::XCommandEnvironment
>());
1056 void SAL_CALL
Content::removeProperty( const OUString
& Name
)
1058 removeProperty( Name
,
1059 uno::Reference
< ucb::XCommandEnvironment
>() );
1063 // XContentCreator methods.
1067 uno::Sequence
< ucb::ContentInfo
> SAL_CALL
1068 Content::queryCreatableContentsInfo()
1070 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
1072 uno::Sequence
< ucb::ContentInfo
> aSeq( 2 );
1075 aSeq
.getArray()[ 0 ].Type
= WEBDAV_CONTENT_TYPE
;
1076 aSeq
.getArray()[ 0 ].Attributes
1077 = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
1078 | ucb::ContentInfoAttribute::KIND_DOCUMENT
;
1080 beans::Property aProp
;
1081 m_pProvider
->getProperty(
1084 uno::Sequence
< beans::Property
> aDocProps( 1 );
1085 aDocProps
.getArray()[ 0 ] = aProp
;
1086 aSeq
.getArray()[ 0 ].Properties
= aDocProps
;
1089 aSeq
.getArray()[ 1 ].Type
= WEBDAV_COLLECTION_TYPE
;
1090 aSeq
.getArray()[ 1 ].Attributes
1091 = ucb::ContentInfoAttribute::KIND_FOLDER
;
1093 uno::Sequence
< beans::Property
> aFolderProps( 1 );
1094 aFolderProps
.getArray()[ 0 ] = aProp
;
1095 aSeq
.getArray()[ 1 ].Properties
= aFolderProps
;
1101 uno::Reference
< ucb::XContent
> SAL_CALL
1102 Content::createNewContent( const ucb::ContentInfo
& Info
)
1104 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
1106 if ( Info
.Type
.isEmpty() )
1107 return uno::Reference
< ucb::XContent
>();
1109 if ( ( Info
.Type
!= WEBDAV_COLLECTION_TYPE
) && ( Info
.Type
!= WEBDAV_CONTENT_TYPE
) )
1110 return uno::Reference
< ucb::XContent
>();
1112 OUString aURL
= m_xIdentifier
->getContentIdentifier();
1114 assert( !aURL
.isEmpty() && "WebdavContent::createNewContent - empty identifier!" );
1116 if ( ( aURL
.lastIndexOf( '/' ) + 1 ) != aURL
.getLength() )
1120 if ( Info
.Type
== WEBDAV_COLLECTION_TYPE
)
1122 aURL
+= "New_Collection";
1123 isCollection
= true;
1127 aURL
+= "New_Content";
1128 isCollection
= false;
1131 uno::Reference
< ucb::XContentIdentifier
> xId(
1132 new ::ucbhelper::ContentIdentifier( aURL
) );
1134 // create the local content
1137 return new ::webdav_ucp::Content( m_xContext
,
1140 m_xResAccess
->getSessionFactory(),
1143 catch ( ucb::ContentCreationException
& )
1145 return uno::Reference
< ucb::XContent
>();
1151 OUString
Content::getParentURL()
1153 // <scheme>:// -> ""
1154 // <scheme>://foo -> ""
1155 // <scheme>://foo/ -> ""
1156 // <scheme>://foo/bar -> <scheme>://foo/
1157 // <scheme>://foo/bar/ -> <scheme>://foo/
1158 // <scheme>://foo/bar/abc -> <scheme>://foo/bar/
1160 OUString aURL
= m_xIdentifier
->getContentIdentifier();
1162 sal_Int32 nPos
= aURL
.lastIndexOf( '/' );
1163 if ( nPos
== ( aURL
.getLength() - 1 ) )
1165 // Trailing slash found. Skip.
1166 nPos
= aURL
.lastIndexOf( '/', nPos
);
1169 sal_Int32 nPos1
= aURL
.lastIndexOf( '/', nPos
);
1171 nPos1
= aURL
.lastIndexOf( '/', nPos1
);
1176 return aURL
.copy( 0, nPos
+ 1 );
1180 // Non-interface methods.
1184 uno::Reference
< sdbc::XRow
> Content::getPropertyValues(
1185 const uno::Reference
< uno::XComponentContext
>& rxContext
,
1186 const uno::Sequence
< beans::Property
>& rProperties
,
1187 const ContentProperties
& rData
,
1188 const rtl::Reference
< ::ucbhelper::ContentProviderImplHelper
>& rProvider
,
1189 const OUString
& rContentId
)
1191 // Note: Empty sequence means "get values of all supported properties".
1193 rtl::Reference
< ::ucbhelper::PropertyValueSet
> xRow
1194 = new ::ucbhelper::PropertyValueSet( rxContext
);
1196 if ( rProperties
.hasElements() )
1198 uno::Reference
< beans::XPropertySet
> xAdditionalPropSet
;
1199 bool bTriedToGetAdditionalPropSet
= false;
1201 for ( const beans::Property
& rProp
: rProperties
)
1203 // Process standard UCB, DAV and HTTP properties.
1204 const uno::Any
& rValue
= rData
.getValue( rProp
.Name
);
1205 if ( rValue
.hasValue() )
1207 xRow
->appendObject( rProp
, rValue
);
1211 // Process local Additional Properties.
1212 if ( !bTriedToGetAdditionalPropSet
&& !xAdditionalPropSet
.is() )
1214 xAdditionalPropSet
=
1215 rProvider
->getAdditionalPropertySet( rContentId
,
1217 bTriedToGetAdditionalPropSet
= true;
1220 if ( !xAdditionalPropSet
.is() ||
1221 !xRow
->appendPropertySetValue(
1222 xAdditionalPropSet
, rProp
) )
1224 // Append empty entry.
1225 xRow
->appendVoid( rProp
);
1232 // Append all standard UCB, DAV and HTTP properties.
1233 const std::unique_ptr
< PropertyValueMap
> & xProps
= rData
.getProperties();
1235 ContentProvider
* pProvider
1236 = static_cast< ContentProvider
* >( rProvider
.get() );
1237 beans::Property aProp
;
1239 for ( const auto& rProp
: *xProps
)
1241 pProvider
->getProperty( rProp
.first
, aProp
);
1242 xRow
->appendObject( aProp
, rProp
.second
.value() );
1245 // Append all local Additional Properties.
1246 uno::Reference
< beans::XPropertySet
> xSet
=
1247 rProvider
->getAdditionalPropertySet( rContentId
, false );
1248 xRow
->appendPropertySet( xSet
);
1251 return uno::Reference
< sdbc::XRow
>( xRow
.get() );
1255 void GetPropsUsingHeadRequest(DAVResource
& resource
,
1256 const std::unique_ptr
< DAVResourceAccess
>& xResAccess
,
1257 const std::vector
< OUString
>& aHTTPNames
,
1258 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
1260 if (!aHTTPNames
.empty())
1262 DAVOptions aDAVOptions
;
1263 OUString aTargetURL
= xResAccess
->getURL();
1264 // retrieve the cached options if any
1265 aStaticDAVOptionsCache
.getDAVOptions(aTargetURL
, aDAVOptions
);
1267 // clean cached value of PROPFIND property names
1268 // PROPPATCH can change them
1269 Content::removeCachedPropertyNames(aTargetURL
);
1270 // test if HEAD allowed, if not, throw, should be caught immediately
1271 // SC_GONE used internally by us, see comment in Content::getPropertyValues
1272 // in the catch scope
1273 if (aDAVOptions
.getHttpResponseStatusCode() != SC_GONE
&&
1274 !aDAVOptions
.isHeadAllowed())
1276 throw DAVException(DAVException::DAV_HTTP_ERROR
, "405 Not Implemented", SC_METHOD_NOT_ALLOWED
);
1278 // if HEAD is enabled on this site
1279 // check if there is a relevant HTTP response status code cached
1280 if (aDAVOptions
.getHttpResponseStatusCode() != SC_NONE
)
1282 // throws exception as if there was a server error, a DAV exception
1283 throw DAVException(DAVException::DAV_HTTP_ERROR
,
1284 aDAVOptions
.getHttpResponseStatusText(),
1285 aDAVOptions
.getHttpResponseStatusCode());
1289 xResAccess
->HEAD(aHTTPNames
, resource
, xEnv
);
1294 uno::Reference
< sdbc::XRow
> Content::getPropertyValues(
1295 const uno::Sequence
< beans::Property
>& rProperties
,
1296 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
1298 std::unique_ptr
< ContentProperties
> xProps
;
1299 std::unique_ptr
< ContentProperties
> xCachedProps
;
1300 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
1301 OUString aUnescapedTitle
;
1302 bool bHasAll
= false;
1303 uno::Reference
< ucb::XContentIdentifier
> xIdentifier
;
1304 rtl::Reference
< ::ucbhelper::ContentProviderImplHelper
> xProvider
;
1307 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
1309 aUnescapedTitle
= NeonUri::unescape( m_aEscapedTitle
);
1310 xIdentifier
.set( m_xIdentifier
);
1311 xProvider
.set( m_xProvider
.get() );
1312 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
1314 // First, ask cache...
1317 xCachedProps
.reset(new ContentProperties(*m_xCachedProps
));
1319 std::vector
< OUString
> aMissingProps
;
1320 if ( xCachedProps
->containsAllNames( rProperties
, aMissingProps
) )
1322 // All properties are already in cache! No server access needed.
1326 // use the cached ContentProperties instance
1327 xProps
.reset(new ContentProperties(*xCachedProps
));
1331 if ( !m_bTransient
&& !bHasAll
)
1334 // Obtain values from server...
1337 // First, identify whether resource is DAV or not
1338 bool bNetworkAccessAllowed
= true;
1339 ResourceType eType
= getResourceType(
1340 xEnv
, xResAccess
, &bNetworkAccessAllowed
);
1344 // cache lookup... getResourceType may fill the props cache via
1348 xCachedProps
.reset(new ContentProperties(*m_xCachedProps
));
1350 std::vector
< OUString
> aMissingProps
;
1351 if ( xCachedProps
->containsAllNames(
1352 rProperties
, aMissingProps
) )
1354 // All properties are already in cache! No server access
1359 // use the cached ContentProperties instance
1360 xProps
.reset(new ContentProperties(*xCachedProps
));
1365 // Only DAV resources support PROPFIND
1366 std::vector
< OUString
> aPropNames
;
1368 uno::Sequence
< beans::Property
> aProperties(
1369 rProperties
.getLength() );
1371 if ( !m_aFailedPropNames
.empty() )
1373 sal_Int32 nProps
= rProperties
.getLength();
1374 std::copy(rProperties
.begin(), rProperties
.end(), aProperties
.begin());
1376 aProperties
.realloc( nProps
);
1380 aProperties
= rProperties
;
1383 if ( aProperties
.hasElements() )
1384 ContentProperties::UCBNamesToDAVNames(
1385 aProperties
, aPropNames
);
1387 if ( !aPropNames
.empty() )
1389 std::vector
< DAVResource
> resources
;
1392 xResAccess
->PROPFIND(
1393 DAVZERO
, aPropNames
, resources
, xEnv
);
1395 if ( 1 == resources
.size() )
1397 #if defined SAL_LOG_INFO
1399 // print received resources
1400 for ( const auto& rProp
: resources
[0].properties
)
1402 OUString aPropValue
;
1404 uno::Sequence
< ucb::LockEntry
> aSupportedLocks
;
1405 if( rProp
.Value
>>= aPropValue
)
1406 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << rProp
.Name
<< ":" << aPropValue
);
1407 else if( rProp
.Value
>>= bValue
)
1408 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << rProp
.Name
<< ":" <<
1409 ( bValue
? "true" : "false" ) );
1410 else if( rProp
.Value
>>= aSupportedLocks
)
1412 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << rProp
.Name
<< ":" );
1413 for ( sal_Int32 n
= 0; n
< aSupportedLocks
.getLength(); ++n
)
1415 SAL_INFO( "ucb.ucp.webdav"," scope: "
1416 << ( aSupportedLocks
[ n
].Scope
== css::ucb::LockScope_SHARED
? "shared" : "exclusive" )
1418 << ( aSupportedLocks
[ n
].Type
!= css::ucb::LockType_WRITE
? "" : "write" ) );
1425 xProps
->addProperties(
1427 ContentProperties( resources
[ 0 ] ));
1430 new ContentProperties( resources
[ 0 ] ) );
1433 catch ( DAVException
const & e
)
1435 bNetworkAccessAllowed
= bNetworkAccessAllowed
1436 && shouldAccessNetworkAfterException( e
);
1438 if ( !bNetworkAccessAllowed
)
1440 cancelCommandExecution( e
, xEnv
);
1448 if ( bNetworkAccessAllowed
)
1450 // All properties obtained already?
1451 std::vector
< OUString
> aMissingProps
;
1452 if ( !( xProps
.get()
1453 && xProps
->containsAllNames(
1454 rProperties
, aMissingProps
) )
1455 && !m_bDidGetOrHead
)
1457 // Possibly the missing props can be obtained using a HEAD
1460 std::vector
< OUString
> aHeaderNames
;
1461 ContentProperties::UCBNamesToHTTPNames(
1467 // in case of not DAV PROFIND (previously in program flow) failed
1468 // so we need to add the only prop that's common
1469 // to DAV and NON_DAV: MediaType, that maps to Content-Type
1470 aHeaderNames
.emplace_back("Content-Type" );
1473 if (!aHeaderNames
.empty()) try
1475 DAVResource resource
;
1476 GetPropsUsingHeadRequest(resource
, xResAccess
, aHeaderNames
, xEnv
);
1477 m_bDidGetOrHead
= true;
1480 xProps
->addProperties(
1482 ContentProperties(resource
));
1484 xProps
.reset(new ContentProperties(resource
));
1486 if (m_eResourceType
== NON_DAV
)
1487 xProps
->addProperties(aMissingProps
,
1492 catch ( DAVException
const & e
)
1494 // non "general-purpose servers" may not support HEAD requests
1495 // see http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1
1496 // In this case, perform a partial GET only to get the header info
1497 // vid. http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
1498 // WARNING if the server does not support partial GETs,
1499 // the GET will transfer the whole content
1501 DAVException aLastException
= e
;
1502 OUString aTargetURL
= xResAccess
->getURL();
1504 if ( e
.getError() == DAVException::DAV_HTTP_ERROR
)
1506 // According to the spec. the origin server SHOULD return
1507 // * 405 (Method Not Allowed):
1508 // the method is known but not allowed for the requested resource
1509 // * 501 (Not Implemented):
1510 // the method is unrecognized or not implemented
1511 // * 404 (SC_NOT_FOUND)
1512 // is for google-code server and for MS IIS 10.0 Web server
1513 // when only GET is enabled
1514 if ( aLastException
.getStatus() == SC_NOT_IMPLEMENTED
||
1515 aLastException
.getStatus() == SC_METHOD_NOT_ALLOWED
||
1516 aLastException
.getStatus() == SC_NOT_FOUND
)
1518 SAL_WARN( "ucb.ucp.webdav", "HEAD probably not implemented: fall back to a partial GET" );
1519 aStaticDAVOptionsCache
.setHeadAllowed( aTargetURL
, false );
1520 lcl_sendPartialGETRequest( bError
,
1527 m_bDidGetOrHead
= !bError
;
1533 DAVOptions aDAVOptionsException
;
1535 aDAVOptionsException
.setURL( aTargetURL
);
1536 // check if the error was SC_NOT_FOUND, meaning that the
1537 // GET fall back didn't succeeded and the element is really missing
1538 // we will consider the resource SC_GONE (410) for some time
1539 // we use SC_GONE because has the same meaning of SC_NOT_FOUND (404)
1541 // <https://tools.ietf.org/html/rfc7231#section-6.5.9> (retrieved 2016-10-09)
1542 // apparently it's not used to mark the missing HEAD method (so far...)
1543 sal_uInt16 ResponseStatusCode
=
1544 ( aLastException
.getStatus() == SC_NOT_FOUND
) ?
1546 aLastException
.getStatus();
1547 aDAVOptionsException
.setHttpResponseStatusCode( ResponseStatusCode
);
1548 aDAVOptionsException
.setHttpResponseStatusText( aLastException
.getData() );
1549 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptionsException
,
1550 m_nOptsCacheLifeNotFound
);
1552 if ( !shouldAccessNetworkAfterException( aLastException
) )
1554 cancelCommandExecution( aLastException
, xEnv
);
1562 // might trigger HTTP redirect.
1563 // Therefore, title must be updated here.
1564 NeonUri
aUri( xResAccess
->getURL() );
1565 aUnescapedTitle
= aUri
.GetPathBaseNameUnescaped();
1567 if ( eType
== UNKNOWN
)
1569 xProps
.reset( new ContentProperties( aUnescapedTitle
) );
1572 // For DAV resources we only know the Title, for non-DAV
1573 // resources we additionally know that it is a document.
1578 xProps
.reset(new ContentProperties(aUnescapedTitle
));
1580 xProps
->addProperty("Title", uno::makeAny(aUnescapedTitle
), true);
1585 xProps
.reset( new ContentProperties( aUnescapedTitle
, false ) );
1587 xProps
->addProperty(
1589 uno::makeAny( aUnescapedTitle
),
1592 xProps
->addProperty(
1594 uno::makeAny( false ),
1596 xProps
->addProperty(
1598 uno::makeAny( true ),
1604 // No server access for just created (not yet committed) objects.
1605 // Only a minimal set of properties supported at this stage.
1607 xProps
.reset( new ContentProperties( aUnescapedTitle
,
1611 // Add a default for the properties requested but not found.
1612 // Determine still missing properties, add a default.
1613 // Some client function doesn't expect a void uno::Any,
1614 // but instead wants some sort of default.
1615 std::vector
< OUString
> aMissingProps
;
1616 if ( !xProps
->containsAllNames(
1617 rProperties
, aMissingProps
) )
1620 for ( const auto& rProp
: aMissingProps
)
1622 // For the time being only a couple of properties need to be added
1623 if ( rProp
== "DateModified" || rProp
== "DateCreated" )
1625 util::DateTime aDate
;
1626 xProps
->addProperty(
1628 uno::makeAny( aDate
),
1631 // If WebDAV didn't return the resource type, assume default
1632 // This happens e.g. for lists exported by SharePoint
1633 else if ( rProp
== "IsFolder" )
1635 xProps
->addProperty(
1637 uno::makeAny( false ),
1640 else if ( rProp
== "IsDocument" )
1642 xProps
->addProperty(
1644 uno::makeAny( true ),
1650 for ( const auto& rProperty
: rProperties
)
1652 const OUString rName
= rProperty
.Name
;
1653 if ( rName
== "BaseURI" )
1655 // Add BaseURI property, if requested.
1656 xProps
->addProperty(
1658 uno::makeAny( getBaseURI( xResAccess
) ),
1661 else if ( rName
== "CreatableContentsInfo" )
1663 // Add CreatableContentsInfo property, if requested.
1664 bool bFolder
= false;
1668 xProps
->addProperty(
1669 "CreatableContentsInfo",
1670 uno::makeAny( bFolder
1671 ? queryCreatableContentsInfo()
1672 : uno::Sequence
< ucb::ContentInfo
>() ),
1677 uno::Reference
< sdbc::XRow
> xResultRow
1678 = getPropertyValues( m_xContext
,
1682 xIdentifier
->getContentIdentifier() );
1685 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
1687 if (!m_xCachedProps
)
1688 m_xCachedProps
.reset(new CachableContentProperties(*xProps
));
1690 m_xCachedProps
->addProperties(*xProps
);
1692 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
1693 m_aEscapedTitle
= NeonUri::escapeSegment( aUnescapedTitle
);
1700 uno::Sequence
< uno::Any
> Content::setPropertyValues(
1701 const uno::Sequence
< beans::PropertyValue
>& rValues
,
1702 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
1704 uno::Reference
< ucb::XContentIdentifier
> xIdentifier
;
1705 rtl::Reference
< ContentProvider
> xProvider
;
1707 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
1710 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
1712 xProvider
.set( m_pProvider
);
1713 xIdentifier
.set( m_xIdentifier
);
1714 bTransient
= m_bTransient
;
1715 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
1718 uno::Sequence
< uno::Any
> aRet( rValues
.getLength() );
1719 uno::Sequence
< beans::PropertyChangeEvent
> aChanges( rValues
.getLength() );
1720 sal_Int32 nChanged
= 0;
1722 beans::PropertyChangeEvent aEvent
;
1723 aEvent
.Source
= static_cast< cppu::OWeakObject
* >( this );
1724 aEvent
.Further
= false;
1725 // aEvent.PropertyName =
1726 aEvent
.PropertyHandle
= -1;
1727 // aEvent.OldValue =
1728 // aEvent.NewValue =
1730 std::vector
< ProppatchValue
> aProppatchValues
;
1732 uno::Reference
< ucb::XPersistentPropertySet
> xAdditionalPropSet
;
1733 bool bTriedToGetAdditionalPropSet
= false;
1735 bool bExchange
= false;
1738 sal_Int32 nTitlePos
= -1;
1740 uno::Reference
< beans::XPropertySetInfo
> xInfo
;
1742 const beans::PropertyValue
* pValues
= rValues
.getConstArray();
1743 sal_Int32 nCount
= rValues
.getLength();
1744 for ( sal_Int32 n
= 0; n
< nCount
; ++n
)
1746 const beans::PropertyValue
& rValue
= pValues
[ n
];
1747 const OUString
& rName
= rValue
.Name
;
1749 beans::Property aTmpProp
;
1750 xProvider
->getProperty( rName
, aTmpProp
);
1752 if ( aTmpProp
.Attributes
& beans::PropertyAttribute::READONLY
)
1754 // Read-only property!
1755 aRet
[ n
] <<= lang::IllegalAccessException(
1756 "Property is read-only!",
1757 static_cast< cppu::OWeakObject
* >( this ) );
1765 if ( rName
== "ContentType" )
1767 // Read-only property!
1768 aRet
[ n
] <<= lang::IllegalAccessException(
1769 "Property is read-only!",
1770 static_cast< cppu::OWeakObject
* >( this ) );
1772 else if ( rName
== "IsDocument" )
1774 // Read-only property!
1775 aRet
[ n
] <<= lang::IllegalAccessException(
1776 "Property is read-only!",
1777 static_cast< cppu::OWeakObject
* >( this ) );
1779 else if ( rName
== "IsFolder" )
1781 // Read-only property!
1782 aRet
[ n
] <<= lang::IllegalAccessException(
1783 "Property is read-only!",
1784 static_cast< cppu::OWeakObject
* >( this ) );
1786 else if ( rName
== "Title" )
1789 if ( rValue
.Value
>>= aNewValue
)
1792 if ( !aNewValue
.isEmpty() )
1796 NeonUri
aURI( xIdentifier
->getContentIdentifier() );
1797 aOldTitle
= aURI
.GetPathBaseNameUnescaped();
1799 if ( aNewValue
!= aOldTitle
)
1801 // modified title -> modified URL -> exchange !
1805 // new value will be set later...
1806 aNewTitle
= aNewValue
;
1808 // remember position within sequence of values (for
1813 catch ( DAVException
const & )
1815 aRet
[ n
] <<= lang::IllegalArgumentException(
1816 "Invalid content identifier!",
1817 static_cast< cppu::OWeakObject
* >( this ),
1823 aRet
[ n
] <<= lang::IllegalArgumentException(
1824 "Empty title not allowed!",
1825 static_cast< cppu::OWeakObject
* >( this ),
1831 aRet
[ n
] <<= beans::IllegalTypeException(
1832 "Property value has wrong type!",
1833 static_cast< cppu::OWeakObject
* >( this ) );
1842 OUString aSpecialName
;
1843 bool bIsSpecial
= DAVProperties::isUCBSpecialProperty(
1844 rName
, aSpecialName
);
1847 xInfo
= getPropertySetInfo( xEnv
,
1848 false /* don't cache data */ );
1850 if ( !xInfo
->hasPropertyByName(
1851 bIsSpecial
? aSpecialName
: rName
) )
1853 // Check, whether property exists. Skip otherwise.
1854 // PROPPATCH::set would add the property automatically, which
1855 // is not allowed for "setPropertyValues" command!
1856 aRet
[ n
] <<= beans::UnknownPropertyException(
1857 "Property is unknown!",
1858 static_cast< cppu::OWeakObject
* >( this ) );
1862 if ( rName
== "Size" )
1864 // Read-only property!
1865 aRet
[ n
] <<= lang::IllegalAccessException(
1866 "Property is read-only!",
1867 static_cast< cppu::OWeakObject
* >( this ) );
1869 else if ( rName
== "DateCreated" )
1871 // Read-only property!
1872 aRet
[ n
] <<= lang::IllegalAccessException(
1873 "Property is read-only!",
1874 static_cast< cppu::OWeakObject
* >( this ) );
1876 else if ( rName
== "DateModified" )
1878 // Read-only property!
1879 aRet
[ n
] <<= lang::IllegalAccessException(
1880 "Property is read-only!",
1881 static_cast< cppu::OWeakObject
* >( this ) );
1883 else if ( rName
== "MediaType" )
1885 // Read-only property!
1886 // (but could be writable, if 'getcontenttype' would be)
1887 aRet
[ n
] <<= lang::IllegalAccessException(
1888 "Property is read-only!",
1889 static_cast< cppu::OWeakObject
* >( this ) );
1891 if ( rName
== "CreatableContentsInfo" )
1893 // Read-only property!
1894 aRet
[ n
] <<= lang::IllegalAccessException(
1895 "Property is read-only!",
1896 static_cast< cppu::OWeakObject
* >( this ) );
1900 if ( getResourceType( xEnv
, xResAccess
) == DAV
)
1902 // Property value will be set on server.
1903 ProppatchValue
aValue( PROPSET
, rName
, rValue
.Value
);
1904 aProppatchValues
.push_back( aValue
);
1908 // Property value will be stored in local property store.
1909 if ( !bTriedToGetAdditionalPropSet
&&
1910 !xAdditionalPropSet
.is() )
1913 = getAdditionalPropertySet( false );
1914 bTriedToGetAdditionalPropSet
= true;
1917 if ( xAdditionalPropSet
.is() )
1922 = xAdditionalPropSet
->getPropertyValue( rName
);
1923 if ( aOldValue
!= rValue
.Value
)
1925 xAdditionalPropSet
->setPropertyValue(
1926 rName
, rValue
.Value
);
1928 aEvent
.PropertyName
= rName
;
1929 aEvent
.OldValue
= aOldValue
;
1930 aEvent
.NewValue
= rValue
.Value
;
1932 aChanges
.getArray()[ nChanged
] = aEvent
;
1936 catch ( beans::UnknownPropertyException
const & e
)
1940 catch ( lang::WrappedTargetException
const & e
)
1944 catch ( beans::PropertyVetoException
const & e
)
1948 catch ( lang::IllegalArgumentException
const & e
)
1955 aRet
[ n
] <<= uno::Exception(
1956 "No property set for storing the value!",
1957 static_cast< cppu::OWeakObject
* >( this ) );
1964 if ( !bTransient
&& !aProppatchValues
.empty() )
1968 // clean cached value of PROPFIND property names
1969 // PROPPATCH can change them
1970 removeCachedPropertyNames( xResAccess
->getURL() );
1971 // Set property values at server.
1972 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
1973 xResAccess
->PROPPATCH( aProppatchValues
, xEnv
);
1975 for ( const auto& rProppatchValue
: aProppatchValues
)
1977 aEvent
.PropertyName
= rProppatchValue
.name
;
1978 aEvent
.OldValue
= uno::Any(); // @@@ too expensive to obtain!
1979 aEvent
.NewValue
= rProppatchValue
.value
;
1981 aChanges
.getArray()[ nChanged
] = aEvent
;
1985 catch ( DAVException
const & e
)
1987 SAL_WARN( "ucb.ucp.webdav", "Content::setPropertyValues - PROPPATCH failed!" );
1988 cancelCommandExecution( e
, xEnv
);
1995 // Assemble new content identifier...
1997 OUString aNewURL
= getParentURL();
1998 if ( aNewURL
.lastIndexOf( '/' ) != ( aNewURL
.getLength() - 1 ) )
2001 aNewURL
+= NeonUri::escapeSegment( aNewTitle
);
2003 uno::Reference
< ucb::XContentIdentifier
> xNewId
2004 = new ::ucbhelper::ContentIdentifier( aNewURL
);
2006 NeonUri
sourceURI( xIdentifier
->getContentIdentifier() );
2007 NeonUri
targetURI( xNewId
->getContentIdentifier() );
2011 targetURI
.SetScheme( sourceURI
.GetScheme() );
2013 // clean cached value of PROPFIND property names
2014 removeCachedPropertyNames( sourceURI
.GetURI() );
2015 removeCachedPropertyNames( targetURI
.GetURI() );
2016 aStaticDAVOptionsCache
.removeDAVOptions( sourceURI
.GetURI() );
2017 aStaticDAVOptionsCache
.removeDAVOptions( targetURI
.GetURI() );
2019 sourceURI
.GetPath(), targetURI
.GetURI(), false, xEnv
);
2021 // @@@ Should check for resources that could not be moved
2022 // (due to source access or target overwrite) and send
2023 // this information through the interaction handler.
2025 // @@@ Existing content should be checked to see if it needs
2026 // to be deleted at the source
2028 // @@@ Existing content should be checked to see if it has
2029 // been overwritten at the target
2031 if ( exchangeIdentity( xNewId
) )
2033 xResAccess
->setURL( aNewURL
);
2035 // DAV resources store all additional props on server!
2036 // // Adapt Additional Core Properties.
2037 // renameAdditionalPropertySet( xOldId->getContentIdentifier(),
2038 // xNewId->getContentIdentifier(),
2043 // Do not set new title!
2047 aRet
[ nTitlePos
] <<= uno::Exception(
2049 static_cast< cppu::OWeakObject
* >( this ) );
2052 catch ( DAVException
const & e
)
2054 // Do not set new title!
2058 aRet
[ nTitlePos
] = MapDAVException( e
, true );
2062 if ( !aNewTitle
.isEmpty() )
2064 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2066 aEvent
.PropertyName
= "Title";
2067 aEvent
.OldValue
<<= aOldTitle
;
2068 aEvent
.NewValue
<<= aNewTitle
;
2070 m_aEscapedTitle
= NeonUri::escapeSegment( aNewTitle
);
2072 aChanges
.getArray()[ nChanged
] = aEvent
;
2078 aChanges
.realloc( nChanged
);
2079 notifyPropertiesChange( aChanges
);
2083 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2084 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
2091 uno::Any
Content::open(
2092 const ucb::OpenCommandArgument3
& rArg
,
2093 const uno::Reference
< ucb::XCommandEnvironment
> & xEnv
)
2097 bool bOpenFolder
= ( ( rArg
.Mode
== ucb::OpenMode::ALL
) ||
2098 ( rArg
.Mode
== ucb::OpenMode::FOLDERS
) ||
2099 ( rArg
.Mode
== ucb::OpenMode::DOCUMENTS
) );
2102 if ( isFolder( xEnv
) )
2106 uno::Reference
< ucb::XDynamicResultSet
> xSet
2107 = new DynamicResultSet( m_xContext
, this, rArg
, xEnv
);
2112 // Error: Not a folder!
2114 OUStringBuffer aMsg
;
2115 if ( getResourceType( xEnv
) == FTP
)
2117 aMsg
.append( "FTP over HTTP proxy: resource cannot "
2118 "be opened as folder! Wrong Open Mode!" );
2122 aMsg
.append( "Non-folder resource cannot be "
2123 "opened as folder! Wrong Open Mode!" );
2126 ucbhelper::cancelCommandExecution(
2128 lang::IllegalArgumentException(
2129 aMsg
.makeStringAndClear(),
2130 static_cast< cppu::OWeakObject
* >( this ),
2137 if ( rArg
.Sink
.is() )
2141 if ( ( rArg
.Mode
== ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE
) ||
2142 ( rArg
.Mode
== ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE
) )
2144 // Currently(?) unsupported.
2145 ucbhelper::cancelCommandExecution(
2147 ucb::UnsupportedOpenModeException(
2149 static_cast< cppu::OWeakObject
* >( this ),
2150 sal_Int16( rArg
.Mode
) ) ),
2155 uno::Reference
< io::XOutputStream
> xOut( rArg
.Sink
, uno::UNO_QUERY
);
2161 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2164 osl::MutexGuard
aGuard( m_aMutex
);
2166 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
2169 xResAccess
->setFlags( rArg
.OpeningFlags
);
2170 DAVResource aResource
;
2171 std::vector
< OUString
> aHeaders
;
2173 removeCachedPropertyNames( xResAccess
->getURL() );
2174 xResAccess
->GET( xOut
, aHeaders
, aResource
, xEnv
);
2175 m_bDidGetOrHead
= true;
2178 osl::MutexGuard
aGuard( m_aMutex
);
2181 if (!m_xCachedProps
)
2182 m_xCachedProps
.reset(
2183 new CachableContentProperties( ContentProperties( aResource
) ) );
2185 m_xCachedProps
->addProperties( ContentProperties( aResource
) );
2187 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
2190 catch ( DAVException
const & e
)
2192 cancelCommandExecution( e
, xEnv
);
2198 uno::Reference
< io::XActiveDataSink
> xDataSink( rArg
.Sink
, uno::UNO_QUERY
);
2199 if ( xDataSink
.is() )
2201 // PULL: wait for client read
2202 OUString aTargetURL
= m_xIdentifier
->getContentIdentifier();
2205 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2207 osl::MutexGuard
aGuard( m_aMutex
);
2209 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
2211 xResAccess
->setFlags( rArg
.OpeningFlags
);
2213 // fill inputstream sync; return if all data present
2214 DAVResource aResource
;
2215 std::vector
< OUString
> aHeaders
;
2217 aTargetURL
= xResAccess
->getURL();
2218 removeCachedPropertyNames( aTargetURL
);
2219 // check if the resource was present on the server
2220 // first update it, if necessary
2221 // if the open is called directly, without the default open sequence,
2222 // e.g. the one used when opening a file looking for properties
2223 // first this call will have no effect, since OPTIONS would have already been called
2224 // as a consequence of getPropertyValues()
2225 DAVOptions aDAVOptions
;
2226 getResourceOptions( xEnv
, aDAVOptions
, xResAccess
);
2228 if ( aDAVOptions
.getHttpResponseStatusCode() != SC_NONE
)
2230 // throws exception as if there was a server error, a DAV exception
2231 throw DAVException( DAVException::DAV_HTTP_ERROR
,
2232 aDAVOptions
.getHttpResponseStatusText(),
2233 aDAVOptions
.getHttpResponseStatusCode() );
2235 uno::Reference
< io::XInputStream
> xIn
2236 = xResAccess
->GET( aHeaders
, aResource
, xEnv
);
2237 m_bDidGetOrHead
= true;
2240 osl::MutexGuard
aGuard( m_aMutex
);
2243 if (!m_xCachedProps
)
2244 m_xCachedProps
.reset(
2245 new CachableContentProperties( ContentProperties( aResource
) ) );
2247 m_xCachedProps
->addProperties(
2248 aResource
.properties
);
2250 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
2253 xDataSink
->setInputStream( xIn
);
2255 catch ( DAVException
const & e
)
2257 //TODO cache the http error if not yet cached
2258 cancelCommandExecution( e
, xEnv
);
2264 // Note: aOpenCommand.Sink may contain an XStream
2265 // implementation. Support for this type of
2266 // sink is optional...
2267 ucbhelper::cancelCommandExecution(
2269 ucb::UnsupportedDataSinkException(
2271 static_cast< cppu::OWeakObject
* >( this ),
2283 const ucb::PostCommandArgument2
& rArg
,
2284 const uno::Reference
< ucb::XCommandEnvironment
> & xEnv
)
2286 uno::Reference
< io::XActiveDataSink
> xSink( rArg
.Sink
, uno::UNO_QUERY
);
2291 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2293 osl::MutexGuard
aGuard( m_aMutex
);
2294 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
2297 removeCachedPropertyNames( xResAccess
->getURL() );
2298 uno::Reference
< io::XInputStream
> xResult
2299 = xResAccess
->POST( rArg
.MediaType
,
2305 osl::MutexGuard
aGuard( m_aMutex
);
2306 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
2309 xSink
->setInputStream( xResult
);
2311 catch ( DAVException
const & e
)
2313 cancelCommandExecution( e
, xEnv
, true );
2319 uno::Reference
< io::XOutputStream
> xResult( rArg
.Sink
, uno::UNO_QUERY
);
2324 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2326 osl::MutexGuard
aGuard( m_aMutex
);
2327 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
2330 removeCachedPropertyNames( xResAccess
->getURL() );
2331 xResAccess
->POST( rArg
.MediaType
,
2338 osl::MutexGuard
aGuard( m_aMutex
);
2339 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
2342 catch ( DAVException
const & e
)
2344 cancelCommandExecution( e
, xEnv
, true );
2350 ucbhelper::cancelCommandExecution(
2352 ucb::UnsupportedDataSinkException(
2354 static_cast< cppu::OWeakObject
* >( this ),
2363 void Content::queryChildren( ContentRefList
& rChildren
)
2365 // Obtain a list with a snapshot of all currently instantiated contents
2366 // from provider and extract the contents which are direct children
2369 ::ucbhelper::ContentRefList aAllContents
;
2370 m_xProvider
->queryExistingContents( aAllContents
);
2372 OUString aURL
= m_xIdentifier
->getContentIdentifier();
2373 sal_Int32 nURLPos
= aURL
.lastIndexOf( '/' );
2375 if ( nURLPos
!= ( aURL
.getLength() - 1 ) )
2377 // No trailing slash found. Append.
2381 sal_Int32 nLen
= aURL
.getLength();
2383 for ( const auto& rChild
: aAllContents
)
2385 ::ucbhelper::ContentImplHelperRef xChild
= rChild
;
2387 = xChild
->getIdentifier()->getContentIdentifier();
2389 // Is aURL a prefix of aChildURL?
2390 if ( ( aChildURL
.getLength() > nLen
) &&
2391 ( aChildURL
.startsWith( aURL
) ) )
2393 sal_Int32 nPos
= aChildURL
.indexOf( '/', nLen
);
2395 if ( ( nPos
== -1 ) ||
2396 ( nPos
== ( aChildURL
.getLength() - 1 ) ) )
2398 // No further slashes / only a final slash. It's a child!
2399 rChildren
.emplace_back(
2400 static_cast< ::webdav_ucp::Content
* >(
2408 void Content::insert(
2409 const uno::Reference
< io::XInputStream
> & xInputStream
,
2410 bool bReplaceExisting
,
2411 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
2413 bool bTransient
, bCollection
;
2414 OUString aEscapedTitle
;
2415 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2418 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2420 bTransient
= m_bTransient
;
2421 bCollection
= m_bCollection
;
2422 aEscapedTitle
= m_aEscapedTitle
;
2423 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
2426 // Check, if all required properties are present.
2428 if ( aEscapedTitle
.isEmpty() )
2430 SAL_WARN( "ucb.ucp.webdav", "Content::insert - Title missing!" );
2432 uno::Sequence
<OUString
> aProps
{ "Title" };
2433 ucbhelper::cancelCommandExecution(
2434 uno::makeAny( ucb::MissingPropertiesException(
2436 static_cast< cppu::OWeakObject
* >( this ),
2442 if ( !bReplaceExisting
)
2444 /* [RFC 2616] - HTTP
2446 The PUT method requests that the enclosed entity be stored under the
2447 supplied Request-URI. If the Request-URI refers to an already
2448 existing resource, the enclosed entity SHOULD be considered as a
2449 modified version of the one residing on the origin server.
2452 /* [RFC 2518] - WebDAV
2454 MKCOL creates a new collection resource at the location specified by
2455 the Request-URI. If the resource identified by the Request-URI is
2456 non-null then the MKCOL MUST fail.
2459 // ==> Complain on PUT, continue on MKCOL.
2460 if ( !bTransient
|| !bCollection
)
2462 ucb::UnsupportedNameClashException
aEx(
2463 "Unable to write without overwrite!",
2464 static_cast< cppu::OWeakObject
* >( this ),
2465 ucb::NameClash::ERROR
);
2467 uno::Reference
< task::XInteractionHandler
> xIH
;
2469 if ( Environment
.is() )
2470 xIH
= Environment
->getInteractionHandler();
2478 uno::Any
aExAsAny( uno::makeAny( aEx
) );
2480 rtl::Reference
< ucbhelper::SimpleInteractionRequest
> xRequest
2481 = new ucbhelper::SimpleInteractionRequest(
2483 ContinuationFlags::Approve
| ContinuationFlags::Disapprove
);
2484 xIH
->handle( xRequest
.get() );
2486 const ContinuationFlags nResp
= xRequest
->getResponse();
2490 case ContinuationFlags::NONE
:
2491 // Not handled; throw.
2495 case ContinuationFlags::Approve
:
2496 // Continue -> Overwrite.
2497 bReplaceExisting
= true;
2500 case ContinuationFlags::Disapprove
:
2502 throw ucb::CommandFailedException(
2504 uno::Reference
< uno::XInterface
>(),
2509 SAL_WARN( "ucb.ucp.webdav", "Content::insert - "
2510 "Unknown interaction selection!" );
2511 throw ucb::CommandFailedException(
2512 "Unknown interaction selection!",
2513 uno::Reference
< uno::XInterface
>(),
2523 // Assemble new content identifier...
2524 OUString aURL
= getParentURL();
2525 if ( aURL
.lastIndexOf( '/' ) != ( aURL
.getLength() - 1 ) )
2528 aURL
+= aEscapedTitle
;
2532 xResAccess
->setURL( aURL
);
2536 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
2537 removeCachedPropertyNames( xResAccess
->getURL() );
2538 xResAccess
->MKCOL( Environment
);
2542 // remove options from cache, PUT may change it
2543 // it will be refreshed when needed
2544 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
2545 removeCachedPropertyNames( xResAccess
->getURL() );
2546 xResAccess
->PUT( xInputStream
, Environment
);
2547 // clean cached value of PROPFIND properties names
2549 // no error , set the resourcetype to unknown type
2550 // the resource may have transitioned from NOT FOUND or UNKNOWN to something else
2551 // depending on the server behaviour
2552 // this will force a recheck of the resource type
2553 m_eResourceType
= UNKNOWN
;
2554 m_eResourceTypeForLocks
= UNKNOWN
;
2556 catch ( DAVException
const & except
)
2560 if ( except
.getStatus() == SC_METHOD_NOT_ALLOWED
)
2562 // [RFC 2518] - WebDAV
2563 // 405 (Method Not Allowed) - MKCOL can only be
2564 // executed on a deleted/non-existent resource.
2566 if ( bReplaceExisting
)
2568 // Destroy old resource.
2571 removeCachedPropertyNames( xResAccess
->getURL() );
2572 xResAccess
->DESTROY( Environment
);
2574 catch ( DAVException
const & e
)
2576 cancelCommandExecution( e
, Environment
, true );
2580 // Insert (recursion!).
2581 insert( xInputStream
, bReplaceExisting
, Environment
);
2584 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2585 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
2596 NeonUri
aURI( aURL
);
2597 aTitle
= aURI
.GetPathBaseNameUnescaped();
2599 catch ( DAVException
const & )
2603 ucbhelper::cancelCommandExecution(
2605 ucb::NameClashException(
2607 static_cast< cppu::OWeakObject
* >( this ),
2608 task::InteractionClassification_ERROR
,
2616 cancelCommandExecution( except
, Environment
, true );
2621 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2622 m_xIdentifier
= new ::ucbhelper::ContentIdentifier( aURL
);
2628 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2629 m_bTransient
= false;
2634 if ( !xInputStream
.is() )
2636 ucbhelper::cancelCommandExecution(
2638 ucb::MissingInputStreamException(
2640 static_cast< cppu::OWeakObject
* >( this ) ) ),
2645 // save the URL since it may change due to redirection
2646 OUString aTargetUrl
= xResAccess
->getURL();
2649 removeCachedPropertyNames( xResAccess
->getURL() );
2650 // remove options from cache, PUT may change it
2651 // it will be refreshed when needed
2652 aStaticDAVOptionsCache
.removeDAVOptions( aTargetUrl
);
2653 xResAccess
->PUT( xInputStream
, Environment
);
2655 catch ( DAVException
const & e
)
2657 cancelCommandExecution( e
, Environment
, true );
2663 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2664 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
2669 void Content::transfer(
2670 const ucb::TransferInfo
& rArgs
,
2671 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
2673 uno::Reference
< ucb::XContentIdentifier
> xIdentifier
;
2674 uno::Reference
< ucb::XContentProvider
> xProvider
;
2675 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
2678 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2680 xIdentifier
.set( m_xIdentifier
);
2681 xProvider
.set( m_xProvider
.get() );
2682 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
2685 NeonUri
sourceURI( rArgs
.SourceURL
);
2686 NeonUri
targetURI( xIdentifier
->getContentIdentifier() );
2688 OUString aTargetURI
;
2691 aTargetURI
= targetURI
.GetPathBaseNameUnescaped();
2693 // Check source's and target's URL scheme
2695 OUString aScheme
= sourceURI
.GetScheme().toAsciiLowerCase();
2696 if ( aScheme
== VNDSUNSTARWEBDAV_URL_SCHEME
)
2698 sourceURI
.SetScheme( HTTP_URL_SCHEME
);
2700 else if ( aScheme
== VNDSUNSTARWEBDAVS_URL_SCHEME
)
2702 sourceURI
.SetScheme( HTTPS_URL_SCHEME
);
2704 else if ( aScheme
== DAV_URL_SCHEME
)
2706 sourceURI
.SetScheme( HTTP_URL_SCHEME
);
2708 else if ( aScheme
== DAVS_URL_SCHEME
)
2710 sourceURI
.SetScheme( HTTPS_URL_SCHEME
);
2712 else if ( aScheme
== WEBDAV_URL_SCHEME
)
2714 sourceURI
.SetScheme( HTTP_URL_SCHEME
);
2716 else if ( aScheme
== WEBDAVS_URL_SCHEME
)
2718 sourceURI
.SetScheme( HTTPS_URL_SCHEME
);
2722 if ( aScheme
!= HTTP_URL_SCHEME
&& aScheme
!= HTTPS_URL_SCHEME
)
2724 ucbhelper::cancelCommandExecution(
2726 ucb::InteractiveBadTransferURLException(
2727 "Unsupported URL scheme!",
2728 static_cast< cppu::OWeakObject
* >( this ) ) ),
2734 aScheme
= targetURI
.GetScheme().toAsciiLowerCase();
2735 if ( aScheme
== VNDSUNSTARWEBDAV_URL_SCHEME
)
2736 targetURI
.SetScheme( HTTP_URL_SCHEME
);
2737 else if ( aScheme
== VNDSUNSTARWEBDAVS_URL_SCHEME
)
2738 targetURI
.SetScheme( HTTPS_URL_SCHEME
);
2739 else if ( aScheme
== DAV_URL_SCHEME
)
2740 targetURI
.SetScheme( HTTP_URL_SCHEME
);
2741 else if ( aScheme
== DAVS_URL_SCHEME
)
2742 targetURI
.SetScheme( HTTPS_URL_SCHEME
);
2743 else if ( aScheme
== WEBDAV_URL_SCHEME
)
2744 targetURI
.SetScheme( HTTP_URL_SCHEME
);
2745 else if ( aScheme
== WEBDAVS_URL_SCHEME
)
2746 targetURI
.SetScheme( HTTPS_URL_SCHEME
);
2748 // @@@ This implementation of 'transfer' only works
2749 // if the source and target are located at same host.
2750 // (Neon does not support cross-server copy/move)
2752 // Check for same host
2754 if ( !sourceURI
.GetHost().isEmpty() &&
2755 ( sourceURI
.GetHost() != targetURI
.GetHost() ) )
2757 ucbhelper::cancelCommandExecution(
2758 uno::makeAny( ucb::InteractiveBadTransferURLException(
2760 static_cast< cppu::OWeakObject
* >( this ) ) ),
2765 OUString aTitle
= rArgs
.NewTitle
;
2767 if ( aTitle
.isEmpty() )
2768 aTitle
= sourceURI
.GetPathBaseNameUnescaped();
2770 if ( aTitle
== "/" )
2776 targetURI
.AppendPath( aTitle
);
2778 OUString aTargetURL
= xIdentifier
->getContentIdentifier();
2779 if ( ( aTargetURL
.lastIndexOf( '/' ) + 1 )
2780 != aTargetURL
.getLength() )
2783 aTargetURL
+= aTitle
;
2785 uno::Reference
< ucb::XContentIdentifier
> xTargetId
2786 = new ::ucbhelper::ContentIdentifier( aTargetURL
);
2788 DAVResourceAccess
aSourceAccess( m_xContext
,
2789 xResAccess
->getSessionFactory(),
2790 sourceURI
.GetURI() );
2792 if ( rArgs
.MoveData
)
2794 uno::Reference
< ucb::XContentIdentifier
> xId
2795 = new ::ucbhelper::ContentIdentifier( rArgs
.SourceURL
);
2797 // Note: The static cast is okay here, because its sure that
2798 // xProvider is always the WebDAVContentProvider.
2799 rtl::Reference
< Content
> xSource
2800 = static_cast< Content
* >(
2801 xProvider
->queryContent( xId
).get() );
2803 // [RFC 2518] - WebDAV
2804 // If a resource exists at the destination and the Overwrite
2805 // header is "T" then prior to performing the move the server
2806 // MUST perform a DELETE with "Depth: infinity" on the
2807 // destination resource. If the Overwrite header is set to
2808 // "F" then the operation will fail.
2810 aStaticDAVOptionsCache
.removeDAVOptions( sourceURI
.GetURI() );
2811 aStaticDAVOptionsCache
.removeDAVOptions( targetURI
.GetURI() );
2812 aSourceAccess
.MOVE( sourceURI
.GetPath(),
2815 == ucb::NameClash::OVERWRITE
,
2820 // Propagate destruction to listeners.
2821 xSource
->destroy( true );
2824 // DAV resources store all additional props on server!
2825 // // Rename own and all children's Additional Core Properties.
2826 // renameAdditionalPropertySet( xId->getContentIdentifier(),
2827 // xTargetId->getContentIdentifier(),
2832 // [RFC 2518] - WebDAV
2833 // If a resource exists at the destination and the Overwrite
2834 // header is "T" then prior to performing the copy the server
2835 // MUST perform a DELETE with "Depth: infinity" on the
2836 // destination resource. If the Overwrite header is set to
2837 // "F" then the operation will fail.
2839 aStaticDAVOptionsCache
.removeDAVOptions( sourceURI
.GetURI() );
2840 aStaticDAVOptionsCache
.removeDAVOptions( targetURI
.GetURI() );
2841 aSourceAccess
.COPY( sourceURI
.GetPath(),
2844 == ucb::NameClash::OVERWRITE
,
2847 // DAV resources store all additional props on server!
2848 // // Copy own and all children's Additional Core Properties.
2849 // copyAdditionalPropertySet( xId->getContentIdentifier(),
2850 // xTargetId->getContentIdentifier(),
2854 // Note: The static cast is okay here, because its sure that
2855 // xProvider is always the WebDAVContentProvider.
2856 rtl::Reference
< Content
> xTarget
2857 = static_cast< Content
* >(
2858 xProvider
->queryContent( xTargetId
).get() );
2860 // Announce transferred content in its new folder.
2861 xTarget
->inserted();
2863 catch ( ucb::IllegalIdentifierException
const & )
2867 catch ( DAVException
const & e
)
2869 // [RFC 2518] - WebDAV
2870 // 412 (Precondition Failed) - The server was unable to maintain
2871 // the liveness of the properties listed in the propertybehavior
2872 // XML element or the Overwrite header is "F" and the state of
2873 // the destination resource is non-null.
2875 if ( e
.getStatus() == SC_PRECONDITION_FAILED
)
2877 switch ( rArgs
.NameClash
)
2879 case ucb::NameClash::ERROR
:
2881 ucbhelper::cancelCommandExecution(
2883 ucb::NameClashException(
2885 static_cast< cppu::OWeakObject
* >( this ),
2886 task::InteractionClassification_ERROR
,
2889 [[fallthrough
]]; // Unreachable
2892 case ucb::NameClash::OVERWRITE
:
2895 case ucb::NameClash::KEEP
: // deprecated
2896 case ucb::NameClash::RENAME
:
2897 case ucb::NameClash::ASK
:
2900 ucbhelper::cancelCommandExecution(
2902 ucb::UnsupportedNameClashException(
2904 static_cast< cppu::OWeakObject
* >( this ),
2905 rArgs
.NameClash
) ),
2912 cancelCommandExecution( e
, Environment
, true );
2917 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2918 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
2923 void Content::destroy( bool bDeletePhysical
)
2925 // @@@ take care about bDeletePhysical -> trashcan support
2926 uno::Reference
< ucb::XContent
> xThis
= this;
2930 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
2932 // Process instantiated children...
2934 ::webdav_ucp::Content::ContentRefList aChildren
;
2935 queryChildren( aChildren
);
2937 for ( auto& rChild
: aChildren
)
2939 rChild
->destroy( bDeletePhysical
);
2943 // returns the resource type, to be checked for locks
2944 Content::ResourceType
Content::resourceTypeForLocks(
2945 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
,
2946 const std::unique_ptr
< DAVResourceAccess
> & rResAccess
)
2948 ResourceType eResourceTypeForLocks
= UNKNOWN
;
2950 osl::MutexGuard
g(m_aMutex
);
2951 //check if cache contains what we need, usually the first PROPFIND on the URI has supported lock
2952 std::unique_ptr
< ContentProperties
> xProps
;
2955 uno::Sequence
< ucb::LockEntry
> aSupportedLocks
;
2956 if ( m_xCachedProps
->getValue( DAVProperties::SUPPORTEDLOCK
)
2957 >>= aSupportedLocks
) //get the cached value for supportedlock
2959 for ( const auto& rSupportedLock
: std::as_const(aSupportedLocks
) )
2961 if ( rSupportedLock
.Scope
2962 == ucb::LockScope_EXCLUSIVE
&&
2964 == ucb::LockType_WRITE
)
2965 eResourceTypeForLocks
= DAV
;
2971 const OUString
& rURL
= m_xIdentifier
->getContentIdentifier();
2973 if ( eResourceTypeForLocks
== UNKNOWN
)
2975 // resource type for lock/unlock operations still unknown, need to ask the server
2977 const OUString
aScheme(
2978 rURL
.copy( 0, rURL
.indexOf( ':' ) ).toAsciiLowerCase() );
2980 if ( aScheme
== FTP_URL_SCHEME
)
2982 eResourceTypeForLocks
= FTP
;
2986 DAVOptions aDAVOptions
;
2987 getResourceOptions( Environment
, aDAVOptions
, rResAccess
);
2988 if( aDAVOptions
.isClass1() ||
2989 aDAVOptions
.isClass2() ||
2990 aDAVOptions
.isClass3() )
2992 // this is at least a DAV, lock to be confirmed
2993 // class 2 is needed for full lock support
2995 // <https://tools.ietf.org/html/rfc4918#section-18.2>
2996 eResourceTypeForLocks
= DAV_NOLOCK
;
2997 if( aDAVOptions
.isClass2() )
2999 // ok, possible lock, check for it
3002 // we need only DAV:supportedlock
3003 std::vector
< DAVResource
> resources
;
3004 std::vector
< OUString
> aPropNames
;
3005 uno::Sequence
< beans::Property
> aProperties( 1 );
3006 aProperties
[ 0 ].Name
= DAVProperties::SUPPORTEDLOCK
;
3008 ContentProperties::UCBNamesToDAVNames( aProperties
, aPropNames
);
3009 rResAccess
->PROPFIND( DAVZERO
, aPropNames
, resources
, Environment
);
3011 bool wasSupportedlockFound
= false;
3013 // only one resource should be returned
3014 if ( resources
.size() == 1 )
3016 // we may have received a bunch of other properties
3017 // (some servers seems to do so)
3018 // but we need only supported lock for this check
3019 // all returned properties are in
3020 // resources.properties[n].Name/.Value
3022 for ( const auto& rProp
: resources
[0].properties
)
3024 if ( rProp
.Name
== DAVProperties::SUPPORTEDLOCK
)
3026 wasSupportedlockFound
= true;
3027 uno::Sequence
< ucb::LockEntry
> aSupportedLocks
;
3028 if ( rProp
.Value
>>= aSupportedLocks
)
3030 bool isSupported
= std::any_of(aSupportedLocks
.begin(), aSupportedLocks
.end(),
3031 [](const ucb::LockEntry
& rLock
) {
3032 // TODO: if the lock type is changed from 'exclusive write' to 'shared write'
3033 // e.g. to implement 'Calc shared file feature', the ucb::LockScope_EXCLUSIVE
3034 // value should be checked as well, adaptation the code may be needed
3035 return rLock
.Scope
== ucb::LockScope_EXCLUSIVE
3036 && rLock
.Type
== ucb::LockType_WRITE
;
3040 // requested locking mode is supported
3041 eResourceTypeForLocks
= DAV
;
3042 SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks - URL: <"
3043 << m_xIdentifier
->getContentIdentifier() << ">, DAV lock/unlock supported");
3052 // PROPFIND failed; check if HEAD contains Content-Disposition: attachment (RFC1806, HTTP/1.1 19.5.1),
3053 // which supposedly means no lock for the resource (happens e.g. with SharePoint exported lists)
3054 OUString sContentDisposition
;
3055 // First, check cached properties
3058 if ((m_xCachedProps
->getValue("Content-Disposition") >>= sContentDisposition
)
3059 && sContentDisposition
.startsWithIgnoreAsciiCase("attachment"))
3061 eResourceTypeForLocks
= DAV_NOLOCK
;
3062 wasSupportedlockFound
= true;
3065 // If no data in cache, try HEAD request
3066 if (sContentDisposition
.isEmpty() && !m_bDidGetOrHead
) try
3068 DAVResource resource
;
3069 GetPropsUsingHeadRequest(resource
, rResAccess
, {"Content-Disposition"}, Environment
);
3070 m_bDidGetOrHead
= true;
3071 for (const auto& it
: resource
.properties
)
3073 if (it
.Name
.equalsIgnoreAsciiCase("Content-Disposition"))
3075 if ((it
.Value
>>= sContentDisposition
) && sContentDisposition
.equalsIgnoreAsciiCase("attachment"))
3077 eResourceTypeForLocks
= DAV_NOLOCK
;
3078 wasSupportedlockFound
= true;
3086 // check if this is still only a DAV_NOLOCK
3087 // a fallback for resources that do not have DAVProperties::SUPPORTEDLOCK property
3088 // we check for the returned OPTION if LOCK is allowed on the resource
3089 if ( !wasSupportedlockFound
&& eResourceTypeForLocks
== DAV_NOLOCK
)
3091 SAL_INFO( "ucb.ucp.webdav", "This WebDAV server has no supportedlock property, check for allowed LOCK method in OPTIONS" );
3092 // ATTENTION: if the lock type is changed from 'exclusive write' to 'shared write'
3093 // e.g. to implement 'Calc shared file feature' on WebDAV directly, and we arrive to this fallback
3094 // and the LOCK is allowed, we should assume that only exclusive write lock is available
3095 // this is just a reminder...
3096 if ( aDAVOptions
.isLockAllowed() )
3097 eResourceTypeForLocks
= DAV
;
3100 catch ( DAVException
const & e
)
3102 rResAccess
->resetUri();
3103 //grab the error code
3104 switch( e
.getStatus() )
3107 SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
3108 << m_xIdentifier
->getContentIdentifier() << "> was not found. ");
3109 eResourceTypeForLocks
= NOT_FOUND
;
3111 // some servers returns SC_FORBIDDEN, instead
3112 // the meaning of SC_FORBIDDEN is, according to <http://tools.ietf.org/html/rfc7231#section-6.5.3>:
3113 // The 403 (Forbidden) status code indicates that the server understood
3114 // the request but refuses to authorize it
3116 // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3117 // part of base http 1.1 RFCs
3118 case SC_NOT_IMPLEMENTED
: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3119 case SC_METHOD_NOT_ALLOWED
: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3120 // they all mean the resource is NON_DAV
3121 SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException (SC_FORBIDDEN, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3122 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3123 eResourceTypeForLocks
= NON_DAV
;
3127 SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException - URL: <"
3128 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3129 eResourceTypeForLocks
= UNKNOWN
;
3135 eResourceTypeForLocks
= NON_DAV
;
3139 osl::MutexGuard
g(m_aMutex
);
3140 if (m_eResourceTypeForLocks
== UNKNOWN
)
3142 m_eResourceTypeForLocks
= eResourceTypeForLocks
;
3147 eResourceTypeForLocks
!= m_eResourceTypeForLocks
, "ucb.ucp.webdav",
3148 "different resource types for <" << rURL
<< ">: "
3149 << +eResourceTypeForLocks
<< " vs. " << +m_eResourceTypeForLocks
);
3151 SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
3152 << m_xIdentifier
->getContentIdentifier() << ">, m_eResourceTypeForLocks: " << m_eResourceTypeForLocks
);
3153 return m_eResourceTypeForLocks
;
3156 Content::ResourceType
Content::resourceTypeForLocks(
3157 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
3159 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
3161 osl::MutexGuard
aGuard( m_aMutex
);
3162 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
3164 Content::ResourceType ret
= resourceTypeForLocks( Environment
, xResAccess
);
3166 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3167 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
3173 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
3175 // prepare aURL to be used in exception, see below
3179 aURL
= getParentURL();
3180 if ( aURL
.lastIndexOf('/') != ( aURL
.getLength() - 1 ) )
3183 aURL
+= m_aEscapedTitle
;
3187 aURL
= m_xIdentifier
->getContentIdentifier();
3192 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
3194 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3195 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
3200 <<= OUString("LibreOffice - http://www.libreoffice.org/");
3203 ucb::LockScope_EXCLUSIVE
,
3204 ucb::LockType_WRITE
,
3205 ucb::LockDepth_ZERO
,
3207 180, // lock timeout in secs
3208 //-1, // infinite lock
3209 uno::Sequence
< OUString
>() );
3211 // OPTIONS may change as a consequence of the lock operation
3212 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
3213 removeCachedPropertyNames( xResAccess
->getURL() );
3214 xResAccess
->LOCK( aLock
, Environment
);
3217 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3218 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
3221 catch ( DAVException
const & e
)
3223 // check if the exception thrown is 'already locked'
3224 // this exception is mapped directly to the ucb correct one, without
3225 // going into the cancelCommandExecution() user interaction
3226 // this exception should be managed by the issuer of 'lock' command
3227 switch( e
.getError() )
3229 case DAVException::DAV_LOCKED
:
3231 SAL_WARN( "ucb.ucp.webdav", "lock() resource already locked - URL: <"
3232 << m_xIdentifier
->getContentIdentifier() << ">");
3234 ucb::InteractiveLockingLockedException(
3236 static_cast< cppu::OWeakObject
* >( this ),
3237 task::InteractionClassification_ERROR
,
3242 case DAVException::DAV_HTTP_AUTH
:
3244 SAL_WARN( "ucb.ucp.webdav", "lock() DAVException Authentication error - URL: <"
3245 << m_xIdentifier
->getContentIdentifier() << ">" );
3246 // DAVException::DAV_HTTP_AUTH exception can mean:
3247 // - interaction handler for credential management not present (happens, depending
3248 // on the LO framework processing)
3249 // - the remote site is a WebDAV with special configuration: read/only for read operations
3250 // and read/write for write operations, the user is not allowed to lock/write and
3251 // she cancelled the credentials request.
3252 // this is not actually an error, but the exception is sent directly from here, avoiding the automatic
3253 // management that takes part in cancelCommandExecution() below
3254 // Unfortunately there is no InteractiveNetwork*Exception available to signal this
3255 // since it mostly happens on read/only part of webdav, this appears to be the most correct exception available
3257 ucb::InteractiveNetworkWriteException(
3258 "Authentication error while trying to lock! Write only WebDAV perhaps?",
3259 static_cast< cppu::OWeakObject
* >( this ),
3260 task::InteractionClassification_ERROR
,
3264 case DAVException::DAV_HTTP_ERROR
:
3265 //grab the error code
3266 switch( e
.getStatus() )
3268 // The 'case SC_NOT_FOUND' just below tries to solve a problem in eXo Platform
3269 // WebDAV connector which apparently fail on resource first creation
3270 // rfc4918 section-7.3 (see link below)
3271 case SC_NOT_FOUND
: // <http://tools.ietf.org/html/rfc7231#section-6.5.4>
3272 // The 'case SC_PRECONDITION_FAILED' just below tries to solve a problem
3273 // in SharePoint when locking the resource on first creation fails due to this:
3274 // <https://msdn.microsoft.com/en-us/library/jj575265%28v=office.12%29.aspx#id15>
3275 // (retrieved on 2015-08-14)
3276 case SC_PRECONDITION_FAILED
: // <http://tools.ietf.org/html/rfc7232#section-4.2>
3277 // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3278 // part of base http 1.1 RFCs
3279 case SC_NOT_IMPLEMENTED
: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3280 case SC_METHOD_NOT_ALLOWED
: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3281 SAL_WARN( "ucb.ucp.webdav", "lock() DAVException (SC_NOT_FOUND, SC_PRECONDITION_FAILED, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3282 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3283 // act as nothing happened
3284 // that's because when a resource is first created
3285 // the lock is sent before the put, so the resource
3286 // is actually created by LOCK, locking it before
3287 // the first PUT, but if LOCK is not supported
3288 // (simple web or DAV with lock disabled) we end with one of these http
3290 // These same errors may be reported when the LOCK on an unmapped
3291 // (i.e. non existent) resource is not implemented.
3292 // Detailed specification in:
3293 // <http://tools.ietf.org/html/rfc4918#section-7.3>
3301 case DAVException::DAV_LOCKED_SELF
:
3302 // we already hold the lock and it is in our internal lockstore
3303 // just return as if the lock was successful
3311 SAL_WARN( "ucb.ucp.webdav","lock() DAVException - URL: <"
3312 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3313 cancelCommandExecution( e
, Environment
);
3319 void Content::unlock(
3320 const uno::Reference
< ucb::XCommandEnvironment
>& Environment
)
3325 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
3327 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3328 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
3331 // check if the target URL is a Class1 DAV
3332 DAVOptions aDAVOptions
;
3333 getResourceOptions( Environment
, aDAVOptions
, xResAccess
);
3335 // at least class one is needed
3336 if( aDAVOptions
.isClass1() )
3338 // remove options from cache, unlock may change it
3339 // it will be refreshed when needed
3340 aStaticDAVOptionsCache
.removeDAVOptions( xResAccess
->getURL() );
3341 // clean cached value of PROPFIND properties names
3342 removeCachedPropertyNames( xResAccess
->getURL() );
3343 xResAccess
->UNLOCK( Environment
);
3347 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3348 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
3351 catch ( DAVException
const & e
)
3353 switch( e
.getError() )
3355 case DAVException::DAV_NOT_LOCKED
:
3356 SAL_WARN( "ucb.ucp.webdav", "unlock() DAVException::DAV_NOT_LOCKED - URL: <"
3357 << m_xIdentifier
->getContentIdentifier() << ">");
3358 // means that we don't own any lock on this resource
3359 // intercepted here to remove a confusing indication to the user
3360 // unfortunately this happens in some WebDAV server configuration
3361 // acting as WebDAV and having lock/unlock enabled only
3362 // for authorized user.
3365 case DAVException::DAV_HTTP_ERROR
:
3366 //grab the error code
3367 switch( e
.getStatus() )
3369 // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3370 // part of base http 1.1 RFCs
3371 case SC_NOT_IMPLEMENTED
: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3372 case SC_METHOD_NOT_ALLOWED
: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3373 SAL_WARN( "ucb.ucp.webdav", "unlock() DAVException (SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3374 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3386 SAL_WARN( "ucb.ucp.webdav","unlock() DAVException - URL: <"
3387 << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3388 cancelCommandExecution( e
, Environment
);
3394 bool Content::exchangeIdentity(
3395 const uno::Reference
< ucb::XContentIdentifier
>& xNewId
)
3400 osl::ClearableGuard
< osl::Mutex
> aGuard( m_aMutex
);
3402 uno::Reference
< ucb::XContent
> xThis
= this;
3404 // Already persistent?
3407 SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - Not persistent!" );
3411 // Exchange own identitity.
3413 // Fail, if a content with given id already exists.
3414 // if ( !hasData( xNewId ) )
3416 OUString aOldURL
= m_xIdentifier
->getContentIdentifier();
3419 if ( exchange( xNewId
) )
3421 // Process instantiated children...
3423 ContentRefList aChildren
;
3424 queryChildren( aChildren
);
3426 for ( const auto& rChild
: aChildren
)
3428 ContentRef xChild
= rChild
;
3430 // Create new content identifier for the child...
3431 uno::Reference
< ucb::XContentIdentifier
>
3432 xOldChildId
= xChild
->getIdentifier();
3433 OUString aOldChildURL
3434 = xOldChildId
->getContentIdentifier();
3435 OUString aNewChildURL
3436 = aOldChildURL
.replaceAt(
3438 aOldURL
.getLength(),
3439 xNewId
->getContentIdentifier() );
3440 uno::Reference
< ucb::XContentIdentifier
> xNewChildId
3441 = new ::ucbhelper::ContentIdentifier( aNewChildURL
);
3443 if ( !xChild
->exchangeIdentity( xNewChildId
) )
3450 SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - "
3451 "Panic! Cannot exchange identity!" );
3456 bool Content::isFolder(
3457 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
3460 osl::MutexGuard
aGuard( m_aMutex
);
3463 return m_bCollection
;
3466 uno::Sequence
< beans::Property
> aProperties( 1 );
3467 aProperties
[ 0 ].Name
= "IsFolder";
3468 aProperties
[ 0 ].Handle
= -1;
3469 uno::Reference
< sdbc::XRow
> xRow( getPropertyValues( aProperties
, xEnv
) );
3474 return xRow
->getBoolean( 1 );
3476 catch ( sdbc::SQLException
const & )
3485 uno::Any
Content::MapDAVException( const DAVException
& e
, bool bWrite
)
3487 // Map DAVException...
3488 uno::Any aException
;
3493 aURL
= getParentURL();
3494 if ( aURL
.lastIndexOf('/') != ( aURL
.getLength() - 1 ) )
3497 aURL
+= m_aEscapedTitle
;
3501 aURL
= m_xIdentifier
->getContentIdentifier();
3504 switch ( e
.getStatus() )
3508 uno::Sequence
< uno::Any
> aArgs( 1 );
3509 aArgs
[ 0 ] <<= beans::PropertyValue(
3512 beans::PropertyState_DIRECT_VALUE
);
3515 ucb::InteractiveAugmentedIOException(
3517 static_cast< cppu::OWeakObject
* >( this ),
3518 task::InteractionClassification_ERROR
,
3519 ucb::IOErrorCode_NOT_EXISTING
,
3527 switch ( e
.getError() )
3529 case DAVException::DAV_HTTP_ERROR
:
3533 ucb::InteractiveNetworkWriteException(
3535 static_cast< cppu::OWeakObject
* >( this ),
3536 task::InteractionClassification_ERROR
,
3540 ucb::InteractiveNetworkReadException(
3542 static_cast< cppu::OWeakObject
* >( this ),
3543 task::InteractionClassification_ERROR
,
3548 case DAVException::DAV_HTTP_LOOKUP
:
3550 ucb::InteractiveNetworkResolveNameException(
3552 static_cast< cppu::OWeakObject
* >( this ),
3553 task::InteractionClassification_ERROR
,
3557 // @@@ No matching InteractiveNetwork*Exception
3558 // case DAVException::DAV_HTTP_AUTH:
3561 // @@@ No matching InteractiveNetwork*Exception
3562 // case DAVException::DAV_HTTP_AUTHPROXY:
3565 case DAVException::DAV_HTTP_TIMEOUT
:
3566 case DAVException::DAV_HTTP_CONNECT
:
3568 ucb::InteractiveNetworkConnectException(
3570 static_cast< cppu::OWeakObject
* >( this ),
3571 task::InteractionClassification_ERROR
,
3575 // @@@ No matching InteractiveNetwork*Exception
3576 // case DAVException::DAV_HTTP_REDIRECT:
3579 // @@@ No matching InteractiveNetwork*Exception
3580 // case DAVException::DAV_SESSION_CREATE:
3583 case DAVException::DAV_INVALID_ARG
:
3585 lang::IllegalArgumentException(
3587 static_cast< cppu::OWeakObject
* >( this ),
3591 case DAVException::DAV_LOCKED
:
3593 ucb::InteractiveLockingLockedException(
3595 static_cast< cppu::OWeakObject
* >( this ),
3596 task::InteractionClassification_ERROR
,
3598 false ); // not SelfOwned
3601 case DAVException::DAV_LOCKED_SELF
:
3603 ucb::InteractiveLockingLockedException(
3605 static_cast< cppu::OWeakObject
* >( this ),
3606 task::InteractionClassification_ERROR
,
3608 true ); // SelfOwned
3611 case DAVException::DAV_NOT_LOCKED
:
3613 ucb::InteractiveLockingNotLockedException(
3615 static_cast< cppu::OWeakObject
* >( this ),
3616 task::InteractionClassification_ERROR
,
3620 case DAVException::DAV_LOCK_EXPIRED
:
3622 ucb::InteractiveLockingLockExpiredException(
3624 static_cast< cppu::OWeakObject
* >( this ),
3625 task::InteractionClassification_ERROR
,
3631 ucb::InteractiveNetworkGeneralException(
3633 static_cast< cppu::OWeakObject
* >( this ),
3634 task::InteractionClassification_ERROR
);
3643 bool Content::shouldAccessNetworkAfterException( const DAVException
& e
)
3645 return !(( e
.getStatus() == SC_NOT_FOUND
) ||
3646 ( e
.getStatus() == SC_GONE
) ||
3647 ( e
.getError() == DAVException::DAV_HTTP_TIMEOUT
) ||
3648 ( e
.getError() == DAVException::DAV_HTTP_LOOKUP
) ||
3649 ( e
.getError() == DAVException::DAV_HTTP_CONNECT
) ||
3650 ( e
.getError() == DAVException::DAV_HTTP_AUTH
) ||
3651 ( e
.getError() == DAVException::DAV_HTTP_AUTHPROXY
));
3655 void Content::cancelCommandExecution(
3656 const DAVException
& e
,
3657 const uno::Reference
< ucb::XCommandEnvironment
> & xEnv
,
3658 bool bWrite
/* = sal_False */ )
3660 ucbhelper::cancelCommandExecution( MapDAVException( e
, bWrite
), xEnv
);
3666 Content::getBaseURI( const std::unique_ptr
< DAVResourceAccess
> & rResAccess
)
3668 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3670 // First, try to obtain value of response header "Content-Location".
3674 m_xCachedProps
->getValue( "Content-Location" ) >>= aLocation
;
3675 if ( !aLocation
.isEmpty() )
3679 // Do not use m_xIdentifier->getContentIdentifier() because it
3680 // for example does not reflect redirects applied to requests
3681 // done using the original URI but m_xResAccess' URI does.
3682 return rtl::Uri::convertRelToAbs( rResAccess
->getURL(),
3685 catch ( rtl::MalformedUriException
const & )
3691 return rResAccess
->getURL();
3694 // resource type is the type of the WebDAV resource
3695 Content::ResourceType
Content::getResourceType(
3696 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
,
3697 const std::unique_ptr
< DAVResourceAccess
> & rResAccess
,
3698 bool * networkAccessAllowed
)
3701 osl::MutexGuard
g(m_aMutex
);
3702 if (m_eResourceType
!= UNKNOWN
) {
3703 return m_eResourceType
;
3707 ResourceType eResourceType
= UNKNOWN
;
3708 DAVOptions aDAVOptions
;
3710 const OUString
& rURL
= rResAccess
->getURL();
3711 const OUString
aScheme(
3712 rURL
.copy( 0, rURL
.indexOf( ':' ) ).toAsciiLowerCase() );
3714 if ( aScheme
== FTP_URL_SCHEME
)
3716 eResourceType
= FTP
;
3720 getResourceOptions( xEnv
, aDAVOptions
, rResAccess
, networkAccessAllowed
);
3722 // at least class one is needed
3723 if( aDAVOptions
.isClass1() )
3727 // Try to fetch some frequently used property value, e.g. those
3728 // used when loading documents... along with identifying whether
3729 // this is a DAV resource.
3730 std::vector
< DAVResource
> resources
;
3731 std::vector
< OUString
> aPropNames
;
3732 uno::Sequence
< beans::Property
> aProperties( 5 );
3733 aProperties
[ 0 ].Name
= "IsFolder";
3734 aProperties
[ 1 ].Name
= "IsDocument";
3735 aProperties
[ 2 ].Name
= "IsReadOnly";
3736 aProperties
[ 3 ].Name
= "MediaType";
3737 aProperties
[ 4 ].Name
= DAVProperties::SUPPORTEDLOCK
;
3739 ContentProperties::UCBNamesToDAVNames( aProperties
, aPropNames
);
3741 rResAccess
->PROPFIND( DAVZERO
, aPropNames
, resources
, xEnv
);
3743 if ( resources
.size() == 1 )
3745 #if defined SAL_LOG_INFO
3747 // print received resources
3748 for ( const auto& rProp
: resources
[0].properties
)
3750 OUString aPropValue
;
3752 uno::Sequence
< ucb::LockEntry
> aSupportedLocks
;
3753 if(rProp
.Value
>>= aPropValue
)
3754 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << rProp
.Name
<< ":" << aPropValue
);
3755 else if( rProp
.Value
>>= bValue
)
3756 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << rProp
.Name
<< ":" <<
3757 ( bValue
? "true" : "false" ) );
3758 else if( rProp
.Value
>>= aSupportedLocks
)
3760 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << rProp
.Name
<< ":" );
3761 for ( sal_Int32 n
= 0; n
< aSupportedLocks
.getLength(); ++n
)
3763 SAL_INFO( "ucb.ucp.webdav","PROPFIND (getResourceType) - supportedlock[" << n
<<"]: scope: "
3764 << ( aSupportedLocks
[ n
].Scope
== css::ucb::LockScope_SHARED
? "shared" : "exclusive" )
3766 << ( aSupportedLocks
[ n
].Type
!= css::ucb::LockType_WRITE
? "" : "write" ) );
3772 osl::MutexGuard
g(m_aMutex
);
3773 m_xCachedProps
.reset(
3774 new CachableContentProperties( ContentProperties( resources
[ 0 ] ) ) );
3775 m_xCachedProps
->containsAllNames(
3776 aProperties
, m_aFailedPropNames
);
3778 eResourceType
= DAV
;
3780 catch ( DAVException
const & e
)
3782 rResAccess
->resetUri();
3784 SAL_WARN( "ucb.ucp.webdav", "Content::getResourceType returned errors, DAV ExceptionCode: " << e
.getError() << ", HTTP error: " << e
.getStatus() );
3786 if ( e
.getStatus() == SC_METHOD_NOT_ALLOWED
)
3788 // Status SC_METHOD_NOT_ALLOWED is a safe indicator that the
3789 // resource is NON_DAV
3790 eResourceType
= NON_DAV
;
3792 else if (networkAccessAllowed
!= nullptr)
3794 *networkAccessAllowed
= *networkAccessAllowed
3795 && shouldAccessNetworkAfterException(e
);
3797 if ( e
.getStatus() == SC_NOT_FOUND
)
3799 // arrives here if OPTIONS is still cached for a resource previously available
3800 // operate on the OPTIONS cache:
3801 // if OPTIONS was not found, do nothing
3802 // else OPTIONS returned on a resource not existent (example a server that allows lock on null resource) set
3803 // not found and adjust lifetime accordingly
3804 DAVOptions aDAVOptionsInner
;
3805 if( aStaticDAVOptionsCache
.getDAVOptions( rURL
, aDAVOptionsInner
) )
3807 // TODO? get redirected url
3808 aDAVOptionsInner
.setHttpResponseStatusCode( e
.getStatus() );
3809 aDAVOptionsInner
.setHttpResponseStatusText( e
.getData() );
3810 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptionsInner
,
3811 m_nOptsCacheLifeNotFound
);
3814 // if the two net events below happen, something
3815 // is going on to the connection so break the command flow
3816 if ( ( e
.getError() == DAVException::DAV_HTTP_TIMEOUT
) ||
3817 ( e
.getError() == DAVException::DAV_HTTP_CONNECT
) )
3819 cancelCommandExecution( e
, xEnv
);
3826 rResAccess
->resetUri();
3828 // first check if the cached error can be mapped to DAVException::DAV_HTTP_TIMEOUT or mapped to DAVException::DAV_HTTP_CONNECT
3829 if ( aDAVOptions
.getHttpResponseStatusCode() == USC_CONNECTION_TIMED_OUT
)
3831 // behave same as DAVException::DAV_HTTP_TIMEOUT or DAVException::DAV_HTTP_CONNECT was thrown
3834 // extract host name and connection port
3835 NeonUri
theUri( rURL
);
3836 const OUString
& aHostName
= theUri
.GetHost();
3837 sal_Int32 nPort
= theUri
.GetPort();
3838 throw DAVException( DAVException::DAV_HTTP_TIMEOUT
,
3839 NeonUri::makeConnectionEndPointString( aHostName
,
3842 catch ( DAVException
& exp
)
3844 cancelCommandExecution( exp
, xEnv
);
3848 if ( aDAVOptions
.getHttpResponseStatusCode() != SC_NOT_FOUND
&&
3849 aDAVOptions
.getHttpResponseStatusCode() != SC_GONE
) // the cached OPTIONS can have SC_GONE
3851 eResourceType
= NON_DAV
;
3855 //resource doesn't exist
3856 if ( networkAccessAllowed
!= nullptr )
3857 *networkAccessAllowed
= false; }
3861 osl::MutexGuard
g(m_aMutex
);
3862 if (m_eResourceType
== UNKNOWN
) {
3863 m_eResourceType
= eResourceType
;
3866 eResourceType
!= m_eResourceType
, "ucb.ucp.webdav",
3867 "different resource types for <" << rURL
<< ">: "
3868 << +eResourceType
<< " vs. " << +m_eResourceType
);
3870 SAL_INFO( "ucb.ucp.webdav", "m_eResourceType for <"<<rURL
<<">: " << m_eResourceType
);
3871 return m_eResourceType
;
3875 Content::ResourceType
Content::getResourceType(
3876 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
3878 std::unique_ptr
< DAVResourceAccess
> xResAccess
;
3880 osl::MutexGuard
aGuard( m_aMutex
);
3881 xResAccess
.reset(new DAVResourceAccess(*m_xResAccess
));
3883 Content::ResourceType
const ret
= getResourceType( xEnv
, xResAccess
);
3885 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
3886 m_xResAccess
.reset(new DAVResourceAccess(*xResAccess
));
3892 void Content::initOptsCacheLifeTime()
3894 // see description in
3895 // officecfg/registry/schema/org/openoffice/Inet.xcs
3896 // for use of these field values.
3898 nAtime
= officecfg::Inet::Settings::OptsCacheLifeImplWeb::get( m_xContext
);
3899 m_nOptsCacheLifeImplWeb
= std::max( sal_uInt32( 0 ),
3900 std::min( nAtime
, sal_uInt32( 3600 ) ) );
3902 nAtime
= officecfg::Inet::Settings::OptsCacheLifeDAV::get( m_xContext
);
3903 m_nOptsCacheLifeDAV
= std::max( sal_uInt32( 0 ),
3904 std::min( nAtime
, sal_uInt32( 3600 ) ) );
3906 nAtime
= officecfg::Inet::Settings::OptsCacheLifeDAVLocked::get( m_xContext
);
3907 m_nOptsCacheLifeDAVLocked
= std::max( sal_uInt32( 0 ),
3908 std::min( nAtime
, sal_uInt32( 3600 ) ) );
3910 nAtime
= officecfg::Inet::Settings::OptsCacheLifeNotImpl::get( m_xContext
);
3911 m_nOptsCacheLifeNotImpl
= std::max( sal_uInt32( 0 ),
3912 std::min( nAtime
, sal_uInt32( 43200 ) ) );
3914 nAtime
= officecfg::Inet::Settings::OptsCacheLifeNotFound::get( m_xContext
);
3915 m_nOptsCacheLifeNotFound
= std::max( sal_uInt32( 0 ),
3916 std::min( nAtime
, sal_uInt32( 30 ) ) );
3920 void Content::getResourceOptions(
3921 const css::uno::Reference
< css::ucb::XCommandEnvironment
>& xEnv
,
3922 DAVOptions
& rDAVOptions
,
3923 const std::unique_ptr
< DAVResourceAccess
> & rResAccess
,
3924 bool * networkAccessAllowed
)
3927 OUString aTargetURL
= rResAccess
->getURL();
3928 DAVOptions aDAVOptions
;
3929 // first check if in cache, if not, then send method to server
3930 if ( !aStaticDAVOptionsCache
.getDAVOptions( aTargetURL
, aDAVOptions
) )
3934 rResAccess
->OPTIONS( aDAVOptions
, xEnv
);
3935 // IMPORTANT:the correctly implemented server will answer without errors, even if the resource is not present
3936 sal_uInt32 nLifeTime
= ( aDAVOptions
.isClass1() ||
3937 aDAVOptions
.isClass2() ||
3938 aDAVOptions
.isClass3() ) ?
3939 m_nOptsCacheLifeDAV
: // a WebDAV site
3940 m_nOptsCacheLifeImplWeb
; // a site implementing OPTIONS but
3942 // if resource is locked, will use a
3943 // different lifetime
3944 if( aDAVOptions
.isLocked() )
3945 nLifeTime
= m_nOptsCacheLifeDAVLocked
;
3947 // check if redirected
3948 aRedirURL
= rResAccess
->getURL();
3949 if( aRedirURL
== aTargetURL
)
3953 // cache this URL's option
3954 aDAVOptions
.setURL( aTargetURL
);
3955 aDAVOptions
.setRedirectedURL( aRedirURL
);
3956 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
3959 catch ( DAVException
const & e
)
3961 // first, remove from cache, will be added if needed, depending on the error received
3962 aStaticDAVOptionsCache
.removeDAVOptions( aTargetURL
);
3963 rResAccess
->resetUri();
3965 aDAVOptions
.setURL( aTargetURL
);
3966 aDAVOptions
.setRedirectedURL( aRedirURL
);
3967 switch( e
.getError() )
3969 case DAVException::DAV_HTTP_TIMEOUT
:
3970 case DAVException::DAV_HTTP_CONNECT
:
3972 // something bad happened to the connection
3973 // not same as not found, this instead happens when the server doesn't exist or doesn't answer at all
3974 // probably a new bit stating 'timed out' should be added to opts var?
3975 // in any case abort the command
3976 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_TIMEOUT or DAV_HTTP_CONNECT for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
3977 // cache the internal unofficial status code
3979 aDAVOptions
.setHttpResponseStatusCode( USC_CONNECTION_TIMED_OUT
);
3980 // used only internally, so the text doesn't really matter...
3981 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
3982 m_nOptsCacheLifeNotFound
);
3983 if ( networkAccessAllowed
!= nullptr )
3985 *networkAccessAllowed
= *networkAccessAllowed
3986 && shouldAccessNetworkAfterException(e
);
3990 case DAVException::DAV_HTTP_LOOKUP
:
3992 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_LOOKUP for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
3993 aDAVOptions
.setHttpResponseStatusCode( USC_LOOKUP_FAILED
);
3994 // used only internally, so the text doesn't really matter...
3995 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
3996 m_nOptsCacheLifeNotFound
);
3997 if ( networkAccessAllowed
!= nullptr )
3999 *networkAccessAllowed
= *networkAccessAllowed
4000 && shouldAccessNetworkAfterException(e
);
4004 case DAVException::DAV_HTTP_AUTH
:
4006 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTH for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
4007 // - the remote site is a WebDAV with special configuration: read/only for read operations
4008 // and read/write for write operations, the user is not allowed to lock/write and
4009 // she cancelled the credentials request.
4010 // this is not actually an error, it means only that for current user this is a standard web,
4011 // though possibly DAV enabled
4012 aDAVOptions
.setHttpResponseStatusCode( USC_AUTH_FAILED
);
4013 // used only internally, so the text doesn't really matter...
4014 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4015 m_nOptsCacheLifeNotFound
);
4016 if ( networkAccessAllowed
!= nullptr )
4018 *networkAccessAllowed
= *networkAccessAllowed
4019 && shouldAccessNetworkAfterException(e
);
4023 case DAVException::DAV_HTTP_AUTHPROXY
:
4025 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTHPROXY for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
4026 aDAVOptions
.setHttpResponseStatusCode( USC_AUTHPROXY_FAILED
);
4027 // used only internally, so the text doesn't really matter...
4028 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4029 m_nOptsCacheLifeNotFound
);
4030 if ( networkAccessAllowed
!= nullptr )
4032 *networkAccessAllowed
= *networkAccessAllowed
4033 && shouldAccessNetworkAfterException(e
);
4037 case DAVException::DAV_HTTP_ERROR
:
4039 switch( e
.getStatus() )
4043 SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_FORBIDDEN for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
4044 // cache it, so OPTIONS won't be called again, this URL does not support it
4045 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4046 m_nOptsCacheLifeNotImpl
);
4049 case SC_BAD_REQUEST
:
4050 case SC_INTERNAL_SERVER_ERROR
:
4052 SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_BAD_REQUEST or SC_INTERNAL_SERVER_ERROR for URL <" << m_xIdentifier
->getContentIdentifier() << ">, HTTP error: "<< e
.getStatus()
4053 << ", '" << e
.getData() << "'" );
4054 // cache it, so OPTIONS won't be called again, this URL detect some problem while answering the method
4055 aDAVOptions
.setHttpResponseStatusCode( e
.getStatus() );
4056 aDAVOptions
.setHttpResponseStatusText( e
.getData() );
4057 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4058 m_nOptsCacheLifeNotFound
);
4061 case SC_NOT_IMPLEMENTED
:
4062 case SC_METHOD_NOT_ALLOWED
:
4064 // OPTIONS method must be implemented in DAV
4065 // resource is NON_DAV, or not advertising it
4066 SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED for URL <" << m_xIdentifier
->getContentIdentifier() << ">, HTTP error: "<< e
.getStatus()
4067 << ", '" << e
.getData() << "'" );
4068 // cache it, so OPTIONS won't be called again, this URL does not support it
4069 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4070 m_nOptsCacheLifeNotImpl
);
4075 // Apparently on IIS 10.0, if you disabled OPTIONS method, this error is the one reported,
4076 // instead of SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED.
4077 // So check if this is an available resource, or a real 'Not Found' event.
4078 sal_uInt32 nLifeTime
= m_nOptsCacheLifeNotFound
;
4079 if( isResourceAvailable( xEnv
, rResAccess
, aDAVOptions
) )
4081 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - Got an SC_NOT_FOUND, but the URL <" << m_xIdentifier
->getContentIdentifier() << "> resource exists" );
4082 nLifeTime
= m_nOptsCacheLifeNotImpl
;
4086 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - SC_NOT_FOUND for URL <" << m_xIdentifier
->getContentIdentifier() << ">" );
4087 if ( networkAccessAllowed
!= nullptr )
4089 *networkAccessAllowed
= *networkAccessAllowed
4090 && shouldAccessNetworkAfterException(e
);
4093 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4099 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAV_HTTP_ERROR, for URL <" << m_xIdentifier
->getContentIdentifier() << ">, HTTP error: "<< e
.getStatus()
4100 << ", '" << e
.getData() << "'" );
4101 aDAVOptions
.setHttpResponseStatusCode( e
.getStatus() );
4102 aDAVOptions
.setHttpResponseStatusText( e
.getData() );
4103 // cache it, so OPTIONS won't be called again, this URL does not support it
4104 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4105 m_nOptsCacheLifeNotImpl
);
4111 // The 'DAVException::DAV_HTTP_REDIRECT' means we reached the maximum
4112 // number of redirections, consider the resource type as UNKNOWN
4113 // possibly a normal web site, not DAV
4114 case DAVException::DAV_HTTP_REDIRECT
:
4117 SAL_WARN( "ucb.ucp.webdav","OPTIONS - General DAVException (or max DAV_HTTP_REDIRECT reached) for URL <" << m_xIdentifier
->getContentIdentifier() << ">, DAV ExceptionCode: "
4118 << e
.getError() << ", HTTP error: "<< e
.getStatus() );
4119 aStaticDAVOptionsCache
.addDAVOptions( aDAVOptions
,
4120 m_nOptsCacheLifeNotImpl
);
4128 // check current response status code, perhaps we need to set networkAccessAllowed
4129 sal_uInt16 CachedResponseStatusCode
= aDAVOptions
.getHttpResponseStatusCode();
4130 if ( networkAccessAllowed
!= nullptr &&
4131 ( ( CachedResponseStatusCode
== SC_NOT_FOUND
) ||
4132 ( CachedResponseStatusCode
== SC_GONE
) ||
4133 ( CachedResponseStatusCode
== USC_CONNECTION_TIMED_OUT
) ||
4134 ( CachedResponseStatusCode
== USC_LOOKUP_FAILED
) ||
4135 ( CachedResponseStatusCode
== USC_AUTH_FAILED
) ||
4136 ( CachedResponseStatusCode
== USC_AUTHPROXY_FAILED
)
4140 *networkAccessAllowed
= false;
4143 rDAVOptions
= aDAVOptions
;
4148 bool Content::isResourceAvailable( const css::uno::Reference
< css::ucb::XCommandEnvironment
>& xEnv
,
4149 const std::unique_ptr
< DAVResourceAccess
> & rResAccess
,
4150 DAVOptions
& rDAVOptions
)
4152 std::vector
< OUString
> aHeaderNames
;
4153 DAVResource aResource
;
4157 // To check for the physical URL resource availability, first
4158 // try using a simple HEAD command
4159 // if HEAD is successful, set element found,
4160 rResAccess
->HEAD( aHeaderNames
, aResource
, xEnv
);
4161 rDAVOptions
.setHttpResponseStatusCode( 0 );
4162 rDAVOptions
.setHttpResponseStatusText( OUString() );
4165 catch ( DAVException
const & e
)
4167 if ( e
.getError() == DAVException::DAV_HTTP_ERROR
)
4169 if ( e
.getStatus() == SC_NOT_IMPLEMENTED
||
4170 e
.getStatus() == SC_METHOD_NOT_ALLOWED
||
4171 e
.getStatus() == SC_NOT_FOUND
)
4173 SAL_WARN( "ucb.ucp.webdav", "HEAD probably not implemented: fall back to a partial GET" );
4174 // set in cached OPTIONS "HEAD not implemented"
4175 // so it won't be used again on this resource
4176 rDAVOptions
.setHeadAllowed( false );
4179 // do a GET with a payload of 0, the server does not
4180 // support HEAD (or has HEAD disabled)
4181 DAVRequestHeaders aPartialGet
;
4182 aPartialGet
.emplace_back(
4183 OUString( "Range" ),
4184 OUString( "bytes=0-0" ));
4186 rResAccess
->GET0( aPartialGet
,
4192 catch ( DAVException
const & ex
)
4194 if ( ex
.getError() == DAVException::DAV_HTTP_ERROR
)
4196 rDAVOptions
.setHttpResponseStatusCode( ex
.getStatus() );
4197 rDAVOptions
.setHttpResponseStatusText( ex
.getData() );
4203 rDAVOptions
.setHttpResponseStatusCode( e
.getStatus() );
4204 rDAVOptions
.setHttpResponseStatusText( e
.getData() );
4212 // set SC_NOT_IMPLEMENTED since at a minimum GET must be implemented in a basic Web server
4213 rDAVOptions
.setHttpResponseStatusCode( SC_NOT_IMPLEMENTED
);
4214 rDAVOptions
.setHttpResponseStatusText( OUString() );
4219 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */