diff options
Diffstat (limited to 'ucb')
-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 |
5 files changed, 301 insertions, 77 deletions
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 ); |