summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiuseppe Castagno <giuseppe.castagno@acca-esse.eu>2015-08-28 18:52:36 +0200
committerJan Holesovsky <kendy@collabora.com>2015-10-05 11:14:50 +0000
commitb4576f3da4d90139fc5140962d13cb91dab98797 (patch)
tree12f4d4a16e0c6d5d9c9995315da37b96c6629a87
parent8ea85c264f22c76d393c0f78e9db7df51e2c868d (diff)
tdf#82744: fix WebDAV lock/unlock behaviour - part 3
Changes done to the code in sfx2, ucbhelper, ucb, unotools in no particular order - add method helpers to call the ucb lock/unlock - add lock/unlock 'real' management - make DateChange property retrieval working for WebDAV as well - add check for changed content of a WebDAV file, in order to reload it correctly when 'Edit Mode' command is activated from GUI - Unlock WebDAV file while saving only if explicitly enabled Needed in order to avoid the small window of file unlocked state that opens while saving a file. When saving LO actually does as follows: - unlock the prevoius version of the file - prepares operations to save the modified version - lock the new file - save the new version - the lock method is enabled if the DAV resource supports it. In case the lock is not supported, for example example DAV with lock disabled, the lock method is disabled. Exception: when the resource is first created and the lock is not supported: a lock command is sent anyway, because if the resource is not yet present, there is no method to detect the lock/unlock availability in this case. - cppcheck:noExplicitConstructor Change-Id: I0aa876c4e3364d86e5740977b97f3db9a01e4491 Reviewed-on: https://gerrit.libreoffice.org/17189 Reviewed-by: Jan Holesovsky <kendy@collabora.com> Tested-by: Jan Holesovsky <kendy@collabora.com>
-rw-r--r--include/sfx2/docfile.hxx3
-rw-r--r--include/ucbhelper/content.hxx20
-rw-r--r--sfx2/source/doc/docfile.cxx146
-rw-r--r--sfx2/source/doc/objstor.cxx8
-rw-r--r--sfx2/source/view/viewfrm.cxx32
-rw-r--r--ucb/source/ucp/webdav-neon/ContentProperties.hxx6
-rw-r--r--ucb/source/ucp/webdav-neon/NeonSession.cxx50
-rw-r--r--ucb/source/ucp/webdav-neon/webdavcontent.cxx305
-rw-r--r--ucb/source/ucp/webdav-neon/webdavcontent.hxx14
-rw-r--r--ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx3
-rw-r--r--ucbhelper/source/client/content.cxx21
-rw-r--r--unotools/source/misc/mediadescriptor.cxx8
12 files changed, 520 insertions, 96 deletions
diff --git a/include/sfx2/docfile.hxx b/include/sfx2/docfile.hxx
index c3c9e94bbdb6..c19cc35dfaa8 100644
--- a/include/sfx2/docfile.hxx
+++ b/include/sfx2/docfile.hxx
@@ -104,7 +104,7 @@ public:
void UseInteractionHandler( bool );
css::uno::Reference< css::task::XInteractionHandler >
- GetInteractionHandler();
+ GetInteractionHandler( bool bGetAlways = false );
void setStreamToLoadFrom(
const css::uno::Reference<css::io::XInputStream>& xInputStream,
@@ -162,6 +162,7 @@ public:
sal_Int8 ShowLockedDocumentDialog( const LockFileEntry& aData, bool bIsLoading, bool bOwnLock );
void LockOrigFileOnDemand( bool bLoading, bool bNoUI );
+ void DisableUnlockWebDAV( bool bDisableUnlockWebDAV = true );
void UnlockFile( bool bReleaseLockStream );
css::uno::Reference< css::embed::XStorage > GetStorage( bool bCreateTempIfNo = true );
diff --git a/include/ucbhelper/content.hxx b/include/ucbhelper/content.hxx
index cfa632aa020f..520ce2a17cb8 100644
--- a/include/ucbhelper/content.hxx
+++ b/include/ucbhelper/content.hxx
@@ -686,6 +686,25 @@ public:
::com::sun::star::uno::RuntimeException,
::com::sun::star::uno::Exception );
+ /**
+ * This method lock the resource.
+ *
+ */
+ void
+ lock()
+ throw( ::com::sun::star::ucb::CommandAbortedException,
+ ::com::sun::star::uno::RuntimeException,
+ ::com::sun::star::uno::Exception );
+
+ /**
+ * This method unlock the resource.
+ *
+ */
+ void
+ unlock()
+ throw( ::com::sun::star::ucb::CommandAbortedException,
+ ::com::sun::star::uno::RuntimeException,
+ ::com::sun::star::uno::Exception );
// Required properties.
@@ -712,6 +731,7 @@ public:
throw( ::com::sun::star::ucb::CommandAbortedException,
::com::sun::star::uno::RuntimeException,
::com::sun::star::uno::Exception );
+
};
} /* namespace ucbhelper */
diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx
index 26f4820a8ee0..ccdad939071e 100644
--- a/sfx2/source/doc/docfile.cxx
+++ b/sfx2/source/doc/docfile.cxx
@@ -41,6 +41,8 @@
#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
#include <com/sun/star/ucb/CommandFailedException.hpp>
#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
+#include <com/sun/star/ucb/Lock.hpp>
#include <com/sun/star/ucb/XCommandEnvironment.hpp>
#include <com/sun/star/ucb/XContentIdentifierFactory.hpp>
#include <com/sun/star/ucb/XContentProvider.hpp>
@@ -181,6 +183,7 @@ public:
bool m_bSalvageMode:1;
bool m_bVersionsAlreadyLoaded:1;
bool m_bLocked:1;
+ bool m_bDisableUnlockWebDAV:1;
bool m_bGotDateTime:1;
bool m_bRemoveBackup:1;
bool m_bOriginallyReadOnly:1;
@@ -257,6 +260,7 @@ SfxMedium_Impl::SfxMedium_Impl( SfxMedium* pAntiImplP ) :
m_bSalvageMode( false ),
m_bVersionsAlreadyLoaded( false ),
m_bLocked( false ),
+ m_bDisableUnlockWebDAV( false ),
m_bGotDateTime( false ),
m_bRemoveBackup( false ),
m_bOriginallyReadOnly(false),
@@ -381,7 +385,8 @@ void SfxMedium::CheckFileDate( const util::DateTime& aInitDate )
bool SfxMedium::DocNeedsFileDateCheck() const
{
- return !IsReadOnly() && GetURLObject().GetProtocol() == INetProtocol::File;
+ return ( !IsReadOnly() && ( GetURLObject().GetProtocol() == INetProtocol::File ||
+ GetURLObject().isAnyKnownWebDAVScheme() ) );
}
util::DateTime SfxMedium::GetInitFileDate( bool bIgnoreOldValue )
@@ -942,6 +947,100 @@ void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI )
(void) bLoading;
(void) bNoUI;
#else
+ // check if path scheme is http:// or https://
+ // may be this is better if used always, in Android and iOS as well?
+ // if this code should be always there, remember to move the relevant code in UnlockFile method as well !
+
+ if ( GetURLObject().isAnyKnownWebDAVScheme() )
+ {
+ try
+ {
+ bool bResult = pImp->m_bLocked;
+ // so, this is webdav stuff...
+ if ( !bResult )
+ {
+ // no read-write access is necessary on loading if the document is explicitly opened as copy
+ SFX_ITEMSET_ARG( GetItemSet(), pTemplateItem, SfxBoolItem, SID_TEMPLATE, false );
+ bResult = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
+ }
+
+ if ( !bResult && !IsReadOnly() )
+ {
+ sal_Int8 bUIStatus = LOCK_UI_NOLOCK;
+ do
+ {
+ if( !bResult )
+ {
+ Reference< ::com::sun::star::ucb::XCommandEnvironment > xComEnv;
+ uno::Reference< task::XInteractionHandler > xCHandler = GetInteractionHandler( true );
+ xComEnv = new ::ucbhelper::CommandEnvironment(
+ xCHandler, Reference< ::com::sun::star::ucb::XProgressHandler >() );
+
+ ucbhelper::Content aContentToLock(
+ GetURLObject().GetMainURL( INetURLObject::NO_DECODE ),
+ xComEnv, comphelper::getProcessComponentContext() );
+
+ try
+ {
+ aContentToLock.lock();
+ bResult = true;
+ }
+ catch ( ucb::InteractiveLockingLockedException& )
+ {
+ // received when the resource is already locked
+ // get the lock owner, using a special ucb.webdav property
+ // the owner property retrieved here is what the other principal send the server
+ // when activating the lock.
+ // See http://tools.ietf.org/html/rfc4918#section-14.17 for details
+ LockFileEntry aLockData;
+ aLockData[LockFileComponent::OOOUSERNAME] = OUString("Unknown user");
+
+ uno::Sequence< ::com::sun::star::ucb::Lock > aLocks;
+ if( aContentToLock.getPropertyValue( "DAV:lockdiscovery" ) >>= aLocks )
+ {
+ // got at least a lock, show the owner of the first lock returned
+ ::com::sun::star::ucb::Lock aLock = aLocks[0];
+ OUString aOwner;
+ if(aLock.Owner >>= aOwner)
+ aLockData[LockFileComponent::OOOUSERNAME] = aOwner;
+ }
+
+ if ( !bResult && !bNoUI )
+ {
+ bUIStatus = ShowLockedDocumentDialog( aLockData, bLoading, false );
+ }
+ }
+ catch( uno::Exception& )
+ {}
+ }
+ } while( !bResult && bUIStatus == LOCK_UI_TRY );
+ }
+
+ pImp->m_bLocked = bResult;
+
+ if ( !bResult && GetError() == ERRCODE_NONE )
+ {
+ // the error should be set in case it is storing process
+ // or the document has been opened for editing explicitly
+ SFX_ITEMSET_ARG( pImp->m_pSet, pReadOnlyItem, SfxBoolItem, SID_DOC_READONLY, false );
+
+ if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
+ SetError( ERRCODE_IO_ACCESSDENIED, OUString( OSL_LOG_PREFIX ) );
+ else
+ GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ }
+
+ // when the file is locked, get the current file date
+ if ( bResult && DocNeedsFileDateCheck() )
+ GetInitFileDate( true );
+ }
+ catch ( const uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
+ }
+ return;
+ }
+
if (!IsLockingUsed() || GetURLObject().HasError())
return;
@@ -2305,8 +2404,15 @@ void SfxMedium::GetMedium_Impl()
aMedium.addInputStreamOwnLock();
}
else
+ {
+ // add a check for protocol, if it's http or https or provate webdav then add
+ // the interaction handler to be used by the authentication dialog
+ if ( GetURLObject().isAnyKnownWebDAVScheme() )
+ {
+ aMedium[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER()] <<= GetInteractionHandler( true );
+ }
aMedium.addInputStream();
-
+ }
// the ReadOnly property set in aMedium is ignored
// the check is done in LockOrigFileOnDemand() for file and non-file URLs
@@ -2500,10 +2606,10 @@ void SfxMedium::UseInteractionHandler( bool bUse )
::com::sun::star::uno::Reference< ::com::sun::star::task::XInteractionHandler >
-SfxMedium::GetInteractionHandler()
+SfxMedium::GetInteractionHandler( bool bGetAlways )
{
// if interaction isn't allowed explicitly ... return empty reference!
- if ( !pImp->bUseInteractionHandler )
+ if ( !bGetAlways && !pImp->bUseInteractionHandler )
return ::com::sun::star::uno::Reference< ::com::sun::star::task::XInteractionHandler >();
// search a possible existing handler inside cached item set
@@ -2516,7 +2622,7 @@ SfxMedium::GetInteractionHandler()
}
// if default interaction isn't allowed explicitly ... return empty reference!
- if ( !pImp->bAllowDefaultIntHdl )
+ if ( !bGetAlways && !pImp->bAllowDefaultIntHdl )
return ::com::sun::star::uno::Reference< ::com::sun::star::task::XInteractionHandler >();
// otherwise return cached default handler ... if it exist.
@@ -2597,11 +2703,41 @@ void SfxMedium::CloseAndRelease()
UnlockFile( true );
}
+void SfxMedium::DisableUnlockWebDAV( bool bDisableUnlockWebDAV )
+{
+ pImp->m_bDisableUnlockWebDAV = bDisableUnlockWebDAV;
+}
+
void SfxMedium::UnlockFile( bool bReleaseLockStream )
{
#if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
(void) bReleaseLockStream;
#else
+ // check if webdav
+ if ( GetURLObject().isAnyKnownWebDAVScheme() )
+ {
+ if ( pImp->m_bLocked )
+ {
+ // an interaction handler should be used for authentication, if needed
+ try {
+ uno::Reference< ::com::sun::star::task::XInteractionHandler > xHandler = GetInteractionHandler( true );
+ uno::Reference< ::com::sun::star::ucb::XCommandEnvironment > xComEnv;
+ xComEnv = new ::ucbhelper::CommandEnvironment( xHandler,
+ Reference< ::com::sun::star::ucb::XProgressHandler >() );
+ ucbhelper::Content aContentToUnlock( GetURLObject().GetMainURL( INetURLObject::NO_DECODE ), xComEnv, comphelper::getProcessComponentContext());
+ pImp->m_bLocked = false;
+ //check if WebDAV unlock was explicitly disabled
+ if ( !pImp->m_bDisableUnlockWebDAV )
+ aContentToUnlock.unlock();
+ }
+ catch ( uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
+ }
+ }
+ return;
+ }
+
if ( pImp->m_xLockingStream.is() )
{
if ( bReleaseLockStream )
diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx
index 312b4405530f..e3682e305759 100644
--- a/sfx2/source/doc/objstor.cxx
+++ b/sfx2/source/doc/objstor.cxx
@@ -1774,6 +1774,9 @@ bool SfxObjectShell::SaveTo_Impl
}
+// This method contains a call to disable the UNLOCK of a WebDAV resource, that work while saving a file.
+// If the method is called from another process (e.g. not when saving a file),
+// that disabling needs tweaking
bool SfxObjectShell::DisconnectStorage_Impl( SfxMedium& rSrcMedium, SfxMedium& rTargetMedium )
{
// this method disconnects the storage from source medium, and attaches it to the backup created by the target medium
@@ -1794,7 +1797,12 @@ bool SfxObjectShell::DisconnectStorage_Impl( SfxMedium& rSrcMedium, SfxMedium& r
rTargetMedium.ResetError();
xOptStorage->writeAndAttachToStream( uno::Reference< io::XStream >() );
rSrcMedium.CanDisposeStorage_Impl( false );
+ // need to modify this for WebDAV if this method is called outside
+ // the process of saving a file
+ rSrcMedium.DisableUnlockWebDAV();
rSrcMedium.Close();
+ // see comment on the previou third row
+ rSrcMedium.DisableUnlockWebDAV( false );
// now try to create the backup
rTargetMedium.GetBackup_Impl();
diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx
index 33c96235379d..6cce9411dfe7 100644
--- a/sfx2/source/view/viewfrm.cxx
+++ b/sfx2/source/view/viewfrm.cxx
@@ -432,13 +432,31 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq )
INetURLObject aMedObj( pMed->GetName() );
- // the logic below is following, if the document seems not to need to be reloaded and the physical name is different
- // to the logical one, then on file system it can be checked that the copy is still newer than the original and no document reload is required
- if ( ( !bNeedsReload && ( (aMedObj.GetProtocol() == INetProtocol::File &&
- aMedObj.getFSysPath(INetURLObject::FSYS_DETECT) != aPhysObj.getFSysPath(INetURLObject::FSYS_DETECT) &&
- !::utl::UCBContentHelper::IsYounger( aMedObj.GetMainURL( INetURLObject::NO_DECODE ), aPhysObj.GetMainURL( INetURLObject::NO_DECODE ) ))
- || pMed->IsRemote() ) )
- || pVersionItem )
+ // -> tdf#82744
+ // the logic below is following:
+ // if the document seems not to need to be reloaded
+ // and the physical name is different to the logical one,
+ // then on file system it can be checked that the copy is still newer than the original and no document reload is required.
+ // Did some semplification to enhance readability of the 'if' expression
+ //
+ // when the 'http/https' protocol is active, the bool bPhysObjIsYounger relies upon the getlastmodified Property of a WebDAV resource.
+ // Said property should be implemented, but sometimes it's not.
+ // implemented. On this case the reload activated here will not work properly.
+ // TODO: change the check age method for WebDAV to etag (entity-tag) property value, need some rethinking, since the
+ // etag tells that the cache representation (e.g. in LO) is different from the one on the server,
+ // but tells nothing about the age
+ // Details at this link: http://tools.ietf.org/html/rfc4918#section-15, section 15.7
+ bool bPhysObjIsYounger = ::utl::UCBContentHelper::IsYounger( aMedObj.GetMainURL( INetURLObject::NO_DECODE ),
+ aPhysObj.GetMainURL( INetURLObject::NO_DECODE ) );
+ bool bIsWebDAV = aMedObj.isAnyKnownWebDAVScheme();
+
+ if ( ( !bNeedsReload && ( ( aMedObj.GetProtocol() == INetProtocol::File &&
+ aMedObj.getFSysPath( INetURLObject::FSYS_DETECT ) != aPhysObj.getFSysPath( INetURLObject::FSYS_DETECT ) &&
+ !bPhysObjIsYounger )
+ || ( bIsWebDAV && !bPhysObjIsYounger )
+ || ( pMed->IsRemote() && !bIsWebDAV ) ) )
+ || pVersionItem )
+ // <- tdf#82744
{
bool bOK = false;
if ( !pVersionItem )
diff --git a/ucb/source/ucp/webdav-neon/ContentProperties.hxx b/ucb/source/ucp/webdav-neon/ContentProperties.hxx
index 3f844b610869..1e730c836a0d 100644
--- a/ucb/source/ucp/webdav-neon/ContentProperties.hxx
+++ b/ucb/source/ucp/webdav-neon/ContentProperties.hxx
@@ -82,13 +82,13 @@ class ContentProperties
public:
ContentProperties();
- ContentProperties( const DAVResource& rResource );
+ explicit ContentProperties( const DAVResource& rResource );
// Mini props for transient contents.
ContentProperties( const OUString & rTitle, bool bFolder );
// Micro props for non-existing contents.
- ContentProperties( const OUString & rTitle );
+ explicit ContentProperties( const OUString & rTitle );
ContentProperties( const ContentProperties & rOther );
@@ -175,7 +175,7 @@ private:
CachableContentProperties( const CachableContentProperties & ) SAL_DELETED_FUNCTION;
public:
- CachableContentProperties( const ContentProperties & rProps );
+ explicit CachableContentProperties( const ContentProperties & rProps );
void addProperties( const ContentProperties & rProps );
diff --git a/ucb/source/ucp/webdav-neon/NeonSession.cxx b/ucb/source/ucp/webdav-neon/NeonSession.cxx
index 8deb3dcdefce..0e6bef67e496 100644
--- a/ucb/source/ucp/webdav-neon/NeonSession.cxx
+++ b/ucb/source/ucp/webdav-neon/NeonSession.cxx
@@ -831,6 +831,20 @@ void NeonSession::PROPFIND( const OUString & inPath,
const DAVRequestEnvironment & rEnv )
throw ( std::exception )
{
+#if defined SAL_LOG_INFO
+ { //debug
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - inPath: <" << inPath << "> inDepth: " << inDepth );
+ OUString aProps;
+ for(std::vector< OUString >::const_iterator it = inPropNames.begin();
+ it < inPropNames.end(); it++)
+ {
+ aProps += *it;
+ aProps += ", ";
+ }
+ SAL_INFO( "ucb.ucp.webdav", " properties: " << aProps);
+ } //debug
+#endif
+
osl::Guard< osl::Mutex > theGuard( m_aMutex );
Init( rEnv );
@@ -853,6 +867,8 @@ void NeonSession::PROPFIND( const OUString & inPath,
const DAVRequestEnvironment & rEnv )
throw( std::exception )
{
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - inPath: <" << inPath << "> inDepth: " << inDepth );
+
osl::Guard< osl::Mutex > theGuard( m_aMutex );
Init( rEnv );
@@ -865,6 +881,23 @@ void NeonSession::PROPFIND( const OUString & inPath,
ioResInfo,
theRetVal );
+#if defined SAL_LOG_INFO
+ { //debug
+ for ( std::vector< DAVResourceInfo >::const_iterator itres = ioResInfo.begin();
+ itres < ioResInfo.end(); itres++)
+ {
+ OUString aProps;
+ for ( std::vector< OUString >::const_iterator it = (*itres).properties.begin();
+ it < (*itres).properties.end(); it++)
+ {
+ aProps += *it;
+ aProps += ", ";
+ }
+ SAL_INFO( "ucb.ucp.webdav", " returned property names: " << aProps);
+ }
+ } //debug
+#endif
+
HandleError( theRetVal, inPath, rEnv );
}
@@ -1315,6 +1348,18 @@ void NeonSession::LOCK( const OUString & inPath,
{
osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ // before issuing the lock command,
+ // better check first if we already have one on this href
+ if ( m_aNeonLockStore.findByUri(
+ makeAbsoluteURL( inPath ) ) != 0 )
+ {
+ // we already own a lock for this href
+ // no need to ask for another
+ // TODO: add a lockdiscovery request for confirmation
+ // checking the locktoken, the only item that's unique
+ return;
+ }
+
Init( rEnv );
/* Create a depth zero, exclusive write lock, with default timeout
@@ -1612,6 +1657,7 @@ void NeonSession::HandleError( int nError,
sal_uInt16 code = makeStatusCode( aText );
+ SAL_WARN( "ucb.ucp.webdav","Neon received http error: '" << aText << "'");
if ( code == SC_LOCKED )
{
if ( m_aNeonLockStore.findByUri(
@@ -1640,6 +1686,7 @@ void NeonSession::HandleError( int nError,
throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
}
case NE_LOOKUP: // Name lookup failed.
+ SAL_WARN( "ucb.ucp.webdav","Name lookup failed" );
throw DAVException( DAVException::DAV_HTTP_LOOKUP,
NeonUri::makeConnectionEndPointString(
m_aHostName, m_nPort ) );
@@ -1665,6 +1712,7 @@ void NeonSession::HandleError( int nError,
m_aHostName, m_nPort ) );
case NE_FAILED: // The precondition failed
+ SAL_WARN( "ucb.ucp.webdav","The precondition failed" );
throw DAVException( DAVException::DAV_HTTP_FAILED,
NeonUri::makeConnectionEndPointString(
m_aHostName, m_nPort ) );
@@ -1682,7 +1730,7 @@ void NeonSession::HandleError( int nError,
}
default:
{
- OSL_TRACE( "NeonSession::HandleError : Unknown Neon error code!" );
+ SAL_WARN( "ucb.ucp.webdav", "Unknown Neon error code!" );
throw DAVException( DAVException::DAV_HTTP_ERROR,
OUString::createFromAscii(
ne_get_error( m_pHttpSession ) ) );
diff --git a/ucb/source/ucp/webdav-neon/webdavcontent.cxx b/ucb/source/ucp/webdav-neon/webdavcontent.cxx
index bb4e317484a8..0cf9db69ed55 100644
--- a/ucb/source/ucp/webdav-neon/webdavcontent.cxx
+++ b/ucb/source/ucp/webdav-neon/webdavcontent.cxx
@@ -108,7 +108,9 @@ Content::Content(
throw ( ucb::ContentCreationException )
: ContentImplHelper( rxContext, pProvider, Identifier ),
m_eResourceType( UNKNOWN ),
+ m_eResourceTypeForLocks( UNKNOWN ),
m_pProvider( pProvider ),
+ m_rSessionFactory( rSessionFactory ),
m_bTransient( false ),
m_bCollection( false ),
m_bDidGetOrHead( false )
@@ -140,6 +142,7 @@ Content::Content(
throw ( ucb::ContentCreationException )
: ContentImplHelper( rxContext, pProvider, Identifier ),
m_eResourceType( UNKNOWN ),
+ m_eResourceTypeForLocks( UNKNOWN ),
m_pProvider( pProvider ),
m_bTransient( true ),
m_bCollection( isCollection ),
@@ -605,22 +608,28 @@ uno::Any SAL_CALL Content::execute(
// lock
- // supportsExclusiveWriteLock() does not work if the file is being created.
- // The lack of lock functionality is taken care of inside lock(),
- // a temporary measure.
- // This current implementation will result in a wasted lock request issued to web site
- // that don't support it.
- // TODO: need to rewrite the managing of the m_bTransient flag, when the resource is non yet existent
- // and the first lock on a non existed resource first creates it then lock it.
- lock( Environment );
+ 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" && supportsExclusiveWriteLock( Environment ) )
+ else if ( aCommand.Name == "unlock" )
{
// unlock
-
-
- unlock( Environment );
+ if ( resourceTypeForLocks( Environment ) == DAV )
+ unlock( Environment );
}
else if ( aCommand.Name == "createNewContent" && isFolder( Environment ) )
{
@@ -716,9 +725,7 @@ uno::Any SAL_CALL Content::execute(
// Unreachable
}
- OSL_TRACE( "<<<<< Content::execute: end: command: %s",
- OUStringToOString( aCommand.Name,
- RTL_TEXTENCODING_UTF8 ).getStr() );
+ SAL_INFO( "ucb.ucp.webdav", "Content::execute: end: command: " << aCommand.Name );
return aRet;
}
@@ -2021,9 +2028,9 @@ uno::Any Content::open(
// cache headers.
if ( !m_xCachedProps.get())
m_xCachedProps.reset(
- new CachableContentProperties( aResource ) );
+ new CachableContentProperties( ContentProperties( aResource ) ) );
else
- m_xCachedProps->addProperties( aResource );
+ m_xCachedProps->addProperties( ContentProperties( aResource ) );
m_xResAccess.reset(
new DAVResourceAccess( *xResAccess.get() ) );
@@ -2069,7 +2076,7 @@ uno::Any Content::open(
// cache headers.
if ( !m_xCachedProps.get())
m_xCachedProps.reset(
- new CachableContentProperties( aResource ) );
+ new CachableContentProperties( ContentProperties( aResource ) ) );
else
m_xCachedProps->addProperties(
aResource.properties );
@@ -2374,7 +2381,15 @@ void Content::insert(
if ( bCollection )
xResAccess->MKCOL( Environment );
else
+ {
xResAccess->PUT( xInputStream, Environment );
+ }
+ // 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 )
{
@@ -2754,30 +2769,156 @@ void Content::destroy( bool bDeletePhysical )
}
}
-
-bool Content::supportsExclusiveWriteLock(
+// returns the resource type, to be checked for locks
+Content::ResourceType Content::resourceTypeForLocks(
const uno::Reference< ucb::XCommandEnvironment >& Environment )
{
- if ( getResourceType( Environment ) == DAV )
+ 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
+ std::unique_ptr< ContentProperties > xProps;
if ( m_xCachedProps.get() )
{
+ std::unique_ptr< ContentProperties > xCachedProps;
+ xCachedProps.reset( new ContentProperties( *m_xCachedProps.get() ) );
uno::Sequence< ucb::LockEntry > aSupportedLocks;
if ( m_xCachedProps->getValue( DAVProperties::SUPPORTEDLOCK )
- >>= aSupportedLocks )
+ >>= aSupportedLocks ) //get the cached value for supportedlock
{
for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
{
if ( aSupportedLocks[ n ].Scope
- == ucb::LockScope_EXCLUSIVE &&
+ == ucb::LockScope_EXCLUSIVE &&
aSupportedLocks[ n ].Type
- == ucb::LockType_WRITE )
- return true;
+ == ucb::LockType_WRITE )
+ eResourceTypeForLocks = DAV;
}
}
}
}
- return false;
+
+ const OUString & rURL = m_xIdentifier->getContentIdentifier();
+
+ if ( eResourceTypeForLocks == UNKNOWN )
+ {
+ // resource type for lock/unlock operations still unknown, need to ask the server
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ xResAccess.reset( new DAVResourceAccess(
+ m_xContext,
+ m_rSessionFactory,
+ rURL ) );
+
+ const OUString aScheme(
+ rURL.copy( 0, rURL.indexOf( ':' ) ).toAsciiLowerCase() );
+
+ if ( aScheme == FTP_URL_SCHEME )
+ {
+ eResourceTypeForLocks = FTP;
+ }
+ else
+ {
+ try
+ {
+ // we need only DAV:supportedlock
+ std::vector< DAVResource > resources;
+ std::vector< OUString > aPropNames;
+ uno::Sequence< beans::Property > aProperties( 1 );
+ aProperties[ 0 ].Name = DAVProperties::SUPPORTEDLOCK;
+
+ ContentProperties::UCBNamesToDAVNames( aProperties, aPropNames );
+ xResAccess->PROPFIND( DAVZERO, aPropNames, resources, Environment );
+
+ // 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 )
+ {
+ uno::Sequence< ucb::LockEntry > aSupportedLocks;
+ if ( (*it).Value >>= aSupportedLocks )
+ {
+ // this is at least a DAV, no lock confirmed yet
+ eResourceTypeForLocks = DAV_NOLOCK;
+ for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
+ {
+ 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;
+ }
+ }
+ }
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ xResAccess->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 this, instead
+ // TODO: probably remove it, when OPTIONS implemented
+ // 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:
+ // this returned errors are part of base http 1.1 RFCs
+ // see:
+ 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 error: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ eResourceTypeForLocks = NON_DAV;
+ break;
+ default:
+ //fallthrough
+ SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks DAVException - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV error: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ eResourceTypeForLocks = UNKNOWN;
+ }
+ }
+ }
+ }
+ 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;
}
@@ -2836,37 +2977,56 @@ void Content::lock(
// this exception should be managed by the issuer of 'lock' command
switch(e.getError())
{
- case DAVException::DAV_LOCKED:
- {
- throw(ucb::InteractiveLockingLockedException(
- OUString( "Locked!" ),
- static_cast< cppu::OWeakObject * >( this ),
- task::InteractionClassification_ERROR,
- aURL,
- false ));
- }
- break;
- case DAVException::DAV_HTTP_ERROR:
- //grab the error code
- switch( e.getStatus() )
+ case DAVException::DAV_LOCKED:
{
- // this returned error is part of base http 1.1 RFCs
- case SC_NOT_IMPLEMENTED:
- case SC_METHOD_NOT_ALLOWED:
- // this is a temporary measure, means the lock method is not supported
- // TODO: fix the behaviour of m_bTransient when the file is first created
+ SAL_WARN( "ucb.ucp.webdav", "lock: resource already locked - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">");
+ throw
+ ucb::InteractiveLockingLockedException(
+ OUString( "Locked!" ),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL,
+ false );
+ }
+ break;
+ case DAVException::DAV_HTTP_ERROR:
+ //grab the error code
+ switch( e.getStatus() )
+ {
+ // this returned error is part of base http 1.1 RFCs
+ case SC_NOT_IMPLEMENTED:
+ case SC_METHOD_NOT_ALLOWED:
+ SAL_WARN( "ucb.ucp.webdav", "lock: DAVException (SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV error: " << 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
+ // doing the first PUT, but if LOCK is not supported
+ // (simple web or DAV with lock disabled) we end with one of these two http
+ // errors
+ // details to LOCK on an unmapped (i.e. non existent) resource are in:
+ // http://tools.ietf.org/html/rfc4918#section-7.3
+ return;
+ break;
+ 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 successfull
return;
break;
default:
//fallthrough
;
- }
- break;
- default:
- //fallthrough
- ;
}
+ SAL_WARN( "ucb.ucp.webdav","lock: DAVException - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV error: " << e.getError() << ", HTTP error: " << e.getStatus() );
cancelCommandExecution( e, Environment );
// Unreachable
}
@@ -2894,28 +3054,40 @@ void Content::unlock(
}
catch ( DAVException const & e )
{
- switch(e.getError())
+ switch( e.getError() )
{
- case DAVException::DAV_HTTP_ERROR:
- //grab the error code
- switch( e.getStatus() )
- {
- // this returned error is part of base http 1.1 RFCs
- case SC_NOT_IMPLEMENTED:
- case SC_METHOD_NOT_ALLOWED:
- // this is a temporary measure, means the lock method is not supported
- // TODO: fix the behaviour of m_bTransient when the file is first created
+ 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;
break;
+ case DAVException::DAV_HTTP_ERROR:
+ //grab the error code
+ switch( e.getStatus() )
+ {
+ // this returned error is part of base http 1.1 RFCs
+ case SC_NOT_IMPLEMENTED:
+ case SC_METHOD_NOT_ALLOWED:
+ SAL_WARN( "ucb.ucp.webdav", "unlock: DAVException (SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV error: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ return;
+ break;
+ default:
+ //fallthrough
+ ;
+ }
+ break;
default:
//fallthrough
;
- }
- break;
- default:
- //fallthrough
- ;
}
+ SAL_WARN( "ucb.ucp.webdav","unlock: DAVException - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV error: " << e.getError() << ", HTTP error: " << e.getStatus() );
cancelCommandExecution( e, Environment );
// Unreachable
}
@@ -3233,7 +3405,7 @@ Content::getBaseURI( const std::unique_ptr< DAVResourceAccess > & rResAccess )
return OUString( 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,
@@ -3281,11 +3453,10 @@ Content::ResourceType Content::getResourceType(
{
osl::MutexGuard g(m_aMutex);
m_xCachedProps.reset(
- new CachableContentProperties( resources[ 0 ] ) );
+ new CachableContentProperties( ContentProperties( resources[ 0 ] ) ) );
m_xCachedProps->containsAllNames(
aProperties, m_aFailedPropNames );
}
-
eResourceType = DAV;
}
catch ( DAVException const & e )
diff --git a/ucb/source/ucp/webdav-neon/webdavcontent.hxx b/ucb/source/ucp/webdav-neon/webdavcontent.hxx
index 45605899951d..fdee359bc300 100644
--- a/ucb/source/ucp/webdav-neon/webdavcontent.hxx
+++ b/ucb/source/ucp/webdav-neon/webdavcontent.hxx
@@ -78,10 +78,12 @@ class Content : public ::ucbhelper::ContentImplHelper,
{
enum ResourceType
{
- UNKNOWN,
- FTP,
- NON_DAV,
- DAV
+ UNKNOWN, // the type of the Web resource is unknown
+ NOT_FOUND, // the Web resource does not exists
+ FTP, // the Web resource exists but it's ftp
+ NON_DAV, // the Web resource exists but it's not DAV
+ DAV, // the type of the Web resource is DAV with lock/unlock available
+ DAV_NOLOCK // the type of the Web resource is DAV with no lock/unlock available
};
std::unique_ptr< DAVResourceAccess > m_xResAccess;
@@ -89,7 +91,9 @@ class Content : public ::ucbhelper::ContentImplHelper,
m_xCachedProps; // locally cached props
OUString m_aEscapedTitle;
ResourceType m_eResourceType;
+ ResourceType m_eResourceTypeForLocks;
ContentProvider* m_pProvider; // No need for a ref, base class holds object
+ rtl::Reference< DAVSessionFactory > m_rSessionFactory;
bool m_bTransient;
bool m_bCollection;
bool m_bDidGetOrHead;
@@ -197,7 +201,7 @@ private:
static bool shouldAccessNetworkAfterException( const DAVException & e );
- bool supportsExclusiveWriteLock(
+ ResourceType resourceTypeForLocks(
const com::sun::star::uno::Reference<
com::sun::star::ucb::XCommandEnvironment >& Environment );
diff --git a/ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx b/ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx
index 31561a1da330..03f309a00d3c 100644
--- a/ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx
+++ b/ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx
@@ -592,7 +592,8 @@ uno::Sequence< ucb::CommandInfo > Content::getCommands(
return aCmdInfo;
}
- bool bSupportsLocking = supportsExclusiveWriteLock( xEnv );
+ ResourceType eType = resourceTypeForLocks( xEnv );
+ bool bSupportsLocking = ( eType == NOT_FOUND || eType == DAV );
sal_Int32 nPos = aCmdInfo.getLength();
sal_Int32 nMoreCmds = ( bFolder ? 2 : 0 ) + ( bSupportsLocking ? 2 : 0 );
diff --git a/ucbhelper/source/client/content.cxx b/ucbhelper/source/client/content.cxx
index 8edaa5a1cd6a..3ee7688646a9 100644
--- a/ucbhelper/source/client/content.cxx
+++ b/ucbhelper/source/client/content.cxx
@@ -1096,7 +1096,28 @@ bool Content::isDocument()
SAL_WNOUNREACHABLE_CODE_POP
+void Content::lock()
+ throw( CommandAbortedException, RuntimeException, Exception )
+{
+ Command aCommand;
+ aCommand.Name = rtl::OUString::createFromAscii( "lock" );
+ aCommand.Handle = -1; // n/a
+ m_xImpl->executeCommand( aCommand );
+
+}
+
+void Content::unlock()
+ throw( CommandAbortedException, RuntimeException, Exception )
+{
+
+ Command aCommand;
+ aCommand.Name = rtl::OUString::createFromAscii( "unlock" );
+ aCommand.Handle = -1; // n/a
+
+ m_xImpl->executeCommand( aCommand );
+
+}
// Content_Impl Implementation.
diff --git a/unotools/source/misc/mediadescriptor.cxx b/unotools/source/misc/mediadescriptor.cxx
index c757f195606d..f59ed5413e66 100644
--- a/unotools/source/misc/mediadescriptor.cxx
+++ b/unotools/source/misc/mediadescriptor.cxx
@@ -47,6 +47,7 @@
#include <ucbhelper/commandenvironment.hxx>
#include <ucbhelper/activedatasink.hxx>
#include <comphelper/processfactory.hxx>
+#include <tools/urlobj.hxx>
#include <osl/diagnose.h>
namespace utl {
@@ -689,12 +690,7 @@ bool MediaDescriptor::impl_openStreamWithURL( const OUString& sURL, bool bLockFi
// If the protocol is webdav, then we need to treat the stream as readonly, even if the
// operation was requested as read/write explicitly (the WebDAV UCB implementation is monodirectional
// read or write not both at the same time).
- OUString aScheme;
- css::uno::Reference< css::ucb::XContentIdentifier > xContId(
- aContent.get().is() ? aContent.get()->getIdentifier() : 0 );
- if ( xContId.is() )
- aScheme = xContId->getContentProviderScheme();
- if(!aScheme.equalsIgnoreAsciiCase( "http" ) && !aScheme.equalsIgnoreAsciiCase( "https" ))
+ if ( !INetURLObject( sURL ).isAnyKnownWebDAVScheme() )
return false;
}
xStream.clear();