ucb/source/ucp/webdav-curl/DAVTypes.cxx      |   29 +++++++++++++
 ucb/source/ucp/webdav-curl/DAVTypes.hxx      |    8 +++
 ucb/source/ucp/webdav-curl/webdavcontent.cxx |   56 +++++++++++++++++++++++----
 ucb/source/ucp/webdav-curl/webdavcontent.hxx |    3 -
 4 files changed, 88 insertions(+), 8 deletions(-)

New commits:
commit 00fbb60bfecb8ef52b61f1720017106addf6c14d
Author:     Giuseppe Castagno <[email protected]>
AuthorDate: Sat Aug 27 17:31:06 2016 +0200
Commit:     Michael Stahl <[email protected]>
CommitDate: Mon Nov 1 18:47:37 2021 +0100

    ucb: webdav-curl: tdf#101094 (32): Use a partial GET as a fall-back for 
HEAD...
    
    ... partial GET means a GET with no data returned, to test for
    resource 'real' availability in case HEAD fails or is disabled.
    At  the same time disable use of HEAD method on the resource
    for the whole access procedure.
    
    This change is needed to manage web servers that don't permit the
    use of either HEAD or OPTIONS methods.
    To accomodate pure web sites that enable only GET to fetch contents.
    
    [ port of commit 2a148e2b5ea11fd371042e836fa95438ffa738e7 ]
    
    Change-Id: Ie0e2b6edd0860cbf704082500b91631543e7da38
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/123488
    Tested-by: Michael Stahl <[email protected]>
    Reviewed-by: Michael Stahl <[email protected]>

diff --git a/ucb/source/ucp/webdav-curl/DAVTypes.cxx 
b/ucb/source/ucp/webdav-curl/DAVTypes.cxx
index fd4cf2283ecd..6e5e51ef25d5 100644
--- a/ucb/source/ucp/webdav-curl/DAVTypes.cxx
+++ b/ucb/source/ucp/webdav-curl/DAVTypes.cxx
@@ -26,6 +26,7 @@ DAVOptions::DAVOptions() :
     m_isClass1( false ),
     m_isClass2( false ),
     m_isClass3( false ),
+    m_isHeadAllowed( true ),
     m_isLocked( false ),
     m_aAllowedMethods(),
     m_nStaleTime( 0 ),
@@ -40,6 +41,7 @@ DAVOptions::DAVOptions( const DAVOptions & rOther ) :
     m_isClass1( rOther.m_isClass1 ),
     m_isClass2( rOther.m_isClass2 ),
     m_isClass3( rOther.m_isClass3 ),
+    m_isHeadAllowed( rOther.m_isHeadAllowed ),
     m_isLocked( rOther.m_isLocked ),
     m_aAllowedMethods( rOther.m_aAllowedMethods ),
     m_nStaleTime( rOther.m_nStaleTime ),
@@ -175,5 +177,32 @@ bool DAVOptionsCache::isResourceFound( const OUString & 
rURL )
     return true;
 }
 
+bool DAVOptionsCache::isHeadAllowed( const OUString & rURL )
+{
+    osl::MutexGuard aGuard( m_aMutex );
+    OUString aEncodedUrl( ucb_impl::urihelper::encodeURI( DecodeURI(rURL) ) );
+    normalizeURLLastChar( aEncodedUrl );
+
+    DAVOptionsMap::iterator it;
+    it = m_aTheCache.find( aEncodedUrl );
+    if ( it != m_aTheCache.end() )
+    {
+        // first check for stale
+        TimeValue t1;
+        osl_getSystemTime( &t1 );
+        if( (*it).second.getStaleTime() < t1.Seconds )
+        {
+            m_aTheCache.erase( it );
+            return true; // to force again OPTIONS method
+        }
+
+        // check if the resource was present on server
+        return (*it).second.isHeadAllowed();
+    }
+    // this value is needed because some web server don't implement
+    // OPTIONS method, so the resource is considered found,
+    // until detected otherwise
+    return true;
+}
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/ucb/source/ucp/webdav-curl/DAVTypes.hxx 
b/ucb/source/ucp/webdav-curl/DAVTypes.hxx
index 7000945bbf43..be6eedc2d4c0 100644
--- a/ucb/source/ucp/webdav-curl/DAVTypes.hxx
+++ b/ucb/source/ucp/webdav-curl/DAVTypes.hxx
@@ -71,6 +71,8 @@ namespace http_dav_ucp
         bool    m_isClass1;
         bool    m_isClass2;
         bool    m_isClass3;
+        // for server that do not implement it
+        bool    m_isHeadAllowed;
         // Internally used to maintain locked stated of the resource, only
         // if it's a Class 2 resource
         bool    m_isLocked;
@@ -101,6 +103,9 @@ namespace http_dav_ucp
         bool isClass3() { return m_isClass3; };
         void setClass3( bool Class3 = true ) { m_isClass3 = Class3; };
 
+        bool isHeadAllowed() { return m_isHeadAllowed; };
+        void setHeadAllowed( bool HeadAllowed = true ) { m_isHeadAllowed = 
HeadAllowed; };
+
         sal_uInt32  getStaleTime() const { return m_nStaleTime ; };
         void setStaleTime( const sal_uInt32 nStaleTime ) { m_nStaleTime = 
nStaleTime; };
 
@@ -123,6 +128,7 @@ namespace http_dav_ucp
             m_isClass1 = false;
             m_isClass2 = false;
             m_isClass3 = false;
+            m_isHeadAllowed = true;
             m_isLocked = false;
             m_aAllowedMethods.clear();
             m_nStaleTime = 0;
@@ -168,6 +174,8 @@ namespace http_dav_ucp
         */
         bool isResourceFound( const OUString & rURL );
 
+        bool isHeadAllowed( const OUString & rURL );
+
     private:
 
         /// remove the last '/' in aUrl, if it exists
diff --git a/ucb/source/ucp/webdav-curl/webdavcontent.cxx 
b/ucb/source/ucp/webdav-curl/webdavcontent.cxx
index f0a0f9baeb0c..a9e3ef868201 100644
--- a/ucb/source/ucp/webdav-curl/webdavcontent.cxx
+++ b/ucb/source/ucp/webdav-curl/webdavcontent.cxx
@@ -1461,6 +1461,10 @@ uno::Reference< sdbc::XRow > Content::getPropertyValues(
                         // clean cached value of PROPFIND property names
                         // PROPPATCH can change them
                         removeCachedPropertyNames( xResAccess->getURL() );
+                        // test if HEAD allowed, if not, throw, will be 
catched immediately
+                        if ( !aStaticDAVOptionsCache.isHeadAllowed( 
xResAccess->getURL() ) )
+                            throw DAVException( DAVException::DAV_HTTP_ERROR, 
"405 Not Implemented" );
+
                         xResAccess->HEAD( aHeaderNames, resource, xEnv );
                         m_bDidGetOrHead = true;
 
@@ -3971,7 +3975,7 @@ void Content::getResourceOptions(
                             // instead of SC_NOT_IMPLEMENTED or 
SC_METHOD_NOT_ALLOWED.
                             // So check if this is an available resource, or a 
real 'Not Found' event.
                             sal_uInt32 nLifeTime = m_nOptsCacheLifeNotFound;
-                            if( isResourceAvailable(xEnv, rResAccess ) )
+                            if( isResourceAvailable( xEnv, rResAccess, 
rDAVOptions ) )
                             {
                                 nLifeTime = m_nOptsCacheLifeNotImpl;
                                 rDAVOptions.setResourceFound(); // means it 
exists, but it's not DAV
@@ -4028,17 +4032,54 @@ void Content::getResourceOptions(
 
 //static
 bool Content::isResourceAvailable( const css::uno::Reference< 
css::ucb::XCommandEnvironment >& xEnv,
-                                  const std::unique_ptr< DAVResourceAccess > & 
rResAccess )
+                                  const std::unique_ptr< DAVResourceAccess > & 
rResAccess,
+                                  DAVOptions& rDAVOptions )
 {
+    std::vector< rtl::OUString > aHeaderNames;
+    DAVResource aResource;
+
     try
     {
-        // To check for the physical URL resource availability, using a simple 
HEAD command
-        // if HEAD is successfull, set element found.
-        std::vector< OUString > aHeaderNames;
-        DAVResource resource;
-        rResAccess->HEAD( aHeaderNames, resource, xEnv );
+        // To check for the physical URL resource availability, first
+        // try using a simple HEAD command
+        // if HEAD is successful, set element found.
+        rResAccess->HEAD( aHeaderNames, aResource, xEnv );
         return true;
     }
+    catch ( DAVException const & e )
+    {
+        if ( e.getStatus() == SC_NOT_IMPLEMENTED ||
+             e.getStatus() == SC_METHOD_NOT_ALLOWED ||
+             e.getStatus() == SC_NOT_FOUND )
+        {
+            SAL_WARN( "ucb.ucp.webdav", "HEAD not implemented: fall back to a 
partial GET" );
+            // set in cached OPTIONS "HEAD not implemented"
+            // so it won't be used again on this resource
+            rDAVOptions.setHeadAllowed( false );
+            try
+            {
+                // do a GET with a payload of 0, the server does not
+                // support HEAD (or has HEAD disabled)
+                DAVRequestHeaders aPartialGet;
+                aPartialGet.push_back(
+                    DAVRequestHeader(
+                        OUString( "Range" ),
+                        OUString( "bytes=0-0" )));
+
+                rResAccess->GET( aPartialGet,
+                                 aHeaderNames,
+                                 aResource,
+                                 xEnv );
+                return true;
+            }
+            catch (...)
+            {
+                return false;
+            }
+        }
+        else
+            return false;
+    }
     catch ( ... )
     {
         // some error... so set as not found
@@ -4046,6 +4087,7 @@ bool Content::isResourceAvailable( const 
css::uno::Reference< css::ucb::XCommand
         // in rResAccess function method.
         return false;
     }
+    return false;
 }
 
 
diff --git a/ucb/source/ucp/webdav-curl/webdavcontent.hxx 
b/ucb/source/ucp/webdav-curl/webdavcontent.hxx
index f854cc24047e..e7638bcfa70b 100644
--- a/ucb/source/ucp/webdav-curl/webdavcontent.hxx
+++ b/ucb/source/ucp/webdav-curl/webdavcontent.hxx
@@ -297,7 +297,8 @@ public:
                              DAVOptions& rDAVOptions );
 
     static bool isResourceAvailable( const css::uno::Reference< 
css::ucb::XCommandEnvironment >& xEnv,
-                             const std::unique_ptr< DAVResourceAccess > & 
rResAccess);
+                             const std::unique_ptr< DAVResourceAccess > & 
rResAccess,
+                                     DAVOptions& rDAVOptions );
 
     static void removeCachedPropertyNames( const OUString & rURL );
 };

Reply via email to