ucb: webdav-curl: tdf#101094 (8) OPTIONS: Add options check in Content::resourceTypeForLocks

[ port of commit 6ab2cabeae02b6beb3c33238773ba075f41c4bb9 ]

Change-Id: I10f928fb5484738553769f54353ca7637e0780ce
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/123290
Tested-by: Michael Stahl <michael.stahl@allotropia.de>
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
diff --git a/ucb/source/ucp/webdav-curl/webdavcontent.cxx b/ucb/source/ucp/webdav-curl/webdavcontent.cxx
index 96ac6dd..706f3a0 100644
--- a/ucb/source/ucp/webdav-curl/webdavcontent.cxx
+++ b/ucb/source/ucp/webdav-curl/webdavcontent.cxx
@@ -2825,7 +2825,8 @@ void Content::destroy( bool bDeletePhysical )

// returns the resource type, to be checked for locks
Content::ResourceType Content::resourceTypeForLocks(
  const uno::Reference< ucb::XCommandEnvironment >& Environment )
    const uno::Reference< ucb::XCommandEnvironment >& Environment,
    const std::unique_ptr< DAVResourceAccess > & rResAccess)
{
    ResourceType eResourceTypeForLocks = UNKNOWN;
    {
@@ -2857,98 +2858,128 @@ Content::ResourceType Content::resourceTypeForLocks(
    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 ) );

        {
            try
        //{
            DAVOptions aDAVOptions;
            getResourceOptions( Environment, aDAVOptions, rResAccess );
            if( aDAVOptions.isClass1() ||
                aDAVOptions.isClass2() ||
                aDAVOptions.isClass3() )
            {
                // 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 )
                // this is at least a DAV, lock to be confirmed
                // class 2 is needed for full lock support
                // see
                // <https://tools.ietf.org/html/rfc4918#section-18.2>
                eResourceTypeForLocks = DAV_NOLOCK;
                if( aDAVOptions.isClass2() )
                {
                    // 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++)
                    // ok, possible lock, check for it
                    try
                    {
                        if ( (*it).Name ==  DAVProperties::SUPPORTEDLOCK )
                        // 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 );
                        rResAccess->PROPFIND( DAVZERO, aPropNames, resources, Environment );

                        bool wasSupportedlockFound = false;

                        // only one resource should be returned
                        if ( resources.size() == 1 )
                        {
                            uno::Sequence< ucb::LockEntry > aSupportedLocks;
                            if ( (*it).Value >>= aSupportedLocks )
                            // 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)
                            {
                                // this is at least a DAV, no lock confirmed yet
                                eResourceTypeForLocks = DAV_NOLOCK;
                                for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
                                if ( (*it).Name ==  DAVProperties::SUPPORTEDLOCK )
                                {
                                    if ( aSupportedLocks[ n ].Scope == ucb::LockScope_EXCLUSIVE &&
                                         aSupportedLocks[ n ].Type == ucb::LockType_WRITE )
                                    wasSupportedlockFound = true;
                                    uno::Sequence< ucb::LockEntry > aSupportedLocks;
                                    if ( (*it).Value >>= aSupportedLocks )
                                    {
                                        // requested locking mode is supported
                                        eResourceTypeForLocks = DAV;
                                        SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks - URL: <"
                                                  << m_xIdentifier->getContentIdentifier() << ">, DAV lock/unlock supported");
                                        for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
                                        {
                                            // TODO: if the lock type is changed from 'exclusive write' to 'shared write'
                                            // e.g. to implement 'Calc shared file feature', the ucb::LockScope_EXCLUSIVE
                                            // value should be checked as well, adaptation the code may be needed
                                            if ( aSupportedLocks[ n ].Scope == ucb::LockScope_EXCLUSIVE &&
                                                 aSupportedLocks[ n ].Type == ucb::LockType_WRITE )
                                            {
                                                // requested locking mode is supported
                                                eResourceTypeForLocks = DAV;
                                                SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks - URL: <"
                                                          << m_xIdentifier->getContentIdentifier() << ">, DAV lock/unlock supported");
                                                break;
                                            }
                                        }
                                        break;
                                    }
                                }
                                break;
                            }
                        }
                        // check if this is still only a DAV_NOLOCK
                        // a fallback for resources that do not have DAVProperties::SUPPORTEDLOCK property
                        // we check for the returned OPTION if LOCK is allowed on the resource
                        if ( !wasSupportedlockFound && eResourceTypeForLocks == DAV_NOLOCK )
                        {
                            SAL_INFO( "ucb.ucp.webdav", "This WebDAV server has no supportedlock property, check for allowed LOCK method in OPTIONS" );
                            // ATTENTION: if the lock type is changed from 'exclusive write' to 'shared write'
                            // e.g. to implement 'Calc shared file feature' on WebDAV directly, and we arrive to this fallback
                            // and the LOCK is allowed, we should assume that only exclusive write lock is available
                            // this is just a reminder...
                            if ( aDAVOptions.isLockAllowed() )
                                eResourceTypeForLocks = DAV;
                        }
                    }
                    catch ( DAVException const & e )
                    {
                        rResAccess->resetUri();
                        //grab the error code
                        switch( e.getStatus() )
                        {
                            case SC_NOT_FOUND:
                                SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
                                          << m_xIdentifier->getContentIdentifier() << "> was not found. ");
                                eResourceTypeForLocks = NOT_FOUND;
                                break;
                                // some servers returns SC_FORBIDDEN, instead
                                // 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:
                                // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
                                // part of base http 1.1 RFCs
                            case SC_NOT_IMPLEMENTED:        // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
                            case SC_METHOD_NOT_ALLOWED:     // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
                                // they all mean the resource is NON_DAV
                                SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException (SC_FORBIDDEN, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
                                          << m_xIdentifier->getContentIdentifier() << ">, DAV 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;
                        }
                    }
                }
            }
            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 SC_FORBIDDEN, 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:
                        // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
                        // part of base http 1.1 RFCs
                    case SC_NOT_IMPLEMENTED:        // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
                    case SC_METHOD_NOT_ALLOWED:     // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
                        // they all mean the resource is NON_DAV
                        SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException (SC_FORBIDDEN, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
                                  << m_xIdentifier->getContentIdentifier() << ">, DAV 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;
                }
            }
        }
            else
                eResourceTypeForLocks = NON_DAV;

        //}
    }
    osl::MutexGuard g(m_aMutex);
    if (m_eResourceTypeForLocks == UNKNOWN)
@@ -2967,6 +2998,21 @@ Content::ResourceType Content::resourceTypeForLocks(
    return m_eResourceTypeForLocks;
}

Content::ResourceType Content::resourceTypeForLocks(
    const uno::Reference< ucb::XCommandEnvironment >& Environment )
{
    std::unique_ptr< DAVResourceAccess > xResAccess;
    {
        osl::MutexGuard aGuard( m_aMutex );
        xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
    }
    Content::ResourceType ret = resourceTypeForLocks( Environment, xResAccess );
    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );
        m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
    }
    return ret;
}

void Content::lock(
        const uno::Reference< ucb::XCommandEnvironment >& Environment )
@@ -3020,7 +3066,7 @@ void Content::lock(
        // this exception is mapped directly to the ucb correct one, without
        // going into the cancelCommandExecution() user interaction
        // this exception should be managed by the issuer of 'lock' command
        switch(e.getError())
        switch( e.getError() )
        {
            case DAVException::DAV_LOCKED:
            {
diff --git a/ucb/source/ucp/webdav-curl/webdavcontent.hxx b/ucb/source/ucp/webdav-curl/webdavcontent.hxx
index f5a74bd..209f435 100644
--- a/ucb/source/ucp/webdav-curl/webdavcontent.hxx
+++ b/ucb/source/ucp/webdav-curl/webdavcontent.hxx
@@ -173,7 +173,11 @@ private:
    static bool shouldAccessNetworkAfterException( const DAVException & e );

    ResourceType resourceTypeForLocks(
        const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
        const css::uno::Reference< css::ucb::XCommandEnvironment >& rEnvironment,
        const std::unique_ptr< DAVResourceAccess > & rResAccess );

    ResourceType resourceTypeForLocks(
        const css::uno::Reference< css::ucb::XCommandEnvironment >& rEnvironment );

    // XPropertyContainer replacement
    /// @throws css::beans::PropertyExistException