summaryrefslogtreecommitdiffstats
path: root/ucb/source/ucp/webdav-curl/webdavcontent.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'ucb/source/ucp/webdav-curl/webdavcontent.cxx')
-rw-r--r--ucb/source/ucp/webdav-curl/webdavcontent.cxx4305
1 files changed, 4305 insertions, 0 deletions
diff --git a/ucb/source/ucp/webdav-curl/webdavcontent.cxx b/ucb/source/ucp/webdav-curl/webdavcontent.cxx
new file mode 100644
index 000000000..acd850a24
--- /dev/null
+++ b/ucb/source/ucp/webdav-curl/webdavcontent.cxx
@@ -0,0 +1,4305 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+
+#include <cppuhelper/queryinterface.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Inet.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/macros.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/simpleinteractionrequest.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+#include <svl/lockfilecommon.hxx>
+
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/NotRemoveableException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/PropertySetInfoChange.hpp>
+#include <com/sun/star/beans/PropertySetInfoChangeEvent.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/task/PasswordContainerInteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandEnvironment.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingLockExpiredException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingNotLockedException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
+#include <com/sun/star/ucb/MissingInputStreamException.hpp>
+#include <com/sun/star/ucb/MissingPropertiesException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/NameClashException.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument3.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/ucb/PostCommandArgument2.hpp>
+#include <com/sun/star/ucb/PropertyCommandArgument.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
+#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "webdavcontent.hxx"
+#include "webdavprovider.hxx"
+#include "webdavresultset.hxx"
+#include "ContentProperties.hxx"
+#include "CurlUri.hxx"
+#include "UCBDeadPropertyValue.hxx"
+#include "DAVException.hxx"
+#include "DAVProperties.hxx"
+
+using namespace com::sun::star;
+using namespace http_dav_ucp;
+
+namespace
+{
+void lcl_sendPartialGETRequest( bool &bError,
+ DAVException &aLastException,
+ const std::vector< OUString >& rProps,
+ std::vector< OUString > &aHeaderNames,
+ const std::unique_ptr< DAVResourceAccess > &xResAccess,
+ std::unique_ptr< ContentProperties > &xProps,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ DAVResource aResource;
+ DAVRequestHeaders aPartialGet;
+ aPartialGet.push_back(
+ DAVRequestHeader(
+ OUString( "Range" ), // see <https://tools.ietf.org/html/rfc7233#section-3.1>
+ OUString( "bytes=0-0" )));
+
+ bool bIsRequestSize = std::any_of(aHeaderNames.begin(), aHeaderNames.end(),
+ [](const OUString& rHeaderName) { return rHeaderName == "Content-Length"; });
+
+ if ( bIsRequestSize )
+ {
+ // we need to know if the server accepts range requests for a resource
+ // and the range unit it uses
+ aHeaderNames.push_back( OUString( "Accept-Ranges" ) ); // see <https://tools.ietf.org/html/rfc7233#section-2.3>
+ aHeaderNames.push_back( OUString( "Content-Range" ) ); // see <https://tools.ietf.org/html/rfc7233#section-4.2>
+ }
+ try
+ {
+ xResAccess->GET0( aPartialGet, aHeaderNames, aResource, xEnv );
+ bError = false;
+
+ if ( bIsRequestSize )
+ {
+ // the ContentProperties maps "Content-Length" to the UCB "Size" property
+ // This would have an unrealistic value of 1 byte because we did only a partial GET
+ // Solution: if "Content-Range" is present, map it with UCB "Size" property
+ OUString aAcceptRanges, aContentRange, aContentLength;
+ std::vector< DAVPropertyValue > &aResponseProps = aResource.properties;
+ for ( const auto& rResponseProp : aResponseProps )
+ {
+ if ( rResponseProp.Name == "Accept-Ranges" )
+ rResponseProp.Value >>= aAcceptRanges;
+ else if ( rResponseProp.Name == "Content-Range" )
+ rResponseProp.Value >>= aContentRange;
+ else if ( rResponseProp.Name == "Content-Length" )
+ rResponseProp.Value >>= aContentLength;
+ }
+
+ sal_Int64 nSize = 1;
+ if ( aContentLength.getLength() )
+ {
+ nSize = aContentLength.toInt64();
+ }
+
+ // according to http://tools.ietf.org/html/rfc2616#section-3.12
+ // the only range unit defined is "bytes" and implementations
+ // MAY ignore ranges specified using other units.
+ if ( nSize == 1 &&
+ aContentRange.getLength() &&
+ aAcceptRanges == "bytes" )
+ {
+ // Parse the Content-Range to get the size
+ // vid. http://tools.ietf.org/html/rfc2616#section-14.16
+ // Content-Range: <range unit> <bytes range>/<size>
+ sal_Int32 nSlash = aContentRange.lastIndexOf( '/' );
+ if ( nSlash != -1 )
+ {
+ OUString aSize = aContentRange.copy( nSlash + 1 );
+ // "*" means that the instance-length is unknown at the time when the response was generated
+ if ( aSize != "*" )
+ {
+ auto it = std::find_if(aResponseProps.begin(), aResponseProps.end(),
+ [](const DAVPropertyValue& rProp) { return rProp.Name == "Content-Length"; });
+ if (it != aResponseProps.end())
+ {
+ it->Value <<= aSize;
+ }
+ }
+ }
+ }
+ }
+
+ if (xProps)
+ xProps->addProperties(
+ rProps,
+ ContentProperties( aResource ) );
+ else
+ xProps.reset ( new ContentProperties( aResource ) );
+ }
+ catch ( DAVException const & ex )
+ {
+ aLastException = ex;
+ }
+}
+}
+
+// Static value, to manage a simple OPTIONS cache
+// Key is the URL, element is the DAVOptions resulting from an OPTIONS call.
+// Cached DAVOptions have a lifetime that depends on the errors received or not received
+// and on the value of received options.
+static DAVOptionsCache aStaticDAVOptionsCache;
+
+
+// Content Implementation.
+
+
+// ctr for content on an existing webdav resource
+Content::Content(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory )
+: ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_eResourceType( UNKNOWN ),
+ m_eResourceTypeForLocks( UNKNOWN ),
+ m_pProvider( pProvider ),
+ m_bTransient( false ),
+ m_bCollection( false ),
+ m_bDidGetOrHead( false )
+{
+ try
+ {
+ initOptsCacheLifeTime();
+ m_xResAccess.reset( new DAVResourceAccess(
+ rxContext,
+ rSessionFactory,
+ Identifier->getContentIdentifier() ) );
+
+ CurlUri const aURI( Identifier->getContentIdentifier() );
+ m_aEscapedTitle = aURI.GetPathBaseName();
+ }
+ catch ( DAVException const & )
+ {
+ throw ucb::ContentCreationException();
+ }
+}
+
+
+// ctr for content on a non-existing webdav resource
+Content::Content(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory,
+ bool isCollection )
+: ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_eResourceType( UNKNOWN ),
+ m_eResourceTypeForLocks( UNKNOWN ),
+ m_pProvider( pProvider ),
+ m_bTransient( true ),
+ m_bCollection( isCollection ),
+ m_bDidGetOrHead( false )
+{
+ try
+ {
+ initOptsCacheLifeTime();
+ m_xResAccess.reset( new DAVResourceAccess(
+ rxContext, rSessionFactory, Identifier->getContentIdentifier() ) );
+ }
+ catch ( DAVException const & )
+ {
+ throw ucb::ContentCreationException();
+ }
+
+ // Do not set m_aEscapedTitle here! Content::insert relays on this!!!
+}
+
+
+// virtual
+Content::~Content()
+{
+}
+
+
+// XInterface methods.
+
+
+// virtual
+void SAL_CALL Content::acquire() noexcept
+{
+ ContentImplHelper::acquire();
+}
+
+
+// virtual
+void SAL_CALL Content::release() noexcept
+{
+ ContentImplHelper::release();
+}
+
+
+// virtual
+uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
+{
+ // Note: isFolder may require network activities! So call it only
+ // if it is really necessary!!!
+ uno::Any aRet = cppu::queryInterface(
+ rType,
+ static_cast< ucb::XContentCreator * >( this ) );
+ if ( aRet.hasValue() )
+ {
+ try
+ {
+ uno::Reference< task::XInteractionHandler > xIH(
+ task::PasswordContainerInteractionHandler::create(m_xContext) );
+
+ // Supply a command env to isFolder() that contains an interaction
+ // handler that uses the password container service to obtain
+ // credentials without displaying a password gui.
+
+ uno::Reference< ucb::XCommandEnvironment > xCmdEnv(
+ ucb::CommandEnvironment::create(
+ m_xContext,
+ xIH,
+ uno::Reference< ucb::XProgressHandler >() ) );
+
+ return isFolder( xCmdEnv ) ? aRet : uno::Any();
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ return uno::Any();
+ }
+ }
+ return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType );
+}
+
+
+// XTypeProvider methods.
+
+
+XTYPEPROVIDER_COMMON_IMPL( Content );
+
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
+{
+ bool bFolder = false;
+ try
+ {
+ bFolder
+ = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+
+ if ( bFolder )
+ {
+ static cppu::OTypeCollection s_aFolderTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ),
+ CPPU_TYPE_REF( ucb::XContentCreator ) );
+
+ return s_aFolderTypes.getTypes();
+ }
+ else
+ {
+ static cppu::OTypeCollection s_aDocumentTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ) );
+
+ return s_aDocumentTypes.getTypes();
+ }
+}
+
+
+// XServiceInfo methods.
+
+
+// virtual
+OUString SAL_CALL Content::getImplementationName()
+{
+ return "com.sun.star.comp.ucb.WebDAVContent";
+}
+
+
+// virtual
+uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aSNS { WEBDAV_CONTENT_SERVICE_NAME };
+ return aSNS;
+}
+
+
+// XContent methods.
+
+
+// virtual
+OUString SAL_CALL Content::getContentType()
+{
+ bool bFolder = false;
+ try
+ {
+ bFolder
+ = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+
+ if ( bFolder )
+ return WEBDAV_COLLECTION_TYPE;
+
+ return WEBDAV_CONTENT_TYPE;
+}
+
+
+// XCommandProcessor methods.
+
+
+// virtual
+uno::Any SAL_CALL Content::execute(
+ const ucb::Command& aCommand,
+ sal_Int32 /*CommandId*/,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ SAL_INFO("ucb.ucp.webdav", ">>>>> Content::execute: start: command: " << aCommand.Name
+ << ", env: " << (Environment.is() ? "present" : "missing") );
+
+ uno::Any aRet;
+
+ if ( aCommand.Name == "getPropertyValues" )
+ {
+
+ // getPropertyValues
+
+
+ uno::Sequence< beans::Property > Properties;
+ if ( !( aCommand.Argument >>= Properties ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= getPropertyValues( Properties, Environment );
+ }
+ else if ( aCommand.Name == "setPropertyValues" )
+ {
+
+ // setPropertyValues
+
+
+ uno::Sequence< beans::PropertyValue > aProperties;
+ if ( !( aCommand.Argument >>= aProperties ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ if ( !aProperties.getLength() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "No properties!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= setPropertyValues( aProperties, Environment );
+ }
+ else if ( aCommand.Name == "getPropertySetInfo" )
+ {
+
+ // getPropertySetInfo
+
+
+ // Note: Implemented by base class.
+ aRet <<= getPropertySetInfo( Environment,
+ false /* don't cache data */ );
+ }
+ else if ( aCommand.Name == "getCommandInfo" )
+ {
+
+ // getCommandInfo
+
+
+ // Note: Implemented by base class.
+ aRet <<= getCommandInfo( Environment, false );
+ }
+ else if ( aCommand.Name == "open" )
+ {
+
+ // open
+
+
+ ucb::OpenCommandArgument3 aOpenCommand;
+ ucb::OpenCommandArgument2 aTmp;
+ if ( !( aCommand.Argument >>= aTmp ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+ if ( !( aCommand.Argument >>= aOpenCommand ) )
+ {
+ // compat mode, extract Arg2 info into newer structure
+ aOpenCommand.Mode = aTmp.Mode;
+ aOpenCommand.Priority = aTmp.Priority;
+ aOpenCommand.Sink = aTmp.Sink;
+ aOpenCommand.Properties = aTmp.Properties;
+ aOpenCommand.SortingInfo = aTmp.SortingInfo;
+ }
+
+ aRet = open( aOpenCommand, Environment );
+
+ }
+ else if ( aCommand.Name == "insert" )
+ {
+
+ // insert
+
+
+ ucb::InsertCommandArgument arg;
+ if ( !( aCommand.Argument >>= arg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ insert( arg.Data, arg.ReplaceExisting, Environment );
+ }
+ else if ( aCommand.Name == "delete" )
+ {
+
+ // delete
+
+
+ bool bDeletePhysical = false;
+ aCommand.Argument >>= bDeletePhysical;
+
+// KSO: Ignore parameter and destroy the content, if you don't support
+// putting objects into trashcan. ( Since we do not have a trash can
+// service yet (src603), you actually have no other choice. )
+// if ( bDeletePhysical )
+// {
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ // clean cached value of PROPFIND property names
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->DESTROY( Environment );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+// }
+
+ // Propagate destruction.
+ destroy( bDeletePhysical );
+
+ // Remove own and all children's Additional Core Properties.
+ removeAdditionalPropertySet();
+ }
+ else if ( aCommand.Name == "transfer" && isFolder( Environment ) )
+ {
+
+ // transfer
+ // ( Not available at documents )
+
+
+ ucb::TransferInfo transferArgs;
+ if ( !( aCommand.Argument >>= transferArgs ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ transfer( transferArgs, Environment );
+ }
+ else if ( aCommand.Name == "post" )
+ {
+
+ // post
+
+
+ ucb::PostCommandArgument2 aArg;
+ if ( !( aCommand.Argument >>= aArg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ post( aArg, Environment );
+ }
+ else if ( aCommand.Name == "lock" )
+ {
+
+ // lock
+
+ ResourceType eType = resourceTypeForLocks( Environment );
+ // when the resource is not yet present the lock is used to create it
+ // see: http://tools.ietf.org/html/rfc4918#section-7.3
+ // If the resource doesn't exists and the lock is not enabled (DAV with
+ // no lock or a simple web) the error will be dealt with inside lock() method
+ if ( eType == NOT_FOUND ||
+ eType == DAV )
+ {
+ lock( Environment );
+ if ( eType == NOT_FOUND )
+ {
+ m_eResourceType = UNKNOWN; // lock may have created it, need to check again
+ m_eResourceTypeForLocks = UNKNOWN;
+ }
+ }
+ }
+ else if ( aCommand.Name == "unlock" )
+ {
+
+ // unlock
+ // do not check for a DAV resource
+ // the lock store will be checked before sending
+ unlock( Environment );
+ }
+ else if ( aCommand.Name == "createNewContent" &&
+ isFolder( Environment ) )
+ {
+
+ // createNewContent
+
+
+ ucb::ContentInfo aArg;
+ if ( !( aCommand.Argument >>= aArg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= createNewContent( aArg );
+ }
+ else if ( aCommand.Name == "addProperty" )
+ {
+ ucb::PropertyCommandArgument aPropArg;
+ if ( !( aCommand.Argument >>= aPropArg ))
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ }
+
+ // TODO when/if XPropertyContainer is removed,
+ // the command execution can be canceled in addProperty
+ try
+ {
+ addProperty( aPropArg, Environment );
+ }
+ catch ( const beans::PropertyExistException &e )
+ {
+ ucbhelper::cancelCommandExecution( uno::Any( e ), Environment );
+ }
+ catch ( const beans::IllegalTypeException&e )
+ {
+ ucbhelper::cancelCommandExecution( uno::Any( e ), Environment );
+ }
+ catch ( const lang::IllegalArgumentException&e )
+ {
+ ucbhelper::cancelCommandExecution( uno::Any( e ), Environment );
+ }
+ }
+ else if ( aCommand.Name == "removeProperty" )
+ {
+ OUString sPropName;
+ if ( !( aCommand.Argument >>= sPropName ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ }
+
+ // TODO when/if XPropertyContainer is removed,
+ // the command execution can be canceled in removeProperty
+ try
+ {
+ removeProperty( sPropName, Environment );
+ }
+ catch( const beans::UnknownPropertyException &e )
+ {
+ ucbhelper::cancelCommandExecution( uno::Any( e ), Environment );
+ }
+ catch( const beans::NotRemoveableException &e )
+ {
+ ucbhelper::cancelCommandExecution( uno::Any( e ), Environment );
+ }
+ }
+ else
+ {
+
+ // Unsupported command
+
+
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ aCommand.Name,
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ SAL_INFO("ucb.ucp.webdav", "<<<<< Content::execute: end: command: " << aCommand.Name);
+
+ return aRet;
+}
+
+
+// virtual
+void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
+{
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ xResAccess->abort();
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & )
+ {
+ // abort failed!
+ }
+}
+
+
+// XPropertyContainer methods.
+
+
+void Content::addProperty( const css::ucb::PropertyCommandArgument &aCmdArg,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+// if ( m_bTransient )
+// @@@ ???
+ const beans::Property aProperty = aCmdArg.Property;
+ const uno::Any aDefaultValue = aCmdArg.DefaultValue;
+
+ // check property Name
+ if ( !aProperty.Name.getLength() )
+ throw lang::IllegalArgumentException(
+ "\"addProperty\" with empty Property.Name",
+ static_cast< ::cppu::OWeakObject * >( this ),
+ -1 );
+
+ // Check property type.
+ if ( !UCBDeadPropertyValue::supportsType( aProperty.Type ) )
+ throw beans::IllegalTypeException(
+ "\"addProperty\" unsupported Property.Type",
+ static_cast< ::cppu::OWeakObject * >( this ) );
+
+ // check default value
+ if ( aDefaultValue.hasValue() && aDefaultValue.getValueType() != aProperty.Type )
+ throw beans::IllegalTypeException(
+ "\"addProperty\" DefaultValue does not match Property.Type",
+ static_cast< ::cppu::OWeakObject * >( this ) );
+
+
+ // Make sure a property with the requested name does not already
+ // exist in dynamic and static(!) properties.
+
+
+ // Take into account special properties with custom namespace
+ // using <prop:the_propname xmlns:prop="the_namespace">
+ OUString aSpecialName;
+ bool bIsSpecial = DAVProperties::isUCBSpecialProperty( aProperty.Name, aSpecialName );
+
+ // Note: This requires network access!
+ if ( getPropertySetInfo( xEnv, false /* don't cache data */ )
+ ->hasPropertyByName( bIsSpecial ? aSpecialName : aProperty.Name ) )
+ {
+ // Property does already exist.
+ throw beans::PropertyExistException();
+ }
+
+
+ // Add a new dynamic property.
+
+
+ ProppatchValue aValue( PROPSET, aProperty.Name, aDefaultValue );
+
+ std::vector< ProppatchValue > aProppatchValues;
+ aProppatchValues.push_back( aValue );
+
+ try
+ {
+ // Set property value at server.
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ // clean cached value of PROPFIND property names
+ // PROPPATCH can change them
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->PROPPATCH( aProppatchValues, xEnv );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+
+ // Notify propertyset info change listeners.
+ beans::PropertySetInfoChangeEvent evt(
+ static_cast< cppu::OWeakObject * >( this ),
+ bIsSpecial ? aSpecialName : aProperty.Name,
+ -1, // No handle available
+ beans::PropertySetInfoChange::PROPERTY_INSERTED );
+ notifyPropertySetInfoChange( evt );
+ }
+ catch ( DAVException const & e )
+ {
+ if ( e.getStatus() == SC_FORBIDDEN )
+ {
+ // Support for setting arbitrary dead properties is optional!
+
+ // Store property locally.
+ ContentImplHelper::addProperty( bIsSpecial ? aSpecialName : aProperty.Name,
+ aProperty.Attributes,
+ aDefaultValue );
+ }
+ else
+ {
+ if ( shouldAccessNetworkAfterException( e ) )
+ {
+ try
+ {
+ const ResourceType eType = getResourceType( xEnv );
+ switch ( eType )
+ {
+ case UNKNOWN:
+ case DAV:
+ throw lang::IllegalArgumentException();
+
+ case NON_DAV:
+ // Store property locally.
+ ContentImplHelper::addProperty( bIsSpecial ? aSpecialName : aProperty.Name,
+ aProperty.Attributes,
+ aDefaultValue );
+ break;
+
+ default:
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::addProperty - "
+ "Unsupported resource type!" );
+ break;
+ }
+ }
+ catch ( uno::Exception const & )
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::addProperty - "
+ "Unable to determine resource type!" );
+ }
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::addProperty - "
+ "Unable to determine resource type!" );
+ }
+ }
+ }
+}
+
+void Content::removeProperty( const OUString& Name,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+#if 0
+ // @@@ REMOVABLE at the moment not properly set in the PropSetInfo
+ try
+ {
+ beans::Property aProp
+ = getPropertySetInfo( xEnv, false /* don't cache data */ )
+ ->getPropertyByName( Name );
+
+ if ( !( aProp.Attributes & beans::PropertyAttribute::REMOVABLE ) )
+ {
+ // Not removable!
+ throw beans::NotRemoveableException();
+ }
+ }
+ catch ( beans::UnknownPropertyException const & )
+ {
+ //SAL_WARN( "ucb.ucp.webdav", "removeProperty - Unknown property!" );
+ throw;
+ }
+#endif
+
+ // Try to remove property from server.
+ try
+ {
+ std::vector< ProppatchValue > aProppatchValues;
+ ProppatchValue aValue( PROPREMOVE, Name, uno::Any() );
+ aProppatchValues.push_back( aValue );
+
+ // Remove property value from server.
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ // clean cached value of PROPFIND property names
+ // PROPPATCH can change them
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->PROPPATCH( aProppatchValues, xEnv );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+
+ // Notify propertyset info change listeners.
+ beans::PropertySetInfoChangeEvent evt(
+ static_cast< cppu::OWeakObject * >( this ),
+ Name,
+ -1, // No handle available
+ beans::PropertySetInfoChange::PROPERTY_REMOVED );
+ notifyPropertySetInfoChange( evt );
+ }
+ catch ( DAVException const & e )
+ {
+ if ( e.getStatus() == SC_FORBIDDEN )
+ {
+ // Support for setting arbitrary dead properties is optional!
+
+ // Try to remove property from local store.
+ ContentImplHelper::removeProperty( Name );
+ }
+ else
+ {
+ if ( shouldAccessNetworkAfterException( e ) )
+ {
+ try
+ {
+ const ResourceType eType = getResourceType( xEnv );
+ switch ( eType )
+ {
+ case UNKNOWN:
+ case DAV:
+ throw beans::UnknownPropertyException(Name);
+
+ case NON_DAV:
+ // Try to remove property from local store.
+ ContentImplHelper::removeProperty( Name );
+ break;
+
+ default:
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::removeProperty - "
+ "Unsupported resource type!" );
+ break;
+ }
+ }
+ catch ( uno::Exception const & )
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::removeProperty - "
+ "Unable to determine resource type!" );
+ }
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::removeProperty - "
+ "Unable to determine resource type!" );
+// throw beans::UnknownPropertyException();
+ }
+ }
+ }
+}
+
+// virtual
+void SAL_CALL Content::addProperty( const OUString& Name,
+ sal_Int16 Attributes,
+ const uno::Any& DefaultValue )
+{
+ beans::Property aProperty;
+ aProperty.Name = Name;
+ aProperty.Type = DefaultValue.getValueType();
+ aProperty.Attributes = Attributes;
+ aProperty.Handle = -1;
+
+ addProperty( ucb::PropertyCommandArgument( aProperty, DefaultValue ),
+ uno::Reference< ucb::XCommandEnvironment >());
+}
+
+// virtual
+void SAL_CALL Content::removeProperty( const OUString& Name )
+{
+ removeProperty( Name,
+ uno::Reference< ucb::XCommandEnvironment >() );
+}
+
+
+// XContentCreator methods.
+
+
+// virtual
+uno::Sequence< ucb::ContentInfo > SAL_CALL
+Content::queryCreatableContentsInfo()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Sequence< ucb::ContentInfo > aSeq( 2 );
+
+ // document.
+ aSeq.getArray()[ 0 ].Type = WEBDAV_CONTENT_TYPE;
+ aSeq.getArray()[ 0 ].Attributes
+ = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
+ | ucb::ContentInfoAttribute::KIND_DOCUMENT;
+
+ beans::Property aProp;
+ m_pProvider->getProperty( "Title", aProp );
+
+ uno::Sequence< beans::Property > aDocProps( 1 );
+ aDocProps.getArray()[ 0 ] = aProp;
+ aSeq.getArray()[ 0 ].Properties = aDocProps;
+
+ // folder.
+ aSeq.getArray()[ 1 ].Type = WEBDAV_COLLECTION_TYPE;
+ aSeq.getArray()[ 1 ].Attributes
+ = ucb::ContentInfoAttribute::KIND_FOLDER;
+
+ uno::Sequence< beans::Property > aFolderProps( 1 );
+ aFolderProps.getArray()[ 0 ] = aProp;
+ aSeq.getArray()[ 1 ].Properties = aFolderProps;
+ return aSeq;
+}
+
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+Content::createNewContent( const ucb::ContentInfo& Info )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( !Info.Type.getLength() )
+ return uno::Reference< ucb::XContent >();
+
+ if ( ( Info.Type != WEBDAV_COLLECTION_TYPE )
+ &&
+ ( Info.Type != WEBDAV_CONTENT_TYPE ) )
+ return uno::Reference< ucb::XContent >();
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+
+ SAL_WARN_IF( aURL.isEmpty(), "ucb.ucp.webdav",
+ "WebdavContent::createNewContent - empty identifier!" );
+
+ if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
+ aURL += "/";
+
+ bool isCollection;
+ if ( Info.Type == WEBDAV_COLLECTION_TYPE )
+ {
+ aURL += "New_Collection";
+ isCollection = true;
+ }
+ else
+ {
+ aURL += "New_Content";
+ isCollection = false;
+ }
+
+ uno::Reference< ucb::XContentIdentifier > xId(
+ new ::ucbhelper::ContentIdentifier( aURL ) );
+
+ // create the local content
+ try
+ {
+ return new ::http_dav_ucp::Content( m_xContext,
+ m_pProvider,
+ xId,
+ m_xResAccess->getSessionFactory(),
+ isCollection );
+ }
+ catch ( ucb::ContentCreationException & )
+ {
+ return uno::Reference< ucb::XContent >();
+ }
+}
+
+
+// virtual
+OUString Content::getParentURL()
+{
+ // <scheme>:// -> ""
+ // <scheme>://foo -> ""
+ // <scheme>://foo/ -> ""
+ // <scheme>://foo/bar -> <scheme>://foo/
+ // <scheme>://foo/bar/ -> <scheme>://foo/
+ // <scheme>://foo/bar/abc -> <scheme>://foo/bar/
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+
+ sal_Int32 nPos = aURL.lastIndexOf( '/' );
+ if ( nPos == ( aURL.getLength() - 1 ) )
+ {
+ // Trailing slash found. Skip.
+ nPos = aURL.lastIndexOf( '/', nPos );
+ }
+
+ sal_Int32 nPos1 = aURL.lastIndexOf( '/', nPos );
+ if ( nPos1 != -1 )
+ nPos1 = aURL.lastIndexOf( '/', nPos1 );
+
+ if ( nPos1 == -1 )
+ return OUString();
+
+ return aURL.copy( 0, nPos + 1 );
+}
+
+
+// Non-interface methods.
+
+
+// static
+uno::Reference< sdbc::XRow > Content::getPropertyValues(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Sequence< beans::Property >& rProperties,
+ const ContentProperties& rData,
+ const rtl::Reference< ::ucbhelper::ContentProviderImplHelper >& rProvider,
+ const OUString& rContentId )
+{
+ // Note: Empty sequence means "get values of all supported properties".
+
+ rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
+ = new ::ucbhelper::PropertyValueSet( rxContext );
+
+ sal_Int32 nCount = rProperties.getLength();
+ if ( nCount )
+ {
+ uno::Reference< beans::XPropertySet > xAdditionalPropSet;
+ bool bTriedToGetAdditionalPropSet = false;
+
+ const beans::Property* pProps = rProperties.getConstArray();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const beans::Property& rProp = pProps[ n ];
+
+ // Process standard UCB, DAV and HTTP properties.
+ const uno::Any & rValue = rData.getValue( rProp.Name );
+ if ( rValue.hasValue() )
+ {
+ xRow->appendObject( rProp, rValue );
+ }
+ else
+ {
+ // Process local Additional Properties.
+ if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
+ {
+ xAdditionalPropSet =
+ rProvider->getAdditionalPropertySet( rContentId,
+ false );
+ bTriedToGetAdditionalPropSet = true;
+ }
+
+ if ( !xAdditionalPropSet.is() ||
+ !xRow->appendPropertySetValue(
+ xAdditionalPropSet, rProp ) )
+ {
+ // Append empty entry.
+ xRow->appendVoid( rProp );
+ }
+ }
+ }
+ }
+ else
+ {
+ // Append all standard UCB, DAV and HTTP properties.
+
+ const std::unique_ptr< PropertyValueMap > & xProps = rData.getProperties();
+
+ ContentProvider * pProvider
+ = static_cast< ContentProvider * >( rProvider.get() );
+ beans::Property aProp;
+
+ for ( const auto& rProp : *xProps )
+ {
+ if ( pProvider->getProperty( rProp.first, aProp ) )
+ xRow->appendObject( aProp, rProp.second.value() );
+ }
+
+ // Append all local Additional Properties.
+ uno::Reference< beans::XPropertySet > xSet =
+ rProvider->getAdditionalPropertySet( rContentId, false );
+ xRow->appendPropertySet( xSet );
+ }
+
+ return uno::Reference<sdbc::XRow>(xRow);
+}
+
+namespace {
+void GetPropsUsingHeadRequest(DAVResource& resource,
+ const std::unique_ptr< DAVResourceAccess >& xResAccess,
+ const std::vector< OUString >& aHTTPNames,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv)
+{
+ if (!aHTTPNames.empty())
+ {
+ DAVOptions aDAVOptions;
+ OUString aTargetURL = xResAccess->getURL();
+ // retrieve the cached options if any
+ aStaticDAVOptionsCache.getDAVOptions(aTargetURL, aDAVOptions);
+
+ // clean cached value of PROPFIND property names
+ // PROPPATCH can change them
+ Content::removeCachedPropertyNames(aTargetURL);
+ // test if HEAD allowed, if not, throw, should be caught immediately
+ // SC_GONE used internally by us, see comment in Content::getPropertyValues
+ // in the catch scope
+ if (aDAVOptions.getHttpResponseStatusCode() != SC_GONE &&
+ !aDAVOptions.isHeadAllowed())
+ {
+ throw DAVException(DAVException::DAV_HTTP_ERROR, "405 Not Implemented", SC_METHOD_NOT_ALLOWED);
+ }
+ // if HEAD is enabled on this site
+ // check if there is a relevant HTTP response status code cached
+ if (aDAVOptions.getHttpResponseStatusCode() != SC_NONE)
+ {
+ // throws exception as if there was a server error, a DAV exception
+ throw DAVException(DAVException::DAV_HTTP_ERROR,
+ aDAVOptions.getHttpResponseStatusText(),
+ aDAVOptions.getHttpResponseStatusCode());
+ // Unreachable
+ }
+
+ xResAccess->HEAD(aHTTPNames, resource, xEnv);
+ }
+}
+}
+
+uno::Reference< sdbc::XRow > Content::getPropertyValues(
+ const uno::Sequence< beans::Property >& rProperties,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ std::unique_ptr< ContentProperties > xProps;
+ std::unique_ptr< ContentProperties > xCachedProps;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ OUString aUnescapedTitle;
+ bool bHasAll = false;
+ uno::Reference< uno::XComponentContext > xContext;
+ uno::Reference< ucb::XContentIdentifier > xIdentifier;
+ rtl::Reference< ::ucbhelper::ContentProviderImplHelper > xProvider;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ aUnescapedTitle = DecodeURI(m_aEscapedTitle);
+ xContext.set( m_xContext );
+ xIdentifier.set( m_xIdentifier );
+ xProvider = m_xProvider;
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+
+ // First, ask cache...
+ if (m_xCachedProps)
+ {
+ xCachedProps.reset( new ContentProperties( *m_xCachedProps ) );
+
+ std::vector< OUString > aMissingProps;
+ if ( xCachedProps->containsAllNames( rProperties, aMissingProps ) )
+ {
+ // All properties are already in cache! No server access needed.
+ bHasAll = true;
+ }
+
+ // use the cached ContentProperties instance
+ xProps.reset( new ContentProperties( *xCachedProps ) );
+ }
+ }
+
+ bool bNetworkAccessAllowed = true;
+
+ if ( !m_bTransient && !bHasAll )
+ {
+ // Obtain values from server...
+
+
+ // First, identify whether resource is DAV or not
+ const ResourceType eType = getResourceType(
+ xEnv, xResAccess, &bNetworkAccessAllowed );
+
+ if ( eType == DAV )
+ {
+ // cache lookup... getResourceType may fill the props cache via
+ // PROPFIND!
+ if (m_xCachedProps)
+ {
+ xCachedProps.reset(
+ new ContentProperties( *m_xCachedProps ) );
+
+ std::vector< OUString > aMissingProps;
+ if ( xCachedProps->containsAllNames(
+ rProperties, aMissingProps ) )
+ {
+ // All properties are already in cache! No server access
+ // needed.
+ bHasAll = true;
+ }
+
+ // use the cached ContentProperties instance
+ xProps.reset( new ContentProperties( *xCachedProps ) );
+ }
+
+ if ( !bHasAll )
+ {
+ // Only DAV resources support PROPFIND
+ std::vector< OUString > aPropNames;
+
+ uno::Sequence< beans::Property > aProperties(rProperties);
+
+ if ( aProperties.getLength() > 0 )
+ ContentProperties::UCBNamesToDAVNames(
+ aProperties, aPropNames );
+
+ if ( !aPropNames.empty() )
+ {
+ std::vector< DAVResource > resources;
+ try
+ {
+ xResAccess->PROPFIND(
+ DAVZERO, aPropNames, resources, xEnv );
+
+ if ( 1 == resources.size() )
+ {
+#if defined SAL_LOG_INFO
+ {//debug
+ // print received resources
+ std::vector< DAVPropertyValue >::const_iterator it = resources[0].properties.begin();
+ std::vector< DAVPropertyValue >::const_iterator end = resources[0].properties.end();
+ while ( it != end )
+ {
+ OUString aPropValue;
+ bool bValue;
+ uno::Sequence< ucb::LockEntry > aSupportedLocks;
+ if( (*it).Value >>= aPropValue )
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << (*it).Name << ":" << aPropValue );
+ else if( (*it).Value >>= bValue )
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << (*it).Name << ":" <<
+ ( bValue ? "true" : "false" ) );
+ else if( (*it).Value >>= aSupportedLocks )
+ {
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << (*it).Name << ":" );
+ for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
+ {
+ SAL_INFO( "ucb.ucp.webdav"," scope: "
+ << (aSupportedLocks[n].Scope == ucb::LockScope_SHARED ? "shared" : "exclusive")
+ << ", type: "
+ << (aSupportedLocks[n].Type != ucb::LockType_WRITE ? "" : "write") );
+ }
+ }
+ ++it;
+ }
+ }
+#endif
+ if (xProps)
+ xProps->addProperties(
+ aPropNames,
+ ContentProperties( resources[ 0 ] ));
+ else
+ xProps.reset(
+ new ContentProperties( resources[ 0 ] ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ bNetworkAccessAllowed = bNetworkAccessAllowed
+ && shouldAccessNetworkAfterException( e );
+
+ if ( !bNetworkAccessAllowed )
+ {
+ cancelCommandExecution( e, xEnv );
+ // unreachable
+ }
+ }
+ }
+ }
+ }
+
+ if ( bNetworkAccessAllowed )
+ {
+ // All properties obtained already?
+ std::vector< OUString > aMissingProps;
+ if ( !( xProps
+ && xProps->containsAllNames(rProperties, aMissingProps))
+ // i#121922 for non-DAV, uncacheable properties must be fetched
+ // regardless of m_bDidGetOrHead.
+ // But SharePoint may do weird things on HEAD so for DAV
+ // only do this if required.
+ && (eType != DAV || !m_bDidGetOrHead))
+ {
+ // Possibly the missing props can be obtained using a HEAD
+ // request.
+
+ std::vector< OUString > aHeaderNames;
+ ContentProperties::UCBNamesToHTTPNames(
+ rProperties,
+ aHeaderNames );
+
+ if( eType != DAV )
+ {
+ // in case of not DAV PROFIND (previously in program flow) failed
+ // so we need to add the only prop that's common
+ // to DAV and NON_DAV: MediaType, that maps to Content-Type
+ aHeaderNames.push_back( "Content-Type" );
+ }
+
+ if (!aHeaderNames.empty()) try
+ {
+ DAVResource resource;
+ GetPropsUsingHeadRequest(resource, xResAccess, aHeaderNames, xEnv);
+ m_bDidGetOrHead = true;
+
+ if (xProps)
+ xProps->addProperties(
+ aMissingProps,
+ ContentProperties( resource ) );
+ else
+ xProps.reset ( new ContentProperties( resource ) );
+
+ if (m_eResourceType == NON_DAV)
+ xProps->addProperties(aMissingProps,
+ ContentProperties(
+ aUnescapedTitle,
+ false));
+ }
+ catch ( DAVException const & e )
+ {
+ // non "general-purpose servers" may not support HEAD requests
+ // see http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1
+ // In this case, perform a partial GET only to get the header info
+ // vid. http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
+ // WARNING if the server does not support partial GETs,
+ // the GET will transfer the whole content
+ bool bError = true;
+ DAVException aLastException = e;
+ OUString aTargetURL = xResAccess->getURL();
+
+ if ( e.getError() == DAVException::DAV_HTTP_ERROR )
+ {
+ // According to the spec. the origin server SHOULD return
+ // * 405 (Method Not Allowed):
+ // the method is known but not allowed for the requested resource
+ // * 501 (Not Implemented):
+ // the method is unrecognized or not implemented
+ // * 404 (SC_NOT_FOUND)
+ // is for google-code server and for MS IIS 10.0 Web server
+ // when only GET is enabled
+ if ( aLastException.getStatus() == SC_NOT_IMPLEMENTED ||
+ aLastException.getStatus() == SC_METHOD_NOT_ALLOWED ||
+ aLastException.getStatus() == SC_NOT_FOUND )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "HEAD probably not implemented: fall back to a partial GET" );
+ aStaticDAVOptionsCache.setHeadAllowed( aTargetURL, false );
+ lcl_sendPartialGETRequest( bError,
+ aLastException,
+ aMissingProps,
+ aHeaderNames,
+ xResAccess,
+ xProps,
+ xEnv );
+ m_bDidGetOrHead = !bError;
+ }
+ }
+
+ if ( bError )
+ {
+ DAVOptions aDAVOptionsException;
+
+ aDAVOptionsException.setURL( aTargetURL );
+ // check if the error was SC_NOT_FOUND, meaning that the
+ // GET fall back didn't succeeded and the element is really missing
+ // we will consider the resource SC_GONE (410) for some time
+ // we use SC_GONE because has the same meaning of SC_NOT_FOUND (404)
+ // see:
+ // <https://tools.ietf.org/html/rfc7231#section-6.5.9> (retrieved 2016-10-09)
+ // apparently it's not used to mark the missing HEAD method (so far...)
+ sal_uInt16 ResponseStatusCode =
+ ( aLastException.getStatus() == SC_NOT_FOUND ) ?
+ SC_GONE :
+ aLastException.getStatus();
+ aDAVOptionsException.setHttpResponseStatusCode( ResponseStatusCode );
+ aDAVOptionsException.setHttpResponseStatusText( aLastException.getData() );
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptionsException,
+ m_nOptsCacheLifeNotFound );
+
+ if ( !shouldAccessNetworkAfterException( aLastException ) )
+ {
+ cancelCommandExecution( aLastException, xEnv );
+ // unreachable
+ }
+ }
+ }
+ }
+ }
+
+ // might trigger HTTP redirect.
+ // Therefore, title must be updated here.
+ CurlUri const aUri( xResAccess->getURL() );
+ aUnescapedTitle = aUri.GetPathBaseNameUnescaped();
+
+ if ( eType == UNKNOWN )
+ {
+ xProps.reset( new ContentProperties( aUnescapedTitle ) );
+ }
+
+ // For DAV resources we only know the Title, for non-DAV
+ // resources we additionally know that it is a document.
+
+ else if ( eType == DAV )
+ {
+ if (!xProps)
+ xProps.reset(new ContentProperties(aUnescapedTitle));
+ else
+ xProps->addProperty("Title", uno::Any(aUnescapedTitle), true);
+ }
+ else
+ {
+ if (!xProps)
+ xProps.reset( new ContentProperties( aUnescapedTitle, false ) );
+ else
+ xProps->addProperty(
+ "Title",
+ uno::Any( aUnescapedTitle ),
+ true );
+
+ xProps->addProperty(
+ "IsFolder",
+ uno::Any( false ),
+ true );
+ xProps->addProperty(
+ "IsDocument",
+ uno::Any( true ),
+ true );
+ xProps->addProperty(
+ "ContentType",
+ uno::Any( OUString(WEBDAV_CONTENT_TYPE) ),
+ true );
+ }
+ }
+ else
+ {
+ // No server access for just created (not yet committed) objects.
+ // Only a minimal set of properties supported at this stage.
+ if (m_bTransient)
+ xProps.reset( new ContentProperties( aUnescapedTitle,
+ m_bCollection ) );
+ }
+
+ // Add a default for the properties requested but not found.
+ // Determine still missing properties, add a default.
+ // Some client function doesn't expect a void uno::Any,
+ // but instead wants some sort of default.
+ std::vector< OUString > aMissingProps;
+ if ( !xProps->containsAllNames(
+ rProperties, aMissingProps ) )
+ {
+ //
+ for ( std::vector< rtl::OUString >::const_iterator it = aMissingProps.begin();
+ it != aMissingProps.end(); ++it )
+ {
+ // For the time being only a couple of properties need to be added
+ if ( (*it) == "DateModified" || (*it) == "DateCreated" )
+ {
+ util::DateTime aDate;
+ xProps->addProperty(
+ (*it),
+ uno::Any( aDate ),
+ true );
+ }
+ else if (bNetworkAccessAllowed) // don't set these if connection failed
+ {
+ // If WebDAV didn't return the resource type, assume default
+ // This happens e.g. for lists exported by SharePoint
+ if ((*it) == "IsFolder")
+ {
+ xProps->addProperty(
+ (*it),
+ uno::Any( false ),
+ true );
+ }
+ else if ((*it) == "IsDocument")
+ {
+ xProps->addProperty(
+ (*it),
+ uno::Any( true ),
+ true );
+ }
+ }
+ }
+ }
+
+ sal_Int32 nCount = rProperties.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const OUString rName = rProperties[ n ].Name;
+ if ( rName == "BaseURI" )
+ {
+ // Add BaseURI property, if requested.
+ xProps->addProperty(
+ "BaseURI",
+ uno::Any( getBaseURI( xResAccess ) ),
+ true );
+ }
+ else if ( rName == "CreatableContentsInfo" )
+ {
+ // Add CreatableContentsInfo property, if requested.
+ bool bFolder = false;
+ xProps->getValue( "IsFolder" )
+ >>= bFolder;
+ xProps->addProperty(
+ "CreatableContentsInfo",
+ uno::Any( bFolder
+ ? queryCreatableContentsInfo()
+ : uno::Sequence< ucb::ContentInfo >() ),
+ true );
+ }
+ }
+
+ uno::Reference< sdbc::XRow > xResultRow
+ = getPropertyValues( xContext,
+ rProperties,
+ *xProps,
+ xProvider,
+ xIdentifier->getContentIdentifier() );
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if (!m_xCachedProps)
+ m_xCachedProps.reset( new CachableContentProperties( *xProps ) );
+ else
+ m_xCachedProps->addProperties( *xProps );
+
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ m_aEscapedTitle = EncodeSegment(aUnescapedTitle);
+ }
+
+ return xResultRow;
+}
+
+
+uno::Sequence< uno::Any > Content::setPropertyValues(
+ const uno::Sequence< beans::PropertyValue >& rValues,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ uno::Reference< ucb::XContentIdentifier > xIdentifier;
+ rtl::Reference< ContentProvider > xProvider;
+ bool bTransient;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ xProvider.set( m_pProvider );
+ xIdentifier.set( m_xIdentifier );
+ bTransient = m_bTransient;
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ uno::Sequence< uno::Any > aRet( rValues.getLength() );
+ auto aRetRange = asNonConstRange(aRet);
+ uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
+ sal_Int32 nChanged = 0;
+
+ beans::PropertyChangeEvent aEvent;
+ aEvent.Source = static_cast< cppu::OWeakObject * >( this );
+ aEvent.Further = false;
+ // aEvent.PropertyName =
+ aEvent.PropertyHandle = -1;
+ // aEvent.OldValue =
+ // aEvent.NewValue =
+
+ std::vector< ProppatchValue > aProppatchValues;
+ std::vector< sal_Int32 > aProppatchPropsPositions;
+
+ uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet;
+ bool bTriedToGetAdditionalPropSet = false;
+
+ bool bExchange = false;
+ OUString aNewTitle;
+ OUString aOldTitle;
+ sal_Int32 nTitlePos = -1;
+
+ uno::Reference< beans::XPropertySetInfo > xInfo;
+
+ const beans::PropertyValue* pValues = rValues.getConstArray();
+ sal_Int32 nCount = rValues.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const beans::PropertyValue& rValue = pValues[ n ];
+ const OUString & rName = rValue.Name;
+
+ beans::Property aTmpProp;
+ xProvider->getProperty( rName, aTmpProp );
+
+ if ( aTmpProp.Attributes & beans::PropertyAttribute::READONLY )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ continue;
+ }
+
+
+ // Mandatory props.
+
+
+ if ( rName == "ContentType" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "IsDocument" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "IsFolder" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "Title" )
+ {
+ OUString aNewValue;
+ if ( rValue.Value >>= aNewValue )
+ {
+ // No empty titles!
+ if ( aNewValue.getLength() > 0 )
+ {
+ try
+ {
+ CurlUri const aURI(xIdentifier->getContentIdentifier());
+ aOldTitle = aURI.GetPathBaseNameUnescaped();
+
+ if ( aNewValue != aOldTitle )
+ {
+ // modified title -> modified URL -> exchange !
+ if ( !bTransient )
+ bExchange = true;
+
+ // new value will be set later...
+ aNewTitle = aNewValue;
+
+ // remember position within sequence of values (for
+ // error handling).
+ nTitlePos = n;
+ }
+ }
+ catch ( DAVException const & )
+ {
+ aRetRange[ n ] <<= lang::IllegalArgumentException(
+ "Invalid content identifier!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= lang::IllegalArgumentException(
+ "Empty title not allowed!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= beans::IllegalTypeException(
+ "Property value has wrong type!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ else
+ {
+
+ // Optional props.
+
+
+ OUString aSpecialName;
+ bool bIsSpecial = DAVProperties::isUCBSpecialProperty( rName, aSpecialName );
+
+ if ( !xInfo.is() )
+ xInfo = getPropertySetInfo( xEnv,
+ false /* don't cache data */ );
+
+ if ( !xInfo->hasPropertyByName( bIsSpecial ? aSpecialName : rName ) )
+ {
+ // Check, whether property exists. Skip otherwise.
+ // PROPPATCH::set would add the property automatically, which
+ // is not allowed for "setPropertyValues" command!
+ aRetRange[ n ] <<= beans::UnknownPropertyException(
+ "Property is unknown!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ continue;
+ }
+
+ if ( rName == "Size" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "DateCreated" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "DateModified" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "MediaType" )
+ {
+ // Read-only property!
+ // (but could be writable, if 'getcontenttype' would be)
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ if ( rName == "CreatableContentsInfo" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else
+ {
+ if ( getResourceType( xEnv, xResAccess ) == DAV )
+ {
+ // Property value will be set on server.
+ ProppatchValue aValue( PROPSET, rName, rValue.Value );
+ aProppatchValues.push_back( aValue );
+
+ // remember position within sequence of values (for
+ // error handling).
+ aProppatchPropsPositions.push_back( n );
+ }
+ else
+ {
+ // Property value will be stored in local property store.
+ if ( !bTriedToGetAdditionalPropSet &&
+ !xAdditionalPropSet.is() )
+ {
+ xAdditionalPropSet
+ = getAdditionalPropertySet( false );
+ bTriedToGetAdditionalPropSet = true;
+ }
+
+ if ( xAdditionalPropSet.is() )
+ {
+ try
+ {
+ uno::Any aOldValue
+ = xAdditionalPropSet->getPropertyValue( rName );
+ if ( aOldValue != rValue.Value )
+ {
+ xAdditionalPropSet->setPropertyValue(
+ rName, rValue.Value );
+
+ aEvent.PropertyName = rName;
+ aEvent.OldValue = aOldValue;
+ aEvent.NewValue = rValue.Value;
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+ }
+ catch ( beans::UnknownPropertyException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ catch ( lang::WrappedTargetException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ catch ( beans::PropertyVetoException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ catch ( lang::IllegalArgumentException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= uno::Exception(
+ "No property set for storing the value!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ }
+ }
+ } // for
+
+ if ( !bTransient && (!aProppatchValues.empty()) )
+ {
+ try
+ {
+ // clean cached value of PROPFIND property names
+ // PROPPATCH can change them
+ removeCachedPropertyNames( xResAccess->getURL() );
+ // Set property values at server.
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ xResAccess->PROPPATCH( aProppatchValues, xEnv );
+
+ for ( const auto& rProppatchValue : aProppatchValues )
+ {
+ aEvent.PropertyName = rProppatchValue.name;
+ aEvent.OldValue = uno::Any(); // @@@ too expensive to obtain!
+ aEvent.NewValue = rProppatchValue.value;
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+ }
+ catch ( DAVException const & e )
+ {
+// SAL_WARN( "ucb.ucp.webdav",
+// "Content::setPropertyValues - PROPPATCH failed!" );
+
+#if 1
+ cancelCommandExecution( e, xEnv );
+ // unreachable
+#else
+ // Note: PROPPATCH either sets ALL property values OR NOTHING.
+
+ std::vector< sal_Int32 >::const_iterator it
+ = aProppatchPropsPositions.begin();
+ std::vector< sal_Int32 >::const_iterator end
+ = aProppatchPropsPositions.end();
+
+ while ( it != end )
+ {
+ // Set error.
+ aRetRange[ (*it) ] <<= MapDAVException( e, true );
+ ++it;
+ }
+#endif
+ }
+ }
+
+ if ( bExchange )
+ {
+ // Assemble new content identifier...
+
+ OUString aNewURL = getParentURL();
+ if ( aNewURL.lastIndexOf( '/' ) != ( aNewURL.getLength() - 1 ) )
+ aNewURL += "/";
+
+ aNewURL += EncodeSegment(aNewTitle);
+
+ uno::Reference< ucb::XContentIdentifier > xNewId
+ = new ::ucbhelper::ContentIdentifier( aNewURL );
+ uno::Reference< ucb::XContentIdentifier > xOldId = xIdentifier;
+
+ try
+ {
+ CurlUri const sourceURI( xOldId->getContentIdentifier() );
+ CurlUri targetURI( xNewId->getContentIdentifier() );
+
+ targetURI.SetScheme( sourceURI.GetScheme() );
+
+ // clean cached value of PROPFIND property names
+ removeCachedPropertyNames( sourceURI.GetURI() );
+ removeCachedPropertyNames( targetURI.GetURI() );
+ aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
+ aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
+ xResAccess->MOVE(
+ sourceURI.GetRelativeReference(), targetURI.GetURI(), false, xEnv );
+
+ // @@@ Should check for resources that could not be moved
+ // (due to source access or target overwrite) and send
+ // this information through the interaction handler.
+
+ // @@@ Existing content should be checked to see if it needs
+ // to be deleted at the source
+
+ // @@@ Existing content should be checked to see if it has
+ // been overwritten at the target
+
+ if ( exchangeIdentity( xNewId ) )
+ {
+ xResAccess->setURL( aNewURL );
+
+// DAV resources store all additional props on server!
+// // Adapt Additional Core Properties.
+// renameAdditionalPropertySet( xOldId->getContentIdentifier(),
+// xNewId->getContentIdentifier(),
+// true );
+ }
+ else
+ {
+ // Do not set new title!
+ aNewTitle.clear();
+
+ // Set error .
+ aRetRange[ nTitlePos ] <<= uno::Exception(
+ "Exchange failed!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ // Do not set new title!
+ aNewTitle.clear();
+
+ // Set error .
+ aRetRange[ nTitlePos ] = MapDAVException( e, true );
+ }
+ }
+
+ if ( aNewTitle.getLength() )
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ aEvent.PropertyName = "Title";
+ aEvent.OldValue <<= aOldTitle;
+ aEvent.NewValue <<= aNewTitle;
+
+ m_aEscapedTitle = EncodeSegment(aNewTitle);
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+
+ if ( nChanged > 0 )
+ {
+ aChanges.realloc( nChanged );
+ notifyPropertiesChange( aChanges );
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+
+ return aRet;
+}
+
+
+uno::Any Content::open(
+ const ucb::OpenCommandArgument3 & rArg,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ uno::Any aRet;
+
+ bool bOpenFolder = ( ( rArg.Mode == ucb::OpenMode::ALL ) ||
+ ( rArg.Mode == ucb::OpenMode::FOLDERS ) ||
+ ( rArg.Mode == ucb::OpenMode::DOCUMENTS ) );
+ if ( bOpenFolder )
+ {
+ if ( isFolder( xEnv ) )
+ {
+ // Open collection.
+
+ uno::Reference< ucb::XDynamicResultSet > xSet
+ = new DynamicResultSet( m_xContext, this, rArg, xEnv );
+ aRet <<= xSet;
+ }
+ else
+ {
+ // Error: Not a folder!
+
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ lang::IllegalArgumentException(
+ "Non-folder resource cannot be opened as folder! Wrong Open Mode!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+
+ if ( rArg.Sink.is() )
+ {
+ // Open document.
+
+ if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
+ ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
+ {
+ // Currently(?) unsupported.
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::UnsupportedOpenModeException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ sal_Int16( rArg.Mode ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY );
+ if ( xOut.is() )
+ {
+ // PUSH: write data
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ xResAccess.reset(
+ new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ xResAccess->setFlags( rArg.OpeningFlags );
+ DAVResource aResource;
+ std::vector< OUString > aHeaders;
+
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->GET( xOut, aHeaders, aResource, xEnv );
+ m_bDidGetOrHead = true;
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // cache headers.
+ if (!m_xCachedProps)
+ m_xCachedProps.reset(
+ new CachableContentProperties( ContentProperties( aResource ) ) );
+ else
+ m_xCachedProps->addProperties( ContentProperties( aResource ) );
+
+ m_xResAccess.reset(
+ new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, xEnv );
+ // Unreachable
+ }
+ }
+ else
+ {
+ uno::Reference< io::XActiveDataSink > xDataSink( rArg.Sink, uno::UNO_QUERY );
+ if ( xDataSink.is() )
+ {
+ // PULL: wait for client read
+ OUString aTargetURL = m_xIdentifier->getContentIdentifier();
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ xResAccess.reset(
+ new DAVResourceAccess( *m_xResAccess ) );
+ }
+ xResAccess->setFlags( rArg.OpeningFlags );
+
+ // fill inputstream sync; return if all data present
+ DAVResource aResource;
+ std::vector< OUString > aHeaders;
+
+ aTargetURL = xResAccess->getURL();
+ removeCachedPropertyNames( aTargetURL );
+ // check if the resource was present on the server
+ // first update it, if necessary
+ // if the open is called directly, without the default open sequence,
+ // e.g. the one used when opening a file looking for properties
+ // first this call will have no effect, since OPTIONS would have already been called
+ // as a consequence of getPropertyValues()
+ DAVOptions aDAVOptions;
+ getResourceOptions( xEnv, aDAVOptions, xResAccess );
+
+ if (aDAVOptions.getHttpResponseStatusCode() != SC_NONE
+ // tdf#148426 fall back to GET in case of 500
+ && aDAVOptions.getHttpResponseStatusCode() != SC_INTERNAL_SERVER_ERROR)
+ {
+ // throws exception as if there was a server error, a DAV exception
+ throw DAVException( DAVException::DAV_HTTP_ERROR,
+ aDAVOptions.getHttpResponseStatusText(),
+ aDAVOptions.getHttpResponseStatusCode() );
+ }
+ uno::Reference< io::XInputStream > xIn
+ = xResAccess->GET( aHeaders, aResource, xEnv );
+ m_bDidGetOrHead = true;
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // cache headers.
+ if (!m_xCachedProps)
+ m_xCachedProps.reset(
+ new CachableContentProperties( ContentProperties( aResource ) ) );
+ else
+ m_xCachedProps->addProperties(
+ aResource.properties );
+
+ m_xResAccess.reset(
+ new DAVResourceAccess( *xResAccess ) );
+ }
+
+ xDataSink->setInputStream( xIn );
+ }
+ catch ( DAVException const & e )
+ {
+ //TODO cache the http error if not yet cached
+ cancelCommandExecution( e, xEnv );
+ // Unreachable
+ }
+ }
+ else
+ {
+ // Note: aOpenCommand.Sink may contain an XStream
+ // implementation. Support for this type of
+ // sink is optional...
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::UnsupportedDataSinkException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ rArg.Sink ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+ }
+
+ return aRet;
+}
+
+
+void Content::post(
+ const ucb::PostCommandArgument2 & rArg,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ uno::Reference< io::XActiveDataSink > xSink( rArg.Sink, uno::UNO_QUERY );
+ if ( xSink.is() )
+ {
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset(
+ new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ removeCachedPropertyNames( xResAccess->getURL() );
+ uno::Reference< io::XInputStream > xResult
+ = xResAccess->POST( rArg.MediaType,
+ rArg.Referer,
+ rArg.Source,
+ xEnv );
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ m_xResAccess.reset(
+ new DAVResourceAccess( *xResAccess ) );
+ }
+
+ xSink->setInputStream( xResult );
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, xEnv, true );
+ // Unreachable
+ }
+ }
+ else
+ {
+ uno::Reference< io::XOutputStream > xResult( rArg.Sink, uno::UNO_QUERY );
+ if ( xResult.is() )
+ {
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset(
+ new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->POST( rArg.MediaType,
+ rArg.Referer,
+ rArg.Source,
+ xResult,
+ xEnv );
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ m_xResAccess.reset(
+ new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, xEnv, true );
+ // Unreachable
+ }
+ }
+ else
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::UnsupportedDataSinkException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ rArg.Sink ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+}
+
+
+void Content::queryChildren( ContentRefList& rChildren )
+{
+ // Obtain a list with a snapshot of all currently instantiated contents
+ // from provider and extract the contents which are direct children
+ // of this content.
+
+ ::ucbhelper::ContentRefList aAllContents;
+ m_xProvider->queryExistingContents( aAllContents );
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+ sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
+
+ if ( nURLPos != ( aURL.getLength() - 1 ) )
+ {
+ // No trailing slash found. Append.
+ aURL += "/";
+ }
+
+ sal_Int32 nLen = aURL.getLength();
+
+ for ( const auto& rChild : aAllContents )
+ {
+ ::ucbhelper::ContentImplHelperRef xChild = rChild;
+ OUString aChildURL
+ = xChild->getIdentifier()->getContentIdentifier();
+
+ // Is aURL a prefix of aChildURL?
+ if ( ( aChildURL.getLength() > nLen ) &&
+ ( aChildURL.startsWith( aURL ) ) )
+ {
+ sal_Int32 nPos = nLen;
+ nPos = aChildURL.indexOf( '/', nPos );
+
+ if ( ( nPos == -1 ) ||
+ ( nPos == ( aChildURL.getLength() - 1 ) ) )
+ {
+ // No further slashes / only a final slash. It's a child!
+ rChildren.push_back(
+ ::http_dav_ucp::Content::ContentRef(
+ static_cast< ::http_dav_ucp::Content * >(
+ xChild.get() ) ) );
+ }
+ }
+ }
+}
+
+
+void Content::insert(
+ const uno::Reference< io::XInputStream > & xInputStream,
+ bool bReplaceExisting,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ bool bTransient, bCollection;
+ OUString aEscapedTitle;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ bTransient = m_bTransient;
+ bCollection = m_bCollection;
+ aEscapedTitle = m_aEscapedTitle;
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ // Check, if all required properties are present.
+
+ if ( aEscapedTitle.isEmpty() )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::insert - Title missing!" );
+
+ uno::Sequence<OUString> aProps { "Title" };
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::MissingPropertiesException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ aProps ) ),
+ Environment );
+ // Unreachable
+ }
+
+ if ( !bReplaceExisting )
+ {
+ /* [RFC 2616] - HTTP
+
+ The PUT method requests that the enclosed entity be stored under the
+ supplied Request-URI. If the Request-URI refers to an already
+ existing resource, the enclosed entity SHOULD be considered as a
+ modified version of the one residing on the origin server.
+ */
+
+ /* [RFC 2518] - WebDAV
+
+ MKCOL creates a new collection resource at the location specified by
+ the Request-URI. If the resource identified by the Request-URI is
+ non-null then the MKCOL MUST fail.
+ */
+
+ // ==> Complain on PUT, continue on MKCOL.
+ if ( !bTransient || !bCollection )
+ {
+#undef ERROR
+ ucb::UnsupportedNameClashException aEx(
+ "Unable to write without overwrite!",
+ static_cast< cppu::OWeakObject * >( this ),
+ ucb::NameClash::ERROR );
+
+ uno::Reference< task::XInteractionHandler > xIH;
+
+ if ( Environment.is() )
+ xIH = Environment->getInteractionHandler();
+
+ if ( xIH.is() )
+ {
+ uno::Any aExAsAny( aEx );
+
+ rtl::Reference< ucbhelper::SimpleInteractionRequest > xRequest
+ = new ucbhelper::SimpleInteractionRequest(
+ aExAsAny,
+ ContinuationFlags::Approve
+ | ContinuationFlags::Disapprove );
+ xIH->handle( xRequest );
+
+ const ContinuationFlags nResp = xRequest->getResponse();
+
+ switch ( nResp )
+ {
+ case ContinuationFlags::NONE:
+ // Not handled; throw.
+ throw aEx;
+// break;
+
+ case ContinuationFlags::Approve:
+ // Continue -> Overwrite.
+ bReplaceExisting = true;
+ break;
+
+ case ContinuationFlags::Disapprove:
+ // Abort.
+ throw ucb::CommandFailedException(
+ OUString(),
+ uno::Reference< uno::XInterface >(),
+ aExAsAny );
+// break;
+
+ default:
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::insert - "
+ "Unknown interaction selection!" );
+ throw ucb::CommandFailedException(
+ "Unknown interaction selection!",
+ uno::Reference< uno::XInterface >(),
+ aExAsAny );
+// break;
+ }
+ }
+ else
+ {
+ // No IH; throw.
+ throw aEx;
+ }
+ }
+ }
+
+ if ( bTransient )
+ {
+ // Assemble new content identifier...
+ OUString aURL = getParentURL();
+ if ( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ) )
+ aURL += "/";
+
+ aURL += aEscapedTitle;
+
+ try
+ {
+ xResAccess->setURL( aURL );
+
+ if ( bCollection )
+ {
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->MKCOL( Environment );
+ }
+ else
+ {
+ // remove options from cache, PUT may change it
+ // it will be refreshed when needed
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->PUT( xInputStream, Environment );
+ // clean cached value of PROPFIND properties names
+ }
+ // no error , set the resourcetype to unknown type
+ // the resource may have transitioned from NOT FOUND or UNKNOWN to something else
+ // depending on the server behaviour
+ // this will force a recheck of the resource type
+ m_eResourceType = UNKNOWN;
+ m_eResourceTypeForLocks = UNKNOWN;
+ }
+ catch ( DAVException const & except )
+ {
+ if ( bCollection )
+ {
+ if ( except.getStatus() == SC_METHOD_NOT_ALLOWED )
+ {
+ // [RFC 2518] - WebDAV
+ // 405 (Method Not Allowed) - MKCOL can only be
+ // executed on a deleted/non-existent resource.
+
+ if ( bReplaceExisting )
+ {
+ // Destroy old resource.
+ try
+ {
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->DESTROY( Environment );
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+
+ // Insert (recursion!).
+ insert( xInputStream, bReplaceExisting, Environment );
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(
+ new DAVResourceAccess( *xResAccess ) );
+ }
+
+ // Success!
+ return;
+ }
+ else
+ {
+ OUString aTitle;
+ try
+ {
+ CurlUri const aURI( aURL );
+ aTitle = aURI.GetPathBaseNameUnescaped();
+ }
+ catch ( DAVException const & )
+ {
+ }
+
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::NameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aTitle ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+ }
+
+ cancelCommandExecution( except, Environment, true );
+ // Unreachable
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xIdentifier
+ = new ::ucbhelper::ContentIdentifier( aURL );
+ }
+
+ inserted();
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_bTransient = false;
+ }
+ }
+ else
+ {
+ if ( !xInputStream.is() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::MissingInputStreamException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ // save the URL since it may change due to redirection
+ OUString aTargetUrl = xResAccess->getURL();
+ try
+ {
+ removeCachedPropertyNames( xResAccess->getURL() );
+ // remove options from cache, PUT may change it
+ // it will be refreshed when needed
+ aStaticDAVOptionsCache.removeDAVOptions( aTargetUrl );
+ xResAccess->PUT( xInputStream, Environment );
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+}
+
+
+void Content::transfer(
+ const ucb::TransferInfo & rArgs,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ uno::Reference< uno::XComponentContext > xContext;
+ uno::Reference< ucb::XContentIdentifier > xIdentifier;
+ uno::Reference< ucb::XContentProvider > xProvider;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ xContext.set( m_xContext );
+ xIdentifier.set( m_xIdentifier );
+ xProvider.set( m_xProvider );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ OUString aTargetURI;
+ try
+ {
+ CurlUri sourceURI( rArgs.SourceURL );
+ CurlUri targetURI( xIdentifier->getContentIdentifier() );
+
+ aTargetURI = targetURI.GetPathBaseNameUnescaped();
+
+ // Check source's and target's URL scheme
+
+ OUString aScheme = sourceURI.GetScheme().toAsciiLowerCase();
+ if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
+ {
+ sourceURI.SetScheme( HTTP_URL_SCHEME );
+ }
+ else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
+ {
+ sourceURI.SetScheme( HTTPS_URL_SCHEME );
+ }
+ else if ( aScheme == DAV_URL_SCHEME )
+ {
+ sourceURI.SetScheme( HTTP_URL_SCHEME );
+ }
+ else if ( aScheme == DAVS_URL_SCHEME )
+ {
+ sourceURI.SetScheme( HTTPS_URL_SCHEME );
+ }
+ else if (aScheme == WEBDAV_URL_SCHEME)
+ {
+ sourceURI.SetScheme(HTTP_URL_SCHEME);
+ }
+ else if (aScheme == WEBDAVS_URL_SCHEME)
+ {
+ sourceURI.SetScheme(HTTPS_URL_SCHEME);
+ }
+ else
+ {
+ if ( aScheme != HTTP_URL_SCHEME && aScheme != HTTPS_URL_SCHEME )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::InteractiveBadTransferURLException(
+ "Unsupported URL scheme!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+
+ aScheme = targetURI.GetScheme().toAsciiLowerCase();
+ if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
+ targetURI.SetScheme( HTTP_URL_SCHEME );
+ else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
+ targetURI.SetScheme( HTTPS_URL_SCHEME );
+ else if ( aScheme == DAV_URL_SCHEME )
+ targetURI.SetScheme( HTTP_URL_SCHEME );
+ else if ( aScheme == DAVS_URL_SCHEME )
+ targetURI.SetScheme( HTTPS_URL_SCHEME );
+ else if (aScheme == WEBDAV_URL_SCHEME)
+ targetURI.SetScheme(HTTP_URL_SCHEME);
+ else if (aScheme == WEBDAVS_URL_SCHEME)
+ targetURI.SetScheme(HTTPS_URL_SCHEME);
+
+ // @@@ This implementation of 'transfer' only works
+ // if the source and target are located at same host.
+ // (Neon does not support cross-server copy/move)
+
+ // Check for same host
+
+ if ( sourceURI.GetHost().getLength() &&
+ ( sourceURI.GetHost() != targetURI.GetHost() ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::InteractiveBadTransferURLException(
+ "Different hosts!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ OUString aTitle = rArgs.NewTitle;
+
+ if ( aTitle.isEmpty() )
+ aTitle = sourceURI.GetPathBaseNameUnescaped();
+
+ if ( aTitle == "/" )
+ {
+ // kso: ???
+ aTitle.clear();
+ }
+
+ targetURI.AppendPath( aTitle );
+
+ OUString aTargetURL = xIdentifier->getContentIdentifier();
+ if ( ( aTargetURL.lastIndexOf( '/' ) + 1 )
+ != aTargetURL.getLength() )
+ aTargetURL += "/";
+
+ aTargetURL += aTitle;
+
+ uno::Reference< ucb::XContentIdentifier > xTargetId
+ = new ::ucbhelper::ContentIdentifier( aTargetURL );
+
+ DAVResourceAccess aSourceAccess( xContext,
+ xResAccess->getSessionFactory(),
+ sourceURI.GetURI() );
+
+ if ( rArgs.MoveData )
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( rArgs.SourceURL );
+
+ // Note: The static cast is okay here, because its sure that
+ // xProvider is always the WebDAVContentProvider.
+ rtl::Reference< Content > xSource
+ = static_cast< Content * >(
+ xProvider->queryContent( xId ).get() );
+
+ // [RFC 2518] - WebDAV
+ // If a resource exists at the destination and the Overwrite
+ // header is "T" then prior to performing the move the server
+ // MUST perform a DELETE with "Depth: infinity" on the
+ // destination resource. If the Overwrite header is set to
+ // "F" then the operation will fail.
+
+ aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
+ aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
+ aSourceAccess.MOVE( sourceURI.GetRelativeReference(),
+ targetURI.GetURI(),
+ rArgs.NameClash
+ == ucb::NameClash::OVERWRITE,
+ Environment );
+
+ if ( xSource.is() )
+ {
+ // Propagate destruction to listeners.
+ xSource->destroy( true );
+ }
+
+// DAV resources store all additional props on server!
+// // Rename own and all children's Additional Core Properties.
+// renameAdditionalPropertySet( xId->getContentIdentifier(),
+// xTargetId->getContentIdentifier(),
+// true );
+ }
+ else
+ {
+ // [RFC 2518] - WebDAV
+ // If a resource exists at the destination and the Overwrite
+ // header is "T" then prior to performing the copy the server
+ // MUST perform a DELETE with "Depth: infinity" on the
+ // destination resource. If the Overwrite header is set to
+ // "F" then the operation will fail.
+
+ aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
+ aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
+ aSourceAccess.COPY( sourceURI.GetRelativeReference(),
+ targetURI.GetURI(),
+ rArgs.NameClash
+ == ucb::NameClash::OVERWRITE,
+ Environment );
+
+// DAV resources store all additional props on server!
+// // Copy own and all children's Additional Core Properties.
+// copyAdditionalPropertySet( xId->getContentIdentifier(),
+// xTargetId->getContentIdentifier(),
+// true );
+ }
+
+ // Note: The static cast is okay here, because its sure that
+ // xProvider is always the WebDAVContentProvider.
+ rtl::Reference< Content > xTarget
+ = static_cast< Content * >(
+ xProvider->queryContent( xTargetId ).get() );
+
+ // Announce transferred content in its new folder.
+ xTarget->inserted();
+ }
+ catch ( ucb::IllegalIdentifierException const & )
+ {
+ // queryContent
+ }
+ catch ( DAVException const & e )
+ {
+ // [RFC 2518] - WebDAV
+ // 412 (Precondition Failed) - The server was unable to maintain
+ // the liveness of the properties listed in the propertybehavior
+ // XML element or the Overwrite header is "F" and the state of
+ // the destination resource is non-null.
+
+ if ( e.getStatus() == SC_PRECONDITION_FAILED )
+ {
+ switch ( rArgs.NameClash )
+ {
+ case 0/*ucb::NameClash::ERROR*/:
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::NameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aTargetURI ) ),
+ Environment );
+ // Unreachable
+ }
+ [[fallthrough]];
+
+ case ucb::NameClash::OVERWRITE:
+ break;
+
+ case ucb::NameClash::KEEP: // deprecated
+ case ucb::NameClash::RENAME:
+ case ucb::NameClash::ASK:
+ default:
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::UnsupportedNameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ rArgs.NameClash ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+ }
+
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+}
+
+
+void Content::destroy( bool bDeletePhysical )
+{
+ // @@@ take care about bDeletePhysical -> trashcan support
+
+ uno::Reference< ucb::XContent > xThis = this;
+
+ deleted();
+
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ // Process instantiated children...
+
+ ::http_dav_ucp::Content::ContentRefList aChildren;
+ queryChildren( aChildren );
+
+ for ( auto& rChild : aChildren )
+ {
+ rChild->destroy( bDeletePhysical );
+ }
+}
+
+// returns the resource type, to be checked for locks
+Content::ResourceType Content::resourceTypeForLocks(
+ const uno::Reference< ucb::XCommandEnvironment >& Environment,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess)
+{
+ ResourceType eResourceTypeForLocks = UNKNOWN;
+ {
+ osl::MutexGuard g(m_aMutex);
+ //check if cache contains what we need, usually the first PROPFIND on the URI has supported lock
+ if (m_xCachedProps)
+ {
+ uno::Sequence< ucb::LockEntry > aSupportedLocks;
+ if ( m_xCachedProps->getValue( DAVProperties::SUPPORTEDLOCK )
+ >>= aSupportedLocks ) //get the cached value for supportedlock
+ {
+ for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
+ {
+ if ( aSupportedLocks[ n ].Scope
+ == ucb::LockScope_EXCLUSIVE &&
+ aSupportedLocks[ n ].Type
+ == ucb::LockType_WRITE )
+ eResourceTypeForLocks = DAV;
+ }
+ }
+ }
+ }
+
+ const OUString & rURL = m_xIdentifier->getContentIdentifier();
+
+ if ( eResourceTypeForLocks == UNKNOWN )
+ {
+ // resource type for lock/unlock operations still unknown, need to ask the server
+
+ //{
+ DAVOptions aDAVOptions;
+ getResourceOptions( Environment, aDAVOptions, rResAccess );
+ if( aDAVOptions.isClass1() ||
+ aDAVOptions.isClass2() ||
+ aDAVOptions.isClass3() )
+ {
+ // this is at least a DAV, lock to be confirmed
+ // class 2 is needed for full lock support
+ // see
+ // <https://tools.ietf.org/html/rfc4918#section-18.2>
+ eResourceTypeForLocks = DAV_NOLOCK;
+ if( aDAVOptions.isClass2() )
+ {
+ // ok, possible lock, check for it
+ try
+ {
+ // we need only DAV:supportedlock
+ std::vector< DAVResource > resources;
+ std::vector< OUString > aPropNames;
+ uno::Sequence< beans::Property > aProperties( 1 );
+ aProperties.getArray()[ 0 ].Name = DAVProperties::SUPPORTEDLOCK;
+
+ ContentProperties::UCBNamesToDAVNames( aProperties, aPropNames );
+ rResAccess->PROPFIND( DAVZERO, aPropNames, resources, Environment );
+
+ bool wasSupportedlockFound = false;
+
+ // only one resource should be returned
+ if ( resources.size() == 1 )
+ {
+ // we may have received a bunch of other properties
+ // (some servers seems to do so)
+ // but we need only supported lock for this check
+ // all returned properties are in
+ // resources.properties[n].Name/.Value
+
+ std::vector< DAVPropertyValue >::iterator it;
+
+ for ( it = resources[0].properties.begin();
+ it != resources[0].properties.end(); ++it)
+ {
+ if ( (*it).Name == DAVProperties::SUPPORTEDLOCK )
+ {
+ wasSupportedlockFound = true;
+ uno::Sequence< ucb::LockEntry > aSupportedLocks;
+ if ( (*it).Value >>= aSupportedLocks )
+ {
+ for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
+ {
+ // TODO: if the lock type is changed from 'exclusive write' to 'shared write'
+ // e.g. to implement 'Calc shared file feature', the ucb::LockScope_EXCLUSIVE
+ // value should be checked as well, adaptation the code may be needed
+ if ( aSupportedLocks[ n ].Scope == ucb::LockScope_EXCLUSIVE &&
+ aSupportedLocks[ n ].Type == ucb::LockType_WRITE )
+ {
+ // requested locking mode is supported
+ eResourceTypeForLocks = DAV;
+ SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV lock/unlock supported");
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ // PROPFIND failed; check if HEAD contains Content-Disposition: attachment (RFC1806, HTTP/1.1 19.5.1),
+ // which supposedly means no lock for the resource (happens e.g. with SharePoint exported lists)
+ OUString sContentDisposition;
+ // First, check cached properties
+ if (m_xCachedProps)
+ {
+ if ((m_xCachedProps->getValue("Content-Disposition") >>= sContentDisposition)
+ && sContentDisposition.startsWithIgnoreAsciiCase("attachment"))
+ {
+ eResourceTypeForLocks = DAV_NOLOCK;
+ wasSupportedlockFound = true;
+ }
+ }
+ // If no data in cache, try HEAD request
+ if (sContentDisposition.isEmpty() && !m_bDidGetOrHead) try
+ {
+ DAVResource resource;
+ GetPropsUsingHeadRequest(resource, rResAccess, {"Content-Disposition"}, Environment);
+ m_bDidGetOrHead = true;
+ for (const auto& it : resource.properties)
+ {
+ if (it.Name.equalsIgnoreAsciiCase("Content-Disposition"))
+ {
+ if ((it.Value >>= sContentDisposition) && sContentDisposition.equalsIgnoreAsciiCase("attachment"))
+ {
+ eResourceTypeForLocks = DAV_NOLOCK;
+ wasSupportedlockFound = true;
+ }
+ break;
+ }
+ }
+ }
+ catch (...){}
+ }
+ // check if this is still only a DAV_NOLOCK
+ // a fallback for resources that do not have DAVProperties::SUPPORTEDLOCK property
+ // we check for the returned OPTION if LOCK is allowed on the resource
+ if ( !wasSupportedlockFound && eResourceTypeForLocks == DAV_NOLOCK )
+ {
+ SAL_INFO( "ucb.ucp.webdav", "This WebDAV server has no supportedlock property, check for allowed LOCK method in OPTIONS" );
+ // ATTENTION: if the lock type is changed from 'exclusive write' to 'shared write'
+ // e.g. to implement 'Calc shared file feature' on WebDAV directly, and we arrive to this fallback
+ // and the LOCK is allowed, we should assume that only exclusive write lock is available
+ // this is just a reminder...
+ if ( aDAVOptions.isLockAllowed() )
+ eResourceTypeForLocks = DAV;
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ rResAccess->resetUri();
+ //grab the error code
+ switch( e.getStatus() )
+ {
+ case SC_NOT_FOUND:
+ SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
+ << m_xIdentifier->getContentIdentifier() << "> was not found. ");
+ eResourceTypeForLocks = NOT_FOUND;
+ break;
+ // some servers returns SC_FORBIDDEN, instead
+ // the meaning of SC_FORBIDDEN is, according to <http://tools.ietf.org/html/rfc7231#section-6.5.3>:
+ // The 403 (Forbidden) status code indicates that the server understood
+ // the request but refuses to authorize it
+ case SC_FORBIDDEN:
+ // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
+ // part of base http 1.1 RFCs
+ case SC_NOT_IMPLEMENTED: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
+ case SC_METHOD_NOT_ALLOWED: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
+ // they all mean the resource is NON_DAV
+ SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException (SC_FORBIDDEN, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ eResourceTypeForLocks = NON_DAV;
+ break;
+ default:
+ //fallthrough
+ SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ eResourceTypeForLocks = UNKNOWN;
+ }
+ }
+ }
+ }
+ else
+ eResourceTypeForLocks = NON_DAV;
+
+ //}
+ }
+ osl::MutexGuard g(m_aMutex);
+ if (m_eResourceTypeForLocks == UNKNOWN)
+ {
+ m_eResourceTypeForLocks = eResourceTypeForLocks;
+ }
+ else
+ {
+ SAL_WARN_IF(
+ eResourceTypeForLocks != m_eResourceTypeForLocks, "ucb.ucp.webdav",
+ "different resource types for <" << rURL << ">: "
+ << +eResourceTypeForLocks << " vs. " << +m_eResourceTypeForLocks);
+ }
+ SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, m_eResourceTypeForLocks: " << m_eResourceTypeForLocks );
+ return m_eResourceTypeForLocks;
+}
+
+Content::ResourceType Content::resourceTypeForLocks(
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ Content::ResourceType ret = resourceTypeForLocks( Environment, xResAccess );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ return ret;
+}
+
+void Content::lock(
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+// prepare aURL to be used in exception, see below
+ OUString aURL;
+ if ( m_bTransient )
+ {
+ aURL = getParentURL();
+ if ( aURL.lastIndexOf('/') != ( aURL.getLength() - 1 ) )
+ aURL += "/";
+
+ aURL += m_aEscapedTitle;
+ }
+ else
+ {
+ aURL = m_xIdentifier->getContentIdentifier();
+ }
+
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ uno::Any aOwnerAny;
+ OUString const user(officecfg::Office::Common::Save::Document::UseUserData::get()
+ ? " - " + ::svt::LockFileCommon::GetOOOUserName()
+ : OUString());
+ aOwnerAny <<= OUString("LibreOffice" + user);
+
+ ucb::Lock aLock(
+ ucb::LockScope_EXCLUSIVE,
+ ucb::LockType_WRITE,
+ ucb::LockDepth_ZERO,
+ aOwnerAny,
+ 180, // lock timeout in secs
+ //-1, // infinite lock
+ uno::Sequence< OUString >() );
+
+ // OPTIONS may change as a consequence of the lock operation
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->LOCK( aLock, Environment );
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ // check if the exception thrown is 'already locked'
+ // this exception is mapped directly to the ucb correct one, without
+ // going into the cancelCommandExecution() user interaction
+ // this exception should be managed by the issuer of 'lock' command
+ switch( e.getError() )
+ {
+ case DAVException::DAV_LOCKED:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "lock(): resource already locked - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">");
+ throw
+ ucb::InteractiveLockingLockedException(
+ "Locked!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL,
+ false );
+ }
+ break;
+ case DAVException::DAV_HTTP_NOAUTH:
+ case DAVException::DAV_HTTP_AUTH:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "lock(): DAVException Authentication error - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">" );
+ // DAVException::DAV_HTTP_AUTH exception can mean:
+ // - interaction handler for credential management not present (happens, depending
+ // on the LO framework processing)
+ // - the remote site is a WebDAV with special configuration: read/only for read operations
+ // and read/write for write operations, the user is not allowed to lock/write and
+ // she cancelled the credentials request.
+ // this is not actually an error, but the exception is sent directly from here, avoiding the automatic
+ // management that takes part in cancelCommandExecution() below
+ // Unfortunately there is no InteractiveNetwork*Exception available to signal this
+ // since it mostly happens on read/only part of webdav, this appears to be the most correct exception available
+ throw
+ ucb::InteractiveNetworkWriteException(
+ "Authentication error while trying to lock! Write only WebDAV perhaps?",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ }
+ break;
+ case DAVException::DAV_HTTP_ERROR:
+ //grab the error code
+ switch( e.getStatus() )
+ {
+ // The 'case SC_NOT_FOUND' just below tries to solve a problem in eXo Platform
+ // WebDAV connector which apparently fail on resource first creation
+ // rfc4918 section-7.3 (see link below)
+ case SC_NOT_FOUND: // <http://tools.ietf.org/html/rfc7231#section-6.5.4>
+ // The 'case SC_PRECONDITION_FAILED' just below tries to solve a problem
+ // in SharePoint when locking the resource on first creation fails due to this:
+ // <https://msdn.microsoft.com/en-us/library/jj575265%28v=office.12%29.aspx#id15>
+ // (retrieved on 2015-08-14)
+ case SC_PRECONDITION_FAILED: // <http://tools.ietf.org/html/rfc7232#section-4.2>
+ // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
+ // part of base http 1.1 RFCs
+ case SC_NOT_IMPLEMENTED: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
+ case SC_METHOD_NOT_ALLOWED: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
+ SAL_WARN( "ucb.ucp.webdav", "lock() DAVException (SC_NOT_FOUND, SC_PRECONDITION_FAILED, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ // act as nothing happened
+ // that's because when a resource is first created
+ // the lock is sent before the put, so the resource
+ // is actually created by LOCK, locking it before
+ // the first PUT, but if LOCK is not supported
+ // (simple web or DAV with lock disabled) we end with one of these http
+ // errors.
+ // These same errors may be reported when the LOCK on an unmapped
+ // (i.e. non existent) resource is not implemented.
+ // Detailed specification in:
+ // <http://tools.ietf.org/html/rfc4918#section-7.3>
+ return;
+ default:
+ //fallthrough
+ ;
+ }
+ break;
+ case DAVException::DAV_LOCKED_SELF:
+ // we already hold the lock and it is in our internal lockstore
+ // just return as if the lock was successful
+ return;
+ default:
+ //fallthrough
+ ;
+ }
+
+ SAL_WARN( "ucb.ucp.webdav","lock() DAVException - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ cancelCommandExecution( e, Environment, false );
+ // Unreachable
+ }
+}
+
+
+void Content::unlock(
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ // check if the target URL is a Class1 DAV
+ DAVOptions aDAVOptions;
+ getResourceOptions( Environment, aDAVOptions, xResAccess );
+
+ // at least class one is needed
+ if( aDAVOptions.isClass1() )
+ {
+ // remove options from cache, unlock may change it
+ // it will be refreshed when needed
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ // clean cached value of PROPFIND properties names
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->UNLOCK( Environment );
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ switch( e.getError() )
+ {
+ case DAVException::DAV_NOT_LOCKED:
+ SAL_WARN( "ucb.ucp.webdav", "unlock(): DAVException::DAV_NOT_LOCKED - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">");
+ // means that we don't own any lock on this resource
+ // intercepted here to remove a confusing indication to the user
+ // unfortunately this happens in some WebDAV server configuration
+ // acting as WebDAV and having lock/unlock enabled only
+ // for authorized user.
+ return;
+ case DAVException::DAV_HTTP_ERROR:
+ //grab the error code
+ switch( e.getStatus() )
+ {
+ // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
+ // part of base http 1.1 RFCs
+ case SC_NOT_IMPLEMENTED: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
+ case SC_METHOD_NOT_ALLOWED: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
+ SAL_WARN( "ucb.ucp.webdav", "unlock() DAVException (SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ return;
+ default:
+ //fallthrough
+ ;
+ }
+ break;
+ default:
+ //fallthrough
+ ;
+ }
+ SAL_WARN( "ucb.ucp.webdav","unlock() DAVException - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ cancelCommandExecution( e, Environment, false );
+ // Unreachable
+ }
+}
+
+
+bool Content::exchangeIdentity(
+ const uno::Reference< ucb::XContentIdentifier >& xNewId )
+{
+ if ( !xNewId.is() )
+ return false;
+
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Reference< ucb::XContent > xThis = this;
+
+ // Already persistent?
+ if ( m_bTransient )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - Not persistent!" );
+ return false;
+ }
+
+ // Exchange own identity.
+
+ // Fail, if a content with given id already exists.
+// if ( !hasData( xNewId ) )
+ {
+ OUString aOldURL = m_xIdentifier->getContentIdentifier();
+
+ aGuard.clear();
+ if ( exchange( xNewId ) )
+ {
+ // Process instantiated children...
+
+ ContentRefList aChildren;
+ queryChildren( aChildren );
+
+ for ( const auto& rChild : aChildren )
+ {
+ ContentRef xChild = rChild;
+
+ // Create new content identifier for the child...
+ uno::Reference< ucb::XContentIdentifier >
+ xOldChildId = xChild->getIdentifier();
+ OUString aOldChildURL
+ = xOldChildId->getContentIdentifier();
+ OUString aNewChildURL
+ = aOldChildURL.replaceAt(
+ 0,
+ aOldURL.getLength(),
+ xNewId->getContentIdentifier() );
+ uno::Reference< ucb::XContentIdentifier > xNewChildId
+ = new ::ucbhelper::ContentIdentifier( aNewChildURL );
+
+ if ( !xChild->exchangeIdentity( xNewChildId ) )
+ return false;
+ }
+ return true;
+ }
+ }
+
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::exchangeIdentity - "
+ "Panic! Cannot exchange identity!" );
+ return false;
+}
+
+
+bool Content::isFolder(
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_bTransient )
+ return m_bCollection;
+ }
+
+ uno::Sequence< beans::Property > aProperties( 1 );
+ auto pProperties = aProperties.getArray();
+ pProperties[ 0 ].Name = "IsFolder";
+ pProperties[ 0 ].Handle = -1;
+ uno::Reference< sdbc::XRow > xRow( getPropertyValues( aProperties, xEnv ) );
+ if ( xRow.is() )
+ {
+ try
+ {
+ return xRow->getBoolean( 1 );
+ }
+ catch ( sdbc::SQLException const & )
+ {
+ }
+ }
+
+ return false;
+}
+
+
+uno::Any Content::MapDAVException( const DAVException & e, bool bWrite )
+{
+ // Map DAVException...
+ uno::Any aException;
+
+ OUString aURL;
+ if ( m_bTransient )
+ {
+ aURL = getParentURL();
+ if ( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ) )
+ aURL += "/";
+
+ aURL += m_aEscapedTitle;
+ }
+ else
+ {
+ aURL = m_xIdentifier->getContentIdentifier();
+ }
+
+ switch ( e.getStatus() )
+ {
+ case SC_NOT_FOUND:
+ {
+ uno::Sequence<uno::Any> aArgs{ uno::Any(beans::PropertyValue(
+ "Uri", -1, uno::Any(aURL), beans::PropertyState_DIRECT_VALUE)) };
+
+ aException <<=
+ ucb::InteractiveAugmentedIOException(
+ "Not found!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ ucb::IOErrorCode_NOT_EXISTING,
+ aArgs );
+ return aException;
+ }
+ default:
+ break;
+ }
+
+ switch ( e.getError() )
+ {
+ case DAVException::DAV_HTTP_ERROR:
+ {
+ if ( bWrite )
+ aException <<=
+ ucb::InteractiveNetworkWriteException(
+ e.getData(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ else
+ aException <<=
+ ucb::InteractiveNetworkReadException(
+ e.getData(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ break;
+ }
+
+ case DAVException::DAV_HTTP_LOOKUP:
+ aException <<=
+ ucb::InteractiveNetworkResolveNameException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_HTTP_AUTH:
+// break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_HTTP_AUTHPROXY:
+// break;
+
+ case DAVException::DAV_HTTP_TIMEOUT:
+ case DAVException::DAV_HTTP_CONNECT:
+ aException <<=
+ ucb::InteractiveNetworkConnectException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_HTTP_REDIRECT:
+// break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_SESSION_CREATE:
+// break;
+
+ case DAVException::DAV_INVALID_ARG:
+ aException <<=
+ lang::IllegalArgumentException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ break;
+
+ case DAVException::DAV_LOCKED:
+#if 1
+ aException <<=
+ ucb::InteractiveLockingLockedException(
+ "Locked!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL,
+ false ); // not SelfOwned
+#else
+ {
+ uno::Sequence< uno::Any > aArgs( 1 );
+ aArgs[ 0 ] <<= beans::PropertyValue(
+ OUString("Uri"), -1,
+ uno::makeAny(aURL),
+ beans::PropertyState_DIRECT_VALUE);
+
+ aException <<=
+ ucb::InteractiveAugmentedIOException(
+ OUString( "Locked!" ),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ ucb::IOErrorCode_LOCKING_VIOLATION,
+ aArgs );
+ }
+#endif
+ break;
+
+ case DAVException::DAV_LOCKED_SELF:
+ aException <<=
+ ucb::InteractiveLockingLockedException(
+ "Locked (self)!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL,
+ true ); // SelfOwned
+ break;
+
+ case DAVException::DAV_NOT_LOCKED:
+ aException <<=
+ ucb::InteractiveLockingNotLockedException(
+ "Not locked!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL );
+ break;
+
+ case DAVException::DAV_LOCK_EXPIRED:
+ aException <<=
+ ucb::InteractiveLockingLockExpiredException(
+ "Lock expired!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL );
+ break;
+
+ default:
+ aException <<=
+ ucb::InteractiveNetworkGeneralException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR );
+ break;
+ }
+
+ return aException;
+}
+
+
+// static
+bool Content::shouldAccessNetworkAfterException( const DAVException & e )
+{
+ if ( ( e.getStatus() == SC_NOT_FOUND ) ||
+ ( e.getStatus() == SC_GONE ) ||
+ ( e.getError() == DAVException::DAV_HTTP_TIMEOUT ) ||
+ ( e.getError() == DAVException::DAV_HTTP_LOOKUP ) ||
+ ( e.getError() == DAVException::DAV_HTTP_CONNECT ) ||
+ ( e.getError() == DAVException::DAV_HTTP_NOAUTH ) ||
+ ( e.getError() == DAVException::DAV_HTTP_AUTH ) ||
+ ( e.getError() == DAVException::DAV_HTTP_AUTHPROXY ) )
+ return false;
+
+ return true;
+}
+
+
+void Content::cancelCommandExecution(
+ const DAVException & e,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv,
+ bool bWrite /* = false */ )
+{
+ ucbhelper::cancelCommandExecution( MapDAVException( e, bWrite ), xEnv );
+ // Unreachable
+}
+
+
+OUString
+Content::getBaseURI( const std::unique_ptr< DAVResourceAccess > & rResAccess )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ // First, try to obtain value of response header "Content-Location".
+ if (m_xCachedProps)
+ {
+ OUString aLocation;
+ m_xCachedProps->getValue( "Content-Location" ) >>= aLocation;
+ if ( aLocation.getLength() )
+ {
+ try
+ {
+ // Do not use m_xIdentifier->getContentIdentifier() because it
+ // for example does not reflect redirects applied to requests
+ // done using the original URI but m_xResAccess' URI does.
+ return rtl::Uri::convertRelToAbs( rResAccess->getURL(),
+ aLocation );
+ }
+ catch ( rtl::MalformedUriException const & )
+ {
+ }
+ }
+ }
+
+ return rResAccess->getURL();
+}
+
+// resource type is the type of the WebDAV resource
+Content::ResourceType Content::getResourceType(
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess,
+ bool * networkAccessAllowed )
+{
+ {
+ osl::MutexGuard g(m_aMutex);
+ if (m_eResourceType != UNKNOWN) {
+ return m_eResourceType;
+ }
+ }
+
+ ResourceType eResourceType = UNKNOWN;
+ DAVOptions aDAVOptions;
+
+ {
+ getResourceOptions( xEnv, aDAVOptions, rResAccess, networkAccessAllowed );
+
+ // at least class one is needed
+ if( aDAVOptions.isClass1() )
+ {
+ try
+ {
+ // Try to fetch some frequently used property value, e.g. those
+ // used when loading documents... along with identifying whether
+ // this is a DAV resource.
+ std::vector< DAVResource > resources;
+ std::vector< OUString > aPropNames;
+ uno::Sequence< beans::Property > aProperties( 5 );
+ auto pProperties = aProperties.getArray();
+ pProperties[ 0 ].Name = "IsFolder";
+ pProperties[ 1 ].Name = "IsDocument";
+ pProperties[ 2 ].Name = "IsReadOnly";
+ pProperties[ 3 ].Name = "MediaType";
+ pProperties[ 4 ].Name = DAVProperties::SUPPORTEDLOCK;
+
+ ContentProperties::UCBNamesToDAVNames( aProperties, aPropNames );
+
+ rResAccess->PROPFIND( DAVZERO, aPropNames, resources, xEnv );
+
+ if ( resources.size() == 1 )
+ {
+#if defined SAL_LOG_INFO
+ {//debug
+ // print received resources
+ std::vector< DAVPropertyValue >::const_iterator it = resources[0].properties.begin();
+ std::vector< DAVPropertyValue >::const_iterator end = resources[0].properties.end();
+ while ( it != end )
+ {
+ OUString aPropValue;
+ bool bValue;
+ uno::Sequence< ucb::LockEntry > aSupportedLocks;
+ if((*it).Value >>= aPropValue )
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << (*it).Name << ":" << aPropValue );
+ else if( (*it).Value >>= bValue )
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << (*it).Name << ":" <<
+ ( bValue ? "true" : "false" ) );
+ else if( (*it).Value >>= aSupportedLocks )
+ {
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << (*it).Name << ":" );
+ for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
+ {
+ SAL_INFO( "ucb.ucp.webdav","PROPFIND (getResourceType) - supportedlock[" << n <<"]: scope: "
+ << (aSupportedLocks[n].Scope == ucb::LockScope_SHARED ? "shared" : "exclusive")
+ << ", type: "
+ << (aSupportedLocks[n].Type != ucb::LockType_WRITE ? "" : "write") );
+ }
+ }
+ ++it;
+ }
+ }
+#endif
+ osl::MutexGuard g(m_aMutex);
+ m_xCachedProps.reset(
+ new CachableContentProperties( ContentProperties( resources[ 0 ] ) ) );
+ m_xCachedProps->containsAllNames(
+ aProperties, m_aFailedPropNames );
+ }
+ eResourceType = DAV;
+ }
+ catch ( DAVException const & e )
+ {
+ rResAccess->resetUri();
+
+ SAL_WARN( "ucb.ucp.webdav", "Content::getResourceType returned errors, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+
+ if ( e.getStatus() == SC_METHOD_NOT_ALLOWED )
+ {
+ // Status SC_METHOD_NOT_ALLOWED is a safe indicator that the
+ // resource is NON_DAV
+ eResourceType = NON_DAV;
+ }
+ else if (networkAccessAllowed != nullptr)
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ if ( e.getStatus() == SC_NOT_FOUND )
+ {
+ // arrives here if OPTIONS is still cached for a resource previously available
+ // operate on the OPTIONS cache:
+ // if OPTIONS was not found, do nothing
+ // else OPTIONS returned on a resource not existent (example a server that allows lock on null resource) set
+ // not found and adjust lifetime accordingly
+ DAVOptions aDAVOptionsInner;
+ if (aStaticDAVOptionsCache.getDAVOptions(rResAccess->getURL(), aDAVOptionsInner))
+ {
+ // TODO? get redirected url
+ aDAVOptionsInner.setHttpResponseStatusCode( e.getStatus() );
+ aDAVOptionsInner.setHttpResponseStatusText( e.getData() );
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptionsInner,
+ m_nOptsCacheLifeNotFound );
+ }
+ }
+ // if the two net events below happen, something
+ // is going on to the connection so break the command flow
+ if ( ( e.getError() == DAVException::DAV_HTTP_TIMEOUT ) ||
+ ( e.getError() == DAVException::DAV_HTTP_CONNECT ) )
+ {
+ cancelCommandExecution( e, xEnv );
+ // unreachable
+ }
+
+ // cancel command execution is case that no user authentication data has been provided.
+ if ( e.getError() == DAVException::DAV_HTTP_NOAUTH )
+ {
+ cancelCommandExecution( e, uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ }
+ }
+ else
+ {
+ rResAccess->resetUri();
+
+ // first check if the cached error can be mapped to DAVException::DAV_HTTP_TIMEOUT or mapped to DAVException::DAV_HTTP_CONNECT
+ if (aDAVOptions.getHttpResponseStatusCode() == USC_CONNECTION_TIMED_OUT
+ // can't get any reliable info without auth => cancel request
+ || aDAVOptions.getHttpResponseStatusCode() == USC_AUTH_FAILED
+ || aDAVOptions.getHttpResponseStatusCode() == USC_AUTHPROXY_FAILED)
+ {
+ // behave same as DAVException::DAV_HTTP_TIMEOUT or DAVException::DAV_HTTP_CONNECT was thrown
+ try
+ {
+ // extract host name and connection port
+ CurlUri theUri( rResAccess->getURL() );
+ OUString aHostName = theUri.GetHost();
+ sal_Int32 nPort = theUri.GetPort();
+ DAVException::ExceptionCode e{};
+ switch (aDAVOptions.getHttpResponseStatusCode())
+ {
+ case USC_CONNECTION_TIMED_OUT:
+ e = DAVException::DAV_HTTP_TIMEOUT;
+ break;
+ case USC_AUTH_FAILED:
+ e = DAVException::DAV_HTTP_AUTH;
+ break;
+ case USC_AUTHPROXY_FAILED:
+ e = DAVException::DAV_HTTP_AUTHPROXY;
+ break;
+ default:
+ assert(false);
+ }
+ throw DAVException( e,
+ ConnectionEndPointString(aHostName, nPort) );
+ }
+ catch ( DAVException& exp )
+ {
+ cancelCommandExecution( exp, xEnv );
+ }
+ }
+
+ if ( aDAVOptions.getHttpResponseStatusCode() != SC_NOT_FOUND &&
+ aDAVOptions.getHttpResponseStatusCode() != SC_GONE ) // the cached OPTIONS can have SC_GONE
+ {
+ eResourceType = NON_DAV;
+ }
+ else
+ {
+ //resource doesn't exist
+ if ( networkAccessAllowed != nullptr )
+ *networkAccessAllowed = false;
+ }
+ }
+ }
+
+ osl::MutexGuard g(m_aMutex);
+ if (m_eResourceType == UNKNOWN) {
+ m_eResourceType = eResourceType;
+ } else {
+ SAL_WARN_IF(
+ eResourceType != m_eResourceType, "ucb.ucp.webdav",
+ "different resource types for <" << rResAccess->getURL() << ">: "
+ << +eResourceType << " vs. " << +m_eResourceType);
+ }
+ SAL_INFO( "ucb.ucp.webdav", "m_eResourceType for <" << rResAccess->getURL() << ">: " << m_eResourceType );
+ return m_eResourceType;
+}
+
+
+Content::ResourceType Content::getResourceType(
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ const Content::ResourceType & ret = getResourceType( xEnv, xResAccess );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ return ret;
+}
+
+
+void Content::initOptsCacheLifeTime()
+{
+ // see description in
+ // officecfg/registry/schema/org/openoffice/Inet.xcs
+ // for use of these field values.
+ sal_uInt32 nAtime;
+ nAtime = officecfg::Inet::Settings::OptsCacheLifeImplWeb::get();
+ m_nOptsCacheLifeImplWeb = std::max( sal_uInt32( 0 ),
+ std::min( nAtime, sal_uInt32( 3600 ) ) );
+
+ nAtime = officecfg::Inet::Settings::OptsCacheLifeDAV::get();
+ m_nOptsCacheLifeDAV = std::max( sal_uInt32( 0 ),
+ std::min( nAtime, sal_uInt32( 3600 ) ) );
+
+ nAtime = officecfg::Inet::Settings::OptsCacheLifeDAVLocked::get();
+ m_nOptsCacheLifeDAVLocked = std::max( sal_uInt32( 0 ),
+ std::min( nAtime, sal_uInt32( 3600 ) ) );
+
+ nAtime = officecfg::Inet::Settings::OptsCacheLifeNotImpl::get();
+ m_nOptsCacheLifeNotImpl = std::max( sal_uInt32( 0 ),
+ std::min( nAtime, sal_uInt32( 43200 ) ) );
+
+ nAtime = officecfg::Inet::Settings::OptsCacheLifeNotFound::get();
+ m_nOptsCacheLifeNotFound = std::max( sal_uInt32( 0 ),
+ std::min( nAtime, sal_uInt32( 30 ) ) );
+}
+
+
+void Content::getResourceOptions(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
+ DAVOptions& rDAVOptions,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess,
+ bool * networkAccessAllowed )
+{
+ OUString aRedirURL;
+ OUString aTargetURL = rResAccess->getURL();
+ DAVOptions aDAVOptions;
+ // first check if in cache, if not, then send method to server
+ if ( !aStaticDAVOptionsCache.getDAVOptions( aTargetURL, aDAVOptions ) )
+ {
+ try
+ {
+ rResAccess->OPTIONS( aDAVOptions, xEnv );
+ // IMPORTANT:the correctly implemented server will answer without errors, even if the resource is not present
+ sal_uInt32 nLifeTime = ( aDAVOptions.isClass1() ||
+ aDAVOptions.isClass2() ||
+ aDAVOptions.isClass3() ) ?
+ m_nOptsCacheLifeDAV : // a WebDAV site
+ m_nOptsCacheLifeImplWeb; // a site implementing OPTIONS but
+ // it's not DAV
+ // if resource is locked, will use a
+ // different lifetime
+ if( aDAVOptions.isLocked() )
+ nLifeTime = m_nOptsCacheLifeDAVLocked;
+
+ // check if redirected
+ aRedirURL = rResAccess->getURL();
+ if( aRedirURL == aTargetURL)
+ { // no redirection
+ aRedirURL.clear();
+ }
+ // cache this URL's option
+ aDAVOptions.setURL( aTargetURL );
+ aDAVOptions.setRedirectedURL( aRedirURL );
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ nLifeTime );
+ }
+ catch ( DAVException const & e )
+ {
+ // first, remove from cache, will be added if needed, depending on the error received
+ aStaticDAVOptionsCache.removeDAVOptions( aTargetURL );
+ rResAccess->resetUri();
+
+ aDAVOptions.setURL( aTargetURL );
+ aDAVOptions.setRedirectedURL( aRedirURL );
+ switch( e.getError() )
+ {
+ case DAVException::DAV_HTTP_TIMEOUT:
+ case DAVException::DAV_HTTP_CONNECT:
+ {
+ // something bad happened to the connection
+ // not same as not found, this instead happens when the server doesn't exist or doesn't answer at all
+ // probably a new bit stating 'timed out' should be added to opts var?
+ // in any case abort the command
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_TIMEOUT or DAV_HTTP_CONNECT for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ // cache the internal unofficial status code
+
+ aDAVOptions.setHttpResponseStatusCode( USC_CONNECTION_TIMED_OUT );
+ // used only internally, so the text doesn't really matter..
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotFound );
+ if ( networkAccessAllowed != nullptr )
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ }
+ break;
+ case DAVException::DAV_HTTP_LOOKUP:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_LOOKUP for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ aDAVOptions.setHttpResponseStatusCode( USC_LOOKUP_FAILED );
+ // used only internally, so the text doesn't really matter..
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotFound );
+ if ( networkAccessAllowed != nullptr )
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ }
+ break;
+ case DAVException::DAV_HTTP_NOAUTH:
+ case DAVException::DAV_HTTP_AUTH:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTH for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ // - the remote site is a WebDAV with special configuration: read/only for read operations
+ // and read/write for write operations, the user is not allowed to lock/write and
+ // she cancelled the credentials request.
+ // this is not actually an error, it means only that for current user this is a standard web,
+ // though possibly DAV enabled
+ aDAVOptions.setHttpResponseStatusCode( USC_AUTH_FAILED );
+ // used only internally, so the text doesn't really matter..
+ if (xEnv && xEnv->getInteractionHandler())
+ { // only cache if there actually was a chance to request auth
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotFound );
+ }
+ if ( networkAccessAllowed != nullptr )
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ }
+ break;
+ case DAVException::DAV_HTTP_AUTHPROXY:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTHPROXY for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ aDAVOptions.setHttpResponseStatusCode( USC_AUTHPROXY_FAILED );
+ // used only internally, so the text doesn't really matter..
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotFound );
+ if ( networkAccessAllowed != nullptr )
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ }
+ break;
+ case DAVException::DAV_HTTP_ERROR:
+ {
+ switch( e.getStatus() )
+ {
+ case SC_FORBIDDEN:
+ {
+ SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_FORBIDDEN for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ // cache it, so OPTIONS won't be called again, this URL does not support it
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotImpl );
+ }
+ break;
+ case SC_BAD_REQUEST:
+ case SC_INTERNAL_SERVER_ERROR:
+ {
+ SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_BAD_REQUEST or SC_INTERNAL_SERVER_ERROR for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
+ << ", '" << e.getData() << "'" );
+ // cache it, so OPTIONS won't be called again, this URL detect some problem while answering the method
+ aDAVOptions.setHttpResponseStatusCode( e.getStatus() );
+ aDAVOptions.setHttpResponseStatusText( e.getData() );
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotFound );
+ }
+ break;
+ case SC_NOT_IMPLEMENTED:
+ case SC_METHOD_NOT_ALLOWED:
+ {
+ // OPTIONS method must be implemented in DAV
+ // resource is NON_DAV, or not advertising it
+ SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
+ << ", '" << e.getData() << "'" );
+ // cache it, so OPTIONS won't be called again, this URL does not support it
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotImpl );
+ }
+ break;
+ case SC_NOT_FOUND:
+ {
+ // Apparently on IIS 10.0, if you disabled OPTIONS method, this error is the one reported,
+ // instead of SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED.
+ // So check if this is an available resource, or a real 'Not Found' event.
+ sal_uInt32 nLifeTime = m_nOptsCacheLifeNotFound;
+ if( isResourceAvailable( xEnv, rResAccess, aDAVOptions ) )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - Got an SC_NOT_FOUND, but the URL <" << m_xIdentifier->getContentIdentifier() << "> resource exists" );
+ nLifeTime = m_nOptsCacheLifeNotImpl;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - SC_NOT_FOUND for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ if ( networkAccessAllowed != nullptr )
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ }
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ nLifeTime );
+ }
+ break;
+ default:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAV_HTTP_ERROR, for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
+ << ", '" << e.getData() << "'" );
+ aDAVOptions.setHttpResponseStatusCode( e.getStatus() );
+ aDAVOptions.setHttpResponseStatusText( e.getData() );
+ // cache it, so OPTIONS won't be called again, this URL does not support it
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotImpl );
+ }
+ break;
+ }
+ }
+ break;
+ // The 'DAVException::DAV_HTTP_REDIRECT' means we reached the maximum
+ // number of redirections, consider the resource type as UNKNOWN
+ // possibly a normal web site, not DAV
+ case DAVException::DAV_HTTP_REDIRECT:
+ default:
+ {
+ SAL_WARN( "ucb.ucp.webdav","OPTIONS - General DAVException (or max DAV_HTTP_REDIRECT reached) for URL <" << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: "
+ << e.getError() << ", HTTP error: "<< e.getStatus() );
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotImpl );
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ // check current response status code, perhaps we need to set networkAccessAllowed
+ sal_uInt16 CachedResponseStatusCode = aDAVOptions.getHttpResponseStatusCode();
+ if ( networkAccessAllowed != nullptr &&
+ ( ( CachedResponseStatusCode == SC_NOT_FOUND ) ||
+ ( CachedResponseStatusCode == SC_GONE ) ||
+ ( CachedResponseStatusCode == USC_CONNECTION_TIMED_OUT ) ||
+ ( CachedResponseStatusCode == USC_LOOKUP_FAILED ) ||
+ ( CachedResponseStatusCode == USC_AUTH_FAILED ) ||
+ ( CachedResponseStatusCode == USC_AUTHPROXY_FAILED )
+ )
+ )
+ {
+ *networkAccessAllowed = *networkAccessAllowed && false;
+ }
+ }
+ rDAVOptions = aDAVOptions;
+}
+
+//static
+bool Content::isResourceAvailable( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess,
+ DAVOptions& rDAVOptions )
+{
+ std::vector< rtl::OUString > aHeaderNames;
+ DAVResource aResource;
+
+ try
+ {
+ // To check for the physical URL resource availability, first
+ // try using a simple HEAD command
+ // if HEAD is successful, set element found.
+ rResAccess->HEAD( aHeaderNames, aResource, xEnv );
+ rDAVOptions.setHttpResponseStatusCode( 0 );
+ rDAVOptions.setHttpResponseStatusText("");
+ return true;
+ }
+ catch ( DAVException const & e )
+ {
+ if ( e.getError() == DAVException::DAV_HTTP_ERROR )
+ {
+ if ( e.getStatus() == SC_NOT_IMPLEMENTED ||
+ e.getStatus() == SC_METHOD_NOT_ALLOWED ||
+ e.getStatus() == SC_NOT_FOUND )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "HEAD probably not implemented: fall back to a partial GET" );
+ // set in cached OPTIONS "HEAD not implemented"
+ // so it won't be used again on this resource
+ rDAVOptions.setHeadAllowed( false );
+ try
+ {
+ // do a GET with a payload of 0, the server does not
+ // support HEAD (or has HEAD disabled)
+ DAVRequestHeaders aPartialGet;
+ aPartialGet.push_back(
+ DAVRequestHeader(
+ OUString( "Range" ),
+ OUString( "bytes=0-0" )));
+
+ rResAccess->GET0( aPartialGet,
+ aHeaderNames,
+ aResource,
+ xEnv );
+ return true;
+ }
+ catch ( DAVException const & ex )
+ {
+ if ( ex.getError() == DAVException::DAV_HTTP_ERROR )
+ {
+ rDAVOptions.setHttpResponseStatusCode( ex.getStatus() );
+ rDAVOptions.setHttpResponseStatusText( ex.getData() );
+ }
+ }
+ }
+ else
+ {
+ rDAVOptions.setHttpResponseStatusCode( e.getStatus() );
+ rDAVOptions.setHttpResponseStatusText( e.getData() );
+ }
+ }
+ return false;
+ }
+ catch ( ... )
+ {
+ }
+ // set SC_NOT_IMPLEMENTED since at a minimum GET must be implemented in a basic Web server
+ rDAVOptions.setHttpResponseStatusCode( SC_NOT_IMPLEMENTED );
+ rDAVOptions.setHttpResponseStatusText("");
+ return false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */