ucb/source/ucp/webdav-curl/DAVTypes.hxx           |    6 
 ucb/source/ucp/webdav-curl/webdavcontent.cxx      |  271 +++++++++++++---------
 ucb/source/ucp/webdav-curl/webdavcontent.hxx      |    2 
 ucb/source/ucp/webdav-curl/webdavcontentcaps.cxx  |    9 
 ucb/source/ucp/webdav-curl/webdavdatasupplier.cxx |    2 
 5 files changed, 177 insertions(+), 113 deletions(-)

New commits:
commit 06985b1024c2272c7bbfb26dd252a3be48e5d3d8
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Tue Oct 12 16:27:52 2021 +0200
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Mon Nov 1 18:54:24 2021 +0100

    ucb: webdav-curl: loplugin:constfields in ucb
    
    [ replicate commit d8f8b4375998b62431c8605004e7c7d5c921ccc9 ]
    
    Change-Id: I720fdfbeab13e9dd210f11a613cb606e946d68e9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/123505
    Tested-by: Michael Stahl <michael.st...@allotropia.de>
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/ucb/source/ucp/webdav-curl/DAVTypes.hxx 
b/ucb/source/ucp/webdav-curl/DAVTypes.hxx
index e511bdcd31b7..ae3e5a076f60 100644
--- a/ucb/source/ucp/webdav-curl/DAVTypes.hxx
+++ b/ucb/source/ucp/webdav-curl/DAVTypes.hxx
@@ -188,9 +188,9 @@ namespace http_dav_ucp
 
     struct ProppatchValue
     {
-        ProppatchOperation  operation;
-        OUString            name;
-        css::uno::Any       value;
+        ProppatchOperation const  operation;
+        OUString const            name;
+        css::uno::Any const       value;
 
         ProppatchValue( const ProppatchOperation o,
                         const OUString & n,
diff --git a/ucb/source/ucp/webdav-curl/webdavcontent.hxx 
b/ucb/source/ucp/webdav-curl/webdavcontent.hxx
index bfc59d4646a2..43c275cd2e7f 100644
--- a/ucb/source/ucp/webdav-curl/webdavcontent.hxx
+++ b/ucb/source/ucp/webdav-curl/webdavcontent.hxx
@@ -82,7 +82,7 @@ class Content : public ::ucbhelper::ContentImplHelper,
     ResourceType      m_eResourceTypeForLocks;
     ContentProvider*  m_pProvider; // No need for a ref, base class holds 
object
     bool              m_bTransient;
-    bool              m_bCollection;
+    bool const        m_bCollection;
     bool              m_bDidGetOrHead;
     std::vector< OUString > m_aFailedPropNames;
     // Options Cache lifetime
diff --git a/ucb/source/ucp/webdav-curl/webdavdatasupplier.cxx 
b/ucb/source/ucp/webdav-curl/webdavdatasupplier.cxx
index b7dfb86d167c..d7213a0d13ee 100644
--- a/ucb/source/ucp/webdav-curl/webdavdatasupplier.cxx
+++ b/ucb/source/ucp/webdav-curl/webdavdatasupplier.cxx
@@ -104,7 +104,7 @@ struct DataSupplier_Impl
     ResultList                                   m_Results;
     rtl::Reference< Content >                    m_xContent;
     uno::Reference< uno::XComponentContext > m_xContext;
-    sal_Int32                                    m_nOpenMode;
+    sal_Int32 const                              m_nOpenMode;
     bool                                     m_bCountFinal;
     bool                                     m_bThrowException;
 
commit cf64fe2d02dcb7f3126026dc1face21ce3562392
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Tue Apr 4 18:42:51 2017 +0300
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Mon Nov 1 18:54:07 2021 +0100

    ucb: webdav-curl: tdf#106955: Open WebDAV resources on which PROPFIND fails
    
    When PROPFIND fails on a WebDAV resource, its IsDocument property
    stays undefined, and so stream creation fails. Proposed solution
    is to default to IsDocument=true for all WebDAV documents where
    we cannot get the property from server.
    
    Such resources also fail to return their locking options, so
    defaulting to server properties. When later locking is attempted
    on it, the attempt fails with user notification (a dialog saying
    that getting information from server failed). Proposed solution
    is to check Content-Disposition header in such resources, and in
    case it's attachment, disable lock on this resource. The rationale
    for this is that "In a regular HTTP response, the Content-Disposition
    response header is a header indicating if the content is expected
    to be displayed ... as an attachment, that is downloaded and saved
    locally" (see MDN:
    https://developer.mozilla.org/en/docs/Web/HTTP/Headers/Content-Disposition
    
    Also, Content::getProperties wasn't ready for PROPFIND returning
    empty result.
    
    [ port of commit fbc04c97231d629c1b5e9e57203dbe8d8eb06714 ]
    
    Change-Id: If0b4c10ef7b7b108a8779c773c65e25973d32b46
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/123504
    Tested-by: Michael Stahl <michael.st...@allotropia.de>
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/ucb/source/ucp/webdav-curl/webdavcontent.cxx 
b/ucb/source/ucp/webdav-curl/webdavcontent.cxx
index 974867e78685..75f9d0ca2ae9 100644
--- a/ucb/source/ucp/webdav-curl/webdavcontent.cxx
+++ b/ucb/source/ucp/webdav-curl/webdavcontent.cxx
@@ -1260,6 +1260,45 @@ uno::Reference< sdbc::XRow > Content::getPropertyValues(
     return uno::Reference<sdbc::XRow>(xRow);
 }
 
+namespace {
+void GetPropsUsingHeadRequest(DAVResource& resource,
+                              const std::unique_ptr< DAVResourceAccess >& 
xResAccess,
+                              const std::vector< OUString >& aHTTPNames,
+                              const uno::Reference< ucb::XCommandEnvironment 
>& xEnv)
+{
+    if (!aHTTPNames.empty())
+    {
+        DAVOptions aDAVOptions;
+        OUString   aTargetURL = xResAccess->getURL();
+        // retrieve the cached options if any
+        aStaticDAVOptionsCache.getDAVOptions(aTargetURL, aDAVOptions);
+
+        // clean cached value of PROPFIND property names
+        // PROPPATCH can change them
+        Content::removeCachedPropertyNames(aTargetURL);
+        // test if HEAD allowed, if not, throw, should be catched immediately
+        // SC_GONE used internally by us, see comment in 
Content::getPropertyValues
+        // in the catch scope
+        if (aDAVOptions.getHttpResponseStatusCode() != SC_GONE &&
+            !aDAVOptions.isHeadAllowed())
+        {
+            throw DAVException(DAVException::DAV_HTTP_ERROR, "405 Not 
Implemented", SC_METHOD_NOT_ALLOWED);
+        }
+        // if HEAD is enabled on this site
+        // check if there is a relevant HTTP response status code cached
+        if (aDAVOptions.getHttpResponseStatusCode() != SC_NONE)
+        {
+            // throws exception as if there was a server error, a DAV exception
+            throw DAVException(DAVException::DAV_HTTP_ERROR,
+                aDAVOptions.getHttpResponseStatusText(),
+                aDAVOptions.getHttpResponseStatusCode());
+            // Unreachable
+        }
+
+        xResAccess->HEAD(aHTTPNames, resource, xEnv);
+    }
+}
+}
 
 uno::Reference< sdbc::XRow > Content::getPropertyValues(
                 const uno::Sequence< beans::Property >& rProperties,
@@ -1449,117 +1488,89 @@ uno::Reference< sdbc::XRow > 
Content::getPropertyValues(
                     aHeaderNames.push_back( "Content-Type" );
                 }
 
-                if ( !aHeaderNames.empty() )
+                if (!aHeaderNames.empty()) try
                 {
-                    DAVOptions aDAVOptions;
-                    OUString   aTargetURL = xResAccess->getURL();
-                    // retrieve the cached options if any
-                    aStaticDAVOptionsCache.getDAVOptions( aTargetURL, 
aDAVOptions );
-                    try
-                    {
-                        DAVResource resource;
-                        // clean cached value of PROPFIND property names
-                        // PROPPATCH can change them
-                        removeCachedPropertyNames( aTargetURL );
-                        // test if HEAD allowed, if not, throw, will be 
catched immediately
-                        // SC_GONE used internally by us, see comment below
-                        // in the catch scope
-                        if ( aDAVOptions.getHttpResponseStatusCode() != 
SC_GONE &&
-                             !aDAVOptions.isHeadAllowed() )
-                        {
-                            throw DAVException( DAVException::DAV_HTTP_ERROR, 
"405 Not Implemented", SC_METHOD_NOT_ALLOWED );
-                        }
-                        // if HEAD is enabled on this site
-                        // check if there is a relevant HTTP response status 
code cached
-                        if ( aDAVOptions.getHttpResponseStatusCode() != 
SC_NONE )
-                        {
-                            // throws exception as if there was a server 
error, a DAV exception
-                            throw DAVException( DAVException::DAV_HTTP_ERROR,
-                                                
aDAVOptions.getHttpResponseStatusText(),
-                                                
aDAVOptions.getHttpResponseStatusCode() );
-                            // Unreachable
-                        }
-
-                        xResAccess->HEAD( aHeaderNames, resource, xEnv );
-                        m_bDidGetOrHead = true;
+                    DAVResource resource;
+                    GetPropsUsingHeadRequest(resource, xResAccess, 
aHeaderNames, xEnv);
+                    m_bDidGetOrHead = true;
 
-                        if (xProps)
-                            xProps->addProperties(
-                                aMissingProps,
-                                ContentProperties( resource ) );
-                        else
-                            xProps.reset ( new ContentProperties( resource ) );
+                    if (xProps)
+                        xProps->addProperties(
+                            aMissingProps,
+                            ContentProperties( resource ) );
+                    else
+                        xProps.reset ( new ContentProperties( resource ) );
 
-                        if ( m_eResourceType == NON_DAV )
-                            xProps->addProperties( aMissingProps,
-                                                   ContentProperties(
-                                                       aUnescapedTitle,
-                                                       false ) );
-                    }
-                    catch ( DAVException const & e )
+                    if (m_eResourceType == NON_DAV)
+                        xProps->addProperties(aMissingProps,
+                            ContentProperties(
+                                aUnescapedTitle,
+                                false));
+                }
+                catch ( DAVException const & e )
+                {
+                    // non "general-purpose servers" may not support HEAD 
requests
+                    // see 
http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1
+                    // In this case, perform a partial GET only to get the 
header info
+                    // vid. 
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
+                    // WARNING if the server does not support partial GETs,
+                    // the GET will transfer the whole content
+                    bool bError = true;
+                    DAVException aLastException = e;
+                    OUString aTargetURL = xResAccess->getURL();
+
+                    if ( e.getError() == DAVException::DAV_HTTP_ERROR )
                     {
-                        // non "general-purpose servers" may not support HEAD 
requests
-                        // see 
http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1
-                        // In this case, perform a partial GET only to get the 
header info
-                        // vid. 
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
-                        // WARNING if the server does not support partial GETs,
-                        // the GET will transfer the whole content
-                        bool bError = true;
-                        DAVException aLastException = e;
-
-                        if ( e.getError() == DAVException::DAV_HTTP_ERROR )
+                        // According to the spec. the origin server SHOULD 
return
+                        // * 405 (Method Not Allowed):
+                        //      the method is known but not allowed for the 
requested resource
+                        // * 501 (Not Implemented):
+                        //      the method is unrecognized or not implemented
+                        // * 404 (SC_NOT_FOUND)
+                        //      is for google-code server and for MS IIS 10.0 
Web server
+                        //      when only GET is enabled
+                        if ( aLastException.getStatus() == SC_NOT_IMPLEMENTED 
||
+                             aLastException.getStatus() == 
SC_METHOD_NOT_ALLOWED ||
+                             aLastException.getStatus() == SC_NOT_FOUND )
                         {
-                            // According to the spec. the origin server SHOULD 
return
-                            // * 405 (Method Not Allowed):
-                            //      the method is known but not allowed for 
the requested resource
-                            // * 501 (Not Implemented):
-                            //      the method is unrecognized or not 
implemented
-                            // * 404 (SC_NOT_FOUND)
-                            //      is for google-code server and for MS IIS 
10.0 Web server
-                            //      when only GET is enabled
-                            if ( aLastException.getStatus() == 
SC_NOT_IMPLEMENTED ||
-                                 aLastException.getStatus() == 
SC_METHOD_NOT_ALLOWED ||
-                                 aLastException.getStatus() == SC_NOT_FOUND )
-                            {
-                                SAL_WARN( "ucb.ucp.webdav", "HEAD probably not 
implemented: fall back to a partial GET" );
-                                aStaticDAVOptionsCache.setHeadAllowed( 
aTargetURL, false );
-                                lcl_sendPartialGETRequest( bError,
-                                                           aLastException,
-                                                           aMissingProps,
-                                                           aHeaderNames,
-                                                           xResAccess,
-                                                           xProps,
-                                                           xEnv );
-                                m_bDidGetOrHead = !bError;
-                            }
+                            SAL_WARN( "ucb.ucp.webdav", "HEAD probably not 
implemented: fall back to a partial GET" );
+                            aStaticDAVOptionsCache.setHeadAllowed( aTargetURL, 
false );
+                            lcl_sendPartialGETRequest( bError,
+                                                       aLastException,
+                                                       aMissingProps,
+                                                       aHeaderNames,
+                                                       xResAccess,
+                                                       xProps,
+                                                       xEnv );
+                            m_bDidGetOrHead = !bError;
                         }
+                    }
 
-                        if ( bError )
-                        {
-                            DAVOptions aDAVOptionsException;
-
-                            aDAVOptionsException.setURL( aTargetURL );
-                            // check if the error was SC_NOT_FOUND, meaning 
that the
-                            // GET fall back didn't succeeded and the element 
is really missing
-                            // we will consider the resource SC_GONE (410) for 
some time
-                            // we use SC_GONE because has the same meaning of 
SC_NOT_FOUND (404)
-                            // see:
-                            // 
<https://tools.ietf.org/html/rfc7231#section-6.5.9> (retrieved 2016-10-09)
-                            // apparently it's not used to mark the missing 
HEAD method (so far...)
-                            sal_uInt16 ResponseStatusCode =
-                                ( aLastException.getStatus() == SC_NOT_FOUND ) 
?
-                                SC_GONE :
-                                aLastException.getStatus();
-                            aDAVOptionsException.setHttpResponseStatusCode( 
ResponseStatusCode );
-                            aDAVOptionsException.setHttpResponseStatusText( 
aLastException.getData() );
-                            aStaticDAVOptionsCache.addDAVOptions( 
aDAVOptionsException,
-                                                                  
m_nOptsCacheLifeNotFound );
+                    if ( bError )
+                    {
+                        DAVOptions aDAVOptionsException;
+
+                        aDAVOptionsException.setURL( aTargetURL );
+                        // check if the error was SC_NOT_FOUND, meaning that 
the
+                        // GET fall back didn't succeeded and the element is 
really missing
+                        // we will consider the resource SC_GONE (410) for 
some time
+                        // we use SC_GONE because has the same meaning of 
SC_NOT_FOUND (404)
+                        // see:
+                        // <https://tools.ietf.org/html/rfc7231#section-6.5.9> 
(retrieved 2016-10-09)
+                        // apparently it's not used to mark the missing HEAD 
method (so far...)
+                        sal_uInt16 ResponseStatusCode =
+                            ( aLastException.getStatus() == SC_NOT_FOUND ) ?
+                            SC_GONE :
+                            aLastException.getStatus();
+                        aDAVOptionsException.setHttpResponseStatusCode( 
ResponseStatusCode );
+                        aDAVOptionsException.setHttpResponseStatusText( 
aLastException.getData() );
+                        aStaticDAVOptionsCache.addDAVOptions( 
aDAVOptionsException,
+                                                              
m_nOptsCacheLifeNotFound );
 
-                            if ( !shouldAccessNetworkAfterException( 
aLastException ) )
-                            {
-                                cancelCommandExecution( aLastException, xEnv );
-                                // unreachable
-                            }
+                        if ( !shouldAccessNetworkAfterException( 
aLastException ) )
+                        {
+                            cancelCommandExecution( aLastException, xEnv );
+                            // unreachable
                         }
                     }
                 }
@@ -1642,6 +1653,22 @@ uno::Reference< sdbc::XRow > Content::getPropertyValues(
                     uno::makeAny( aDate ),
                     true );
             }
+            // If WebDAV didn't return the resource type, assume default
+            // This happens e.g. for lists exported by SharePoint
+            else if ( (*it) == "IsFolder" )
+            {
+                xProps->addProperty(
+                    (*it),
+                    uno::makeAny( false ),
+                    true );
+            }
+            else if ( (*it) == "IsDocument" )
+            {
+                xProps->addProperty(
+                    (*it),
+                    uno::makeAny( true ),
+                    true );
+            }
         }
     }
 
@@ -2981,8 +3008,6 @@ Content::ResourceType Content::resourceTypeForLocks(
         std::unique_ptr< ContentProperties > xProps;
         if (m_xCachedProps)
         {
-            std::unique_ptr< ContentProperties > xCachedProps;
-            xCachedProps.reset( new ContentProperties(*m_xCachedProps) );
             uno::Sequence< ucb::LockEntry > aSupportedLocks;
             if ( m_xCachedProps->getValue( DAVProperties::SUPPORTEDLOCK )
                  >>= aSupportedLocks )            //get the cached value for 
supportedlock
@@ -3073,6 +3098,42 @@ Content::ResourceType Content::resourceTypeForLocks(
                                 }
                             }
                         }
+                        else
+                        {
+                            // PROPFIND failed; check if HEAD contains 
Content-Disposition: attachment (RFC1806, HTTP/1.1 19.5.1),
+                            // which supposedly means no lock for the resource 
(happens e.g. with SharePoint exported lists)
+                            OUString sContentDisposition;
+                            // First, check cached properties
+                            if (m_xCachedProps)
+                            {
+                                if 
((m_xCachedProps->getValue("Content-Disposition") >>= sContentDisposition)
+                                    && 
sContentDisposition.startsWithIgnoreAsciiCase("attachment"))
+                                {
+                                    eResourceTypeForLocks = DAV_NOLOCK;
+                                    wasSupportedlockFound = true;
+                                }
+                            }
+                            // If no data in cache, try HEAD request
+                            if (sContentDisposition.isEmpty() && 
!m_bDidGetOrHead) try
+                            {
+                                DAVResource resource;
+                                GetPropsUsingHeadRequest(resource, rResAccess, 
{"Content-Disposition"}, Environment);
+                                m_bDidGetOrHead = true;
+                                for (const auto& it : resource.properties)
+                                {
+                                    if 
(it.Name.equalsIgnoreAsciiCase("Content-Disposition"))
+                                    {
+                                        if ((it.Value >>= sContentDisposition) 
&& sContentDisposition.equalsIgnoreAsciiCase("attachment"))
+                                        {
+                                            eResourceTypeForLocks = DAV_NOLOCK;
+                                            wasSupportedlockFound = true;
+                                        }
+                                        break;
+                                    }
+                                }
+                            }
+                            catch (...){}
+                        }
                         // 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
diff --git a/ucb/source/ucp/webdav-curl/webdavcontentcaps.cxx 
b/ucb/source/ucp/webdav-curl/webdavcontentcaps.cxx
index e3ece1b3d8fc..f4bef01c631b 100644
--- a/ucb/source/ucp/webdav-curl/webdavcontentcaps.cxx
+++ b/ucb/source/ucp/webdav-curl/webdavcontentcaps.cxx
@@ -319,10 +319,13 @@ uno::Sequence< beans::Property > Content::getProperties(
                     props = aPropsNames.getPropertiesNames();
                 }
 
-                // Note: vector always contains exactly one resource info, 
because
+                // Note: vector should contain exactly one resource info, 
because
                 //       we used a depth of DAVZERO for PROPFIND.
-                aPropSet.insert( (*props.begin()).properties.begin(),
-                                 (*props.begin()).properties.end() );
+                if (props.size() == 1)
+                {
+                    aPropSet.insert( (*props.begin()).properties.begin(),
+                                     (*props.begin()).properties.end() );
+                }
             }
             catch ( DAVException const & )
             {

Reply via email to