[Libreoffice-commits] core.git: include/sfx2 include/ucbhelper sfx2/source ucbhelper/source ucb/source unotools/source

Giuseppe Castagno giuseppe.castagno at acca-esse.eu
Mon Oct 5 04:15:20 PDT 2015


 include/sfx2/docfile.hxx                         |    3 
 include/ucbhelper/content.hxx                    |   20 +
 sfx2/source/doc/docfile.cxx                      |  146 ++++++++++-
 sfx2/source/doc/objstor.cxx                      |    8 
 sfx2/source/view/viewfrm.cxx                     |   32 +-
 ucb/source/ucp/webdav-neon/ContentProperties.hxx |    6 
 ucb/source/ucp/webdav-neon/NeonSession.cxx       |   50 +++
 ucb/source/ucp/webdav-neon/webdavcontent.cxx     |  305 +++++++++++++++++------
 ucb/source/ucp/webdav-neon/webdavcontent.hxx     |   14 -
 ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx |    3 
 ucbhelper/source/client/content.cxx              |   21 +
 unotools/source/misc/mediadescriptor.cxx         |    8 
 12 files changed, 520 insertions(+), 96 deletions(-)

New commits:
commit b4576f3da4d90139fc5140962d13cb91dab98797
Author: Giuseppe Castagno <giuseppe.castagno at acca-esse.eu>
Date:   Fri Aug 28 18:52:36 2015 +0200

    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 at collabora.com>
    Tested-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/include/sfx2/docfile.hxx b/include/sfx2/docfile.hxx
index c3c9e94..c19cc35 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 cfa632a..520ce2a 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 26f4820..ccdad93 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 312b440..e3682e3 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 33c9623..6cce941 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 3f844b6..1e730c8 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 8deb3dc..0e6bef6 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 bb4e317..0cf9db6 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 4560589..fdee359 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 31561a1..03f309a 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 8edaa5a..3ee7688 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 c757f19..f59ed54 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();


More information about the Libreoffice-commits mailing list