diff options
-rw-r--r-- | include/sfx2/docfile.hxx | 3 | ||||
-rw-r--r-- | include/ucbhelper/content.hxx | 20 | ||||
-rw-r--r-- | sfx2/source/doc/docfile.cxx | 146 | ||||
-rw-r--r-- | sfx2/source/doc/objstor.cxx | 8 | ||||
-rw-r--r-- | sfx2/source/view/viewfrm.cxx | 32 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-neon/ContentProperties.hxx | 6 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-neon/NeonSession.cxx | 50 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-neon/webdavcontent.cxx | 305 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-neon/webdavcontent.hxx | 14 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx | 3 | ||||
-rw-r--r-- | ucbhelper/source/client/content.cxx | 21 | ||||
-rw-r--r-- | unotools/source/misc/mediadescriptor.cxx | 8 |
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(); |